Scaffold a Lexicon
The fastest way to start a new lexicon is with chant init lexicon:
chant init lexicon acmecd lexicons/acmenpm installThe 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.
Generated files
Section titled “Generated files”36 files, listed here as initLexiconCommand writes them
(packages/core/src/cli/commands/init-lexicon.ts).
| File | What it is |
|---|---|
src/plugin.ts | The LexiconPlugin object. Every required method is wired to a real implementation or a TODO |
src/index.ts | Re-exports the plugin and the serializer. This is the package entry point |
src/serializer.ts | Serializer stub emitting minimal JSON |
src/codegen/generate.ts | Calls core’s generatePipeline with TODO callbacks |
src/codegen/generate-cli.ts | tsx entry point behind just generate |
src/codegen/docs-cli.ts | tsx entry point behind just docs |
src/codegen/naming.ts | NamingStrategy config with empty data tables |
src/codegen/package.ts | Calls core’s packagePipeline |
src/codegen/docs.ts | Calls core’s docsPipeline |
src/spec/fetch.ts | fetchWithCache stub with a TODO URL |
src/spec/parse.ts | parseSchema stub with a ParseResult type |
src/lint/rules/sample.ts | One example LintRule using the derived prefix |
src/lint/rules/index.ts | Rule barrel the plugin’s lintRules() returns |
src/lsp/completions.ts, src/lsp/hover.ts | LSP providers, registered on the plugin |
src/lsp/completions.test.ts, src/lsp/hover.test.ts | Their tests |
src/plugin.test.ts, src/serializer.test.ts | The two test files chant dev check-lexicon requires by name |
src/validate.ts, src/validate-cli.ts | Artifact validation and its tsx entry point |
src/package-cli.ts | tsx entry point behind npm run bundle, chained into prepack, which writes src/generated/ and dist/ |
src/generated/.gitkeep | Placeholder. generate writes the real artifacts here |
package.json, tsconfig.json, tsconfig.build.json | Package and compiler config |
justfile | generate, validate, docs, docs-build, package recipes |
.gitignore, README.md | Ignores 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.
Key files
Section titled “Key files”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.
What the scaffold leaves for you
Section titled “What the scaffold leaves for you”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 check | What closes it |
|---|---|
postSynthChecks() returns at least 1 check | Write checks in src/lint/post-synth/ and return them. See Post-Synth Checks |
Every shipped example builds and passes its own post-synth checks | The 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 |
Next steps
Section titled “Next steps”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.