Static site generator powered by
webpack
bundling,handlebars
templating andbrowser-sync
live-reloading.
- Overview
- Installation
- CLI interface
- API interface
- Development mode
- Source and Destination directories
- Stages
- Entrypoint files
- Target browsers
- Babel
- Handlebars
- Webpack
- Metalsmith
- Developing m9-generator
m9-generator is static site generator relying on handlebars
for static files templating, webpack
for JavaScript and CSS bundling and browser-sync
for local live-reload development server.
m9-generator follows future-focused approach and provides support to new standard solutions when handling CSS and JavaScript.
- JavaScript: Stage3
- CSS: Stage2, nesting-rules, custom-media-queries
NB! Use
yarn
instead ofnpm
. m9-generator depends onpeerDependencies
inpackage.json
and so far onlyyarn
installs them.
NB! It is assumed that per-site directory layout with package.json is used in this documentation examples. Use
yarn init
to create one.
Install @tsertkov/m9-generator
as your local depencency:
$ yarn add @tsertkov/m9-generator
Add following scripts
in your site package.json
:
"scripts": {
"m9": "m9",
"test": "m9 test",
"dev": "m9 dev",
"build": "m9 build"
}
To run m9-generator tasks with yarn
locally
$ yarn test
$ yarn run dev
$ yarn run build
$ yarn run m9
$ yarn run m9 <task>
m9-generator comes with m9
command line tool to execute gulp
tasks.
Run yarn run m9
to execute default help task printing complete list of available tasks to run with yarn run m9 <task>
.
There are tasks and sub-tasks in m9-generator. Tasks are always a groups of sub-tasks.
Main tasks are:
build
: build static site files into destination directorydev
: continuously build and live-reload site with local development servertest
: test site
Run ./node_modules/.bin/m9
to see default help screen:
Main Tasks
------------------------------
build
dev
help
test
Sub Tasks
------------------------------
build-clean
build-copy
build-metalsmith
build-webpack
dev-browsersync
dev-watch
test-standard
Run ./node_modules/.bin/m9 --tasks
to see complete task tree generated by gulp
.
[13:23:38] ├── build-clean
[13:23:38] ├── build-copy
[13:23:38] ├── build-metalsmith
[13:23:38] ├── build-webpack
[13:23:38] ├── dev-browsersync
[13:23:38] ├── dev-watch
[13:23:38] ├── help
[13:23:38] ├── test-standard
[13:23:38] ├─┬ build
[13:23:38] │ └─┬ <series>
[13:23:38] │ ├── build-clean
[13:23:38] │ ├── build-copy
[13:23:38] │ ├── build-webpack
[13:23:38] │ └── build-metalsmith
[13:23:38] ├─┬ dev
[13:23:38] │ └─┬ <series>
[13:23:38] │ ├── build-clean
[13:23:38] │ ├── build-copy
[13:23:38] │ ├── dev-browsersync
[13:23:38] │ ├── build-metalsmith
[13:23:38] │ └── dev-watch
[13:23:38] ├─┬ test
[13:23:38] │ └─┬ <series>
[13:23:38] │ └── test-standard
[13:23:38] └─┬ default
[13:23:38] └─┬ <series>
[13:23:38] └── help
m9-generator exports preconfigured gulp
instance. See gulp API docs for details.
Example using m9-generator API:
const m9 = require('@tsertkov/m9-generator')()
m9.series('build')(err => {
if (err) {
console.log('error happened', err)
} else {
console.log('build done')
}
})
yarn run dev
executes BrowserSync serving static files from build directory connected to webpack-dev-middleware for dynamic asset serving.
Look for BrowserSync details in output:
[Browsersync] Access URLs:
--------------------------------------
Local: https://localhost:3000
External: https://192.168.0.1:3000
--------------------------------------
UI: http://localhost:3001
UI External: http://192.168.0.1:3001
--------------------------------------
[Browsersync] Serving files from: /Users/example/Projects/example.com/build
Updates to most of the src/**/*
files must trigger recompilation and live-reload updated pages opened in browsers.
Run yarn run test
to execute automated tests for current site.
m9
works by reading input from files in source directory, builds site, then persists files in destination directory. By default source and destination directories are ./src
and ./build
accordingly.
Directories can be set with command line argument --src=<SRC>
, --dst=<DST>
or environment variable SRC
and DST
.
Stage defaults to development
and defines environment current site is building for.
Stage can be set with command line argument --stage
or STAGE
environment variable:
STAGE=staging yarn run build
yarn run build --stage=staging
m9-generator uses metalsmith
to build static files and webpack
to bundle JavaScript and CSS. Both are relying on entrypoint files as their input.
Each file matched by <SRC>/pages/**/*.hbs
pattern is processed by metalsmith
resulting in one or many files build in destination directory. All other files in <SRC>/pages/**/*
are copied as is.
Each file matched by <SRC>/scripts/*.js|.css
pattern is bundled in corresponding JS or CSS file in <DST>/assets
directory.
Webpack loaders for JavaScript and CSS are respecting configured target browsers defined in .browserslistrc
file.
NB! The most pragmatic browserlist query is used by default when
.browserslistrc
file is not given.
Two different babel transformation processes are running in m9-generator. One is done by @babel/register
transpiling files on the fly for current node version using require hook. Another by webpack bundling client-side package with babel-loader
.
Both transformations use the same presets @babel/preset-stage-3
and @babel/preset-env
, but differ in targets for preset-env
.
Handlebars is simple, powerful and extendable templating system. Custom Partials and Helpers allow building reusable components with ease. Read more on https://handlebarsjs.com/.
By default handlebars partials are loaded from <SRC>/partials/*.hbs
. Read more about handlebars partials on https://handlebarsjs.com/partials.html.
Layouts are implemented by native handlebars partial blocks.
Layout example <SRC>/partials/layout-default.hbs
:
Page template example <SRC>/pages/index.html.hbs
:
Helpers are loaded with <SRC>/helpers/*.js
pattern.
These are native JavaScript files and must use module.exports = (content) => 'output string'
format to enable dynamic loading.
188 helpers from handlebars-helpers
are available to handlebars engine. Before writing your own first consider searching similar in handlebars-helpers.
Webpack searches for entry points in <SRC>/scripts/*.js
and <SRC>/styles/*.css
and writes files in <DST>/assets/
.
There is <DST>/assets/manifest.json
file with assets manifest pre-loaded into templating context as __assets
variable.
development
mode is enabled when stage === 'development'
is true. Otherwise production
mode is active. Different optimizations and loader settings are enabled based on active mode.
*.hbs
files are converted to compiled handlebars template functions including resolved helpers, partials and runtime automatically.
import itemCard from '../../partials/item-card.hbs'
// ..
const item = { key: "value" }
const html = itemCard(item)
// ..
Read more about handlebars-loader
at https://github.com/pcardune/handlebars-loader#readme.
*.js
files are processed by babel using browsers target defined in .browserslistrc
or default one for browsers.
Ream more about babel-loader
at https://github.com/babel/babel-loader#readme.
*.css
files are processed by PostCSS with postcss-preset-env
.
Read more about postcss-loader
at https://github.com/postcss/postcss-loader#readme.
In development
mode webpack-visualizer-plugin is enabled and results are avalable at https://localhost:3000/webpack-visualizer/
or other domain/port you use locally.
Metalsmith compiles static files found in <SRC>/pages/
into <DST>/
directory. It extracts front-matter headers and passes *.hbs
files through Handlebars templating system.
m9-generator builds single configuration object internally. This object can be modified per site by exporting configuration function in <SRC>/config.js
.
Stage aware configuration file example:
const config = {
default: {
apiEndpoint: 'https://staging-api.example.com'
},
development: {
site_url: 'https://localhost:3000'
},
staging: {
site_url: 'https://staging.example.com'
},
production: {
site_url: 'https://example.com',
apiEndpoint: 'https://api.example.com'
}
}
module.exports = m9config => Object.assign(
m9config,
Object.assign(
{},
config.default,
config[m9config.stage]
)
)
Metalsmith is parsing front-matter headers in template files using ---
as delimiter. Front-matter content must be in TOML format. It is used to control metalsmith plugins and overwrite data context variables.
m9-generator preloads data context and exponses it to Handlebars templating. There are two types of input data: static content in form of json files and dynamic content as JavaScript files.
System variables avaialble in template context are prefixed with __
.
Variable name | Description |
---|---|
__assets |
Webpack assets manifest: asset-name -> asset-filename |
__config |
Configuration object |
Static content is loaded from JSON files found in <SRC>/content/static
directory.
For example following static content files in content/static
directory:
// content/static/user.json
{
[
"name": "user1",
"email": "user1@example.com",
"group": [{
"ID": 1
}
],
[
"name": "user2",
"email": "user2@example.com",
"group": [{
"ID": 2
}
]
}
// content/static/group.json
{
[
"id": 1,
"name": "group1"
],
[
"id": 2,
"name": "group2"
]
}
// content/static/site.json
{
"adminEmail": "admin@example.com",
"title": "site title",
"description": "site description"
}
This content is accessible in handlebars templates as variables: user
, group
and site
accordingly to JSON file names.
It is possible to define dynamic (programmable) content and expose it to template data. Each JavaScript file in <SRC>/content/dynamic
must follow pattern <content-type>.js
and export function of a form:
module.exports = function (proto, content, config) {
// do something with the proto (it is actual entity obj in fact)
Object.defineProperty(proto, 'content', {
configurable: true,
enumerable: true,
get: () => {
// return smth
return 'empty value'
}
})
return proto
}
Augmenting initial content loaded from static JSON files with dynamic functions is a common integration method. In cases when static content of the same content type is not found new empty object is passed as proto
to a content function.
All files and folders from <SRC>/public/
directory are copied to destination <DST>
directory. This is a good place to put static image files, favicons, etc.
Gulp tasks found matching <SRC>/tasks/*.js
are automatically registrered and available via m9 cli.
For example <SRC>/tasks/custom-task.js
to define custom-task
:
module.exports = async function customTask (config) {
console.log('Custom task test')
}
JavaScript sources of m9-generator are transpiled with babel. Sources are located in src/
directory. Compiled files are placed in dist/
.
Run npm run build-dist
to compile files in dist/
.
By default m9 runs from dist/
directory. To run from src/
during development pass --m9-use-src
command line argument.
Documentation site is embedded with m9-generator. Its source directory is docs-src/
and destination docs/
.
Run npm run dev
to start development server for documentation site.
Run npm link
inside m9-generator local repo to link it from global node_modules. To link dev version of m9-generator in site project run npm link @tsertkov/m9-generator
.
For example assume having ~/m9-generator
and ~/my-example-site
with generator repo and site repo accordingly. Then to run example-site using m9-generator from ~/m9-generator
do the following:
$ cd ~/m9-generator
$ npm link
$ cd ~/my-example-site
$ npm link @tsertkov/m9-generator
$ npm run dev --m9-use-src
Use npm version
command to publish new version of npm package and push new git tag to remote.