Spec-Up is a technical specification development tool that enables you to create rich specification documents for standards bodies and engineering projects by writing in an extended version of markdown that features all the bells and whistles - for example: advanced syntax highlighting, notice blocks, complex tables, charts, UML diagrams, and more.
Installing Spec-Up is easy peasy lemon squeezy:
-
Node.js 18+ and
npmare required to run spec-up. WSL2 users should look here for specific instructions. -
Run
npm install spec-upin the root directory of the repo to install all dependencies. -
Create a
specs.jsonfile in the root folder of your repository to specify configuration values used to render your spec documents. The following fields are currently supported:plugins(ARRAY, optional) - Plugins that should run for every spec in the project. These use the same plugin entry formats described below in the Plugins section.specs(ARRAY, required) - Thespecsarray contains descriptor objects for each spec you are generating in your project:-
spec_directory(STRING, required) - The repo-root-relative location of your spec's markdown file directory. If you do not providemarkdown_paths, Spec-Up readsspec.mdfrom this directory by default. -
title(STRING, required) - The title rendered in the generated document's H1 text and page title. -
logo(PATH/URI STRING, optional) - A logo asset path or URI. -
logo_link(URI STRING, optional) - The URI you want your logo to point to in the rendered page. -
markdown_paths(ARRAY, optional) - If you want to use files other thanspec.md, list them here in the order you want them assembled. Paths are resolved relative tospec_directory. -
katex(BOOLEAN, optional) - Enables TeX support via KaTeX. KaTeX assets are bundled automatically from the installed dependency. -
output_path(STRING, optional) - If you want the generated spec document written somewhere other thanspec_directory(for example the project root for GitHub Pages publishing), specify another repo-root-relative path here. -
source(OBJECT, optional) - Configures repo-specific data used by repo-aware UI features. Today this supports GitHub repositories viahost,account, andrepo:{ "host": "github", "account": "decentralized-identity", "repo": "sidetree" } -
external_specs(ARRAY, optional) - Enables[[xref: ...]]references to terms from other Spec-Up outputs. Each entry is a single-key object mapping a short handle to the external spec URL. -
assets(ARRAY, optional) - Injects extra CSS or JS assets into the rendered page when you need custom page-level behavior or styling. Asset objects supportpath, optionalinject("head"or"body"for JS), and optionalmodulefor JS modules. -
content_security_policy(BOOLEAN or STRING, optional) - By default, Spec-Up does not emit a CSP meta tag. Set this totrueto emit Spec-Up's generated CSP, or set it to a string to inject a custom CSP value. -
plugins(ARRAY, optional) - Plugins that should run only for this spec.
-
-
Render either programmatically or through the Vite-based workflow used by this repo:
- Programmatic:
require('spec-up')({ nowatch: true })renders once, whilerequire('spec-up')()renders and keeps file watchers running. - Vite workflow: use the
vite.config.mjspattern in this repo and the package scripts shown below.
- Programmatic:
Boom! That's it. You're ready to start rendering specs as HTML sites locally and/or pushing them to github pages however you see fit to automate.
If your specs.json, package.json, and vite.config.mjs files are in the project root, Spec-Up can be run from the root of your repo in four common modes:
| command | behavior |
|---|---|
npm run build |
runs vite build, rebuilds compiled frontend assets when needed, and renders the configured specs once. |
npm run edit |
runs vite build --watch so spec markdown, injected assets, plugin files, and frontend source are watched in one Vite-hosted loop. |
npm run render |
alias for vite build if you prefer the old command name. |
npm run dev |
runs the Vite dev server; Spec-Up renders through a Vite plugin and triggers reloads when your spec sources change. |
Spec-Up now supports plugins at the top level of specs.json, per-spec via plugins, or programmatically via require('spec-up')({ plugins: [...] }).
Plugin entries can be either:
- a relative module path string such as
"./plugins/my-plugin.js" - an object with
resolveandoptions - a plugin object or factory passed through
options.plugins
Plugins can hook into the render lifecycle with methods like:
beforeRendertransformMarkdownmarkdownTemplatesconfigureMarkdownIttransformRenderedHtmlafterRendertransformPageHtmlextendAssetTagsafterWrite
The above scripts can easily be triggered by github actions. See this repo's example
The recommended method for hosting multiple historical versions of a given specification at the same URL is simply to duplicate the source file(s) in a subdirectory and to host each version in a fixed subdirectory of the output target (i.e., the GitHub-Pages site). These multiple set-up and output directories can be set by multiple spec objects in the specs array of the specs.json file. For example:
{
"specs": [
{
"title": "Wallet And Credential Interactions",
"spec_directory": "./",
"output_path": "./build",
"logo": "https://rawcdn.githack.com/decentralized-identity/decentralized-identity.github.io/a3ca39717e440302d1fd99a796e7f00e1c42eb2d/images/logo-flat.svg",
"logo_link": "https://identity.foundation",
"source": {
"host": "github",
"account": "decentralized-identity",
"repo": "waci-presentation-exchange"
}
},
{
"title": "Wallet And Credential Interactions",
"spec_directory": "./v0.1.0",
"output_path": "./build/v0.1.0",
"logo": "https://rawcdn.githack.com/decentralized-identity/decentralized-identity.github.io/a3ca39717e440302d1fd99a796e7f00e1c42eb2d/images/logo-flat.svg",
"logo_link": "https://identity.foundation",
"source": {
"host": "github",
"account": "decentralized-identity",
"repo": "waci-presentation-exchange"
}
}
]
}Note: when copying a version into a subdirectory, relative references, including image links or [[include]] blocks that copy in example files or test vectors, may break; a quick CTRL-F "../" is recommended
In the above example, the files in ./v0.1.0 will not be rendered by the build process that searches "./" for markdown files and vice versa-- changing either will not trigger a new build of the other in each PR.
DIF Recommends 3-decimal versions à la SemVer (i.e., v0.1.0 instead of v0.1).
Links from the currently/nightly/unstable spec to stable/archival versions and vice versa are not automatically generated by the current version of spec-up, so the recommended best practice is to manually add links above the "Editors" section. See:
Examples from Presentation Exchange
Unstable version:
Presentation Exchange 2.0.0
==================
**Specification Status:** Working Group Draft
**Latest Draft:**
[identity.foundation/presentation-exchange](https://identity.foundation/presentation-exchange)
**Ratified Versions:**
~ **v1.0.0** - [https://identity.foundation/presentation-exchange/spec/v1.0.0](https://identity.foundation/presentation-exchange/spec/v1.0.0)
Stable Version:
Presentation Exchange v1.0.0
==================
**Specification Status:** DIF Ratified Specification
**Latest Draft:**
[identity.foundation/presentation-exchange](https://identity.foundation/presentation-exchange)
Additionally, some editors may prefer to keep an immutable archive in a system like web.archive.org. For example:
**Specification Status:** Draft V0.1 (snapshotted and archived on [web.archive.org](https://web.archive.org/web/20211206215823/https://identity.foundation/waci-presentation-exchange/))
- WSL2 users are recommended to use the
bashoption rather thanPowerShellin the terminal of Visual Studio Code. - Spec-Up now targets Node.js 18 and newer.
