@intentius/chant 0.0.1
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/README.md +365 -0
- package/package.json +22 -0
- package/src/attrref.test.ts +148 -0
- package/src/attrref.ts +50 -0
- package/src/barrel.test.ts +157 -0
- package/src/barrel.ts +101 -0
- package/src/bench.test.ts +227 -0
- package/src/build.test.ts +437 -0
- package/src/build.ts +425 -0
- package/src/builder.test.ts +312 -0
- package/src/builder.ts +56 -0
- package/src/child-project.ts +44 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/README.md +26 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +16 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +8 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +7 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/examples/getting-started/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/justfile +26 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +29 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate-cli.ts +8 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate.ts +74 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/naming.ts +33 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/rollback.ts +45 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/coverage.ts +11 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/generated/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/generator.ts +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/parser.ts +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/index.ts +9 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/rules/index.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/rules/sample.ts +18 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lsp/completions.ts +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lsp/hover.ts +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +110 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/serializer.ts +24 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/spec/fetch.ts +21 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/spec/parse.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate.ts +24 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/tsconfig.json +10 -0
- package/src/cli/commands/__fixtures__/sample-rule.ts +11 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +222 -0
- package/src/cli/commands/build.test.ts +149 -0
- package/src/cli/commands/build.ts +344 -0
- package/src/cli/commands/diff.test.ts +148 -0
- package/src/cli/commands/diff.ts +221 -0
- package/src/cli/commands/doctor.test.ts +239 -0
- package/src/cli/commands/doctor.ts +224 -0
- package/src/cli/commands/import.test.ts +379 -0
- package/src/cli/commands/import.ts +335 -0
- package/src/cli/commands/init-lexicon.test.ts +297 -0
- package/src/cli/commands/init-lexicon.ts +993 -0
- package/src/cli/commands/init.test.ts +317 -0
- package/src/cli/commands/init.ts +505 -0
- package/src/cli/commands/licenses.ts +165 -0
- package/src/cli/commands/lint.test.ts +332 -0
- package/src/cli/commands/lint.ts +408 -0
- package/src/cli/commands/list.test.ts +100 -0
- package/src/cli/commands/list.ts +108 -0
- package/src/cli/commands/update.test.ts +38 -0
- package/src/cli/commands/update.ts +207 -0
- package/src/cli/conflict-check.test.ts +255 -0
- package/src/cli/conflict-check.ts +89 -0
- package/src/cli/debug.ts +8 -0
- package/src/cli/format.test.ts +140 -0
- package/src/cli/format.ts +133 -0
- package/src/cli/handlers/build.ts +58 -0
- package/src/cli/handlers/dev.ts +38 -0
- package/src/cli/handlers/init.ts +46 -0
- package/src/cli/handlers/lint.ts +36 -0
- package/src/cli/handlers/misc.ts +57 -0
- package/src/cli/handlers/serve.ts +26 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/lsp/capabilities.ts +46 -0
- package/src/cli/lsp/diagnostics.ts +52 -0
- package/src/cli/lsp/server.test.ts +618 -0
- package/src/cli/lsp/server.ts +393 -0
- package/src/cli/main.test.ts +257 -0
- package/src/cli/main.ts +224 -0
- package/src/cli/mcp/resources/context.ts +59 -0
- package/src/cli/mcp/server.test.ts +747 -0
- package/src/cli/mcp/server.ts +402 -0
- package/src/cli/mcp/tools/build.ts +117 -0
- package/src/cli/mcp/tools/import.ts +48 -0
- package/src/cli/mcp/tools/lint.ts +45 -0
- package/src/cli/plugins.test.ts +31 -0
- package/src/cli/plugins.ts +94 -0
- package/src/cli/registry.ts +73 -0
- package/src/cli/reporters/stylish.test.ts +282 -0
- package/src/cli/reporters/stylish.ts +186 -0
- package/src/cli/watch.test.ts +81 -0
- package/src/cli/watch.ts +101 -0
- package/src/codegen/case.test.ts +30 -0
- package/src/codegen/case.ts +11 -0
- package/src/codegen/coverage.ts +167 -0
- package/src/codegen/docs.ts +634 -0
- package/src/codegen/fetch.test.ts +119 -0
- package/src/codegen/fetch.ts +261 -0
- package/src/codegen/generate-registry.test.ts +118 -0
- package/src/codegen/generate-registry.ts +107 -0
- package/src/codegen/generate-runtime-index.test.ts +81 -0
- package/src/codegen/generate-runtime-index.ts +99 -0
- package/src/codegen/generate-typescript.test.ts +146 -0
- package/src/codegen/generate-typescript.ts +161 -0
- package/src/codegen/generate.ts +206 -0
- package/src/codegen/json-patch.test.ts +113 -0
- package/src/codegen/json-patch.ts +151 -0
- package/src/codegen/json-schema.test.ts +196 -0
- package/src/codegen/json-schema.ts +209 -0
- package/src/codegen/naming.ts +201 -0
- package/src/codegen/package.ts +161 -0
- package/src/codegen/rollback.test.ts +92 -0
- package/src/codegen/rollback.ts +115 -0
- package/src/codegen/topo-sort.test.ts +69 -0
- package/src/codegen/topo-sort.ts +46 -0
- package/src/codegen/typecheck.test.ts +37 -0
- package/src/codegen/typecheck.ts +74 -0
- package/src/codegen/validate.test.ts +86 -0
- package/src/codegen/validate.ts +143 -0
- package/src/composite.test.ts +426 -0
- package/src/composite.ts +243 -0
- package/src/config.test.ts +91 -0
- package/src/config.ts +87 -0
- package/src/declarable.test.ts +160 -0
- package/src/declarable.ts +47 -0
- package/src/detectLexicon.test.ts +236 -0
- package/src/detectLexicon.ts +37 -0
- package/src/discovery/cache.test.ts +78 -0
- package/src/discovery/cache.ts +86 -0
- package/src/discovery/collect.test.ts +269 -0
- package/src/discovery/collect.ts +51 -0
- package/src/discovery/cycles.test.ts +238 -0
- package/src/discovery/cycles.ts +107 -0
- package/src/discovery/files.test.ts +154 -0
- package/src/discovery/files.ts +61 -0
- package/src/discovery/graph.test.ts +476 -0
- package/src/discovery/graph.ts +150 -0
- package/src/discovery/import.test.ts +199 -0
- package/src/discovery/import.ts +20 -0
- package/src/discovery/index.test.ts +272 -0
- package/src/discovery/index.ts +132 -0
- package/src/discovery/resolve.test.ts +267 -0
- package/src/discovery/resolve.ts +54 -0
- package/src/errors.test.ts +138 -0
- package/src/errors.ts +86 -0
- package/src/import/base-parser.test.ts +67 -0
- package/src/import/base-parser.ts +48 -0
- package/src/import/generator.ts +21 -0
- package/src/import/ir-utils.test.ts +103 -0
- package/src/import/ir-utils.ts +87 -0
- package/src/import/parser.ts +41 -0
- package/src/index.ts +60 -0
- package/src/intrinsic-interpolation.test.ts +91 -0
- package/src/intrinsic-interpolation.ts +89 -0
- package/src/intrinsic.test.ts +69 -0
- package/src/intrinsic.ts +43 -0
- package/src/lexicon-integrity.test.ts +94 -0
- package/src/lexicon-integrity.ts +69 -0
- package/src/lexicon-manifest.test.ts +101 -0
- package/src/lexicon-manifest.ts +71 -0
- package/src/lexicon-output.test.ts +182 -0
- package/src/lexicon-output.ts +82 -0
- package/src/lexicon-schema.test.ts +239 -0
- package/src/lexicon-schema.ts +144 -0
- package/src/lexicon.ts +212 -0
- package/src/lint/config-overrides.test.ts +254 -0
- package/src/lint/config.test.ts +644 -0
- package/src/lint/config.ts +375 -0
- package/src/lint/declarative.test.ts +256 -0
- package/src/lint/declarative.ts +187 -0
- package/src/lint/engine.test.ts +465 -0
- package/src/lint/engine.ts +172 -0
- package/src/lint/named-checks.test.ts +37 -0
- package/src/lint/named-checks.ts +33 -0
- package/src/lint/parser.test.ts +129 -0
- package/src/lint/parser.ts +42 -0
- package/src/lint/post-synth.test.ts +113 -0
- package/src/lint/post-synth.ts +76 -0
- package/src/lint/presets/relaxed.json +19 -0
- package/src/lint/presets/strict.json +19 -0
- package/src/lint/rule-loader.test.ts +67 -0
- package/src/lint/rule-loader.ts +67 -0
- package/src/lint/rule-options.test.ts +141 -0
- package/src/lint/rule.test.ts +196 -0
- package/src/lint/rule.ts +98 -0
- package/src/lint/rules/barrel-import-style.test.ts +80 -0
- package/src/lint/rules/barrel-import-style.ts +59 -0
- package/src/lint/rules/composite-scope.ts +55 -0
- package/src/lint/rules/cor017-composite-name-match.test.ts +107 -0
- package/src/lint/rules/cor017-composite-name-match.ts +108 -0
- package/src/lint/rules/cor018-composite-prefer-lexicon-type.test.ts +172 -0
- package/src/lint/rules/cor018-composite-prefer-lexicon-type.ts +167 -0
- package/src/lint/rules/declarable-naming-convention.test.ts +69 -0
- package/src/lint/rules/declarable-naming-convention.ts +70 -0
- package/src/lint/rules/enforce-barrel-import.test.ts +169 -0
- package/src/lint/rules/enforce-barrel-import.ts +81 -0
- package/src/lint/rules/enforce-barrel-ref.test.ts +114 -0
- package/src/lint/rules/enforce-barrel-ref.ts +75 -0
- package/src/lint/rules/evl001-non-literal-expression.test.ts +158 -0
- package/src/lint/rules/evl001-non-literal-expression.ts +149 -0
- package/src/lint/rules/evl002-control-flow-resource.test.ts +110 -0
- package/src/lint/rules/evl002-control-flow-resource.ts +61 -0
- package/src/lint/rules/evl003-dynamic-property-access.test.ts +63 -0
- package/src/lint/rules/evl003-dynamic-property-access.ts +41 -0
- package/src/lint/rules/evl004-spread-non-const.test.ts +130 -0
- package/src/lint/rules/evl004-spread-non-const.ts +111 -0
- package/src/lint/rules/evl005-resource-block-body.test.ts +59 -0
- package/src/lint/rules/evl005-resource-block-body.ts +49 -0
- package/src/lint/rules/evl006-barrel-usage.test.ts +63 -0
- package/src/lint/rules/evl006-barrel-usage.ts +95 -0
- package/src/lint/rules/evl007-invalid-siblings.test.ts +87 -0
- package/src/lint/rules/evl007-invalid-siblings.ts +139 -0
- package/src/lint/rules/evl008-unresolvable-barrel-ref.test.ts +118 -0
- package/src/lint/rules/evl008-unresolvable-barrel-ref.ts +140 -0
- package/src/lint/rules/evl009-composite-no-constant.test.ts +162 -0
- package/src/lint/rules/evl009-composite-no-constant.ts +171 -0
- package/src/lint/rules/evl010-composite-no-transform.test.ts +121 -0
- package/src/lint/rules/evl010-composite-no-transform.ts +69 -0
- package/src/lint/rules/export-required.test.ts +213 -0
- package/src/lint/rules/export-required.ts +158 -0
- package/src/lint/rules/file-declarable-limit.test.ts +148 -0
- package/src/lint/rules/file-declarable-limit.ts +96 -0
- package/src/lint/rules/flat-declarations.test.ts +210 -0
- package/src/lint/rules/flat-declarations.ts +70 -0
- package/src/lint/rules/index.ts +99 -0
- package/src/lint/rules/no-cyclic-declarable-ref.test.ts +135 -0
- package/src/lint/rules/no-cyclic-declarable-ref.ts +178 -0
- package/src/lint/rules/no-redundant-type-import.test.ts +129 -0
- package/src/lint/rules/no-redundant-type-import.ts +85 -0
- package/src/lint/rules/no-redundant-value-cast.test.ts +51 -0
- package/src/lint/rules/no-redundant-value-cast.ts +46 -0
- package/src/lint/rules/no-string-ref.test.ts +100 -0
- package/src/lint/rules/no-string-ref.ts +66 -0
- package/src/lint/rules/no-unused-declarable-import.test.ts +74 -0
- package/src/lint/rules/no-unused-declarable-import.ts +103 -0
- package/src/lint/rules/no-unused-declarable.test.ts +134 -0
- package/src/lint/rules/no-unused-declarable.ts +118 -0
- package/src/lint/rules/prefer-namespace-import.test.ts +102 -0
- package/src/lint/rules/prefer-namespace-import.ts +63 -0
- package/src/lint/rules/single-concern-file.test.ts +156 -0
- package/src/lint/rules/single-concern-file.ts +98 -0
- package/src/lint/rules/stale-barrel-types.ts +60 -0
- package/src/lint/selectors.test.ts +113 -0
- package/src/lint/selectors.ts +188 -0
- package/src/lsp/lexicon-providers.ts +191 -0
- package/src/lsp/types.ts +79 -0
- package/src/mcp/types.ts +22 -0
- package/src/project/scan.test.ts +178 -0
- package/src/project/scan.ts +182 -0
- package/src/project/sync.test.ts +87 -0
- package/src/project/sync.ts +46 -0
- package/src/project-validation.test.ts +64 -0
- package/src/project-validation.ts +79 -0
- package/src/pseudo-parameter.test.ts +39 -0
- package/src/pseudo-parameter.ts +47 -0
- package/src/runtime.ts +68 -0
- package/src/serializer-walker.test.ts +124 -0
- package/src/serializer-walker.ts +83 -0
- package/src/serializer.ts +42 -0
- package/src/sort.test.ts +290 -0
- package/src/sort.ts +58 -0
- package/src/stack-output.ts +82 -0
- package/src/types.test.ts +307 -0
- package/src/types.ts +46 -0
- package/src/utils.test.ts +195 -0
- package/src/utils.ts +46 -0
- package/src/validation.test.ts +308 -0
- package/src/validation.ts +50 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { typecheckDTS } from "./typecheck";
|
|
3
|
+
|
|
4
|
+
describe("typecheckDTS", () => {
|
|
5
|
+
test("passes valid .d.ts", async () => {
|
|
6
|
+
const content = `
|
|
7
|
+
export declare class Bucket {
|
|
8
|
+
readonly type: string;
|
|
9
|
+
readonly bucketName?: string;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
const result = await typecheckDTS(content);
|
|
13
|
+
expect(result.ok).toBe(true);
|
|
14
|
+
expect(result.diagnostics).toHaveLength(0);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("fails on undefined type reference", async () => {
|
|
18
|
+
const content = `
|
|
19
|
+
export declare class Broken {
|
|
20
|
+
readonly value: UndefinedType;
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
const result = await typecheckDTS(content);
|
|
24
|
+
expect(result.ok).toBe(false);
|
|
25
|
+
expect(result.diagnostics.length).toBeGreaterThan(0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("fails on syntax error", async () => {
|
|
29
|
+
const content = `
|
|
30
|
+
export declare class {
|
|
31
|
+
this is not valid typescript
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
const result = await typecheckDTS(content);
|
|
35
|
+
expect(result.ok).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-check generated .d.ts files by spawning tsc.
|
|
3
|
+
*/
|
|
4
|
+
import { writeFileSync, mkdirSync, rmSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { tmpdir } from "os";
|
|
7
|
+
|
|
8
|
+
export interface TypeCheckResult {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
diagnostics: string[];
|
|
11
|
+
fileCount: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Type-check a .d.ts content string by writing to a temp file and running tsc.
|
|
16
|
+
*/
|
|
17
|
+
export async function typecheckDTS(content: string): Promise<TypeCheckResult> {
|
|
18
|
+
// Create temp directory
|
|
19
|
+
const dir = join(tmpdir(), `chant-typecheck-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
20
|
+
mkdirSync(dir, { recursive: true });
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Write the .d.ts file
|
|
24
|
+
const dtsPath = join(dir, "index.d.ts");
|
|
25
|
+
writeFileSync(dtsPath, content);
|
|
26
|
+
|
|
27
|
+
// Write a minimal tsconfig
|
|
28
|
+
const tsconfig = {
|
|
29
|
+
compilerOptions: {
|
|
30
|
+
strict: true,
|
|
31
|
+
noEmit: true,
|
|
32
|
+
skipLibCheck: false,
|
|
33
|
+
target: "ES2022",
|
|
34
|
+
module: "ES2022",
|
|
35
|
+
moduleResolution: "bundler",
|
|
36
|
+
},
|
|
37
|
+
include: ["index.d.ts"],
|
|
38
|
+
};
|
|
39
|
+
writeFileSync(join(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
40
|
+
|
|
41
|
+
// Run tsc
|
|
42
|
+
const proc = Bun.spawn(["bunx", "tsc", "--noEmit", "--project", "tsconfig.json"], {
|
|
43
|
+
cwd: dir,
|
|
44
|
+
stdout: "pipe",
|
|
45
|
+
stderr: "pipe",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const [stdout, stderr] = await Promise.all([
|
|
49
|
+
new Response(proc.stdout).text(),
|
|
50
|
+
new Response(proc.stderr).text(),
|
|
51
|
+
]);
|
|
52
|
+
const exitCode = await proc.exited;
|
|
53
|
+
|
|
54
|
+
// Parse diagnostics from stdout (tsc writes errors to stdout)
|
|
55
|
+
const output = stdout + stderr;
|
|
56
|
+
const diagnostics = output
|
|
57
|
+
.split("\n")
|
|
58
|
+
.filter((line) => line.trim().length > 0)
|
|
59
|
+
.filter((line) => /error TS\d+/.test(line) || /:\d+:\d+/.test(line));
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
ok: exitCode === 0,
|
|
63
|
+
diagnostics,
|
|
64
|
+
fileCount: 1,
|
|
65
|
+
};
|
|
66
|
+
} finally {
|
|
67
|
+
// Cleanup
|
|
68
|
+
try {
|
|
69
|
+
rmSync(dir, { recursive: true, force: true });
|
|
70
|
+
} catch {
|
|
71
|
+
// Best effort cleanup
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { mkdirSync, writeFileSync, rmSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
import { validateLexiconArtifacts } from "./validate";
|
|
6
|
+
|
|
7
|
+
function makeTempDir(): string {
|
|
8
|
+
const dir = join(tmpdir(), `chant-validate-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
9
|
+
mkdirSync(dir, { recursive: true });
|
|
10
|
+
return dir;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("validateLexiconArtifacts", () => {
|
|
14
|
+
test("fails when lexicon JSON is missing", async () => {
|
|
15
|
+
const dir = makeTempDir();
|
|
16
|
+
const genDir = join(dir, "src", "generated");
|
|
17
|
+
mkdirSync(genDir, { recursive: true });
|
|
18
|
+
|
|
19
|
+
const result = await validateLexiconArtifacts({
|
|
20
|
+
lexiconJsonFilename: "lexicon-test.json",
|
|
21
|
+
requiredNames: ["Resource"],
|
|
22
|
+
basePath: dir,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(result.success).toBe(false);
|
|
26
|
+
const jsonCheck = result.checks.find((c) => c.name === "lexicon-json-exists");
|
|
27
|
+
expect(jsonCheck?.ok).toBe(false);
|
|
28
|
+
expect(jsonCheck?.error).toContain("lexicon-test.json not found");
|
|
29
|
+
|
|
30
|
+
rmSync(dir, { recursive: true, force: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("passes with valid artifacts and required names", async () => {
|
|
34
|
+
const dir = makeTempDir();
|
|
35
|
+
const genDir = join(dir, "src", "generated");
|
|
36
|
+
mkdirSync(genDir, { recursive: true });
|
|
37
|
+
|
|
38
|
+
const lexiconData = {
|
|
39
|
+
Resource: {
|
|
40
|
+
resourceType: "Test::Storage::Bucket",
|
|
41
|
+
kind: "resource",
|
|
42
|
+
lexicon: "test",
|
|
43
|
+
attrs: { arn: "Arn" },
|
|
44
|
+
createOnly: ["/properties/Name"],
|
|
45
|
+
propertyConstraints: { Name: { minLength: 1 } },
|
|
46
|
+
constraints: [{ name: "c1", type: "required_or" }],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
writeFileSync(join(genDir, "lexicon-test.json"), JSON.stringify(lexiconData));
|
|
50
|
+
writeFileSync(join(genDir, "index.d.ts"), "export declare class Resource { readonly type: string; }");
|
|
51
|
+
|
|
52
|
+
const result = await validateLexiconArtifacts({
|
|
53
|
+
lexiconJsonFilename: "lexicon-test.json",
|
|
54
|
+
requiredNames: ["Resource"],
|
|
55
|
+
basePath: dir,
|
|
56
|
+
coverageThresholds: { minPropertyPct: 1 },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(result.success).toBe(true);
|
|
60
|
+
expect(result.checks.every((c) => c.ok)).toBe(true);
|
|
61
|
+
|
|
62
|
+
rmSync(dir, { recursive: true, force: true });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("detects missing required names", async () => {
|
|
66
|
+
const dir = makeTempDir();
|
|
67
|
+
const genDir = join(dir, "src", "generated");
|
|
68
|
+
mkdirSync(genDir, { recursive: true });
|
|
69
|
+
|
|
70
|
+
writeFileSync(join(genDir, "lexicon-test.json"), JSON.stringify({ Foo: { resourceType: "T", kind: "resource", lexicon: "t" } }));
|
|
71
|
+
writeFileSync(join(genDir, "index.d.ts"), "export {};");
|
|
72
|
+
|
|
73
|
+
const result = await validateLexiconArtifacts({
|
|
74
|
+
lexiconJsonFilename: "lexicon-test.json",
|
|
75
|
+
requiredNames: ["Bar", "Baz"],
|
|
76
|
+
basePath: dir,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const namesCheck = result.checks.find((c) => c.name === "required-names");
|
|
80
|
+
expect(namesCheck?.ok).toBe(false);
|
|
81
|
+
expect(namesCheck?.error).toContain("Bar");
|
|
82
|
+
expect(namesCheck?.error).toContain("Baz");
|
|
83
|
+
|
|
84
|
+
rmSync(dir, { recursive: true, force: true });
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic validation framework for lexicon artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Provides configurable validation checks that any lexicon can use
|
|
5
|
+
* by passing lexicon-specific configuration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { computeCoverage, checkThresholds, type CoverageThresholds } from "./coverage";
|
|
11
|
+
|
|
12
|
+
export interface ValidateCheck {
|
|
13
|
+
name: string;
|
|
14
|
+
ok: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ValidateResult {
|
|
19
|
+
success: boolean;
|
|
20
|
+
checks: ValidateCheck[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface LexiconValidationConfig {
|
|
24
|
+
/** Filename of the lexicon JSON (e.g. "lexicon-mydom.json") */
|
|
25
|
+
lexiconJsonFilename: string;
|
|
26
|
+
/** Required backward-compatible export names to check in lexicon JSON */
|
|
27
|
+
requiredNames: string[];
|
|
28
|
+
/** Base path of the lexicon package */
|
|
29
|
+
basePath: string;
|
|
30
|
+
/** Path to the generated directory (defaults to basePath/src/generated) */
|
|
31
|
+
generatedDir?: string;
|
|
32
|
+
/** Coverage thresholds (optional) */
|
|
33
|
+
coverageThresholds?: CoverageThresholds;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate generated lexicon artifacts using the provided configuration.
|
|
38
|
+
*/
|
|
39
|
+
export async function validateLexiconArtifacts(config: LexiconValidationConfig): Promise<ValidateResult> {
|
|
40
|
+
const generatedDir = config.generatedDir ?? join(config.basePath, "src", "generated");
|
|
41
|
+
const checks: ValidateCheck[] = [];
|
|
42
|
+
|
|
43
|
+
// Check 1: lexicon JSON exists and parses
|
|
44
|
+
const lexiconPath = join(generatedDir, config.lexiconJsonFilename);
|
|
45
|
+
let lexiconData: Record<string, unknown> | null = null;
|
|
46
|
+
|
|
47
|
+
if (!existsSync(lexiconPath)) {
|
|
48
|
+
checks.push({ name: "lexicon-json-exists", ok: false, error: `${config.lexiconJsonFilename} not found` });
|
|
49
|
+
} else {
|
|
50
|
+
try {
|
|
51
|
+
const raw = readFileSync(lexiconPath, "utf-8");
|
|
52
|
+
lexiconData = JSON.parse(raw);
|
|
53
|
+
checks.push({ name: "lexicon-json-exists", ok: true });
|
|
54
|
+
} catch (err) {
|
|
55
|
+
checks.push({
|
|
56
|
+
name: "lexicon-json-exists",
|
|
57
|
+
ok: false,
|
|
58
|
+
error: `Failed to parse ${config.lexiconJsonFilename}: ${err instanceof Error ? err.message : String(err)}`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check 2: index.d.ts exists
|
|
64
|
+
const dtsPath = join(generatedDir, "index.d.ts");
|
|
65
|
+
if (!existsSync(dtsPath)) {
|
|
66
|
+
checks.push({ name: "types-exist", ok: false, error: "index.d.ts not found" });
|
|
67
|
+
} else {
|
|
68
|
+
checks.push({ name: "types-exist", ok: true });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check 3: Required backward-compatible names present in lexicon JSON
|
|
72
|
+
if (lexiconData && config.requiredNames.length > 0) {
|
|
73
|
+
const missing = config.requiredNames.filter((name) => !(name in lexiconData!));
|
|
74
|
+
if (missing.length > 0) {
|
|
75
|
+
checks.push({
|
|
76
|
+
name: "required-names",
|
|
77
|
+
ok: false,
|
|
78
|
+
error: `Missing required names: ${missing.join(", ")}`,
|
|
79
|
+
});
|
|
80
|
+
} else {
|
|
81
|
+
checks.push({ name: "required-names", ok: true });
|
|
82
|
+
}
|
|
83
|
+
} else if (!lexiconData && config.requiredNames.length > 0) {
|
|
84
|
+
checks.push({
|
|
85
|
+
name: "required-names",
|
|
86
|
+
ok: false,
|
|
87
|
+
error: "Skipped — lexicon JSON not available",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check 4: Coverage thresholds (only if lexicon JSON was loaded)
|
|
92
|
+
if (lexiconData && config.coverageThresholds) {
|
|
93
|
+
try {
|
|
94
|
+
const raw = readFileSync(lexiconPath, "utf-8");
|
|
95
|
+
const report = computeCoverage(raw);
|
|
96
|
+
const result = checkThresholds(report, config.coverageThresholds);
|
|
97
|
+
if (result.ok) {
|
|
98
|
+
checks.push({ name: "coverage-thresholds", ok: true });
|
|
99
|
+
} else {
|
|
100
|
+
checks.push({
|
|
101
|
+
name: "coverage-thresholds",
|
|
102
|
+
ok: false,
|
|
103
|
+
error: result.violations.join("; "),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
checks.push({
|
|
108
|
+
name: "coverage-thresholds",
|
|
109
|
+
ok: false,
|
|
110
|
+
error: `Coverage check failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check 5: Type-check generated .d.ts
|
|
116
|
+
if (existsSync(dtsPath)) {
|
|
117
|
+
try {
|
|
118
|
+
const { typecheckDTS } = await import("./typecheck");
|
|
119
|
+
const dtsContent = readFileSync(dtsPath, "utf-8");
|
|
120
|
+
const tcResult = await typecheckDTS(dtsContent);
|
|
121
|
+
if (tcResult.ok) {
|
|
122
|
+
checks.push({ name: "types-compile", ok: true });
|
|
123
|
+
} else {
|
|
124
|
+
checks.push({
|
|
125
|
+
name: "types-compile",
|
|
126
|
+
ok: false,
|
|
127
|
+
error: `TypeScript errors: ${tcResult.diagnostics.slice(0, 5).join("; ")}`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
checks.push({
|
|
132
|
+
name: "types-compile",
|
|
133
|
+
ok: false,
|
|
134
|
+
error: `Type-check failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
success: checks.every((c) => c.ok),
|
|
141
|
+
checks,
|
|
142
|
+
};
|
|
143
|
+
}
|