@intentius/chant 0.44.14 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/discover.d.ts +26 -2
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +10 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/cli/build-params-cli.d.ts +16 -7
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +20 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts +9 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +13 -0
- package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +24 -14
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +14 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts +5 -0
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/lexicon-surface-diff.d.ts +6 -0
- package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +28 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts +13 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/search.d.ts +27 -0
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +7 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/docs-pages.d.ts +39 -0
- package/dist/codegen/docs-pages.d.ts.map +1 -0
- package/dist/codegen/docs-sections.d.ts.map +1 -1
- package/dist/codegen/docs-sidebar.d.ts +13 -2
- package/dist/codegen/docs-sidebar.d.ts.map +1 -1
- package/dist/codegen/docs-types.d.ts +30 -10
- package/dist/codegen/docs-types.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts +14 -2
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.d.ts +11 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -1
- package/dist/codegen/surface-snapshot.d.ts +27 -3
- package/dist/codegen/surface-snapshot.d.ts.map +1 -1
- package/dist/codegen/validate.d.ts +21 -3
- package/dist/codegen/validate.d.ts.map +1 -1
- package/dist/components/capability-plugin.d.ts +13 -2
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/driver.d.ts +38 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/config.d.ts +85 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/env.d.ts +12 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +100 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/graph-ir.d.ts +12 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ops.d.ts +17 -0
- package/dist/graph-ops.d.ts.map +1 -0
- package/dist/lexicon.d.ts +182 -2
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +23 -2
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts +8 -0
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/teardown.d.ts +130 -0
- package/dist/lifecycle/teardown.d.ts.map +1 -0
- package/dist/lifecycle/types.d.ts +7 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lint/engine.d.ts +6 -2
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lint/policy.d.ts.map +1 -1
- package/dist/lint/rule.d.ts +31 -0
- package/dist/lint/rule.d.ts.map +1 -1
- package/dist/lint/rules/cor021-env-literal-name.d.ts +3 -0
- package/dist/lint/rules/cor021-env-literal-name.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +2 -1
- package/dist/lint/rules/index.d.ts.map +1 -1
- package/dist/managed-fields.d.ts +33 -21
- package/dist/managed-fields.d.ts.map +1 -1
- package/dist/observation.d.ts +23 -1
- package/dist/observation.d.ts.map +1 -1
- package/dist/op/builders.d.ts +36 -7
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/index.d.ts +1 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/testing.d.ts +136 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/yaml.d.ts +0 -8
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/__snapshots__/okf.test.ts.snap +20 -0
- package/src/audit/discover.test.ts +55 -1
- package/src/audit/discover.ts +70 -2
- package/src/audit/edge-init-safety.test.ts +149 -18
- package/src/audit/report-model.ts +12 -1
- package/src/audit/rules-doc.ts +1 -0
- package/src/build-params.test.ts +52 -0
- package/src/cli/build-params-cli.test.ts +25 -8
- package/src/cli/build-params-cli.ts +37 -23
- package/src/cli/commands/__fixtures__/audit-coverage/.github/workflows/ci.yml +5 -0
- package/src/cli/commands/__fixtures__/audit-coverage/Dockerfile +2 -0
- package/src/cli/commands/__fixtures__/audit-coverage/infra/main.tf +3 -0
- package/src/cli/commands/__fixtures__/audit-coverage/infra/stack.json +6 -0
- package/src/cli/commands/__fixtures__/audit-coverage/k8s/deploy.yaml +12 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/pages/getting-started.mdx +16 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +10 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate-cli.ts +3 -1
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate.ts +2 -1
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +2 -1
- package/src/cli/commands/audit.test.ts +83 -1
- package/src/cli/commands/audit.ts +121 -28
- package/src/cli/commands/build.test.ts +275 -5
- package/src/cli/commands/build.ts +86 -14
- package/src/cli/commands/check-lexicon-docs.ts +30 -0
- package/src/cli/commands/check-lexicon-examples.test.ts +73 -7
- package/src/cli/commands/check-lexicon-examples.ts +66 -17
- package/src/cli/commands/check-lexicon.test.ts +46 -2
- package/src/cli/commands/check-lexicon.ts +64 -4
- package/src/cli/commands/init-lexicon/templates/codegen.ts +5 -2
- package/src/cli/commands/init-lexicon/templates/docs.ts +35 -2
- package/src/cli/commands/init-lexicon.test.ts +42 -1
- package/src/cli/commands/init-lexicon.ts +3 -1
- package/src/cli/commands/lexicon-rolling-upgrade.ts +1 -1
- package/src/cli/commands/lexicon-surface-diff.test.ts +1 -1
- package/src/cli/commands/lexicon-surface-diff.ts +10 -1
- package/src/cli/commands/lexicon-surface-diff.update.test.ts +112 -0
- package/src/cli/commands/lint.ts +19 -6
- package/src/cli/commands/onboard.test.ts +319 -225
- package/src/cli/commands/onboard.ts +144 -58
- package/src/cli/handlers/build.test.ts +3 -3
- package/src/cli/handlers/build.ts +2 -0
- package/src/cli/handlers/components.ts +2 -2
- package/src/cli/handlers/graph.test.ts +44 -0
- package/src/cli/handlers/graph.ts +24 -4
- package/src/cli/handlers/lifecycle.test.ts +348 -1
- package/src/cli/handlers/lifecycle.ts +235 -3
- package/src/cli/handlers/run.test.ts +5 -5
- package/src/cli/handlers/run.ts +1 -0
- package/src/cli/handlers/search-live.test.ts +217 -0
- package/src/cli/handlers/search.test.ts +41 -1
- package/src/cli/handlers/search.ts +60 -8
- package/src/cli/main.ts +17 -4
- package/src/cli/registry.ts +7 -0
- package/src/cli/security-boundary.test.ts +2 -1
- package/src/codegen/docs-pages.test.ts +194 -0
- package/src/codegen/docs-pages.ts +138 -0
- package/src/codegen/docs-sections.ts +0 -8
- package/src/codegen/docs-sidebar.ts +50 -37
- package/src/codegen/docs-types.ts +32 -4
- package/src/codegen/docs.ts +78 -40
- package/src/codegen/lexicon-regen.ts +22 -2
- package/src/codegen/rolling-upgrade.test.ts +1 -1
- package/src/codegen/rolling-upgrade.ts +1 -1
- package/src/codegen/surface-snapshot.test.ts +139 -0
- package/src/codegen/surface-snapshot.ts +297 -122
- package/src/codegen/validate.test.ts +49 -3
- package/src/codegen/validate.ts +36 -7
- package/src/components/auto-release.ts +1 -1
- package/src/components/capability-plugin.ts +23 -2
- package/src/components/driver.test.ts +45 -0
- package/src/components/driver.ts +64 -27
- package/src/components/index.ts +2 -0
- package/src/components/starter-plugin.ts +5 -2
- package/src/config.test.ts +40 -0
- package/src/config.ts +159 -8
- package/src/discovery/fold-import.test.ts +427 -2
- package/src/discovery/fold-import.ts +163 -7
- package/src/discovery/sandbox/driver.test.ts +11 -0
- package/src/env.test.ts +35 -1
- package/src/env.ts +17 -3
- package/src/fold/fold.test.ts +41 -0
- package/src/fold/fold.ts +372 -2
- package/src/graph-ir-live.test.ts +42 -0
- package/src/graph-ir.ts +42 -2
- package/src/graph-ops.test.ts +69 -0
- package/src/graph-ops.ts +42 -0
- package/src/lexicon.ts +182 -2
- package/src/lifecycle/change-set.test.ts +41 -0
- package/src/lifecycle/change-set.ts +30 -2
- package/src/lifecycle/deep-observe.ts +4 -0
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/observe.ts +29 -2
- package/src/lifecycle/replay.ts +3 -0
- package/src/lifecycle/snapshot.ts +3 -0
- package/src/lifecycle/teardown.test.ts +537 -0
- package/src/lifecycle/teardown.ts +357 -0
- package/src/lifecycle/types.ts +7 -0
- package/src/lint/engine.ts +7 -1
- package/src/lint/policy.ts +14 -3
- package/src/lint/rule.ts +23 -0
- package/src/lint/rules/cor021-env-literal-name.test.ts +128 -0
- package/src/lint/rules/cor021-env-literal-name.ts +114 -0
- package/src/lint/rules/index.ts +4 -1
- package/src/managed-fields.test.ts +15 -42
- package/src/managed-fields.ts +43 -26
- package/src/observation.test.ts +24 -8
- package/src/observation.ts +48 -4
- package/src/op/builders.ts +40 -7
- package/src/op/index.ts +1 -1
- package/src/ownership.test.ts +63 -1
- package/src/testing.test.ts +261 -0
- package/src/testing.ts +338 -0
- package/src/yaml.test.ts +15 -0
- package/src/yaml.ts +19 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync, readdirSync } from "fs";
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync, readdirSync, realpathSync } from "fs";
|
|
3
|
+
import { execFileSync } from "child_process";
|
|
3
4
|
import { join, dirname } from "path";
|
|
4
5
|
import { tmpdir } from "os";
|
|
5
6
|
import { fileURLToPath } from "url";
|
|
@@ -63,6 +64,7 @@ describe("initLexiconCommand", () => {
|
|
|
63
64
|
"docs/astro.config.mjs",
|
|
64
65
|
"docs/src/content.config.ts",
|
|
65
66
|
"docs/src/content/docs/index.mdx",
|
|
67
|
+
"docs/pages/getting-started.mdx",
|
|
66
68
|
"examples/getting-started/package.json",
|
|
67
69
|
"examples/getting-started/src/infra.ts",
|
|
68
70
|
"src/generated/.gitkeep",
|
|
@@ -265,6 +267,45 @@ describe("scaffold content validation", () => {
|
|
|
265
267
|
const content = readFileSync(join(targetDir, "src/lint/rules/index.ts"), "utf-8");
|
|
266
268
|
expect(content).toContain("export const rules");
|
|
267
269
|
});
|
|
270
|
+
|
|
271
|
+
// #1614: generate-cli.ts lives in src/codegen/, so the package root is three
|
|
272
|
+
// dirnames up. Two dirnames is src/, and the first `npm run generate` then
|
|
273
|
+
// writes src/src/generated/. Run the scaffolded CLI for real with generate.ts
|
|
274
|
+
// replaced by a stub that reports where it was told to write.
|
|
275
|
+
test("generate-cli.ts resolves pkgDir to the package root, so generate writes src/generated (#1614)", () => {
|
|
276
|
+
const cliPath = join(targetDir, "src/codegen/generate-cli.ts");
|
|
277
|
+
expect(readFileSync(cliPath, "utf-8")).toContain(
|
|
278
|
+
"dirname(dirname(dirname(fileURLToPath(import.meta.url))))",
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
writeFileSync(
|
|
282
|
+
join(targetDir, "src/codegen/generate.ts"),
|
|
283
|
+
[
|
|
284
|
+
'import { mkdirSync, writeFileSync } from "fs";',
|
|
285
|
+
'import { join } from "path";',
|
|
286
|
+
"export async function generate() { return {}; }",
|
|
287
|
+
"export function writeGeneratedFiles(_result: unknown, pkgDir: string) {",
|
|
288
|
+
' const dir = join(pkgDir, "src/generated");',
|
|
289
|
+
" mkdirSync(dir, { recursive: true });",
|
|
290
|
+
' writeFileSync(join(dir, "lexicon.json"), "{}");',
|
|
291
|
+
" process.stdout.write(pkgDir);",
|
|
292
|
+
"}",
|
|
293
|
+
"",
|
|
294
|
+
].join("\n"),
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const tsx = join(__dirname, "../../../../../node_modules/.bin/tsx");
|
|
298
|
+
const out = execFileSync(tsx, [cliPath], { cwd: targetDir, encoding: "utf-8" });
|
|
299
|
+
|
|
300
|
+
expect(realpathSync(out.trim())).toBe(realpathSync(targetDir));
|
|
301
|
+
expect(existsSync(join(targetDir, "src/generated/lexicon.json"))).toBe(true);
|
|
302
|
+
expect(existsSync(join(targetDir, "src/src"))).toBe(false);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test("generate.ts default pkgDir is also the package root (#1614)", () => {
|
|
306
|
+
const content = readFileSync(join(targetDir, "src/codegen/generate.ts"), "utf-8");
|
|
307
|
+
expect(content).toContain("pkgDir ?? dirname(dirname(dirname(fileURLToPath(import.meta.url))))");
|
|
308
|
+
});
|
|
268
309
|
});
|
|
269
310
|
|
|
270
311
|
// ── Fixture snapshot tests ──────────────────────────────────────────
|
|
@@ -10,7 +10,7 @@ import { generateSampleRuleTs, generateLintRulesIndexTs } from "./init-lexicon/t
|
|
|
10
10
|
import { generateLspCompletionsTs, generateLspHoverTs } from "./init-lexicon/templates/lsp";
|
|
11
11
|
import { generatePackageJson, generateTsConfig, generateTsConfigBuild, generateJustfile, generateGitignore, generateReadme, generateSerializerTs, generateValidateTs, generateValidateCliTs } from "./init-lexicon/templates/project";
|
|
12
12
|
import { generatePluginTestTs, generateSerializerTestTs, generateCompletionsTestTs, generateHoverTestTs } from "./init-lexicon/templates/tests";
|
|
13
|
-
import { generateDocsPackageJson, generateDocsTsConfig, generateDocsAstroConfig, generateDocsContentConfig, generateDocsIndexMdx } from "./init-lexicon/templates/docs";
|
|
13
|
+
import { generateDocsPackageJson, generateDocsTsConfig, generateDocsAstroConfig, generateDocsContentConfig, generateDocsIndexMdx, generateDocsGettingStartedMdx } from "./init-lexicon/templates/docs";
|
|
14
14
|
import { generateExamplePackageJson, generateExampleInfraTs } from "./init-lexicon/templates/examples";
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -115,6 +115,7 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
|
|
|
115
115
|
"docs/src",
|
|
116
116
|
"docs/src/content",
|
|
117
117
|
"docs/src/content/docs",
|
|
118
|
+
"docs/pages",
|
|
118
119
|
"examples/getting-started",
|
|
119
120
|
"examples/getting-started/src",
|
|
120
121
|
];
|
|
@@ -159,6 +160,7 @@ export async function initLexiconCommand(options: InitLexiconOptions): Promise<I
|
|
|
159
160
|
"docs/astro.config.mjs": generateDocsAstroConfig(name),
|
|
160
161
|
"docs/src/content.config.ts": generateDocsContentConfig(),
|
|
161
162
|
"docs/src/content/docs/index.mdx": generateDocsIndexMdx(name),
|
|
163
|
+
"docs/pages/getting-started.mdx": generateDocsGettingStartedMdx(name),
|
|
162
164
|
"examples/getting-started/package.json": generateExamplePackageJson(name),
|
|
163
165
|
"examples/getting-started/src/infra.ts": generateExampleInfraTs(name, names),
|
|
164
166
|
};
|
|
@@ -46,7 +46,7 @@ export async function runRollingUpgrade(
|
|
|
46
46
|
lexicon: "aws",
|
|
47
47
|
hasUpgrade: false,
|
|
48
48
|
severity: "none",
|
|
49
|
-
delta: { added: [], changed: [], removed: [], severity: "none" },
|
|
49
|
+
delta: { added: [], changed: [], removed: [], renamed: [], severity: "none" },
|
|
50
50
|
deltaText: "",
|
|
51
51
|
validationOk: false,
|
|
52
52
|
failures: [{ step: "setup", output: `Lexicon directory not found: ${dir}` }],
|
|
@@ -21,7 +21,7 @@ function result(over: Partial<RegenResult>): RegenResult {
|
|
|
21
21
|
ok: true,
|
|
22
22
|
changed: true,
|
|
23
23
|
severity: "additive",
|
|
24
|
-
delta: { added: [], changed: [], removed: [], severity: "additive" },
|
|
24
|
+
delta: { added: [], changed: [], removed: [], renamed: [], severity: "additive" },
|
|
25
25
|
deltaText: "",
|
|
26
26
|
failures: [],
|
|
27
27
|
freshSnapshot: { surface: "x" } as unknown as RegenResult["freshSnapshot"],
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
*
|
|
10
10
|
* The snapshot file is written to `<lexicon-dir>/surface.snapshot.json`.
|
|
11
11
|
* Pass `--update-snapshot` to commit the fresh snapshot after a successful run.
|
|
12
|
+
*
|
|
13
|
+
* An update run is exempt from the `surface-matches-snapshot` validate check
|
|
14
|
+
* and from nothing else (#1825): that check fails on the stale baseline the
|
|
15
|
+
* run exists to replace, and with an `"always"` gate (#1475) the documented
|
|
16
|
+
* re-baseline flow would deadlock. Any other failing step still refuses the
|
|
17
|
+
* write, so a broken generate cannot be baselined.
|
|
12
18
|
*/
|
|
13
19
|
|
|
14
20
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -62,7 +68,7 @@ export async function runSurfaceDiff(opts: SurfaceDiffOptions): Promise<RegenRes
|
|
|
62
68
|
ok: false,
|
|
63
69
|
changed: false,
|
|
64
70
|
severity: "none",
|
|
65
|
-
delta: { added: [], changed: [], removed: [], severity: "none" },
|
|
71
|
+
delta: { added: [], changed: [], removed: [], renamed: [], severity: "none" },
|
|
66
72
|
deltaText: "",
|
|
67
73
|
failures: [failure],
|
|
68
74
|
freshSnapshot: null,
|
|
@@ -78,6 +84,9 @@ export async function runSurfaceDiff(opts: SurfaceDiffOptions): Promise<RegenRes
|
|
|
78
84
|
skipLint: opts.skipLint,
|
|
79
85
|
skipExamples: !opts.runExamples,
|
|
80
86
|
pinnedDigestPath: opts.pinnedDigestPath,
|
|
87
|
+
// The update run skips only the surface-matches-snapshot validate check —
|
|
88
|
+
// the staleness it is about to fix (#1825).
|
|
89
|
+
updatingSnapshot: opts.updateSnapshot,
|
|
81
90
|
});
|
|
82
91
|
|
|
83
92
|
// Update snapshot when requested and the run succeeded
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `--update-snapshot` against a failing snapshot gate (#1825).
|
|
3
|
+
*
|
|
4
|
+
* Since #1475 the k8s/azure gates run in "always" mode, so a stale baseline
|
|
5
|
+
* fails validate on every run — including the update run whose whole purpose
|
|
6
|
+
* is to replace that baseline. These tests run the real pipeline (no mocks)
|
|
7
|
+
* against a fixture lexicon whose validate script behaves like an "always"
|
|
8
|
+
* gate: it fails while the committed snapshot is stale, unless the run is
|
|
9
|
+
* exempted via CHANT_SNAPSHOT_UPDATE.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, test, expect } from "vitest";
|
|
13
|
+
import { mkdtempSync, rmSync, writeFileSync, readFileSync } from "fs";
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
import { tmpdir } from "os";
|
|
16
|
+
import { runSurfaceDiff } from "./lexicon-surface-diff";
|
|
17
|
+
import { SNAPSHOT_FILENAME } from "../../codegen/lexicon-regen";
|
|
18
|
+
import { parseSnapshot } from "../../codegen/surface-snapshot";
|
|
19
|
+
|
|
20
|
+
const STALE_SNAPSHOT = JSON.stringify({
|
|
21
|
+
schemaVersion: 1,
|
|
22
|
+
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
23
|
+
entries: { Queue: { kind: "resource", resourceType: "X::Y::Queue", attrs: [], props: [] } },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const GENERATE_SCRIPT = `node -e "
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
const dir = path.join(process.cwd(), 'src', 'generated');
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
fs.writeFileSync(path.join(dir, 'lexicon-test.json'), JSON.stringify({Widget:{kind:'resource',resourceType:'X::Y::Widget',attrs:{Id:'Id'}}}));
|
|
32
|
+
fs.writeFileSync(path.join(dir, 'index.d.ts'), 'export declare class Widget { constructor(props: { Name?: string; }); readonly Id: string; }');
|
|
33
|
+
"`;
|
|
34
|
+
|
|
35
|
+
// Emulates a checkSurfaceSnapshot: "always" gate: fail while the committed
|
|
36
|
+
// snapshot does not describe the generated surface, unless the run carries the
|
|
37
|
+
// update exemption.
|
|
38
|
+
const GATED_VALIDATE_SCRIPT = `node -e "
|
|
39
|
+
if (process.env.CHANT_SNAPSHOT_UPDATE === '1') process.exit(0);
|
|
40
|
+
const fs = require('fs');
|
|
41
|
+
const path = require('path');
|
|
42
|
+
const snap = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'surface.snapshot.json'), 'utf-8'));
|
|
43
|
+
process.exit(snap.entries && snap.entries.Widget ? 0 : 1);
|
|
44
|
+
"`;
|
|
45
|
+
|
|
46
|
+
function makeLexiconDir(validateScript: string): string {
|
|
47
|
+
const dir = mkdtempSync(join(tmpdir(), "chant-sd-update-"));
|
|
48
|
+
const pkg = {
|
|
49
|
+
name: "@intentius/chant-lexicon-test",
|
|
50
|
+
version: "0.1.0",
|
|
51
|
+
type: "module",
|
|
52
|
+
scripts: {
|
|
53
|
+
generate: GENERATE_SCRIPT,
|
|
54
|
+
validate: validateScript,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
writeFileSync(join(dir, "package.json"), JSON.stringify(pkg, null, 2));
|
|
58
|
+
writeFileSync(join(dir, SNAPSHOT_FILENAME), STALE_SNAPSHOT);
|
|
59
|
+
return dir;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const baseOpts = { skipBundle: true, skipBuild: true, skipLint: true };
|
|
63
|
+
|
|
64
|
+
describe("surface-diff --update-snapshot vs. an \"always\" snapshot gate (#1825)", () => {
|
|
65
|
+
test("without --update-snapshot the stale snapshot still fails validate", async () => {
|
|
66
|
+
const dir = makeLexiconDir(GATED_VALIDATE_SCRIPT);
|
|
67
|
+
try {
|
|
68
|
+
const result = await runSurfaceDiff({ lexiconDir: dir, ...baseOpts });
|
|
69
|
+
expect(result.ok).toBe(false);
|
|
70
|
+
expect(result.failures.some((f) => f.step === "validate")).toBe(true);
|
|
71
|
+
} finally {
|
|
72
|
+
rmSync(dir, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("stale snapshot + healthy codegen: the update succeeds and the snapshot matches after", async () => {
|
|
77
|
+
const dir = makeLexiconDir(GATED_VALIDATE_SCRIPT);
|
|
78
|
+
try {
|
|
79
|
+
const result = await runSurfaceDiff({ lexiconDir: dir, ...baseOpts, updateSnapshot: true });
|
|
80
|
+
expect(result.ok).toBe(true);
|
|
81
|
+
expect(result.failures).toEqual([]);
|
|
82
|
+
|
|
83
|
+
// The baseline now describes the generated surface, not the stale one.
|
|
84
|
+
const written = parseSnapshot(readFileSync(join(dir, SNAPSHOT_FILENAME), "utf-8"));
|
|
85
|
+
expect(written.entries.Widget).toBeDefined();
|
|
86
|
+
expect(written.entries.Queue).toBeUndefined();
|
|
87
|
+
|
|
88
|
+
// A second run against the fresh baseline is green with no exemption.
|
|
89
|
+
const rerun = await runSurfaceDiff({ lexiconDir: dir, ...baseOpts });
|
|
90
|
+
expect(rerun.ok).toBe(true);
|
|
91
|
+
expect(rerun.changed).toBe(false);
|
|
92
|
+
} finally {
|
|
93
|
+
rmSync(dir, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("stale snapshot + another failing validate check: the update refuses", async () => {
|
|
98
|
+
// A validate that fails even with the exemption stands in for any check
|
|
99
|
+
// other than surface-matches-snapshot. The exemption covers only the
|
|
100
|
+
// staleness check, so a broken generate cannot be baselined.
|
|
101
|
+
const dir = makeLexiconDir("node -e \"process.exit(1)\"");
|
|
102
|
+
try {
|
|
103
|
+
const result = await runSurfaceDiff({ lexiconDir: dir, ...baseOpts, updateSnapshot: true });
|
|
104
|
+
expect(result.ok).toBe(false);
|
|
105
|
+
expect(result.failures.some((f) => f.step === "validate")).toBe(true);
|
|
106
|
+
// The stale baseline is untouched.
|
|
107
|
+
expect(readFileSync(join(dir, SNAPSHOT_FILENAME), "utf-8")).toBe(STALE_SNAPSHOT);
|
|
108
|
+
} finally {
|
|
109
|
+
rmSync(dir, { recursive: true, force: true });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
package/src/cli/commands/lint.ts
CHANGED
|
@@ -20,6 +20,8 @@ import { GENERATED_MARKER } from "../../discovery/files";
|
|
|
20
20
|
|
|
21
21
|
// Import config loader
|
|
22
22
|
import { loadConfig, resolveRulesForFile, resolveConfiguredSeverity, findProjectRoot } from "../../lint/config";
|
|
23
|
+
import { loadChantConfig } from "../../config";
|
|
24
|
+
import type { LintProjectConfig } from "../../lint/rule";
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* Type guard to check if a value conforms to the LintRule interface.
|
|
@@ -447,6 +449,17 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
447
449
|
const config = loadConfig(projectRoot);
|
|
448
450
|
const hasOverrides = config.overrides && config.overrides.length > 0;
|
|
449
451
|
|
|
452
|
+
// chant #1221 — the project's chant.config slice for config-aware rules
|
|
453
|
+
// (COR021 reads `environments` + `ownership`), threaded into every
|
|
454
|
+
// runLint() call below via LintContext.projectConfig. Best-effort: a
|
|
455
|
+
// directory with no project config lints with those rules silent.
|
|
456
|
+
let projectConfig: LintProjectConfig | undefined;
|
|
457
|
+
try {
|
|
458
|
+
projectConfig = (await loadChantConfig(projectRoot)).config as LintProjectConfig;
|
|
459
|
+
} catch {
|
|
460
|
+
projectConfig = undefined;
|
|
461
|
+
}
|
|
462
|
+
|
|
450
463
|
// Load all rules from lexicon plugins (core "chant" + lexicon-specific)
|
|
451
464
|
const loaded = await loadAllPluginRules(projectRoot);
|
|
452
465
|
let allRules = loaded.rules;
|
|
@@ -470,7 +483,7 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
470
483
|
let diagnostics: LintDiagnostic[];
|
|
471
484
|
let suppressed: Array<LintDiagnostic & { reason?: string }> = [];
|
|
472
485
|
if (options.rules) {
|
|
473
|
-
const result = await runLint(files, options.rules, undefined, intrinsics);
|
|
486
|
+
const result = await runLint(files, options.rules, undefined, intrinsics, projectConfig);
|
|
474
487
|
diagnostics = result.diagnostics;
|
|
475
488
|
suppressed = result.suppressed;
|
|
476
489
|
} else if (hasOverrides) {
|
|
@@ -478,13 +491,13 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
478
491
|
for (const file of files) {
|
|
479
492
|
const relativePath = relative(projectRoot, file);
|
|
480
493
|
const { rules: fileRules, ruleOptions } = getDefaultRules(projectRoot, relativePath, allRules);
|
|
481
|
-
const result = await runLint([file], fileRules, ruleOptions, intrinsics);
|
|
494
|
+
const result = await runLint([file], fileRules, ruleOptions, intrinsics, projectConfig);
|
|
482
495
|
diagnostics.push(...result.diagnostics);
|
|
483
496
|
suppressed.push(...result.suppressed);
|
|
484
497
|
}
|
|
485
498
|
} else {
|
|
486
499
|
const { rules, ruleOptions } = getDefaultRules(projectRoot, undefined, allRules);
|
|
487
|
-
const result = await runLint(files, rules, ruleOptions, intrinsics);
|
|
500
|
+
const result = await runLint(files, rules, ruleOptions, intrinsics, projectConfig);
|
|
488
501
|
diagnostics = result.diagnostics;
|
|
489
502
|
suppressed = result.suppressed;
|
|
490
503
|
}
|
|
@@ -518,7 +531,7 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
518
531
|
|
|
519
532
|
// Re-lint after fixes to get updated diagnostics
|
|
520
533
|
if (options.rules) {
|
|
521
|
-
const postResult = await runLint(files, options.rules, undefined, intrinsics);
|
|
534
|
+
const postResult = await runLint(files, options.rules, undefined, intrinsics, projectConfig);
|
|
522
535
|
diagnostics = postResult.diagnostics;
|
|
523
536
|
suppressed = postResult.suppressed;
|
|
524
537
|
} else if (hasOverrides) {
|
|
@@ -527,13 +540,13 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
527
540
|
for (const file of files) {
|
|
528
541
|
const relativePath = relative(projectRoot, file);
|
|
529
542
|
const { rules: fileRules, ruleOptions } = getDefaultRules(projectRoot, relativePath, allRules);
|
|
530
|
-
const postResult = await runLint([file], fileRules, ruleOptions, intrinsics);
|
|
543
|
+
const postResult = await runLint([file], fileRules, ruleOptions, intrinsics, projectConfig);
|
|
531
544
|
diagnostics.push(...postResult.diagnostics);
|
|
532
545
|
suppressed.push(...postResult.suppressed);
|
|
533
546
|
}
|
|
534
547
|
} else {
|
|
535
548
|
const { rules, ruleOptions } = getDefaultRules(projectRoot, undefined, allRules);
|
|
536
|
-
const postResult = await runLint(files, rules, ruleOptions, intrinsics);
|
|
549
|
+
const postResult = await runLint(files, rules, ruleOptions, intrinsics, projectConfig);
|
|
537
550
|
diagnostics = postResult.diagnostics;
|
|
538
551
|
suppressed = postResult.suppressed;
|
|
539
552
|
}
|