ssg-api
TypeScript API to generate a static website.
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 context = new SsgContextImpl("fr")
const ssg = new Ssg(config).add(firstStep).add(nextStep)
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 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
Steps
The Ssg execute a number of Steps, sequentially.
A Step can do anything and return its own results, 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)
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,
so its repository is a good place to find examples. RR0:
- implemented a TimeReplaceCommand to replace
<time>yyyy-mm-dd hh:mm</time>
tags with links to a page about that very date. - implemented 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. - implemented a OutlineReplaceCommand to insert an outline of the current HTML page.
- implemented a LinkReplaceCommand to insert navigation links matching the
"start"
, "contents"
, "prev"
and "next"
relationships of the current HTML page. - runs a CopyStep to copy images in the output dir;
- implemented a RR0SsgContext specializes
SsgContextImpl
aht adds access to locale-specific messages and time context. - implemented a AnchorReplaceCommand to add trailing slash to links and a
target="_blank"
if the url is outside of the current website. - implemented CaseDirectoryStep and PeopleDirectoryStep to generate live indexes of UFO cases and people subdirectories.
- initializes a Ssg config with the abovementioned replacements and runs it in its build app.