@intentius/chant 0.0.4 → 0.0.5
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 +10 -351
- package/package.json +1 -1
- package/src/bench.test.ts +3 -54
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +12 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate.ts +22 -18
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +12 -2
- package/src/cli/commands/import.test.ts +1 -1
- package/src/cli/commands/init-lexicon.ts +34 -20
- package/src/cli/commands/init.test.ts +10 -14
- package/src/cli/commands/init.ts +2 -7
- package/src/cli/commands/lint.ts +9 -33
- package/src/cli/main.ts +1 -1
- package/src/codegen/docs-interpolation.test.ts +2 -2
- package/src/codegen/docs.ts +5 -4
- package/src/codegen/generate-registry.test.ts +1 -1
- package/src/codegen/generate-registry.ts +3 -3
- package/src/codegen/package.ts +28 -1
- package/src/codegen/validate.ts +16 -0
- package/src/discovery/files.ts +6 -6
- package/src/discovery/import.ts +1 -1
- package/src/index.ts +0 -1
- package/src/lint/engine.ts +1 -5
- package/src/lint/rule.ts +0 -18
- package/src/lint/rules/evl009-composite-no-constant.test.ts +24 -8
- package/src/lint/rules/evl009-composite-no-constant.ts +50 -29
- package/src/lint/rules/index.ts +1 -22
- package/src/stack-output.ts +3 -3
- package/src/barrel.test.ts +0 -157
- package/src/barrel.ts +0 -101
- package/src/lint/rules/barrel-import-style.test.ts +0 -80
- package/src/lint/rules/barrel-import-style.ts +0 -59
- package/src/lint/rules/enforce-barrel-import.test.ts +0 -169
- package/src/lint/rules/enforce-barrel-import.ts +0 -81
- package/src/lint/rules/enforce-barrel-ref.test.ts +0 -114
- package/src/lint/rules/enforce-barrel-ref.ts +0 -75
- package/src/lint/rules/evl006-barrel-usage.test.ts +0 -63
- package/src/lint/rules/evl006-barrel-usage.ts +0 -95
- package/src/lint/rules/evl008-unresolvable-barrel-ref.test.ts +0 -118
- package/src/lint/rules/evl008-unresolvable-barrel-ref.ts +0 -140
- package/src/lint/rules/prefer-namespace-import.test.ts +0 -102
- package/src/lint/rules/prefer-namespace-import.ts +0 -63
- package/src/lint/rules/stale-barrel-types.ts +0 -60
- package/src/project/scan.test.ts +0 -178
- package/src/project/scan.ts +0 -182
- package/src/project/sync.test.ts +0 -87
- package/src/project/sync.ts +0 -46
|
@@ -22,14 +22,13 @@ describe("initCommand", () => {
|
|
|
22
22
|
expect(result.createdFiles).toContain("tsconfig.json");
|
|
23
23
|
expect(result.createdFiles).toContain("chant.config.ts");
|
|
24
24
|
expect(result.createdFiles).toContain(".gitignore");
|
|
25
|
-
expect(result.createdFiles).toContain("src/_.ts");
|
|
26
25
|
expect(result.createdFiles).toContain("src/config.ts");
|
|
27
26
|
expect(result.createdFiles).toContain("src/data-bucket.ts");
|
|
28
27
|
expect(result.createdFiles).toContain("src/logs-bucket.ts");
|
|
29
28
|
});
|
|
30
29
|
});
|
|
31
30
|
|
|
32
|
-
test("aws source files use
|
|
31
|
+
test("aws source files use direct imports", async () => {
|
|
33
32
|
await withTestDir(async (testDir) => {
|
|
34
33
|
const options: InitOptions = {
|
|
35
34
|
path: testDir,
|
|
@@ -41,15 +40,15 @@ describe("initCommand", () => {
|
|
|
41
40
|
await initCommand(options);
|
|
42
41
|
|
|
43
42
|
const configContent = readFileSync(join(testDir, "src", "config.ts"), "utf-8");
|
|
44
|
-
expect(configContent).toContain('
|
|
43
|
+
expect(configContent).toContain('from "@intentius/chant-lexicon-aws"');
|
|
45
44
|
|
|
46
45
|
const dataBucketContent = readFileSync(join(testDir, "src", "data-bucket.ts"), "utf-8");
|
|
47
|
-
expect(dataBucketContent).toContain('
|
|
48
|
-
expect(dataBucketContent).toContain('
|
|
46
|
+
expect(dataBucketContent).toContain('from "@intentius/chant-lexicon-aws"');
|
|
47
|
+
expect(dataBucketContent).toContain('from "./config"');
|
|
49
48
|
|
|
50
49
|
const logsBucketContent = readFileSync(join(testDir, "src", "logs-bucket.ts"), "utf-8");
|
|
51
|
-
expect(logsBucketContent).toContain('
|
|
52
|
-
expect(logsBucketContent).toContain('
|
|
50
|
+
expect(logsBucketContent).toContain('from "@intentius/chant-lexicon-aws"');
|
|
51
|
+
expect(logsBucketContent).toContain('from "./config"');
|
|
53
52
|
});
|
|
54
53
|
});
|
|
55
54
|
|
|
@@ -177,7 +176,7 @@ describe("initCommand", () => {
|
|
|
177
176
|
});
|
|
178
177
|
});
|
|
179
178
|
|
|
180
|
-
test("
|
|
179
|
+
test("does not generate barrel file", async () => {
|
|
181
180
|
await withTestDir(async (testDir) => {
|
|
182
181
|
const options: InitOptions = {
|
|
183
182
|
path: testDir,
|
|
@@ -188,13 +187,11 @@ describe("initCommand", () => {
|
|
|
188
187
|
|
|
189
188
|
await initCommand(options);
|
|
190
189
|
|
|
190
|
+
// No _.ts barrel — direct imports are used instead
|
|
191
191
|
const barrelPath = join(testDir, "src", "_.ts");
|
|
192
|
-
expect(existsSync(barrelPath)).toBe(
|
|
192
|
+
expect(existsSync(barrelPath)).toBe(false);
|
|
193
193
|
|
|
194
|
-
|
|
195
|
-
expect(barrelContent).toContain('export * from "./config"');
|
|
196
|
-
|
|
197
|
-
// No index.ts — barrel re-exports cause duplicate entity errors during build
|
|
194
|
+
// No index.ts either
|
|
198
195
|
const indexPath = join(testDir, "src", "index.ts");
|
|
199
196
|
expect(existsSync(indexPath)).toBe(false);
|
|
200
197
|
});
|
|
@@ -218,7 +215,6 @@ describe("initCommand", () => {
|
|
|
218
215
|
expect(coreContent).toContain("Value<T>");
|
|
219
216
|
expect(coreContent).toContain("Serializer");
|
|
220
217
|
expect(coreContent).toContain("ChantConfig");
|
|
221
|
-
expect(coreContent).toContain("barrel");
|
|
222
218
|
|
|
223
219
|
const corePkg = join(testDir, ".chant", "types", "core", "package.json");
|
|
224
220
|
expect(existsSync(corePkg)).toBe(true);
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -214,8 +214,6 @@ export interface ChantConfig {
|
|
|
214
214
|
};
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
/** Barrel proxy — lazy-loads all sibling exports */
|
|
218
|
-
export declare function barrel(dir: string): Record<string, unknown>;
|
|
219
217
|
`;
|
|
220
218
|
}
|
|
221
219
|
|
|
@@ -345,7 +343,7 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
|
|
|
345
343
|
warnings,
|
|
346
344
|
);
|
|
347
345
|
|
|
348
|
-
// Generate source files from plugin (
|
|
346
|
+
// Generate source files from plugin (if available)
|
|
349
347
|
let sourceFiles: Record<string, string> = {};
|
|
350
348
|
try {
|
|
351
349
|
const plugin = await loadPlugin(options.lexicon);
|
|
@@ -353,10 +351,7 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
|
|
|
353
351
|
sourceFiles = plugin.initTemplates();
|
|
354
352
|
}
|
|
355
353
|
} catch {
|
|
356
|
-
// Plugin not yet installed —
|
|
357
|
-
sourceFiles = {
|
|
358
|
-
"_.ts": "// Barrel — re-export shared config here\n",
|
|
359
|
-
};
|
|
354
|
+
// Plugin not yet installed — no source files to scaffold
|
|
360
355
|
}
|
|
361
356
|
for (const [filename, content] of Object.entries(sourceFiles)) {
|
|
362
357
|
writeIfNotExists(
|
package/src/cli/commands/lint.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve, join } from "path";
|
|
2
2
|
import { readFileSync, writeFileSync, readdirSync, statSync } from "fs";
|
|
3
3
|
import { runLint } from "../../lint/engine";
|
|
4
|
-
import type { LintRule, LintDiagnostic, LintFix
|
|
4
|
+
import type { LintRule, LintDiagnostic, LintFix } from "../../lint/rule";
|
|
5
5
|
import { loadPlugins, resolveProjectLexicons } from "../plugins";
|
|
6
6
|
import { formatStylish, formatJson, formatSarif } from "../reporters/stylish";
|
|
7
7
|
import { loadLocalRules } from "../../lint/rule-loader";
|
|
@@ -241,54 +241,30 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
241
241
|
// Get all TypeScript files
|
|
242
242
|
const files = getTypeScriptFiles(infraPath);
|
|
243
243
|
|
|
244
|
-
// Build barrel exports context for EVL008
|
|
245
|
-
let runOptions: LintRunOptions | undefined;
|
|
246
|
-
try {
|
|
247
|
-
const { scanProject } = require("../../project/scan");
|
|
248
|
-
const scan = scanProject(infraPath);
|
|
249
|
-
const barrelExports = new Set<string>(scan.exports.map((e: { name: string }) => e.name));
|
|
250
|
-
const projectExports = new Map<string, { file: string; className: string }>();
|
|
251
|
-
for (const exp of scan.exports) {
|
|
252
|
-
projectExports.set(exp.name, { file: exp.file, className: exp.className });
|
|
253
|
-
}
|
|
254
|
-
runOptions = { barrelExports, projectExports, projectScan: scan };
|
|
255
|
-
} catch {
|
|
256
|
-
// No barrel file found — EVL008/COR016 will be no-ops
|
|
257
|
-
}
|
|
258
|
-
|
|
259
244
|
// Run lint — use per-file rules when overrides are present
|
|
260
245
|
let diagnostics: LintDiagnostic[];
|
|
261
246
|
if (options.rules) {
|
|
262
|
-
diagnostics = await runLint(files, options.rules, undefined
|
|
247
|
+
diagnostics = await runLint(files, options.rules, undefined);
|
|
263
248
|
} else if (hasOverrides) {
|
|
264
249
|
diagnostics = [];
|
|
265
250
|
for (const file of files) {
|
|
266
251
|
const relativePath = file.slice(infraPath.length + 1);
|
|
267
252
|
const { rules: fileRules, ruleOptions } = getDefaultRules(infraPath, relativePath, allRules);
|
|
268
|
-
const fileDiagnostics = await runLint([file], fileRules, ruleOptions
|
|
253
|
+
const fileDiagnostics = await runLint([file], fileRules, ruleOptions);
|
|
269
254
|
diagnostics.push(...fileDiagnostics);
|
|
270
255
|
}
|
|
271
256
|
} else {
|
|
272
257
|
const { rules, ruleOptions } = getDefaultRules(infraPath, undefined, allRules);
|
|
273
|
-
diagnostics = await runLint(files, rules, ruleOptions
|
|
258
|
+
diagnostics = await runLint(files, rules, ruleOptions);
|
|
274
259
|
}
|
|
275
260
|
|
|
276
261
|
// Apply fixes if requested
|
|
277
262
|
if (options.fix) {
|
|
278
|
-
//
|
|
279
|
-
for (const diag of diagnostics) {
|
|
280
|
-
if (diag.fix?.kind === "write-file" && diag.fix.params) {
|
|
281
|
-
const path = diag.fix.params.path as string;
|
|
282
|
-
const content = diag.fix.params.content as string;
|
|
283
|
-
writeFileSync(path, content, "utf-8");
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Group remaining fixes by file (exclude write-file fixes)
|
|
263
|
+
// Group fixes by file
|
|
288
264
|
const fixesByFile = new Map<string, LintFix[]>();
|
|
289
265
|
|
|
290
266
|
for (const diag of diagnostics) {
|
|
291
|
-
if (diag.fix
|
|
267
|
+
if (diag.fix) {
|
|
292
268
|
const existing = fixesByFile.get(diag.file) ?? [];
|
|
293
269
|
existing.push(diag.fix);
|
|
294
270
|
fixesByFile.set(diag.file, existing);
|
|
@@ -303,18 +279,18 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
303
279
|
// Re-lint after fixes to get updated diagnostics
|
|
304
280
|
let postFixDiagnostics: LintDiagnostic[];
|
|
305
281
|
if (options.rules) {
|
|
306
|
-
postFixDiagnostics = await runLint(files, options.rules, undefined
|
|
282
|
+
postFixDiagnostics = await runLint(files, options.rules, undefined);
|
|
307
283
|
} else if (hasOverrides) {
|
|
308
284
|
postFixDiagnostics = [];
|
|
309
285
|
for (const file of files) {
|
|
310
286
|
const relativePath = file.slice(infraPath.length + 1);
|
|
311
287
|
const { rules: fileRules, ruleOptions } = getDefaultRules(infraPath, relativePath, allRules);
|
|
312
|
-
const fileDiagnostics = await runLint([file], fileRules, ruleOptions
|
|
288
|
+
const fileDiagnostics = await runLint([file], fileRules, ruleOptions);
|
|
313
289
|
postFixDiagnostics.push(...fileDiagnostics);
|
|
314
290
|
}
|
|
315
291
|
} else {
|
|
316
292
|
const { rules, ruleOptions } = getDefaultRules(infraPath, undefined, allRules);
|
|
317
|
-
postFixDiagnostics = await runLint(files, rules, ruleOptions
|
|
293
|
+
postFixDiagnostics = await runLint(files, rules, ruleOptions);
|
|
318
294
|
}
|
|
319
295
|
diagnostics.length = 0;
|
|
320
296
|
diagnostics.push(...postFixDiagnostics);
|
package/src/cli/main.ts
CHANGED
|
@@ -161,7 +161,7 @@ const registry: CommandDef[] = [
|
|
|
161
161
|
{ name: "import", handler: runImport },
|
|
162
162
|
{ name: "init", handler: runInit },
|
|
163
163
|
{ name: "init lexicon", handler: runInitLexicon },
|
|
164
|
-
|
|
164
|
+
{ name: "update", handler: runUpdate },
|
|
165
165
|
{ name: "doctor", handler: runDoctor },
|
|
166
166
|
|
|
167
167
|
// Dev subcommands
|
|
@@ -12,7 +12,7 @@ describe("expandFileMarkers", () => {
|
|
|
12
12
|
mkdirSync(join(dir, "sub"), { recursive: true });
|
|
13
13
|
writeFileSync(
|
|
14
14
|
join(dir, "example.ts"),
|
|
15
|
-
'import
|
|
15
|
+
'import { Bucket } from "@intentius/chant-lexicon-aws";\n\nexport const bucket = new Bucket({\n bucketName: "test",\n});\n',
|
|
16
16
|
);
|
|
17
17
|
writeFileSync(
|
|
18
18
|
join(dir, "sub", "nested.ts"),
|
|
@@ -27,7 +27,7 @@ describe("expandFileMarkers", () => {
|
|
|
27
27
|
test("expands full file marker", () => {
|
|
28
28
|
const result = expandFileMarkers("Before\n\n{{file:example.ts}}\n\nAfter", dir);
|
|
29
29
|
expect(result).toContain('```typescript title="example.ts"');
|
|
30
|
-
expect(result).toContain('import
|
|
30
|
+
expect(result).toContain('import { Bucket } from "@intentius/chant-lexicon-aws";');
|
|
31
31
|
expect(result).toContain("```");
|
|
32
32
|
expect(result).toStartWith("Before\n\n");
|
|
33
33
|
expect(result).toEndWith("\n\nAfter");
|
package/src/codegen/docs.ts
CHANGED
|
@@ -190,10 +190,11 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
190
190
|
// Generate pages
|
|
191
191
|
const pages = new Map<string, string>();
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
let overviewContent = generateOverview(config, manifest, resources, properties, serviceGroups, rules);
|
|
194
|
+
if (config.examplesDir) {
|
|
195
|
+
overviewContent = expandFileMarkers(overviewContent, config.examplesDir);
|
|
196
|
+
}
|
|
197
|
+
pages.set("index.mdx", overviewContent);
|
|
197
198
|
const suppress = new Set(config.suppressPages ?? []);
|
|
198
199
|
|
|
199
200
|
// Extra pages from lexicon config
|
|
@@ -69,7 +69,7 @@ describe("buildRegistry", () => {
|
|
|
69
69
|
typeName: "Test::S3::Bucket",
|
|
70
70
|
attributes: [],
|
|
71
71
|
properties: [],
|
|
72
|
-
propertyTypes: [{ name: "Bucket_Versioning",
|
|
72
|
+
propertyTypes: [{ name: "Bucket_Versioning", specType: "Versioning" }],
|
|
73
73
|
},
|
|
74
74
|
];
|
|
75
75
|
const naming = makeNaming(resources);
|
|
@@ -15,11 +15,11 @@ export interface RegistryResource {
|
|
|
15
15
|
typeName: string;
|
|
16
16
|
attributes: { name: string }[];
|
|
17
17
|
properties: { name: string; constraints: PropertyConstraints }[];
|
|
18
|
-
propertyTypes: { name: string;
|
|
18
|
+
propertyTypes: { name: string; specType: string }[];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export interface RegistryConfig<E> {
|
|
22
|
-
/** Short name extractor (e.g.
|
|
22
|
+
/** Short name extractor (e.g. shortName from the spec type). */
|
|
23
23
|
shortName: (typeName: string) => string;
|
|
24
24
|
/** Build a resource entry from parsed data. */
|
|
25
25
|
buildEntry: (
|
|
@@ -80,7 +80,7 @@ export function buildRegistry<E>(
|
|
|
80
80
|
for (const pt of r.propertyTypes) {
|
|
81
81
|
const defName = extractDefName(pt.name, shortName);
|
|
82
82
|
const ptName = propertyTypeName(tsName, defName);
|
|
83
|
-
const ptEntry = config.buildPropertyEntry(typeName, pt.
|
|
83
|
+
const ptEntry = config.buildPropertyEntry(typeName, pt.specType);
|
|
84
84
|
entries[ptName] = ptEntry;
|
|
85
85
|
|
|
86
86
|
if (ptAliases) {
|
package/src/codegen/package.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* assemble BundleSpec → compute integrity → attach metadata.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { readFileSync, readdirSync } from "fs";
|
|
8
|
+
import { readFileSync, readdirSync, writeFileSync, mkdirSync } from "fs";
|
|
9
9
|
import { join } from "path";
|
|
10
10
|
import type { BundleSpec, LexiconManifest } from "../lexicon";
|
|
11
11
|
import { computeIntegrity } from "../lexicon-integrity";
|
|
@@ -147,6 +147,33 @@ export function collectRules(
|
|
|
147
147
|
return rules;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Write a BundleSpec to the given dist directory.
|
|
152
|
+
*
|
|
153
|
+
* Creates the directory structure and writes all artifacts:
|
|
154
|
+
* manifest.json, meta.json, types/index.d.ts, rules/*, skills/*, integrity.json.
|
|
155
|
+
*/
|
|
156
|
+
export function writeBundleSpec(spec: BundleSpec, distDir: string): void {
|
|
157
|
+
mkdirSync(join(distDir, "types"), { recursive: true });
|
|
158
|
+
mkdirSync(join(distDir, "rules"), { recursive: true });
|
|
159
|
+
mkdirSync(join(distDir, "skills"), { recursive: true });
|
|
160
|
+
|
|
161
|
+
writeFileSync(join(distDir, "manifest.json"), JSON.stringify(spec.manifest, null, 2));
|
|
162
|
+
writeFileSync(join(distDir, "meta.json"), spec.registry);
|
|
163
|
+
writeFileSync(join(distDir, "types", "index.d.ts"), spec.typesDTS);
|
|
164
|
+
|
|
165
|
+
for (const [name, content] of spec.rules) {
|
|
166
|
+
writeFileSync(join(distDir, "rules", name), content);
|
|
167
|
+
}
|
|
168
|
+
for (const [name, content] of spec.skills) {
|
|
169
|
+
writeFileSync(join(distDir, "skills", name), content);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (spec.integrity) {
|
|
173
|
+
writeFileSync(join(distDir, "integrity.json"), JSON.stringify(spec.integrity, null, 2));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
150
177
|
/**
|
|
151
178
|
* Collect skills from a plugin's skill definitions.
|
|
152
179
|
*/
|
package/src/codegen/validate.ts
CHANGED
|
@@ -141,3 +141,19 @@ export async function validateLexiconArtifacts(config: LexiconValidationConfig):
|
|
|
141
141
|
checks,
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Print validation results to stderr and throw on failure.
|
|
147
|
+
*/
|
|
148
|
+
export function printValidationResult(result: ValidateResult): void {
|
|
149
|
+
for (const check of result.checks) {
|
|
150
|
+
const status = check.ok ? "OK" : "FAIL";
|
|
151
|
+
const msg = check.error ? ` — ${check.error}` : "";
|
|
152
|
+
console.error(` [${status}] ${check.name}${msg}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!result.success) {
|
|
156
|
+
throw new Error("Validation failed");
|
|
157
|
+
}
|
|
158
|
+
console.error("All validation checks passed.");
|
|
159
|
+
}
|
package/src/discovery/files.ts
CHANGED
|
@@ -30,12 +30,12 @@ export async function findInfraFiles(path: string): Promise<string[]> {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (entry.isDirectory()) {
|
|
33
|
-
// Child project boundary — a directory with its own
|
|
34
|
-
// separate scope, but only if we've already found the project's
|
|
35
|
-
// source root
|
|
36
|
-
//
|
|
37
|
-
const
|
|
38
|
-
if (existsSync(
|
|
33
|
+
// Child project boundary — a directory with its own chant.config.ts
|
|
34
|
+
// is a separate scope, but only if we've already found the project's
|
|
35
|
+
// own source root. The first config directory is the source root, not
|
|
36
|
+
// a child project.
|
|
37
|
+
const configPath = join(fullPath, "chant.config.ts");
|
|
38
|
+
if (existsSync(configPath)) {
|
|
39
39
|
if (sourceRoot === null) {
|
|
40
40
|
sourceRoot = fullPath;
|
|
41
41
|
} else {
|
package/src/discovery/import.ts
CHANGED
|
@@ -11,7 +11,7 @@ export async function importModule(
|
|
|
11
11
|
path: string
|
|
12
12
|
): Promise<Record<string, unknown>> {
|
|
13
13
|
try {
|
|
14
|
-
return
|
|
14
|
+
return await import(path);
|
|
15
15
|
} catch (error) {
|
|
16
16
|
const message =
|
|
17
17
|
error instanceof Error ? error.message : "Unknown import error";
|
package/src/index.ts
CHANGED
|
@@ -42,7 +42,6 @@ export * from "./lexicon-schema";
|
|
|
42
42
|
export * from "./config";
|
|
43
43
|
export * from "./validation";
|
|
44
44
|
export * from "./project-validation";
|
|
45
|
-
export { barrel } from "./barrel";
|
|
46
45
|
export * from "./codegen/naming";
|
|
47
46
|
export * from "./codegen/fetch";
|
|
48
47
|
export * from "./codegen/generate";
|
package/src/lint/engine.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LintRule, LintDiagnostic, LintContext
|
|
1
|
+
import type { LintRule, LintDiagnostic, LintContext } from "./rule";
|
|
2
2
|
import { parseFile } from "./parser";
|
|
3
3
|
import { readFileSync } from "fs";
|
|
4
4
|
|
|
@@ -120,7 +120,6 @@ export async function runLint(
|
|
|
120
120
|
files: string[],
|
|
121
121
|
rules: LintRule[],
|
|
122
122
|
ruleOptions?: Map<string, Record<string, unknown>>,
|
|
123
|
-
runOptions?: LintRunOptions,
|
|
124
123
|
): Promise<LintDiagnostic[]> {
|
|
125
124
|
const allDiagnostics: LintDiagnostic[] = [];
|
|
126
125
|
const allRuleIds = new Set(rules.map((r) => r.id));
|
|
@@ -140,9 +139,6 @@ export async function runLint(
|
|
|
140
139
|
entities: [],
|
|
141
140
|
filePath,
|
|
142
141
|
lexicon: undefined,
|
|
143
|
-
barrelExports: runOptions?.barrelExports,
|
|
144
|
-
projectExports: runOptions?.projectExports,
|
|
145
|
-
projectScan: runOptions?.projectScan,
|
|
146
142
|
};
|
|
147
143
|
|
|
148
144
|
// Execute each rule
|
package/src/lint/rule.ts
CHANGED
|
@@ -56,24 +56,6 @@ export interface LintContext {
|
|
|
56
56
|
filePath: string;
|
|
57
57
|
/** Optional lexicon context (undefined for core rules) */
|
|
58
58
|
lexicon?: string;
|
|
59
|
-
/** Export names from the barrel file (for EVL008) */
|
|
60
|
-
barrelExports?: Set<string>;
|
|
61
|
-
/** All project exports keyed by name (for EVL008) */
|
|
62
|
-
projectExports?: Map<string, { file: string; className: string }>;
|
|
63
|
-
/** Project scan result (for COR016) */
|
|
64
|
-
projectScan?: import("../project/scan").ProjectScan;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Options for extending the lint context with project-level information
|
|
69
|
-
*/
|
|
70
|
-
export interface LintRunOptions {
|
|
71
|
-
/** Export names from the barrel file */
|
|
72
|
-
barrelExports?: Set<string>;
|
|
73
|
-
/** All project exports keyed by name */
|
|
74
|
-
projectExports?: Map<string, { file: string; className: string }>;
|
|
75
|
-
/** Project scan result (for COR016) */
|
|
76
|
-
projectScan?: import("../project/scan").ProjectScan;
|
|
77
59
|
}
|
|
78
60
|
|
|
79
61
|
/**
|
|
@@ -31,7 +31,7 @@ describe("EVL009: composite-no-constant", () => {
|
|
|
31
31
|
expect(diags).toHaveLength(1);
|
|
32
32
|
expect(diags[0].ruleId).toBe("EVL009");
|
|
33
33
|
expect(diags[0].message).toContain("assumeRolePolicyDocument");
|
|
34
|
-
expect(diags[0].message).toContain("
|
|
34
|
+
expect(diags[0].message).toContain("import directly");
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
test("flags inline array with objects that doesn't reference props", () => {
|
|
@@ -61,11 +61,25 @@ describe("EVL009: composite-no-constant", () => {
|
|
|
61
61
|
expect(diags[0].message).toContain("policies");
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
test("allows
|
|
64
|
+
test("allows imported refs (direct import)", () => {
|
|
65
65
|
const ctx = createContext(`
|
|
66
|
-
|
|
66
|
+
import { lambdaTrustPolicy } from "./defaults";
|
|
67
|
+
const MyComp = Composite((props) => {
|
|
68
|
+
const role = new Role({
|
|
69
|
+
assumeRolePolicyDocument: lambdaTrustPolicy,
|
|
70
|
+
});
|
|
71
|
+
return { role };
|
|
72
|
+
}, "MyComp");
|
|
73
|
+
`);
|
|
74
|
+
expect(evl009CompositeNoConstantRule.check(ctx)).toHaveLength(0);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("allows namespace import refs", () => {
|
|
78
|
+
const ctx = createContext(`
|
|
79
|
+
import * as defaults from "./defaults";
|
|
80
|
+
const MyComp = Composite((props) => {
|
|
67
81
|
const role = new Role({
|
|
68
|
-
assumeRolePolicyDocument:
|
|
82
|
+
assumeRolePolicyDocument: defaults.lambdaTrustPolicy,
|
|
69
83
|
});
|
|
70
84
|
return { role };
|
|
71
85
|
}, "MyComp");
|
|
@@ -99,8 +113,9 @@ describe("EVL009: composite-no-constant", () => {
|
|
|
99
113
|
|
|
100
114
|
test("allows sibling member reference", () => {
|
|
101
115
|
const ctx = createContext(`
|
|
116
|
+
import { trustPolicy } from "./defaults";
|
|
102
117
|
const MyComp = Composite((props) => {
|
|
103
|
-
const role = new Role({ assumeRolePolicyDocument:
|
|
118
|
+
const role = new Role({ assumeRolePolicyDocument: trustPolicy });
|
|
104
119
|
const func = new Function({
|
|
105
120
|
config: { roleArn: role.arn },
|
|
106
121
|
});
|
|
@@ -146,16 +161,17 @@ describe("EVL009: composite-no-constant", () => {
|
|
|
146
161
|
expect(diags).toHaveLength(2);
|
|
147
162
|
});
|
|
148
163
|
|
|
149
|
-
test("allows array wrapping
|
|
164
|
+
test("allows array wrapping imported ref", () => {
|
|
150
165
|
const ctx = createContext(`
|
|
166
|
+
import { lambdaBasicExecutionArn } from "./defaults";
|
|
151
167
|
const MyComp = Composite((props) => {
|
|
152
168
|
const role = new Role({
|
|
153
|
-
managedPolicyArns: [
|
|
169
|
+
managedPolicyArns: [lambdaBasicExecutionArn],
|
|
154
170
|
});
|
|
155
171
|
return { role };
|
|
156
172
|
}, "MyComp");
|
|
157
173
|
`);
|
|
158
|
-
// Array with
|
|
174
|
+
// Array with imported ref inside — not flagged
|
|
159
175
|
// Also it's an array of identifiers, no objects inside
|
|
160
176
|
expect(evl009CompositeNoConstantRule.check(ctx)).toHaveLength(0);
|
|
161
177
|
});
|