@static-pages/cli v3.0.1
Static Pages / CLI
This package is the static pages generator command-line tool.
It does not bundle readers or writers, you must install them separately.
Globally installed readers and writers are supported, just set the NODE_PATH env variable to point to your global npm packages folder. Run npm root -g to find where it is.
Yet another static pages generator? Yes! Because I browsed the whole jamstack scene, but could not find one which 1. uses MVC pattern 2. can read input from any source (YAML, JSON, front-matter style markdowns, database etc.) 3. can render with any template engine (Twig, ejs, Pug, Mustache etc.) 4. supports incremental builds 5. has a flexible CLI tool (see @static-pages/cli on npm) 6. has a Docker image (see staticpages/cli on dockerhub) 7. written in JS (preferably TypeScript) 8. easy to extend with JS code 9. learning and using is easy (Gatsby, Hugo, Jekyll, Eleventy etc. are so cool but harder to learn and configure)
And because I wrote a ton of custom static generators before; I tought I can improve the concepts to a point where its (hopefully) useful for others.
Where should I use this?
This project targets small and medium sized projects. The rendering process tries to be as fast as possible so its also useful when you need performance.
Documentation
Usage
Show information about the command line arguments:
$ staticpages --helpThere are two usage patterns available, one is to create a configuration file and load that, alternatively you can supply the configuration via command line arguments.
Load configuration from a file:
$ staticpages [-c|--config <file>]Example:
$ staticpages --config staticpages.yamlSet configuration with CLI options:
$ staticpages [--from <package>]
[--from.module <name>]
[--from.export <name>]
[--from.args.* <value>]
[--to <package>]
[--to.module <name>]
[--to.export <name>]
[--to.args.* <value>]
[--controller <package>]
[--controller.module <package>]
[--controller.export <name>]
[--variables.* <value>]Example:
$ staticpages --from.module @static-pages/markdown-reader \
--from.args.cwd pages \
--to.module @static-pages/twig-writer \
--to.args.view content.html.twig \
--controller ./controllers/my-pages-controller.jsTip: This tool uses .env files. Controlles can access these env variables in the usual way via
process.env.
Configuration file format
Can be in JSON or YAML format. It must contain one or more Route entries (in array or simply as one object).
A Route defines
- a data source (
from), - a controller (
controller) where your data can be transformed and - a destination (
to) which will render the final page.
Additional variables can be added to the
Route, these properties will be accessible in thecontrollervia thethiscontext.
Formally:
interface Route {
from: string | {
module: string;
export?: string;
args?: unknown;
};
to: string | {
module: string;
export?: string;
args?: unknown;
};
controller?: string | {
module: string;
export?: string;
};
variables?: {
[additionalProps: string]: unknown;
};
}The from property can be a string to an npm package or a local commonjs module OR an object with the following keys:
from.moduleis a string to an npm package or a local commonjs module. The module must export a factory function that returns anIterableor anAsyncIterable.from.exportdefines the exported factory function name. Defaults to use thedefaultexport, if not exists then fallbacks tomodule.exports.from.argsis passed to the reader factory function as arguments. If args is not an array, it is converted to an array.
The to property can be a string to an npm package or a local commonjs module OR an object with the following keys:
to.moduleis a string to an npm package or a local commonjs module. The module must export a factory function that returns a(data: object): voidfunction.to.exportdefines the exported factory function name. Defaults to use thedefaultexport, if not exists then fallbacks tomodule.exports.to.argsis passed to the writer factory function as arguments. If args is not an array, it is converted to an array.
The controller property can be a string to an npm package or a local commonjs module OR an object with the following keys:
controller.moduleis a string to an npm package or a local commonjs module.controller.exportdefines the exported factory function name. Defaults to use thedefaultexport, if not exists then fallbacks tomodule.exports.
The variables contains additional properties that should be accessible from the controller context (as this.<variable>).
Sample with a single route
from:
module: '@static-pages/markdown-reader'
args:
cwd: pages
pattern: '**/*.md'
to:
module: '@static-pages/twig-writer'
args:
view: content.html.twig
viewsDir: path/to/views/folder
outDir: path/to/output/folder
controller: ./controllers/my-controller.jsSample with multiple routes
- from: '@static-pages/markdown-reader'
to:
module: '@static-pages/twig-writer'
args:
view: content.html.twig
viewsDir: path/to/views/folder
outDir: path/to/output/folder
controller: ./controllers/my-pages-controller.js
- from:
module: '@static-pages/yaml-reader'
args:
cwd: home
pattern: '*.yaml'
to:
module: '@static-pages/twig-writer'
args:
view: home.html.twig
viewsDir: path/to/views/folder
outDir: path/to/output/folder
controller: ./controllers/my-home-controller.jsTip: Controllers can be stored along with the *.md/*.yaml/data files in your local repository.
Advanced from.args and to.args parsing
Sometimes reader and writer args requires a callback function or some complex data structure that is available in an external JS file, eg. the view argument of the @static-pages/twig-writer can accept a function.
In these cases we introduce naming conventions on property names to allow this cli tool to parse and provide the needed objects.
These naming conventions are appiled recursively on each property name of the entire args object.
Functions
Functions can be parsed from string and loaded in the following way: append :function keyword to the property name of the target propery.
to:
module: '@static-pages/twig-writer'
args:
view:function: (d) => d.layoutThe :function keyword will be stripped from the property name and the string value will be converted to a function before calling the writer factory (twigWriter(args) in this case).
Importing from JS files
Append :import keyword to the propery name to import from JS files.
Provide the value as a string (import that module with the default import) or as an object with module and export properties. The export property is optional and defaults to default.
to:
module: '@static-pages/twig-writer'
args:
globals:import:
module: ./myglobals.js
export: globals// myglobals.js
module.exports = {
globals: {
myProp: 'myValue'
}
};This way to.args.globals will contain the { myProp: 'myValue' } object.
Stop the parsing on specific objects
There are some cases where parsing the configuration can be an unwanted feature.
In these cases append the :raw keyword to your property name and the given value will be preserved as-is, parsing stops on that level.
to:
module: '@static-pages/twig-writer'
args:
globals:raw:
my:function: some valueIn this case the to.args.globals will be { 'my:function': 'some value' }. Notice the :function postfixed property is kept as-is.
Do you really need this CLI tool?
Its possible to use the @static-pages/core package alone for automated builds if you keep your configuration in a js file. This eliminates the complexity of this CLI tool.
Usually the recommended production setup is to use the JS API and ignore this CLI tool.
See below an example, start it like node generate.js:
// ./generate.js
const { staticPages } = require('@static-pages/core');
const { markdownReader } = require('@static-pages/markdown-reader');
const { twigWriter } = require('@static-pages/twig-writer');
staticPages([{
from: markdownReader({
cwd: 'pages',
pattern: '**/*.md',
}),
to: twigWriter({
view: 'content.html.twig',
viewsDir: 'path/to/views/folder',
outDir: 'path/to/output/folder',
}),
controller: function(data) {
data.commitHash = yourGetCommitHashFn();
return data;
}
}]).catch(e => { console.error(e); process.exit(1); });Missing a feature?
Create an issue describing your needs. If it fits the scope of the project I will implement it or you can implement it your own and submit a pull request.
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago