@intentius/chant 0.21.0 → 0.23.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/build.d.ts +7 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/cli/build-params-cli.d.ts +55 -0
- package/dist/cli/build-params-cli.d.ts.map +1 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +14 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/lsp/server.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +33 -2
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts +28 -0
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +71 -9
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/index.d.ts +27 -4
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/fold/subset.d.ts +39 -2
- package/dist/fold/subset.d.ts.map +1 -1
- package/dist/lint/engine.d.ts +11 -1
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lint/rule.d.ts +14 -0
- package/dist/lint/rule.d.ts.map +1 -1
- package/dist/serializer-walker.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/build.ts +9 -0
- package/src/cli/build-params-cli.test.ts +139 -0
- package/src/cli/build-params-cli.ts +107 -0
- package/src/cli/commands/build.ts +25 -36
- package/src/cli/commands/check-lexicon-examples.ts +16 -1
- package/src/cli/commands/lint.test.ts +74 -0
- package/src/cli/commands/lint.ts +33 -9
- package/src/cli/handlers/build.test.ts +147 -0
- package/src/cli/handlers/build.ts +23 -8
- package/src/cli/handlers/run.test.ts +160 -5
- package/src/cli/handlers/run.ts +46 -8
- package/src/cli/lsp/server.ts +7 -2
- package/src/components/cli-support.test.ts +221 -3
- package/src/components/cli-support.ts +37 -6
- package/src/components/discover.test.ts +63 -1
- package/src/components/discover.ts +42 -0
- package/src/discovery/fold-import.test.ts +328 -1
- package/src/discovery/fold-import.ts +414 -31
- package/src/discovery/index.test.ts +131 -0
- package/src/discovery/index.ts +53 -8
- package/src/discovery/sandbox/fold-boundary.test.ts +254 -0
- package/src/fold/fold.ts +6 -2
- package/src/fold/subset.test.ts +95 -15
- package/src/fold/subset.ts +46 -5
- package/src/lint/engine.ts +12 -0
- package/src/lint/rule.ts +14 -0
- package/src/lint/rules/evl001-non-literal-expression.test.ts +39 -0
- package/src/lint/rules/evl001-non-literal-expression.ts +1 -1
- package/src/serializer-walker.ts +14 -0
package/dist/lint/rule.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type * as ts from "typescript";
|
|
2
|
+
import type { IntrinsicDef } from "../lexicon.js";
|
|
2
3
|
/**
|
|
3
4
|
* Severity level for lint diagnostics
|
|
4
5
|
*/
|
|
@@ -55,6 +56,19 @@ export interface LintContext {
|
|
|
55
56
|
filePath: string;
|
|
56
57
|
/** Optional lexicon context (undefined for core rules) */
|
|
57
58
|
lexicon?: string;
|
|
59
|
+
/**
|
|
60
|
+
* chant #1106 — the active lexicons' registered intrinsics (`Ref`,
|
|
61
|
+
* `GetAtt`, ...), threaded down from `runLint` (../lint/engine.ts) so a
|
|
62
|
+
* rule built on the shared `../fold/subset.ts` predicate
|
|
63
|
+
* (`findSubsetViolation`/`checkObjectMember`, used by EVL001) gets the
|
|
64
|
+
* SAME answer `fold()` does for a registered, opted-in call. Mirrors how
|
|
65
|
+
* `discover()` has threaded `IntrinsicDef[]` into the fold path since
|
|
66
|
+
* #1039/#1105. Undefined when the caller hasn't resolved a project's
|
|
67
|
+
* lexicons (a bare unit test constructing a `LintContext` directly, for
|
|
68
|
+
* instance) — subset.ts then falls back to its pre-#1044 answer: every
|
|
69
|
+
* call is a violation.
|
|
70
|
+
*/
|
|
71
|
+
intrinsics?: readonly IntrinsicDef[];
|
|
58
72
|
}
|
|
59
73
|
/**
|
|
60
74
|
* Configuration value for a rule: either a severity string or a [severity, options] tuple
|
package/dist/lint/rule.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/lint/rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/lint/rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG,OAAO,GAAG,aAAa,GAAG,UAAU,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,gDAAgD;IAChD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxB,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG,cAAc,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC9E,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC;IAC1B,sCAAsC;IACtC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,CAAC,QAAQ,GAAG,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAExF;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,QAAQ,EAAE,QAAQ,CAAC;IACnB,4BAA4B;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,KAAK,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,EAAE,CAAC;IACjF,gDAAgD;IAChD,GAAG,CAAC,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,EAAE,CAAC;CACvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializer-walker.d.ts","sourceRoot":"","sources":["../src/serializer-walker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM/C,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACzD,mEAAmE;IACnE,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1C,+DAA+D;IAC/D,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC;CAChF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,EACpC,OAAO,EAAE,iBAAiB,GACzB,OAAO,
|
|
1
|
+
{"version":3,"file":"serializer-walker.d.ts","sourceRoot":"","sources":["../src/serializer-walker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM/C,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACzD,mEAAmE;IACnE,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1C,+DAA+D;IAC/D,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC;CAChF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,EACpC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAuET"}
|
package/package.json
CHANGED
package/src/build.ts
CHANGED
|
@@ -170,6 +170,14 @@ export interface BuildOptions {
|
|
|
170
170
|
*/
|
|
171
171
|
intrinsics?: IntrinsicDef[];
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* chant #1063 — the lexicon names loaded for this build. Passed straight
|
|
175
|
+
* through to {@link DiscoveryOptions.lexicons} in `./discovery/index`;
|
|
176
|
+
* ignored unless {@link fold} is set. The CLI populates this from
|
|
177
|
+
* `options.plugins.map(p => p.name)`.
|
|
178
|
+
*/
|
|
179
|
+
lexicons?: readonly string[];
|
|
180
|
+
|
|
173
181
|
/**
|
|
174
182
|
* chant #1045 Phase 2 — opt-in: run-fallback files (or, when {@link fold}
|
|
175
183
|
* isn't set, every file) execute together, isolated, in one sandboxed
|
|
@@ -513,6 +521,7 @@ export async function build(
|
|
|
513
521
|
const discoveryResult = await discover(path, {
|
|
514
522
|
fold: options?.fold,
|
|
515
523
|
intrinsics: options?.intrinsics,
|
|
524
|
+
lexicons: options?.lexicons,
|
|
516
525
|
sandbox: options?.sandbox,
|
|
517
526
|
buildParams: options?.buildParams,
|
|
518
527
|
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the CLI-layer build-time-parameter helpers factored out by chant
|
|
3
|
+
* #1108 (`parseParamFlags`/`resolveCliBuildParams`) — the exact sequence
|
|
4
|
+
* `chant build` (`./commands/build.ts`'s `buildCommand`) runs, now shared with
|
|
5
|
+
* the component deploy driver (`./handlers/run.ts`, `./handlers/build.ts`'s
|
|
6
|
+
* generate mode) so both resolve `--param`/`--params-file`/a declared `env`
|
|
7
|
+
* mapping/`chant.config.ts`'s `buildParams` defaults identically. Precedence
|
|
8
|
+
* itself is `../build-params.ts`'s `resolveBuildParams`'s responsibility
|
|
9
|
+
* (see `../build-params.test.ts`) — these tests cover the CLI-specific glue:
|
|
10
|
+
* flag parsing, `--params-file` reading, error formatting, and logging.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, test, expect, vi } from "vitest";
|
|
13
|
+
import { writeFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { parseParamFlags, resolveCliBuildParams } from "./build-params-cli";
|
|
17
|
+
import type { BuildParamsConfig } from "../build-params";
|
|
18
|
+
|
|
19
|
+
describe("parseParamFlags", () => {
|
|
20
|
+
test("undefined/empty input yields undefined (matches resolveBuildParams's 'no cli input' shape)", () => {
|
|
21
|
+
expect(parseParamFlags(undefined)).toBeUndefined();
|
|
22
|
+
expect(parseParamFlags([])).toBeUndefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("parses repeated name=value flags into a flat record", () => {
|
|
26
|
+
expect(parseParamFlags(["tier=production", "replicas=3"])).toEqual({
|
|
27
|
+
tier: "production",
|
|
28
|
+
replicas: "3",
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("a value containing '=' keeps everything after the first '=' (only the first splits)", () => {
|
|
33
|
+
expect(parseParamFlags(["url=https://example.com?a=b"])).toEqual({
|
|
34
|
+
url: "https://example.com?a=b",
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("a flag with no '=' becomes an empty-string value", () => {
|
|
39
|
+
expect(parseParamFlags(["flag-only"])).toEqual({ "flag-only": "" });
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("resolveCliBuildParams", () => {
|
|
44
|
+
test("cli beats params-file beats a declared env mapping beats the default — the exact chant build precedence", () => {
|
|
45
|
+
const defs: BuildParamsConfig = { tier: { type: "string", default: "light", env: "TIER_ENV" } };
|
|
46
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
47
|
+
try {
|
|
48
|
+
const result = resolveCliBuildParams(defs, { cli: { tier: "production" } });
|
|
49
|
+
expect(result).toEqual({
|
|
50
|
+
success: true,
|
|
51
|
+
provenance: [{ name: "tier", value: "production", source: "cli" }],
|
|
52
|
+
errors: [],
|
|
53
|
+
});
|
|
54
|
+
} finally {
|
|
55
|
+
errorSpy.mockRestore();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("logs every resolved parameter as '[param] name = value (source)'", () => {
|
|
60
|
+
const defs: BuildParamsConfig = { tier: { type: "string", default: "light" } };
|
|
61
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
62
|
+
try {
|
|
63
|
+
resolveCliBuildParams(defs, {});
|
|
64
|
+
const logged = errorSpy.mock.calls.map((call) => String(call[0]));
|
|
65
|
+
expect(logged.some((line) => line.includes("[param] tier") && line.includes("light") && line.includes("default"))).toBe(true);
|
|
66
|
+
} finally {
|
|
67
|
+
errorSpy.mockRestore();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("no declared params → success with empty provenance and no logging", () => {
|
|
72
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
73
|
+
try {
|
|
74
|
+
const result = resolveCliBuildParams(undefined, {});
|
|
75
|
+
expect(result).toEqual({ success: true, provenance: [], errors: [] });
|
|
76
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
77
|
+
} finally {
|
|
78
|
+
errorSpy.mockRestore();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("an unresolved required parameter is a formatted error, not a thrown exception", () => {
|
|
83
|
+
const defs: BuildParamsConfig = { tier: { type: "string" } };
|
|
84
|
+
const result = resolveCliBuildParams(defs, {});
|
|
85
|
+
expect(result.success).toBe(false);
|
|
86
|
+
expect(result.provenance).toEqual([]);
|
|
87
|
+
expect(result.errors.some((e) => e.includes('"tier"') && e.includes("--param"))).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("an enum violation is a formatted error naming the parameter and the offending value", () => {
|
|
91
|
+
const defs: BuildParamsConfig = { tier: { type: "string", enum: ["light", "production"] } };
|
|
92
|
+
const result = resolveCliBuildParams(defs, { cli: { tier: "bogus" } });
|
|
93
|
+
expect(result.success).toBe(false);
|
|
94
|
+
expect(result.errors.some((e) => e.includes('"tier"') && e.includes("bogus"))).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("an unknown --param name is a formatted error", () => {
|
|
98
|
+
const defs: BuildParamsConfig = { tier: { type: "string", default: "light" } };
|
|
99
|
+
const result = resolveCliBuildParams(defs, { cli: { nonexistent: "x" } });
|
|
100
|
+
expect(result.success).toBe(false);
|
|
101
|
+
expect(result.errors.some((e) => e.includes("nonexistent") && e.includes("--param"))).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("--params-file", () => {
|
|
105
|
+
let dir: string;
|
|
106
|
+
|
|
107
|
+
test("reads and resolves values from a JSON file (second precedence, after --param)", () => {
|
|
108
|
+
dir = mkdtempSync(join(tmpdir(), "chant-build-params-cli-test-"));
|
|
109
|
+
try {
|
|
110
|
+
const file = join(dir, "params.json");
|
|
111
|
+
writeFileSync(file, JSON.stringify({ tier: "from-file", env: "from-file-env" }));
|
|
112
|
+
const defs: BuildParamsConfig = {
|
|
113
|
+
tier: { type: "string", default: "light" },
|
|
114
|
+
env: { type: "string", default: "dev" },
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
118
|
+
try {
|
|
119
|
+
const result = resolveCliBuildParams(defs, { cli: { tier: "from-cli" }, paramsFile: file });
|
|
120
|
+
expect(result.success).toBe(true);
|
|
121
|
+
const byName = new Map(result.provenance.map((p) => [p.name, p]));
|
|
122
|
+
expect(byName.get("tier")).toEqual({ name: "tier", value: "from-cli", source: "cli" });
|
|
123
|
+
expect(byName.get("env")).toEqual({ name: "env", value: "from-file-env", source: "params-file" });
|
|
124
|
+
} finally {
|
|
125
|
+
errorSpy.mockRestore();
|
|
126
|
+
}
|
|
127
|
+
} finally {
|
|
128
|
+
rmSync(dir, { recursive: true, force: true });
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("an unreadable/unparsable --params-file is a formatted error naming the path, not a thrown exception", () => {
|
|
133
|
+
const defs: BuildParamsConfig = { tier: { type: "string", default: "light" } };
|
|
134
|
+
const result = resolveCliBuildParams(defs, { paramsFile: "/nonexistent/path/params.json" });
|
|
135
|
+
expect(result.success).toBe(false);
|
|
136
|
+
expect(result.errors.some((e) => e.includes("--params-file") && e.includes("/nonexistent/path/params.json"))).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { resolveBuildParams, type BuildParamsConfig } from "../build-params";
|
|
4
|
+
import type { BuildParamProvenance } from "../provenance";
|
|
5
|
+
import { formatError, formatInfo } from "./format";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shared CLI-layer wiring for chant #1064's build-time parameters, factored
|
|
9
|
+
* out (chant #1108) so every command that discovers project source before
|
|
10
|
+
* running it resolves `chant.config.ts`'s declared `buildParams` the exact
|
|
11
|
+
* same way: `chant build` (`../commands/build.ts`'s `buildCommand`), the
|
|
12
|
+
* component deploy driver (`./handlers/run.ts`'s `chant run --components`
|
|
13
|
+
* local + `--temporal` paths), and generate mode (`./handlers/build.ts`'s
|
|
14
|
+
* `chant build --components --generate <lexicon>`).
|
|
15
|
+
*
|
|
16
|
+
* Before #1108, only `buildCommand` ran this sequence — `chant run
|
|
17
|
+
* --components` never resolved `--param`/`--params-file`/a declared `env`
|
|
18
|
+
* mapping at all, so a `*.component.ts` file reading `params.<name>`
|
|
19
|
+
* (`@intentius/chant/params`) always saw `{}`, no matter what a CI job
|
|
20
|
+
* exported into the environment. See ../build-params.ts's module doc for the
|
|
21
|
+
* full precedence rules this wraps.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** Parse repeated `--param name=value` flags into a flat `{ name: value }` record — the raw (unvalidated) strings {@link resolveCliBuildParams} resolves against a project's declared `buildParams`. `undefined` when no `--param` flag was given, matching `resolveBuildParams`'s "no cli input" shape. */
|
|
25
|
+
export function parseParamFlags(entries?: string[]): Record<string, string> | undefined {
|
|
26
|
+
if (!entries?.length) return undefined;
|
|
27
|
+
return Object.fromEntries(
|
|
28
|
+
entries.map((entry) => {
|
|
29
|
+
const eq = entry.indexOf("=");
|
|
30
|
+
return eq === -1 ? [entry, ""] : [entry.slice(0, eq), entry.slice(eq + 1)];
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Inputs {@link resolveCliBuildParams} needs from a parsed CLI invocation — the `--param`/`--params-file` half of {@link resolveBuildParams}'s `BuildParamsInput` (the `env` half is always `process.env`, resolved internally). */
|
|
36
|
+
export interface CliBuildParamsArgs {
|
|
37
|
+
/** Already-parsed `--param name=value` flags (see {@link parseParamFlags}), or the CLI's already-typed `Record<string,string>` (`chant build`'s own flag parsing does this itself — see `./handlers/build.ts`). */
|
|
38
|
+
cli?: Record<string, string>;
|
|
39
|
+
/** `--params-file <path>` — a JSON file of `{ "name": value }` values, read and parsed here. */
|
|
40
|
+
paramsFile?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CliBuildParamsResolution {
|
|
44
|
+
success: boolean;
|
|
45
|
+
/** Every successfully resolved parameter. Empty when the project declares none, or when `success` is `false`. */
|
|
46
|
+
provenance: BuildParamProvenance[];
|
|
47
|
+
/** `formatError`-wrapped messages, ready to print as-is. Empty when `success` is `true`. */
|
|
48
|
+
errors: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve this invocation's declared build-time parameters (`chant.config.ts`'s
|
|
53
|
+
* `buildParams`) against `args`/the process environment, and log each
|
|
54
|
+
* resolved value — the identical resolution + logging sequence `chant build`
|
|
55
|
+
* runs (`../commands/build.ts`'s `buildCommand`), factored out so every other
|
|
56
|
+
* command that discovers project source runs it too (chant #1108).
|
|
57
|
+
*
|
|
58
|
+
* A resolution failure (an unknown `--param`/`--params-file` name, a missing
|
|
59
|
+
* required value, a type/enum mismatch, or an unreadable `--params-file`) is
|
|
60
|
+
* returned as `{ success: false, errors }` — never thrown — so the caller can
|
|
61
|
+
* print each message and exit non-zero exactly like a build error, matching
|
|
62
|
+
* chant #1064's acceptance criterion that this never surfaces as a thrown
|
|
63
|
+
* error from inside user source.
|
|
64
|
+
*
|
|
65
|
+
* On success, every resolved parameter is logged via `console.error` as
|
|
66
|
+
* `[param] <name> = <value> (<source>)` — unconditional, not gated on
|
|
67
|
+
* `--verbose`, so a build's environment-varying inputs are always visible,
|
|
68
|
+
* the same way #1022's fold decisions are.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveCliBuildParams(
|
|
71
|
+
buildParamsConfig: BuildParamsConfig | undefined,
|
|
72
|
+
args: CliBuildParamsArgs,
|
|
73
|
+
): CliBuildParamsResolution {
|
|
74
|
+
const errors: string[] = [];
|
|
75
|
+
|
|
76
|
+
let fromFile: Record<string, unknown> | undefined;
|
|
77
|
+
if (args.paramsFile) {
|
|
78
|
+
try {
|
|
79
|
+
fromFile = JSON.parse(readFileSync(resolve(args.paramsFile), "utf-8"));
|
|
80
|
+
} catch (err) {
|
|
81
|
+
errors.push(
|
|
82
|
+
formatError({
|
|
83
|
+
message: `Failed to read/parse --params-file "${args.paramsFile}": ${err instanceof Error ? err.message : String(err)}`,
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const resolution = resolveBuildParams(buildParamsConfig, {
|
|
90
|
+
cli: args.cli,
|
|
91
|
+
fromFile,
|
|
92
|
+
env: process.env,
|
|
93
|
+
});
|
|
94
|
+
for (const message of resolution.errors) {
|
|
95
|
+
errors.push(formatError({ message }));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (errors.length > 0) {
|
|
99
|
+
return { success: false, provenance: [], errors };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const p of resolution.provenance) {
|
|
103
|
+
console.error(formatInfo(`[param] ${p.name} = ${JSON.stringify(p.value)} (${p.source})`));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { success: true, provenance: resolution.provenance, errors: [] };
|
|
107
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { build } from "../../build";
|
|
2
2
|
import { loadChantConfig, resolveOwnershipMarker, resolveFoldEnabled, resolveSandboxEnabled } from "../../config";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveCliBuildParams } from "../build-params-cli";
|
|
4
4
|
import type { Serializer, SerializerResult } from "../../serializer";
|
|
5
5
|
import type { LexiconPlugin } from "../../lexicon";
|
|
6
6
|
import { runPostSynthChecks } from "../../lint/post-synth";
|
|
7
7
|
import { loadPolicyChecks } from "../../lint/policy";
|
|
8
8
|
import { sortedJsonReplacer } from "../../utils";
|
|
9
9
|
import { formatError, formatWarning, formatSuccess, formatBold, formatInfo } from "../format";
|
|
10
|
-
import { writeFileSync, mkdirSync
|
|
10
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
11
11
|
import { resolve, dirname, join, relative } from "path";
|
|
12
12
|
import { watchDirectory, formatTimestamp, formatChangedFiles } from "../watch";
|
|
13
13
|
|
|
@@ -155,44 +155,24 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
155
155
|
// as fold, resolved independently.
|
|
156
156
|
const sandbox = resolveSandboxEnabled(config, options.sandbox);
|
|
157
157
|
|
|
158
|
-
// #1064
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
} catch (err) {
|
|
170
|
-
errors.push(
|
|
171
|
-
formatError({
|
|
172
|
-
message: `Failed to read/parse --params-file "${options.paramsFile}": ${err instanceof Error ? err.message : String(err)}`,
|
|
173
|
-
}),
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
const paramsResolution = resolveBuildParams(config.buildParams, {
|
|
158
|
+
// #1064 (factored into ../build-params-cli.ts's resolveCliBuildParams by
|
|
159
|
+
// #1108, so the component deploy driver runs the identical sequence) —
|
|
160
|
+
// resolve declared build-time parameters (chant.config.ts's buildParams)
|
|
161
|
+
// against this invocation's --param/--params-file/declared env mapping,
|
|
162
|
+
// BEFORE calling build() — a resolution failure (an unknown name, a
|
|
163
|
+
// missing required value, a type/enum mismatch) is reported as a chant
|
|
164
|
+
// build error naming the parameter, never a thrown error from inside user
|
|
165
|
+
// source (which is what loomster's hand-rolled `tierFromEnv()`-style
|
|
166
|
+
// validators did before migrating to this mechanism). Also logs every
|
|
167
|
+
// resolved value (`[param] name = value (source)`) on success.
|
|
168
|
+
const paramsResolution = resolveCliBuildParams(config.buildParams, {
|
|
178
169
|
cli: options.params,
|
|
179
|
-
|
|
180
|
-
env: process.env,
|
|
170
|
+
paramsFile: options.paramsFile,
|
|
181
171
|
});
|
|
182
|
-
|
|
183
|
-
errors.push(
|
|
184
|
-
}
|
|
185
|
-
if (errors.length > 0) {
|
|
172
|
+
if (!paramsResolution.success) {
|
|
173
|
+
errors.push(...paramsResolution.errors);
|
|
186
174
|
return { success: false, resourceCount: 0, fileCount: 0, errors, warnings };
|
|
187
175
|
}
|
|
188
|
-
// #1064 — build-provenance visibility: report every resolved build-time
|
|
189
|
-
// parameter (name, value, and which source won it) the same
|
|
190
|
-
// unconditional-log-not-gated-on---verbose way #1022's fold decisions are
|
|
191
|
-
// reported just below, so a build's environment-varying inputs are as
|
|
192
|
-
// visible as its fold-vs-run choices.
|
|
193
|
-
for (const p of paramsResolution.provenance) {
|
|
194
|
-
console.error(formatInfo(`[param] ${p.name} = ${JSON.stringify(p.value)} (${p.source})`));
|
|
195
|
-
}
|
|
196
176
|
|
|
197
177
|
// #1039 — thread each loaded plugin's registered intrinsics (e.g. AWS's
|
|
198
178
|
// `Sub`) through to the fold path, so a file using a registered intrinsic
|
|
@@ -201,6 +181,14 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
201
181
|
// any), hence the guard.
|
|
202
182
|
const intrinsics = options.plugins?.flatMap((plugin) => plugin.intrinsics?.() ?? []) ?? [];
|
|
203
183
|
|
|
184
|
+
// #1063 — the same loaded plugins, by NAME, are this build's allowlist for
|
|
185
|
+
// following a bare import specifier into a lexicon package (so `Azure`,
|
|
186
|
+
// `GCP`, `S3Actions`, `CI` fold as values). A plugin's `name` is the
|
|
187
|
+
// lexicon name `loadPlugin()` was called with, which is exactly what
|
|
188
|
+
// `@intentius/chant-lexicon-<name>` was imported from — see
|
|
189
|
+
// ../plugins.ts and fold-import.ts's `lexiconPackageName`.
|
|
190
|
+
const lexicons = options.plugins?.map((plugin) => plugin.name) ?? [];
|
|
191
|
+
|
|
204
192
|
// Run the build
|
|
205
193
|
const result = await build(infraPath, options.serializers, undefined, {
|
|
206
194
|
ownership,
|
|
@@ -208,6 +196,7 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
208
196
|
fold,
|
|
209
197
|
sandbox,
|
|
210
198
|
intrinsics,
|
|
199
|
+
lexicons,
|
|
211
200
|
buildParams: paramsResolution.provenance,
|
|
212
201
|
});
|
|
213
202
|
|
|
@@ -32,6 +32,7 @@ import { join } from "path";
|
|
|
32
32
|
import { build } from "../../build";
|
|
33
33
|
import { findInfraFiles } from "../../discovery/files";
|
|
34
34
|
import { detectLexicons } from "../../detectLexicon";
|
|
35
|
+
import { loadChantConfig } from "../../config";
|
|
35
36
|
import { loadPlugins } from "../plugins";
|
|
36
37
|
|
|
37
38
|
export interface ExampleBuildResult {
|
|
@@ -64,7 +65,21 @@ export async function checkExamplesBuild(lexiconDir: string): Promise<ExampleBui
|
|
|
64
65
|
const files = await findInfraFiles(srcDir);
|
|
65
66
|
if (files.length === 0) continue;
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
// An example's own chant.config.{ts,json} (living beside src/, not in
|
|
69
|
+
// it) can declare `lexicons` explicitly — the same precedence real
|
|
70
|
+
// `chant build` gives it via resolveProjectLexicons() in ../plugins.ts.
|
|
71
|
+
// Needed for examples like helm's HelmRender ones, whose composite
|
|
72
|
+
// legitimately produces entities tagged with a lexicon (e.g. "k8s")
|
|
73
|
+
// that no source-file import mentions — detection from imports alone
|
|
74
|
+
// can never see it, so the example built with only its "own" lexicon's
|
|
75
|
+
// plugin and silently dropped every entity of the other, undetectable
|
|
76
|
+
// one. Detection from source-file imports (unchanged) remains the
|
|
77
|
+
// fallback for the many examples with no declared `lexicons` list.
|
|
78
|
+
const { config: exampleConfig } = await loadChantConfig(join(examplesDir, entry.name));
|
|
79
|
+
const lexiconNames =
|
|
80
|
+
exampleConfig.lexicons && exampleConfig.lexicons.length > 0
|
|
81
|
+
? exampleConfig.lexicons
|
|
82
|
+
: await detectLexicons(files);
|
|
68
83
|
if (lexiconNames.length === 0) {
|
|
69
84
|
results.push({
|
|
70
85
|
example: entry.name,
|
|
@@ -332,6 +332,80 @@ describe("plugin integration", () => {
|
|
|
332
332
|
});
|
|
333
333
|
});
|
|
334
334
|
|
|
335
|
+
/**
|
|
336
|
+
* chant #1106 — `runLint`'s EVL rules now receive the active lexicons'
|
|
337
|
+
* registered intrinsics (threaded from `loadAllPluginRules`'s
|
|
338
|
+
* `plugin.intrinsics?.()`, mirroring how `../commands/build.ts` gathers the
|
|
339
|
+
* same set for the fold path). Before this, `chant lint` flagged
|
|
340
|
+
* `Ref(...)` — a registered, opted-in call-form intrinsic (aws's
|
|
341
|
+
* `lexicons/aws/src/plugin.ts`) that `chant build --fold` folds cleanly —
|
|
342
|
+
* as EVL001, purely because EVL001's shared `../../fold/subset.ts`
|
|
343
|
+
* predicate never saw the registry. These use the real aws lexicon plugin
|
|
344
|
+
* (already a workspace dependency) rather than a mock, so the registration
|
|
345
|
+
* this asserts against is the one that actually ships.
|
|
346
|
+
*/
|
|
347
|
+
describe("lintCommand — EVL/intrinsic registry convergence (#1106)", () => {
|
|
348
|
+
let testDir: string;
|
|
349
|
+
|
|
350
|
+
beforeEach(async () => {
|
|
351
|
+
testDir = join(tmpdir(), `chant-lint-intrinsics-test-${Date.now()}-${Math.random()}`);
|
|
352
|
+
await mkdir(testDir, { recursive: true });
|
|
353
|
+
process.env.NO_COLOR = "1";
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
afterEach(async () => {
|
|
357
|
+
await rm(testDir, { recursive: true, force: true });
|
|
358
|
+
delete process.env.NO_COLOR;
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("a registered, opted-in intrinsic call (aws's Ref) is not flagged as EVL001", async () => {
|
|
362
|
+
await writeFile(join(testDir, "chant.config.json"), JSON.stringify({ lexicons: ["aws"] }));
|
|
363
|
+
await writeFile(
|
|
364
|
+
join(testDir, "index.ts"),
|
|
365
|
+
`
|
|
366
|
+
import { Ref } from "@intentius/chant-lexicon-aws";
|
|
367
|
+
|
|
368
|
+
class Queue {
|
|
369
|
+
constructor(_props: Record<string, unknown>) {}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export const environment = "prod";
|
|
373
|
+
export const queue = new Queue({ name: Ref(environment) });
|
|
374
|
+
`,
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
const result = await lintCommand({ path: testDir, format: "stylish" });
|
|
378
|
+
|
|
379
|
+
expect(result.diagnostics.filter((d) => d.ruleId === "EVL001")).toHaveLength(0);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test("an unregistered call is still flagged as EVL001 in the same project", async () => {
|
|
383
|
+
await writeFile(join(testDir, "chant.config.json"), JSON.stringify({ lexicons: ["aws"] }));
|
|
384
|
+
await writeFile(
|
|
385
|
+
join(testDir, "index.ts"),
|
|
386
|
+
`
|
|
387
|
+
import { Ref } from "@intentius/chant-lexicon-aws";
|
|
388
|
+
|
|
389
|
+
class Queue {
|
|
390
|
+
constructor(_props: Record<string, unknown>) {}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function makeName(): string {
|
|
394
|
+
return "generated";
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export const queue = new Queue({ name: makeName() });
|
|
398
|
+
`,
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const result = await lintCommand({ path: testDir, format: "stylish" });
|
|
402
|
+
|
|
403
|
+
const evl001 = result.diagnostics.filter((d) => d.ruleId === "EVL001");
|
|
404
|
+
expect(evl001).toHaveLength(1);
|
|
405
|
+
expect(evl001[0].message).toContain("statically evaluable");
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
335
409
|
describe("lintCommand — project-root config resolution (scoped lint)", () => {
|
|
336
410
|
let testDir: string;
|
|
337
411
|
|
package/src/cli/commands/lint.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { readFileSync, writeFileSync, readdirSync, statSync } from "fs";
|
|
|
3
3
|
import { execFileSync } from "child_process";
|
|
4
4
|
import { runLint, parseDisableComments } from "../../lint/engine";
|
|
5
5
|
import type { LintRule, LintDiagnostic, LintFix } from "../../lint/rule";
|
|
6
|
+
import type { IntrinsicDef } from "../../lexicon";
|
|
6
7
|
import { loadPlugins, resolveProjectLexicons } from "../plugins";
|
|
7
8
|
import { formatStylish, formatJson, formatSarif } from "../reporters/stylish";
|
|
8
9
|
import { loadLocalRules } from "../../lint/rule-loader";
|
|
@@ -86,8 +87,17 @@ export async function loadPluginRules(
|
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
89
|
* Load all lint rules: core COR/EVL rules, then lexicon plugin rules.
|
|
90
|
+
*
|
|
91
|
+
* Also returns the active lexicons' registered intrinsics (chant #1106) —
|
|
92
|
+
* gathered here because this is where the project's lexicon plugins are
|
|
93
|
+
* already resolved and loaded (`loadPlugins`), the same set `../commands/
|
|
94
|
+
* build.ts` reads `plugin.intrinsics?.()` off of for the fold path. Handed
|
|
95
|
+
* back alongside `rules` so `lintCommand` can thread it into every
|
|
96
|
+
* `runLint` call without loading plugins a second time.
|
|
89
97
|
*/
|
|
90
|
-
async function loadAllPluginRules(
|
|
98
|
+
async function loadAllPluginRules(
|
|
99
|
+
projectPath: string,
|
|
100
|
+
): Promise<{ rules: Map<string, LintRule>; intrinsics: IntrinsicDef[] }> {
|
|
91
101
|
const rules = new Map<string, LintRule>();
|
|
92
102
|
|
|
93
103
|
// Load core COR/EVL rules directly
|
|
@@ -106,6 +116,13 @@ async function loadAllPluginRules(projectPath: string): Promise<Map<string, Lint
|
|
|
106
116
|
// Load only project lexicon plugins (no "chant" injection)
|
|
107
117
|
const plugins = await loadPlugins(lexiconNames);
|
|
108
118
|
|
|
119
|
+
// chant #1106 — the same plugins' registered intrinsics (`Ref`, `GetAtt`,
|
|
120
|
+
// ...), so EVL001 can answer "does this call fold?" exactly like fold()
|
|
121
|
+
// does instead of flagging every call as a violation. A plugin's
|
|
122
|
+
// `intrinsics` is an optional extension (not every lexicon defines any),
|
|
123
|
+
// hence the guard.
|
|
124
|
+
const intrinsics = plugins.flatMap((plugin) => plugin.intrinsics?.() ?? []);
|
|
125
|
+
|
|
109
126
|
for (const plugin of plugins) {
|
|
110
127
|
if (plugin.lintRules) {
|
|
111
128
|
for (const r of plugin.lintRules()) {
|
|
@@ -127,7 +144,7 @@ async function loadAllPluginRules(projectPath: string): Promise<Map<string, Lint
|
|
|
127
144
|
rules.set(r.id, r);
|
|
128
145
|
}
|
|
129
146
|
|
|
130
|
-
return rules;
|
|
147
|
+
return { rules, intrinsics };
|
|
131
148
|
}
|
|
132
149
|
|
|
133
150
|
/**
|
|
@@ -428,7 +445,14 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
428
445
|
const hasOverrides = config.overrides && config.overrides.length > 0;
|
|
429
446
|
|
|
430
447
|
// Load all rules from lexicon plugins (core "chant" + lexicon-specific)
|
|
431
|
-
|
|
448
|
+
const loaded = await loadAllPluginRules(projectRoot);
|
|
449
|
+
let allRules = loaded.rules;
|
|
450
|
+
// chant #1106 — the active lexicons' registered intrinsics, threaded into
|
|
451
|
+
// every runLint() call below so EVL001 answers exactly like fold() does
|
|
452
|
+
// for a registered, opted-in call (`Ref(...)`, `GetAtt(...)`) instead of
|
|
453
|
+
// flagging it. Computed once here regardless of which branch below runs,
|
|
454
|
+
// same as `allRules`.
|
|
455
|
+
const intrinsics = loaded.intrinsics;
|
|
432
456
|
|
|
433
457
|
// Merge in any config-level plugin rules (custom .ts rule files)
|
|
434
458
|
if (config.plugins && config.plugins.length > 0) {
|
|
@@ -443,7 +467,7 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
443
467
|
let diagnostics: LintDiagnostic[];
|
|
444
468
|
let suppressed: Array<LintDiagnostic & { reason?: string }> = [];
|
|
445
469
|
if (options.rules) {
|
|
446
|
-
const result = await runLint(files, options.rules, undefined);
|
|
470
|
+
const result = await runLint(files, options.rules, undefined, intrinsics);
|
|
447
471
|
diagnostics = result.diagnostics;
|
|
448
472
|
suppressed = result.suppressed;
|
|
449
473
|
} else if (hasOverrides) {
|
|
@@ -451,13 +475,13 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
451
475
|
for (const file of files) {
|
|
452
476
|
const relativePath = relative(projectRoot, file);
|
|
453
477
|
const { rules: fileRules, ruleOptions } = getDefaultRules(projectRoot, relativePath, allRules);
|
|
454
|
-
const result = await runLint([file], fileRules, ruleOptions);
|
|
478
|
+
const result = await runLint([file], fileRules, ruleOptions, intrinsics);
|
|
455
479
|
diagnostics.push(...result.diagnostics);
|
|
456
480
|
suppressed.push(...result.suppressed);
|
|
457
481
|
}
|
|
458
482
|
} else {
|
|
459
483
|
const { rules, ruleOptions } = getDefaultRules(projectRoot, undefined, allRules);
|
|
460
|
-
const result = await runLint(files, rules, ruleOptions);
|
|
484
|
+
const result = await runLint(files, rules, ruleOptions, intrinsics);
|
|
461
485
|
diagnostics = result.diagnostics;
|
|
462
486
|
suppressed = result.suppressed;
|
|
463
487
|
}
|
|
@@ -491,7 +515,7 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
491
515
|
|
|
492
516
|
// Re-lint after fixes to get updated diagnostics
|
|
493
517
|
if (options.rules) {
|
|
494
|
-
const postResult = await runLint(files, options.rules, undefined);
|
|
518
|
+
const postResult = await runLint(files, options.rules, undefined, intrinsics);
|
|
495
519
|
diagnostics = postResult.diagnostics;
|
|
496
520
|
suppressed = postResult.suppressed;
|
|
497
521
|
} else if (hasOverrides) {
|
|
@@ -500,13 +524,13 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
|
|
|
500
524
|
for (const file of files) {
|
|
501
525
|
const relativePath = relative(projectRoot, file);
|
|
502
526
|
const { rules: fileRules, ruleOptions } = getDefaultRules(projectRoot, relativePath, allRules);
|
|
503
|
-
const postResult = await runLint([file], fileRules, ruleOptions);
|
|
527
|
+
const postResult = await runLint([file], fileRules, ruleOptions, intrinsics);
|
|
504
528
|
diagnostics.push(...postResult.diagnostics);
|
|
505
529
|
suppressed.push(...postResult.suppressed);
|
|
506
530
|
}
|
|
507
531
|
} else {
|
|
508
532
|
const { rules, ruleOptions } = getDefaultRules(projectRoot, undefined, allRules);
|
|
509
|
-
const postResult = await runLint(files, rules, ruleOptions);
|
|
533
|
+
const postResult = await runLint(files, rules, ruleOptions, intrinsics);
|
|
510
534
|
diagnostics = postResult.diagnostics;
|
|
511
535
|
suppressed = postResult.suppressed;
|
|
512
536
|
}
|