Skip to content

Scaffold a Lexicon

The fastest way to start a new lexicon is with chant init lexicon:

Terminal window
chant init lexicon acme
cd lexicons/acme
npm install

The name is the resolution key, so pick the one the published package will carry. chant init lexicon acme derives @intentius/chant-lexicon-acme, the variables acmePlugin and acmeSerializer, and the rule prefix ACM (the first three alphanumeric characters, upper-cased). The target directory defaults to ./lexicons/<name>; pass a second positional argument to put it elsewhere, and --force to write into a directory that is not empty. Those are the only two arguments the command takes, see the init lexicon CLI reference.

The scaffold compiles as generated, and every required lifecycle method is present. What it does not do is pass chant dev check-lexicon, by design: five tier-1 checks fail on a fresh scaffold and each one names work you still owe. See What the scaffold leaves for you.

36 files, listed here as initLexiconCommand writes them (packages/core/src/cli/commands/init-lexicon.ts).

FileWhat it is
src/plugin.tsThe LexiconPlugin object. Every required method is wired to a real implementation or a TODO
src/index.tsRe-exports the plugin and the serializer. This is the package entry point
src/serializer.tsSerializer stub emitting minimal JSON
src/codegen/generate.tsCalls core’s generatePipeline with TODO callbacks
src/codegen/generate-cli.tstsx entry point behind just generate
src/codegen/docs-cli.tstsx entry point behind just docs
src/codegen/naming.tsNamingStrategy config with empty data tables
src/codegen/package.tsCalls core’s packagePipeline
src/codegen/docs.tsCalls core’s docsPipeline
src/spec/fetch.tsfetchWithCache stub with a TODO URL
src/spec/parse.tsparseSchema stub with a ParseResult type
src/lint/rules/sample.tsOne example LintRule using the derived prefix
src/lint/rules/index.tsRule barrel the plugin’s lintRules() returns
src/lsp/completions.ts, src/lsp/hover.tsLSP providers, registered on the plugin
src/lsp/completions.test.ts, src/lsp/hover.test.tsTheir tests
src/plugin.test.ts, src/serializer.test.tsThe two test files chant dev check-lexicon requires by name
src/validate.ts, src/validate-cli.tsArtifact validation and its tsx entry point
src/package-cli.tstsx entry point behind npm run bundle, chained into prepack, which writes src/generated/ and dist/
src/generated/.gitkeepPlaceholder. generate writes the real artifacts here
package.json, tsconfig.json, tsconfig.build.jsonPackage and compiler config
justfilegenerate, validate, docs, docs-build, package recipes
.gitignore, README.mdIgnores snapshots, dist and cache; README covers getting started
docs/A Starlight site: astro.config.mjs, package.json, tsconfig.json, src/content.config.ts, src/content/docs/index.mdx, pages/getting-started.mdx
examples/getting-started/One example project: package.json and src/infra.ts

Neither dist/ nor src/generated/* is scaffolded. dist/ is written by writeBundleSpec during packaging, and src/generated/ by writeGeneratedArtifacts during generation.

src/plugin.ts holds the plugin object. The scaffold points generate, validate, package and docs at real modules and leaves coverage and postSynthChecks() as TODOs:

import type { LexiconPlugin } from "@intentius/chant/lexicon";
import { acmeSerializer } from "./serializer";
import { rules } from "./lint/rules";
export const acmePlugin: LexiconPlugin = {
name: "acme",
serializer: acmeSerializer,
async generate(options?: { verbose?: boolean }): Promise<void> {
const { generate } = await import("./codegen/generate");
await generate(options);
},
// validate, coverage, package ...
lintRules() {
return rules;
},
};

src/index.ts re-exports acmePlugin and acmeSerializer. Its last line is a commented-out export * from "./generated/index"; uncomment it once generation has produced that file, or the resource classes are unreachable to users.

src/codegen/generate.ts is where core’s generatePipeline is called with your provider-specific callbacks, and where most of the work happens. See Implement Generate.

src/serializer.ts converts evaluated resources to your target format. See Create a Serializer.

src/codegen/package.ts calls core’s packagePipeline to produce a distributable bundle. See Package & Publish.

Run chant dev check-lexicon lexicons/acme on a fresh scaffold and two tier-1 checks fail (chant #2090 closed the other three). Each is a real gap rather than a defect in the tool:

Failing checkWhat closes it
postSynthChecks() returns at least 1 checkWrite checks in src/lint/post-synth/ and return them. See Post-Synth Checks
Every shipped example builds and passes its own post-synth checksThe scaffolded examples/getting-started/src/infra.ts is entirely commented out, so it builds to nothing. Declare at least one real resource in it once your generated types exist

Implement the generation pipeline next, connecting your upstream spec to generatePipeline. See Implement Generate.

Once the lexicon is built and packaged, run chant dev onboard to wire it into CI, Docker smoke tests, and the npm publish workflow. The full sequence is the authoring workflow.