Building on kickstartDS means repeating a handful of tasks: compiling tokens, dereferencing schemas, generating types, producing CMS configuration, previewing components.
The CLI wraps those into single commands, so a project's package.json stays readable and the pipeline stays the same across projects.
| Fact | Detail |
|---|---|
| Commands | Tokens, schemas, types, CMS config, previews |
| Stack | TypeScript / Node |
| Started | May 2022 |
What's in it
The same commands this site's build runs for tokens, schemas and CMS configuration.
Cuts the per-project glue code that used to be copy-pasted between repositories.
In detail
What is actually inside it
rc files plus .schema.json siblings: editors validate them, CI fails early, and the CLI can explain a wrong key instead of ignoring it.
Configurations are dereferenced with @apidevtools/json-schema-ref-parser before use, which is what lets layer-based schemas stay readable in the repository.
| Detail | Value |
|---|---|
| Binaries | `kickstartDS`, `ksDS` |
| Conventions | per-command `.…rc.json` + JSON Schema |
| Started | May 2022 |
Technical notes
The decisions behind it
Why rc files rather than flags?
rc file is reviewable and reproducible; a long flag list in a package.json script is neither.Every command, end to end
Five groups, fourteen leaf commands, one shared task runner
Each command is a pair: a Command declaration in src/commands/<group>/<name>.ts that only describes options, and the real logic in src/tasks/<group>/<name>-task.ts. index.ts registers the five groups — tokens, schema, cms, completion, example — through Commander's executableFile mechanism, so every group runs in its own Node process and can be invoked directly as dist/src/commands/<group>.js.
The shared runner is src/tasks/task.ts. init() resolves configuration; start() then runs up to three phases: prerequisite checks, the run steps, or the matching revert steps when --revert is passed. All work happens inside ${tempdir()}/<module>-<command>-<identifier> — for example /tmp/tokens-init-init-task — via pushd/popd; results are copied back to the project directory only after a step returns, so a crashed command leaves the checkout untouched.
What the runner is meant to offer is not quite what it does today. The pre-run wipe is gated on tmpDir.length > 5 && tmpDir.startsWith('/tmp'), so it silently skips whenever tempdir() points somewhere else — the macOS default $TMPDIR, for instance — and stale files from an earlier run survive. And all twelve task modules still carry // TODO implement revert subtask as their revert body: --revert logs that it finished successfully and changes nothing.
| Command | What a run does |
|---|---|
| `tokens init` | Reads the branding token file (`src/token/branding-token.json` by default) and writes a Style Dictionary dictionary to `src/token/dictionary`. |
| `tokens compile` | Builds that dictionary with Style Dictionary, using the project's `sd.config.cjs` or a built-in config when it is absent, then copies each platform's files back to its `buildPath`. |
| `tokens tofigma` | Validates and serialises tokens against the Figma template schema — work in progress. |
| `schema types` | `<name>.schema.json` → a colocated `<Name>Props.ts`; imports resolve per `$id`, so kickstartDS-hosted schemas come from `@kickstartds/<module>/lib/<name>/typing`. |
| `schema dereference` | Inlines `$ref` / `allOf` and writes `<name>.schema.dereffed.json` next to the source. |
| `schema layer` | Emits `declare module "@kickstartds/<module>/lib/<name>/typing"` overrides into `src/types/<Name>Props.d.ts`. |
| `schema defaults` | Derives JSON Schema defaults and writes them as `DeepPartial<...>` objects to `<Name>Defaults.ts`. |
| `cms storyblok | uniform | stackbit | staticcms` | Classifies schemas into templates, globals and components, then writes `components.123456.json`, `components.json`, `models.json` or `config.yml` under `src/cms`. |
Wired, unused, unreachable
What the option tables and the dependency list actually say
tokens, schema, cms, completion, example — 14 leaf commands across 12 task modules.--revert reports success regardless.src/ and index.ts, including the three self-documented defects up top: --debug, unknown commands, completions.shell, schema, tokens, json); git, npm, docker and onepassword have no caller.--rc-only and --cleanup are plain Commander booleans defaulting to true, with no negated form. Against Commander 9.2.0, --no-rc-only, --rc-only=false and --cleanup=false are all rejected as unknown options. The JSON-Schema-driven Inquirer prompt flow sits behind --rc-only=false, and the README's "resume a late failure with --cleanup false" tip cannot be expressed on the command line at all.
The package ships four base rc files — .tokens-initrc.json, .tokens-compilerc.json, .tokens-tofigmarc.json and .example-demorc.json — matched by "files": [".*rc.json"]. cosmiconfig("<module>-<command>") looks for .schema-typesrc.json, .schema-storyblokrc.json and friends for the schema and CMS commands, but those are absent — as are the .<module>-<command>rc.schema.json files that prompting.ts reads synchronously on every prompt.
util/analytics.ts activates only when all four of INFLUXDB_URL, INFLUXDB_TOKEN, INFLUXDB_ORG and INFLUXDB_BUCKET are set. It then writes an InfluxDB timing point tagged with module, command, instance, a fresh uuid and the OS user. The same 180-day mean query also decides whether to swap the console logger for a progress bar, and only when the cached step timings sum above 2500 ms.
task.ts wires in git (simple-git against ssh://git@github.com), npm (install, unpublish, getVersion), docker (dockerode streaming container logs through byline) and onepassword (hard-coded ruhmesmeile.1password.com). None is referenced by a shipped task; they are the scaffolding of an internal extension-migration workflow the public CLI does not expose.
Rough edges, one by one
Where a senior engineer gets bitten
Which packages must a project install for the CLI to work?
@kickstartds/jsonschema-utils, @kickstartds/jsonschema2types, jsonschema2storyblok, jsonschema2stackbit, jsonschema2staticcms, jsonschema2uniform and @kickstartds/core all sit in peerDependencies. util/schema.ts imports them lazily (await import('@kickstartds/jsonschema2storyblok')), so a missing generator fails at the step that needs it rather than at startup. util/tokens.ts resolves @kickstartds/style-dictionary from the calling project with createRequire, and its default Style Dictionary config adds the icon SVGs from <core>/source/design-tokens/icons/*.svg.What does `cms storyblok` actually write?
src/components/**/*.(schema|definitions|interface).json, classifies each $id by its schema name using the --templates / --globals / --components lists, and writes the result to the literal path src/cms/components.123456.json. --update-config reads that same file back and iterates its existing components — but the loop body is empty, so the merge path is a stub and the output file name is hard-coded.Why does `tokens tofigma` finish without syncing anything?
figmaFile.json against assets/tokens/figma/figma-tokens.schema.json with Ajv (removeAdditional, allErrors; invalid input dumps the errors and exits 1), dereferences that schema, merges allOf, compiles it to figma-file.d.ts, walks it with json-schema-traverse and writes parsed-tokens.json. Two things stop it short: parsedTokens is still initialised as {} behind a TODO, and the syncToFigma call is not awaited. Its prerequisite check also demands a tokens/ directory in the project root even though the command advertises --token-dictionary-path — an option the tofigma task never receives.Where do generated files land?
schema types, dereference and defaults write next to the schema they came from: ButtonProps.ts, button.schema.dereffed.json, ButtonDefaults.ts. schema layer is the exception, writing src/types/<Name>Props.d.ts files that wrap the generated interfaces in declare module "@kickstartds/<module>/lib/<name>/typing". CMS output goes under --configuration-path (default src/cms). The globs follow the schema naming convention: *.schema.json, plus *.definitions.json and *.interface.json for the CMS and layer tasks.Are `kickstartDS` and `ksDS` different binaries?
bin entries map to bin/index.js, which is a shebang plus import '../dist/index.js'. The figlet banner and version line are a top-level console.log executed before program.parse, so they print for --version too. The global -d, --debug flag is declared in index.ts but the logger is created with hard-coded settings and a // TODO this debugging switch does not work right now; only the per-command --debug reaches the task runner, where it flips ShellJS silent mode off.Is the Node requirement enforced?
package.json carries "node": ">=18.1" at the top level instead of under engines, so no package manager checks it; .nvmrc pins 18 and CircleCI builds on cimg/node:18.16.1 with yarn tsc as the only build step. The published tarball contains dist plus the .*rc.json / .*rc.schema.json files, and main points at bin/index.js while exports points at dist/index.js.Links
Where to find this one
