ssg-api
TypeScript API to generate a static website.
Setup
Install the 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 context = new SsgContextImpl("fr")
new Ssg(config)
.add(someStep)
.add(otherStep)
.start(context)
.then(result => console.log("Completed", result))
.catch(err => console.error(err, context.inputFile.name, "=>", context.outputFile.name))
Testing
No that this is a native ESM package so it may not be supported by all test frameworks out of the box.
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
Step
The Ssg execute a number of Steps, sequentially.
A Step can do anything, but here are some pre-defined steps:
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)
.then(result => console.log("Completed", result))
.catch(err => console.error(err, context.inputFile.name, "=>", context.outputFile.name))
You can create your own steps by implementing the SsgStep interface.
Context
A SsgContext is provided to Ssg methods 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).
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.
A number of concrete and abstract predefined replace commands are available.
(you'll find a number of SSI ones because RR0 used to rely on them).
and others in the repository.
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))
Examples
ssg-api has been developed to generate the RR0 website,
soits repository is a good place to find examples:
- TimeReplaceCommand replaces
<time>yyyy-mm-dd hh:mm</time>
tags with links to a page about that very date. - PlaceReplacer uses a new
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. - the website build app initializes a Ssg config with the abovementioned replacements and runs it.
- the RR0SsgContext specializes
SsgContextImpl
to add access to locale-specific messages and time context.