ssg-api
TypeScript API to generate output files from input files.
It can be used to generate:
- a static website from HTML templates (but those templates can include client-side JavaScript and CSS of course).
- (and/or) other files such as configuration files (for instance convert an
.htaccess
file to a netlify.toml
file)
Table of contents
Setup
Install ssg-api
as a project dependency:
npm install --save ssg-api
Then import the required types to implement your own SSG code:
import {Ssg, SsgContextImpl, SsgConfig} from "ssg-api"
const config: SsgConfig = {outDir: "out"}
const ssg = new Ssg(config)
.add(firstStep)
.add(nextStep)
const context = new SsgContextImpl("fr")
try {
const result = await ssg.start(context)
console.log("Completed", result)
} catch (e) {
console.error(err, context.inputFile.name, "=>", context.outputFile.name)
}
Testing
ssg-api
is a provided as a native ESM package, so:
For instance, Jest will require some specifics in its jest.config.js
to transform the package code (here as ts-jest config):
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
transformIgnorePatterns: ["node_modules/(?!ssg-api)"],
transform: {
"^.+\\.[tj]s$": "ts-jest"
}
}
Concepts
Steps
The Ssg execute a number of Steps, sequentially.
A Step can do anything and return its own results.
Some predefined steps are provided:
Example:
import {SsgConfig, SsgContextImpl, Ssg, ContentStep, CopyStep} from "ssg-api"
const config: SsgConfig = {outDir: "out"}
const context = new SsgContextImpl("fr")
new Ssg(config)
.add(new ContentStep(contentConfigs, outputFunc))
.add(dir1SubdirectoriesStep)
.add(dir2SubdirectoriesStep)
.add(...anArrayOfSteps)
.add(new CopyStep(copiesToDo))
.start(context)
You can create your own steps by implementing the SsgStep interface.
ContentStep
A ContentStep is parameterized by its ContentStepConfig
s.
Each of these configs specifies how the ContentStep will:
- gather each file from its specified
roots
. - execute its
replacements
on each of them, until the file doesn't change anymore. - saves the modified file contents in according to its
outputSpec
.
Replacements
ContentStep replacements are classes implementing the ReplaceCommand
interface,
so you can define your own replacements.
A number predefined replace commands are also available:
- Abstract classes mutualize the basics of different replacement techniques:
- Concrete classes specialize those techniques to achieve concrete goals, such as:
and others in the repository (you'll find a number of SSI ones because RR0 used to rely on them).
const contentConfigs: ContentStepConfig[] = [
{
roots: [".htaccess"],
replacements: [new HtAccessToNetlifyConfigReplaceCommand("https://rr0.org/")],
getOutputFile(context: SsgContext): FileInfo {
return getFileInfo(context, "netlify.toml", "utf-8")
}
},
{
roots: ["index.html", "404.html", "pages/**/*.html"],
replacements: [
new SsiIncludeReplaceCommand(),
new TitleReplaceCommand(),
new StringEchoVarReplaceCommand("mail"),
new AngularExpressionReplaceCommand(),
new SsiEchoVarReplaceCommand("copyright"),
new SsiIfReplaceCommand(),
new SsiSetVarReplaceCommand("title", (match: string, ...args: any[]) => `<title>${args[0]}</title>`),
new SsiLastModifiedReplaceCommand(context.time.options),
new AuthorReplaceCommand(),
new HtmlTagReplaceCommand("time", new MyTimeReplacerFactory()),
new ClassRegexReplaceCommand("people", new MyPeopleClassReplacerFactory()),
new ClassRegexReplaceCommand("part(.?)", new MyPartXReplacerFactory()),
new LinkReplaceCommand(),
new AnchorReplaceCommand("https://my.base.url/")
],
getOutputFile(context: SsgContext): FileInfo {
return context.inputFile
}
}
]
new Ssg(config)
.add(new ContentStep(contentConfigs, outputFunc))
.start(context)
.then(result => console.log("Completed", result))
.catch(err => console.error(err, context.inputFile.name, "=>", context.outputFile.name))
FileInfo
Ssg manipulates files through a FileInfo
types, which contain:
- the
name
of the file (including relative path) - the detected (or specified)
encoding
of the file contents (utf8
, latin1
, etc.) - the
contents
of the file, as a string - the
lastModified
Date of the file - the detected (or specified)
lang
uage of the file contents (file_fr.txt
will imply french for instance).
You can:
- get one for an existing (likely input) file using
FileInfo.read(context, fileName, encoding?)
- get or create in memory (if it doesn't exist) using
FileInfo.readOrNew(context, fileName, encoding?)
- save its (likely output) contents it in the output directory, using
context.outputFile.write()
HtmlFileInfo
HTML files automatic parsing will provide additional properties:
title
will contain the value of the <title>
tag, if anymeta
will contain values of url
, copyright
and author
meta tags (a repeated author
meta tag will result in an array of author strings)links
will contain values of start
, contents
, prev
and next
relationships
Context
A SsgContext is provided to Ssg methods (SsgStep.execute(content)
and ReplaceCommand.execute(context)
) to carry information about :
- the current
locale
(s) - the current
inputFile
that has been read - the current
outputFile
that is about to be written - current values of variables values (through
getVar()
) that may have been set by current or previous replacements (through setVar()
).
It also provides utility logging methods (log()
/warn()
/error()
/debug()
) and a clone()
method.
You can create your own context by implementing the SsgContext interface
(typically to provide custom info to custom steps).
Examples
ssg-api
has been developed to generate the RR0 website,
so its repository is a good place to find examples.
A good place to start is to look at RR0's build source code to see
how it initializes and run its configuration, comprised of:
- a TimeReplaceCommand to replace
<time>yyyy-mm-dd hh:mm</time>
tags with links to a page about that very date. - a PlaceReplacer to be used through a
ClassDomReplaceCommand("place", new PlaceReplacerFactory(placeService))
to replace <span class="place">Paris (France)</span>
tags with a clickable tag to display the map of the mentioned place. - a OutlineReplaceCommand to insert an outline of the current HTML page.
- a LinkReplaceCommand to insert navigation links matching the
"start"
, "contents"
, "prev"
and "next"
relationships of the current HTML page. - a CopyStep to copy images in the output dir;
- a RR0SsgContext specializes
SsgContextImpl
aht adds access to locale-specific messages and time context. - a AnchorReplaceCommand to add trailing slash to links and a
target="_blank"
if the url is outside of the current website. - a CaseDirectoryStep and PeopleDirectoryStep to generate live indexes of UFO cases and people subdirectories.