@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,158 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import * as ts from "typescript";
|
|
3
|
+
import { evl001NonLiteralExpressionRule } from "./evl001-non-literal-expression";
|
|
4
|
+
import type { LintContext } from "../rule";
|
|
5
|
+
|
|
6
|
+
function createContext(code: string, filePath = "test.ts"): LintContext {
|
|
7
|
+
const sourceFile = ts.createSourceFile(filePath, code, ts.ScriptTarget.Latest, true);
|
|
8
|
+
return { sourceFile, entities: [], filePath, lexicon: undefined };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("EVL001: non-literal-expression", () => {
|
|
12
|
+
test("rule metadata", () => {
|
|
13
|
+
expect(evl001NonLiteralExpressionRule.id).toBe("EVL001");
|
|
14
|
+
expect(evl001NonLiteralExpressionRule.severity).toBe("error");
|
|
15
|
+
expect(evl001NonLiteralExpressionRule.category).toBe("correctness");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("allows string literal", () => {
|
|
19
|
+
const ctx = createContext(`new Bucket({ bucketName: "my-bucket" });`);
|
|
20
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("allows numeric literal", () => {
|
|
24
|
+
const ctx = createContext(`new Queue({ maxSize: 100 });`);
|
|
25
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("allows boolean literal", () => {
|
|
29
|
+
const ctx = createContext(`new Bucket({ versioning: true });`);
|
|
30
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("allows identifier reference", () => {
|
|
34
|
+
const ctx = createContext(`new Bucket({ encryption: myEncryption });`);
|
|
35
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("allows property access", () => {
|
|
39
|
+
const ctx = createContext(`new Bucket({ role: config.roleArn });`);
|
|
40
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("allows object literal", () => {
|
|
44
|
+
const ctx = createContext(`new Bucket({ tags: { env: "prod" } });`);
|
|
45
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("allows array literal", () => {
|
|
49
|
+
const ctx = createContext(`new Bucket({ items: [1, 2, 3] });`);
|
|
50
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("allows template expression", () => {
|
|
54
|
+
const ctx = createContext("new Bucket({ name: `prefix-${suffix}` });");
|
|
55
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("allows tagged template", () => {
|
|
59
|
+
const ctx = createContext("new Bucket({ name: Sub`${AWS.StackName}-data` });");
|
|
60
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("allows binary expression", () => {
|
|
64
|
+
const ctx = createContext(`new Queue({ timeout: base + 10 });`);
|
|
65
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("allows conditional expression", () => {
|
|
69
|
+
const ctx = createContext(`new Bucket({ versioning: isProd ? true : false });`);
|
|
70
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("allows as cast", () => {
|
|
74
|
+
const ctx = createContext(`new Bucket({ name: value as string });`);
|
|
75
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("allows new expression as value", () => {
|
|
79
|
+
const ctx = createContext(`new Outer({ inner: new Inner({}) });`);
|
|
80
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("flags function call", () => {
|
|
84
|
+
const ctx = createContext(`new Bucket({ name: getName() });`);
|
|
85
|
+
const diags = evl001NonLiteralExpressionRule.check(ctx);
|
|
86
|
+
expect(diags).toHaveLength(1);
|
|
87
|
+
expect(diags[0].ruleId).toBe("EVL001");
|
|
88
|
+
expect(diags[0].severity).toBe("error");
|
|
89
|
+
expect(diags[0].message).toContain("statically evaluable");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("flags method call", () => {
|
|
93
|
+
const ctx = createContext(`new Bucket({ name: config.getName() });`);
|
|
94
|
+
const diags = evl001NonLiteralExpressionRule.check(ctx);
|
|
95
|
+
expect(diags).toHaveLength(1);
|
|
96
|
+
expect(diags[0].ruleId).toBe("EVL001");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("flags await expression", () => {
|
|
100
|
+
const ctx = createContext(`new Bucket({ data: await fetchData() });`);
|
|
101
|
+
const diags = evl001NonLiteralExpressionRule.check(ctx);
|
|
102
|
+
expect(diags).toHaveLength(1);
|
|
103
|
+
expect(diags[0].ruleId).toBe("EVL001");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("flags multiple violations", () => {
|
|
107
|
+
const ctx = createContext(`new Bucket({ a: foo(), b: bar() });`);
|
|
108
|
+
const diags = evl001NonLiteralExpressionRule.check(ctx);
|
|
109
|
+
expect(diags).toHaveLength(2);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("does not flag constructor with no arguments", () => {
|
|
113
|
+
const ctx = createContext(`new Bucket();`);
|
|
114
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("does not flag constructor with non-object argument", () => {
|
|
118
|
+
const ctx = createContext(`new SomeClass("string");`);
|
|
119
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("does not flag inside Composite() factory callback", () => {
|
|
123
|
+
const ctx = createContext(`
|
|
124
|
+
const MyComp = Composite((props) => {
|
|
125
|
+
const role = new Role({
|
|
126
|
+
policies: props.policyStatements
|
|
127
|
+
? props.policyStatements.map(s => ({ Effect: s.effect }))
|
|
128
|
+
: undefined,
|
|
129
|
+
});
|
|
130
|
+
return { role };
|
|
131
|
+
});
|
|
132
|
+
`);
|
|
133
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("does not flag inside _.Composite() factory callback", () => {
|
|
137
|
+
const ctx = createContext(`
|
|
138
|
+
const MyComp = _.Composite((props) => {
|
|
139
|
+
const func = new _.Function({ name: props.name });
|
|
140
|
+
return { func };
|
|
141
|
+
});
|
|
142
|
+
`);
|
|
143
|
+
expect(evl001NonLiteralExpressionRule.check(ctx)).toHaveLength(0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("still flags outside Composite() in same file", () => {
|
|
147
|
+
const ctx = createContext(`
|
|
148
|
+
const MyComp = Composite((props) => {
|
|
149
|
+
const role = new Role({ name: props.name });
|
|
150
|
+
return { role };
|
|
151
|
+
});
|
|
152
|
+
const bucket = new Bucket({ name: getName() });
|
|
153
|
+
`);
|
|
154
|
+
const diags = evl001NonLiteralExpressionRule.check(ctx);
|
|
155
|
+
expect(diags).toHaveLength(1);
|
|
156
|
+
expect(diags[0].ruleId).toBe("EVL001");
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import type { LintRule, LintContext, LintDiagnostic } from "../rule";
|
|
3
|
+
import { isInsideCompositeFactory } from "./composite-scope";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* EVL001: Non-Literal Expression in Resource Constructor
|
|
7
|
+
*
|
|
8
|
+
* Resource constructor property values must be statically evaluable.
|
|
9
|
+
* Allowed: literals, identifiers, property access, object/array literals,
|
|
10
|
+
* template expressions, binary/unary/conditional, as/satisfies casts.
|
|
11
|
+
* Blocked: function calls, method calls, and other dynamic expressions.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
function isStaticallyEvaluable(node: ts.Node): boolean {
|
|
15
|
+
// Literals
|
|
16
|
+
if (
|
|
17
|
+
ts.isStringLiteral(node) ||
|
|
18
|
+
ts.isNumericLiteral(node) ||
|
|
19
|
+
ts.isNoSubstitutionTemplateLiteral(node) ||
|
|
20
|
+
node.kind === ts.SyntaxKind.TrueKeyword ||
|
|
21
|
+
node.kind === ts.SyntaxKind.FalseKeyword ||
|
|
22
|
+
node.kind === ts.SyntaxKind.NullKeyword ||
|
|
23
|
+
node.kind === ts.SyntaxKind.UndefinedKeyword
|
|
24
|
+
) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Identifiers (variable references)
|
|
29
|
+
if (ts.isIdentifier(node)) return true;
|
|
30
|
+
|
|
31
|
+
// Property access: obj.prop
|
|
32
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
33
|
+
return isStaticallyEvaluable(node.expression);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Element access with static key: obj["key"] or obj[0]
|
|
37
|
+
if (ts.isElementAccessExpression(node)) {
|
|
38
|
+
return (
|
|
39
|
+
isStaticallyEvaluable(node.expression) &&
|
|
40
|
+
(ts.isStringLiteral(node.argumentExpression) ||
|
|
41
|
+
ts.isNumericLiteral(node.argumentExpression))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Object literals — check all property values
|
|
46
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
47
|
+
return node.properties.every((prop) => {
|
|
48
|
+
if (ts.isPropertyAssignment(prop)) return isStaticallyEvaluable(prop.initializer);
|
|
49
|
+
if (ts.isShorthandPropertyAssignment(prop)) return true;
|
|
50
|
+
if (ts.isSpreadAssignment(prop)) return isStaticallyEvaluable(prop.expression);
|
|
51
|
+
return false;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Array literals — check all elements
|
|
56
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
57
|
+
return node.elements.every((el) => isStaticallyEvaluable(el));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Template expressions (tagged or untagged)
|
|
61
|
+
if (ts.isTemplateExpression(node)) return true;
|
|
62
|
+
if (ts.isTaggedTemplateExpression(node)) return true;
|
|
63
|
+
|
|
64
|
+
// Binary expressions: a + b, a ?? b, etc.
|
|
65
|
+
if (ts.isBinaryExpression(node)) {
|
|
66
|
+
return isStaticallyEvaluable(node.left) && isStaticallyEvaluable(node.right);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Prefix unary: !x, -x
|
|
70
|
+
if (ts.isPrefixUnaryExpression(node)) {
|
|
71
|
+
return isStaticallyEvaluable(node.operand);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Conditional: a ? b : c
|
|
75
|
+
if (ts.isConditionalExpression(node)) {
|
|
76
|
+
return (
|
|
77
|
+
isStaticallyEvaluable(node.condition) &&
|
|
78
|
+
isStaticallyEvaluable(node.whenTrue) &&
|
|
79
|
+
isStaticallyEvaluable(node.whenFalse)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Type assertions: x as T, x satisfies T
|
|
84
|
+
if (ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) {
|
|
85
|
+
return isStaticallyEvaluable(node.expression);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Parenthesized expression
|
|
89
|
+
if (ts.isParenthesizedExpression(node)) {
|
|
90
|
+
return isStaticallyEvaluable(node.expression);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Non-null assertion: x!
|
|
94
|
+
if (ts.isNonNullExpression(node)) {
|
|
95
|
+
return isStaticallyEvaluable(node.expression);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Spread element in arrays
|
|
99
|
+
if (ts.isSpreadElement(node)) {
|
|
100
|
+
return isStaticallyEvaluable(node.expression);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// new Expression (resource constructors) — allowed as property values
|
|
104
|
+
if (ts.isNewExpression(node)) return true;
|
|
105
|
+
|
|
106
|
+
// Everything else (call expressions, etc.) is not statically evaluable
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function checkNode(node: ts.Node, context: LintContext, diagnostics: LintDiagnostic[]): void {
|
|
111
|
+
// Skip resource constructors inside Composite() factory callbacks
|
|
112
|
+
if (ts.isNewExpression(node) && !isInsideCompositeFactory(node)) {
|
|
113
|
+
if (node.arguments && node.arguments.length > 0) {
|
|
114
|
+
const firstArg = node.arguments[0];
|
|
115
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
116
|
+
for (const prop of firstArg.properties) {
|
|
117
|
+
if (ts.isPropertyAssignment(prop)) {
|
|
118
|
+
if (!isStaticallyEvaluable(prop.initializer)) {
|
|
119
|
+
const { line, character } = context.sourceFile.getLineAndCharacterOfPosition(
|
|
120
|
+
prop.initializer.getStart(context.sourceFile),
|
|
121
|
+
);
|
|
122
|
+
diagnostics.push({
|
|
123
|
+
file: context.filePath,
|
|
124
|
+
line: line + 1,
|
|
125
|
+
column: character + 1,
|
|
126
|
+
ruleId: "EVL001",
|
|
127
|
+
severity: "error",
|
|
128
|
+
message: `Non-literal expression in resource constructor property — value must be statically evaluable`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
ts.forEachChild(node, (child) => checkNode(child, context, diagnostics));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const evl001NonLiteralExpressionRule: LintRule = {
|
|
141
|
+
id: "EVL001",
|
|
142
|
+
severity: "error",
|
|
143
|
+
category: "correctness",
|
|
144
|
+
check(context: LintContext): LintDiagnostic[] {
|
|
145
|
+
const diagnostics: LintDiagnostic[] = [];
|
|
146
|
+
checkNode(context.sourceFile, context, diagnostics);
|
|
147
|
+
return diagnostics;
|
|
148
|
+
},
|
|
149
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import * as ts from "typescript";
|
|
3
|
+
import { evl002ControlFlowResourceRule } from "./evl002-control-flow-resource";
|
|
4
|
+
import type { LintContext } from "../rule";
|
|
5
|
+
|
|
6
|
+
function createContext(code: string, filePath = "test.ts"): LintContext {
|
|
7
|
+
const sourceFile = ts.createSourceFile(filePath, code, ts.ScriptTarget.Latest, true);
|
|
8
|
+
return { sourceFile, entities: [], filePath, lexicon: undefined };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("EVL002: control-flow-resource", () => {
|
|
12
|
+
test("rule metadata", () => {
|
|
13
|
+
expect(evl002ControlFlowResourceRule.id).toBe("EVL002");
|
|
14
|
+
expect(evl002ControlFlowResourceRule.severity).toBe("error");
|
|
15
|
+
expect(evl002ControlFlowResourceRule.category).toBe("correctness");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("allows top-level resource constructor", () => {
|
|
19
|
+
const ctx = createContext(`export const bucket = new Bucket({ name: "x" });`);
|
|
20
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("flags resource inside if statement", () => {
|
|
24
|
+
const ctx = createContext(`
|
|
25
|
+
if (isProd) {
|
|
26
|
+
const bucket = new Bucket({ name: "x" });
|
|
27
|
+
}
|
|
28
|
+
`);
|
|
29
|
+
const diags = evl002ControlFlowResourceRule.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].ruleId).toBe("EVL002");
|
|
32
|
+
expect(diags[0].message).toContain("control flow");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("flags resource inside for loop", () => {
|
|
36
|
+
const ctx = createContext(`
|
|
37
|
+
for (let i = 0; i < 3; i++) {
|
|
38
|
+
const bucket = new Bucket({ name: "x" });
|
|
39
|
+
}
|
|
40
|
+
`);
|
|
41
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("flags resource inside for-of loop", () => {
|
|
45
|
+
const ctx = createContext(`
|
|
46
|
+
for (const x of items) {
|
|
47
|
+
const bucket = new Bucket({ name: x });
|
|
48
|
+
}
|
|
49
|
+
`);
|
|
50
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("flags resource inside for-in loop", () => {
|
|
54
|
+
const ctx = createContext(`
|
|
55
|
+
for (const k in obj) {
|
|
56
|
+
const bucket = new Bucket({ name: k });
|
|
57
|
+
}
|
|
58
|
+
`);
|
|
59
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("flags resource inside while loop", () => {
|
|
63
|
+
const ctx = createContext(`
|
|
64
|
+
while (true) {
|
|
65
|
+
const bucket = new Bucket({ name: "x" });
|
|
66
|
+
}
|
|
67
|
+
`);
|
|
68
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("flags resource inside do-while loop", () => {
|
|
72
|
+
const ctx = createContext(`
|
|
73
|
+
do {
|
|
74
|
+
const bucket = new Bucket({ name: "x" });
|
|
75
|
+
} while (false);
|
|
76
|
+
`);
|
|
77
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("flags resource inside switch statement", () => {
|
|
81
|
+
const ctx = createContext(`
|
|
82
|
+
switch (env) {
|
|
83
|
+
case "prod":
|
|
84
|
+
const bucket = new Bucket({ name: "x" });
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
`);
|
|
88
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(1);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("flags resource inside try block", () => {
|
|
92
|
+
const ctx = createContext(`
|
|
93
|
+
try {
|
|
94
|
+
const bucket = new Bucket({ name: "x" });
|
|
95
|
+
} catch (e) {}
|
|
96
|
+
`);
|
|
97
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(1);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("flags nested control flow", () => {
|
|
101
|
+
const ctx = createContext(`
|
|
102
|
+
if (isProd) {
|
|
103
|
+
for (const x of items) {
|
|
104
|
+
const bucket = new Bucket({ name: "x" });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
`);
|
|
108
|
+
expect(evl002ControlFlowResourceRule.check(ctx)).toHaveLength(1);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import type { LintRule, LintContext, LintDiagnostic } from "../rule";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* EVL002: Control Flow Wrapping Resources
|
|
6
|
+
*
|
|
7
|
+
* Resource constructors (NewExpression) must not appear inside
|
|
8
|
+
* control flow statements (if, for, while, switch, try, etc.).
|
|
9
|
+
* Resources must be unconditionally declared.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const CONTROL_FLOW_KINDS = new Set([
|
|
13
|
+
ts.SyntaxKind.IfStatement,
|
|
14
|
+
ts.SyntaxKind.ForStatement,
|
|
15
|
+
ts.SyntaxKind.ForInStatement,
|
|
16
|
+
ts.SyntaxKind.ForOfStatement,
|
|
17
|
+
ts.SyntaxKind.WhileStatement,
|
|
18
|
+
ts.SyntaxKind.DoStatement,
|
|
19
|
+
ts.SyntaxKind.SwitchStatement,
|
|
20
|
+
ts.SyntaxKind.TryStatement,
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
function isInsideControlFlow(node: ts.Node): boolean {
|
|
24
|
+
let current = node.parent;
|
|
25
|
+
while (current) {
|
|
26
|
+
if (CONTROL_FLOW_KINDS.has(current.kind)) return true;
|
|
27
|
+
current = current.parent;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function checkNode(node: ts.Node, context: LintContext, diagnostics: LintDiagnostic[]): void {
|
|
33
|
+
if (ts.isNewExpression(node)) {
|
|
34
|
+
if (isInsideControlFlow(node)) {
|
|
35
|
+
const { line, character } = context.sourceFile.getLineAndCharacterOfPosition(
|
|
36
|
+
node.getStart(context.sourceFile),
|
|
37
|
+
);
|
|
38
|
+
diagnostics.push({
|
|
39
|
+
file: context.filePath,
|
|
40
|
+
line: line + 1,
|
|
41
|
+
column: character + 1,
|
|
42
|
+
ruleId: "EVL002",
|
|
43
|
+
severity: "error",
|
|
44
|
+
message: "Resource constructor inside control flow — resources must be unconditionally declared",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ts.forEachChild(node, (child) => checkNode(child, context, diagnostics));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const evl002ControlFlowResourceRule: LintRule = {
|
|
53
|
+
id: "EVL002",
|
|
54
|
+
severity: "error",
|
|
55
|
+
category: "correctness",
|
|
56
|
+
check(context: LintContext): LintDiagnostic[] {
|
|
57
|
+
const diagnostics: LintDiagnostic[] = [];
|
|
58
|
+
checkNode(context.sourceFile, context, diagnostics);
|
|
59
|
+
return diagnostics;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import * as ts from "typescript";
|
|
3
|
+
import { evl003DynamicPropertyAccessRule } from "./evl003-dynamic-property-access";
|
|
4
|
+
import type { LintContext } from "../rule";
|
|
5
|
+
|
|
6
|
+
function createContext(code: string, filePath = "test.ts"): LintContext {
|
|
7
|
+
const sourceFile = ts.createSourceFile(filePath, code, ts.ScriptTarget.Latest, true);
|
|
8
|
+
return { sourceFile, entities: [], filePath, lexicon: undefined };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("EVL003: dynamic-property-access", () => {
|
|
12
|
+
test("rule metadata", () => {
|
|
13
|
+
expect(evl003DynamicPropertyAccessRule.id).toBe("EVL003");
|
|
14
|
+
expect(evl003DynamicPropertyAccessRule.severity).toBe("error");
|
|
15
|
+
expect(evl003DynamicPropertyAccessRule.category).toBe("correctness");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("allows dot property access", () => {
|
|
19
|
+
const ctx = createContext(`const x = obj.prop;`);
|
|
20
|
+
expect(evl003DynamicPropertyAccessRule.check(ctx)).toHaveLength(0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("allows string literal key", () => {
|
|
24
|
+
const ctx = createContext(`const x = obj["key"];`);
|
|
25
|
+
expect(evl003DynamicPropertyAccessRule.check(ctx)).toHaveLength(0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("allows numeric literal key", () => {
|
|
29
|
+
const ctx = createContext(`const x = arr[0];`);
|
|
30
|
+
expect(evl003DynamicPropertyAccessRule.check(ctx)).toHaveLength(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("flags variable key", () => {
|
|
34
|
+
const ctx = createContext(`const x = obj[key];`);
|
|
35
|
+
const diags = evl003DynamicPropertyAccessRule.check(ctx);
|
|
36
|
+
expect(diags).toHaveLength(1);
|
|
37
|
+
expect(diags[0].ruleId).toBe("EVL003");
|
|
38
|
+
expect(diags[0].message).toContain("Dynamic property access");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("flags expression key", () => {
|
|
42
|
+
const ctx = createContext(`const x = obj[a + b];`);
|
|
43
|
+
expect(evl003DynamicPropertyAccessRule.check(ctx)).toHaveLength(1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("flags function call key", () => {
|
|
47
|
+
const ctx = createContext(`const x = obj[getKey()];`);
|
|
48
|
+
expect(evl003DynamicPropertyAccessRule.check(ctx)).toHaveLength(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("flags template literal key", () => {
|
|
52
|
+
const ctx = createContext("const x = obj[`key-${i}`];");
|
|
53
|
+
expect(evl003DynamicPropertyAccessRule.check(ctx)).toHaveLength(1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("flags multiple violations", () => {
|
|
57
|
+
const ctx = createContext(`
|
|
58
|
+
const a = obj[x];
|
|
59
|
+
const b = arr[y];
|
|
60
|
+
`);
|
|
61
|
+
expect(evl003DynamicPropertyAccessRule.check(ctx)).toHaveLength(2);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import type { LintRule, LintContext, LintDiagnostic } from "../rule";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* EVL003: Dynamic Property Access
|
|
6
|
+
*
|
|
7
|
+
* Computed property access (obj[key]) must use a string or numeric literal key.
|
|
8
|
+
* Dynamic keys (variables, expressions) are not statically evaluable.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
function checkNode(node: ts.Node, context: LintContext, diagnostics: LintDiagnostic[]): void {
|
|
12
|
+
if (ts.isElementAccessExpression(node)) {
|
|
13
|
+
const arg = node.argumentExpression;
|
|
14
|
+
if (!ts.isStringLiteral(arg) && !ts.isNumericLiteral(arg)) {
|
|
15
|
+
const { line, character } = context.sourceFile.getLineAndCharacterOfPosition(
|
|
16
|
+
node.getStart(context.sourceFile),
|
|
17
|
+
);
|
|
18
|
+
diagnostics.push({
|
|
19
|
+
file: context.filePath,
|
|
20
|
+
line: line + 1,
|
|
21
|
+
column: character + 1,
|
|
22
|
+
ruleId: "EVL003",
|
|
23
|
+
severity: "error",
|
|
24
|
+
message: "Dynamic property access — computed key must be a string or numeric literal",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ts.forEachChild(node, (child) => checkNode(child, context, diagnostics));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const evl003DynamicPropertyAccessRule: LintRule = {
|
|
33
|
+
id: "EVL003",
|
|
34
|
+
severity: "error",
|
|
35
|
+
category: "correctness",
|
|
36
|
+
check(context: LintContext): LintDiagnostic[] {
|
|
37
|
+
const diagnostics: LintDiagnostic[] = [];
|
|
38
|
+
checkNode(context.sourceFile, context, diagnostics);
|
|
39
|
+
return diagnostics;
|
|
40
|
+
},
|
|
41
|
+
};
|