This package allows you to easily build a documentation site with Webpack/Markdown/EJS. It provides:

Usage no npm install needed!

<script type="module">
  import seamlyDocSite from 'https://cdn.skypack.dev/@seamly/doc-site';


Seamly Doc Site builder

This package allows you to easily build a documentation site with Webpack/Markdown/EJS. It provides:

  • A Webpack config transformer (lib/config/site.js) with:
    • Support for loading site data from PROJECT_PATH/site.conf.js
    • Support for pre-configured loaders
  • A EJS Webpack loader (loaders/ejs-layout-loader.js) that supports:
    • Frontmatter (in combination with the frontmatter-markdown-loader)
    • Layouts
    • Loading of templates from this package
    • Table of content generation
  • A HTML URL rewrite loader (loaders/html-url-rewrite-loader.js) that supports:
    • Rewriting links (it will resolve links so it will work with webpack publicPaths)
    • Requireable resources (<img src=""> and <link href="~..."> only)
  • A set of EJS includes, layout and a default stylesheet to create Seamly documentation sites.

Setting up your site

Some assumptions:

  • You use Webpack 4.x
  • You have directory from which you serve your public files. We use public in the examples below

In your webpack.config.js:

const site = require('@seamly/doc-site/lib/config/site')

const SITE_ROOT = path.resolve(__dirname, 'public')

// Your usual webpack setup.
module.exports = function(env = {}, argv = {}) {
  const config = {}

  return site.addSiteConfig(config, {siteRoot: SITE_ROOT})

In your public folder add markdown (.md), HTML (.html) or EJS (.ejs) files. Add some front-matter to define what layout to use.

layout: doc.ejs
toc: false
pageTitle: My page

# This is my page

Additionally you can setup a site.conf.js which should expose an object as in the example below. It will be used in the layout/templates.

module.exports =  {
  // Title of the site (optional, default is name from package.json)
  title: "My Site",

  // Subtitle (optional, default is version from package.json)
  subTitle: "by me",

  // Extra items to render in the HEAD tag (optional)
  head: [
    "<script src='/lib/helpers.js'></script>"

  // The navigation tree. Can only be 2 levels deep (rest is ignored)
  // The URL in the toplevel is optional
  navigation: [
      title: "Demos",
      children: [
          title: "Demo 1",
          url: "/demos/demo1.html"
          title: "Demo 2",
          url: "/demos/demo2.html"
      title: "Versions",
      url: "/versions"

The nitty gritty

Config transformer


The addSiteConfig config transform will take your Webpack config as first parameter and will add the following:

  • Get all .md, .html and .ejs files within the provided siteRoot matching exclude and include and add them as entries to the webpack config.
  • Rules for:
    • Handling image/font files matching exclude and include
    • Handling .scss files within the @seamly/doc-site package (make sure you exclude them if you have another rule matching .scss files)
    • Handling .html and .ejs files matching exclude and include
    • Handling .md files matching exclude and include
  • Read package.json and expose version, name and description to templates in the data.pkg key
  • Read site.conf.js and expose it to templates in the data.site key

You can pass the following options:

  • siteRoot (String, required): the directory where your site resides
  • templateGlobals (Object): an object of extra properties to expose to the templates in the data key
  • outputName (String,Function): Function or string passed to file-loader
  • exclude (String,Array): Passed to webpack rules
  • include (String,Array): Passed to webpack rules


Loader chain to use for processing HTML and emitting HTML to files.


Loader chain to use for processing EJS files with markdown support. Uses htmlExtractLoaders to emit files.


Loader chain to use for processing markdown files, will wrap with layout. Will not emit files.


Loader chain to use for processing .scss Sass files


This loader does 3 things:

  • Optionally execute the source
  • getFrontMatter and getContent to fetch the actual content and metadata
  • Load a layout file if it exists (uses frontmatter.layout or options.defaultLayout if not defined)
  • Generate a TOC of the content only (based on tags) if options.toc=true
  • Wrap the content in the loaded layout by string replace
  • Execute the content + layout as one .ejs template with:
    • data.frontMatter
    • data.toc (if generated)

Options for this loader are:

  • exec (boolean): Wether or not we should execute the module
  • contentPlaceholder (String): The replacement string to inject the content into the layout
  • layoutPath (Array): Path where to find layouts
  • globals (Object): Data to merge into top level data
  • toc (boolean): Should we generate TOC?
  • getFrontMatter (Function): What to use to extract the frontmatter
  • getContent (Function): What to use to get the content
  • defaultLayout (String, null): The defaul layout to us


The html-url-rewrite-loader is a (limited) drop-in replacement for the HTML loader it will:

  • require and potentially inline all img[src] attributes
  • require link[rel=stylesheet][href^="~"]
  • will rewrite local URL's in a[href], link[rel=stylesheet][href] and script[src]