Write Lint Rules
Lexicons can contribute three kinds of validation: imperative rules (TypeScript compiler API), declarative rules (pattern matching DSL), and post-synth checks (validate serialized output).
Imperative Rules
Section titled “Imperative Rules”Imperative rules use the TypeScript compiler API to inspect AST nodes:
import type { LintRule, LintContext, LintDiagnostic } from "@intentius/chant";
export const myRule: LintRule = { id: "MYD001", severity: "warning", category: "style", description: "Example custom rule",
check(context: LintContext): LintDiagnostic[] { // Your rule logic here return []; },};Return these from your plugin’s lintRules() method.
Declarative Rules
Section titled “Declarative Rules”Declarative rules use the rule() builder for common patterns:
import type { RuleSpec } from "@intentius/chant";
const spec: RuleSpec = { id: "MYD002", severity: "warning", category: "style", description: "Resource names must be lowercase", selector: "resource > property", match: { pattern: /^[A-Z]/ }, message: "Property name '{node}' should be lowercase",};Return these from your plugin’s declarativeRules() method.
Post-synth Checks
Section titled “Post-synth Checks”Post-synth checks validate the serialized output after the build pipeline completes:
import type { PostSynthCheck, PostSynthDiagnostic } from "@intentius/chant";
const myCheck: PostSynthCheck = { id: "MYD010", description: "Check output constraints",
check(context): PostSynthDiagnostic[] { // Validate the serialized output return []; },};Return these from your plugin’s postSynthChecks() method.
Rule ID Conventions
Section titled “Rule ID Conventions”Rule IDs follow the pattern {PREFIX}{CATEGORY}{NUMBER}:
- Prefix — your lexicon’s
rulePrefix(e.g.MY,AWS,K8S) - Category — single letter:
D(declarative),S(style),C(correctness),E(evaluability) - Number — three-digit sequential number
Testing
Section titled “Testing”Post-synth checks
Section titled “Post-synth checks”Every post-synth check needs both positive and negative tests. Use a makeCtx helper:
function makeCtx(yaml: string) { return { outputs: new Map([["my-lexicon", yaml]]), };}
describe("MYD010: description", () => { test("flags bad pattern", () => { const yaml = `...bad yaml...`; const diags = myCheck.check(makeCtx(yaml)); expect(diags.length).toBeGreaterThanOrEqual(1); expect(diags[0].checkId).toBe("MYD010"); });
test("no diagnostic when correct", () => { const yaml = `...good yaml...`; const diags = myCheck.check(makeCtx(yaml)); expect(diags).toHaveLength(0); });});Imperative and declarative rules
Section titled “Imperative and declarative rules”Test imperative rules by constructing a minimal AST context and verifying the returned diagnostics array. Test declarative rules by running the rule() builder output against sample input strings.
See Testing Your Lexicon for the full testing guide, including patterns for all test file types.
Next Steps
Section titled “Next Steps”With rules in place, add LSP & MCP providers for editor integration.