@intentius/chant 0.21.0 → 0.22.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/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +34 -2
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/index.d.ts +12 -0
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/fold/subset.d.ts +23 -0
- package/dist/fold/subset.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/commands/build.ts +9 -0
- package/src/cli/commands/check-lexicon-examples.ts +16 -1
- package/src/discovery/fold-import.test.ts +328 -1
- package/src/discovery/fold-import.ts +212 -11
- package/src/discovery/index.ts +16 -1
- package/src/fold/fold.ts +6 -2
- package/src/fold/subset.test.ts +67 -1
- package/src/fold/subset.ts +30 -3
- package/src/serializer-walker.ts +14 -0
package/dist/build.d.ts
CHANGED
|
@@ -87,6 +87,13 @@ export interface BuildOptions {
|
|
|
87
87
|
* The CLI populates this from `options.plugins.flatMap(p => p.intrinsics?.() ?? [])`.
|
|
88
88
|
*/
|
|
89
89
|
intrinsics?: IntrinsicDef[];
|
|
90
|
+
/**
|
|
91
|
+
* chant #1063 — the lexicon names loaded for this build. Passed straight
|
|
92
|
+
* through to {@link DiscoveryOptions.lexicons} in `./discovery/index`;
|
|
93
|
+
* ignored unless {@link fold} is set. The CLI populates this from
|
|
94
|
+
* `options.plugins.map(p => p.name)`.
|
|
95
|
+
*/
|
|
96
|
+
lexicons?: readonly string[];
|
|
90
97
|
/**
|
|
91
98
|
* chant #1045 Phase 2 — opt-in: run-fallback files (or, when {@link fold}
|
|
92
99
|
* isn't set, every file) execute together, isolated, in one sandboxed
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAsB,MAAM,UAAU,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,cAAc,EAAiC,MAAM,UAAU,CAAC;AACzE,OAAO,EAAE,aAAa,EAAmB,MAAM,kBAAkB,CAAC;AAGlE,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAmB,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAKvF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CACb,MAAM,EACN;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CACtD,CAAC;IACF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,wEAAwE;IACxE,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,gDAAgD;IAChD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3C,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;IAClB,yEAAyE;IACzE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACjC,YAAY,EAAE,MAAM,EAAE,GACrB,UAAU,CAuEZ;AAED;;GAEG;AACH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEjC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;OAKG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAE5B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC;IAChD,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,qDAAqD;IACrD,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,qDAAqD;IACrD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,6DAA6D;IAC7D,MAAM,EAAE,KAAK,CAAC,cAAc,GAAG,UAAU,CAAC,CAAC;IAC3C,wDAAwD;IACxD,QAAQ,EAAE,aAAa,CAAC;IACxB,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,YAAY,EAAE,CAAC;IAE9B;;;;;;OAMG;IACH,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAChC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CActC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAChC,aAAa,EAAE,CA4DjB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAChC,aAAa,EAAE,CAyFjB;AA6ED;;;;;;;GAOG;AACH,wBAAsB,KAAK,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,UAAU,EAAE,EACzB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAC9B,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AASnD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2CAA2C;IAC3C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAoB/C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2JAA2J;IAC3J,WAAW,CAAC,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;CACjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AASnD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2CAA2C;IAC3C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAoB/C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2JAA2J;IAC3J,WAAW,CAAC,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;CACjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAoW9E;AAiDD;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAIlD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAItD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EACrB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACxC,MAAM,IAAI,CAmCZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-lexicon-examples.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/check-lexicon-examples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;
|
|
1
|
+
{"version":3,"file":"check-lexicon-examples.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/check-lexicon-examples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAUH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAoE1F"}
|
|
@@ -147,9 +147,41 @@ export interface FoldSession {
|
|
|
147
147
|
* doesn't use build-time parameters pays nothing extra here.
|
|
148
148
|
*/
|
|
149
149
|
readonly buildParams?: Readonly<Record<string, BuildParamValue>>;
|
|
150
|
+
/**
|
|
151
|
+
* chant #1063 — the exact package specifiers of the lexicons LOADED for
|
|
152
|
+
* this build (`@intentius/chant-lexicon-aws`, …), derived from the lexicon
|
|
153
|
+
* names the build already resolved (`resolveProjectLexicons` ->
|
|
154
|
+
* `loadPlugins`, see ../cli/plugins.ts). This is the entire allowlist
|
|
155
|
+
* {@link buildExternals} will follow a bare import specifier into — see
|
|
156
|
+
* {@link activeLexiconPackage} for why the set is matched by TEXT and
|
|
157
|
+
* built from names the build already knows, rather than by resolving
|
|
158
|
+
* specifiers to find out what they are.
|
|
159
|
+
*
|
|
160
|
+
* Empty when the caller supplied no lexicon list, which disables
|
|
161
|
+
* lexicon-package resolution entirely rather than falling back to
|
|
162
|
+
* something more permissive: "an active lexicon of this build" is the
|
|
163
|
+
* boundary, and a build that can't say what its lexicons are hasn't
|
|
164
|
+
* established one.
|
|
165
|
+
*/
|
|
166
|
+
readonly lexiconPackages: ReadonlySet<string>;
|
|
150
167
|
}
|
|
151
|
-
/**
|
|
152
|
-
|
|
168
|
+
/**
|
|
169
|
+
* chant #1063 — the package specifier a lexicon NAME (`"aws"`, `"gitlab"`)
|
|
170
|
+
* is installed under. The one naming convention the whole CLI already
|
|
171
|
+
* depends on: `loadPlugin(name)` imports exactly this
|
|
172
|
+
* (../cli/plugins.ts), `detectLexicons` scans source for exactly this
|
|
173
|
+
* (../detectLexicon.ts), and `chant init` writes exactly this into
|
|
174
|
+
* package.json.
|
|
175
|
+
*/
|
|
176
|
+
export declare function lexiconPackageName(lexiconName: string): string;
|
|
177
|
+
/**
|
|
178
|
+
* Create a fresh, empty {@link FoldSession}.
|
|
179
|
+
*
|
|
180
|
+
* @param lexicons - chant #1063: the lexicon NAMES active for this build
|
|
181
|
+
* (`["aws", "k8s"]`). Converted to package specifiers via
|
|
182
|
+
* {@link lexiconPackageName}; see {@link FoldSession.lexiconPackages}.
|
|
183
|
+
*/
|
|
184
|
+
export declare function createFoldSession(intrinsics?: readonly IntrinsicDef[], buildParams?: Readonly<Record<string, BuildParamValue>>, lexicons?: readonly string[]): FoldSession;
|
|
153
185
|
/**
|
|
154
186
|
* Attempt to fold one source file with zero execution of its own top-level
|
|
155
187
|
* code. Returns the folded, instantiated entities on success, or a reason
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fold-import.d.ts","sourceRoot":"","sources":["../../src/discovery/fold-import.ts"],"names":[],"mappings":"AAMA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"fold-import.d.ts","sourceRoot":"","sources":["../../src/discovery/fold-import.ts"],"names":[],"mappings":"AAMA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAkB3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,mGAAmG;AACnG,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,iBAAiB,CAAC,CAAC;AAElF,MAAM,MAAM,cAAc,GACtB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB;;;;;;;;;OASG;IACH,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC;;;;;;;;;;;;OAYG;IACH,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAClC,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IACzB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;IACjE;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC/C;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,GAAE,SAAS,YAAY,EAAO,EACxC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,EACvD,QAAQ,GAAE,SAAS,MAAM,EAAO,GAC/B,WAAW,CAUb;AAiwDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,SAAS,YAAY,EAAO,EACxC,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,cAAc,CAAC,CAEzB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,GACrD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CA8EtB"}
|
|
@@ -43,6 +43,18 @@ export interface DiscoveryOptions {
|
|
|
43
43
|
* then falls back to run at the unregistered tag).
|
|
44
44
|
*/
|
|
45
45
|
intrinsics?: IntrinsicDef[];
|
|
46
|
+
/**
|
|
47
|
+
* chant #1063 — the lexicon NAMES loaded for this build (`["aws", "k8s"]`),
|
|
48
|
+
* i.e. what `resolveProjectLexicons()` returned and `loadPlugins()` then
|
|
49
|
+
* imported. Threaded into the fold session as the ALLOWLIST of packages a
|
|
50
|
+
* bare import specifier may be resolved into, so a lexicon's plain data
|
|
51
|
+
* exports (`Azure`/`GCP`'s pseudo-parameter namespaces, AWS's `S3Actions`,
|
|
52
|
+
* gitlab's `CI`) fold as identifier values instead of failing the file.
|
|
53
|
+
* Only meaningful when {@link fold} is set. Default: none — a caller that
|
|
54
|
+
* doesn't say which lexicons are active gets no bare-specifier resolution
|
|
55
|
+
* at all, rather than a looser fallback.
|
|
56
|
+
*/
|
|
57
|
+
lexicons?: readonly string[];
|
|
46
58
|
/**
|
|
47
59
|
* chant #1045 Phase 2 — opt-in: whatever would otherwise reach the
|
|
48
60
|
* in-process `importModule` step (every file, when {@link fold} isn't set;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQ/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAI1D;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAE5B;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,8DAA8D;IAC9D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,qDAAqD;IACrD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB;;;OAGG;IACH,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQ/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAI1D;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAE5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAE7B;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,8DAA8D;IAC9D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,qDAAqD;IACrD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB;;;OAGG;IACH,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAsQjG"}
|
package/dist/fold/fold.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../src/fold/fold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAyC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,
|
|
1
|
+
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../src/fold/fold.ts"],"names":[],"mappings":"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"}
|
package/dist/fold/subset.d.ts
CHANGED
|
@@ -105,11 +105,34 @@ export declare const SUPPORTED_UNARY_OPERATORS: ReadonlySet<ts.SyntaxKind>;
|
|
|
105
105
|
export declare function isLiteralPropertyName(node: ts.PropertyName): node is ts.Identifier | ts.StringLiteral | ts.NumericLiteral;
|
|
106
106
|
/** An element-access key foldable without execution: a string or numeric LITERAL only (EVL003 semantics). */
|
|
107
107
|
export declare function isLiteralElementKey(node: ts.Expression): node is ts.StringLiteral | ts.NumericLiteral;
|
|
108
|
+
/**
|
|
109
|
+
* A short, single-line rendering of `node`'s source text for embedding in a
|
|
110
|
+
* diagnostic message — never the raw `getText()`, which reproduces the
|
|
111
|
+
* node's ENTIRE source verbatim and can span dozens of lines for a real
|
|
112
|
+
* composite call or object literal (chant #1054: a fold fallback reason that
|
|
113
|
+
* embeds one of these buries the actual error after it, and breaks any
|
|
114
|
+
* line-oriented consumer of `[fold:run]` output). Internal whitespace
|
|
115
|
+
* (including newlines) collapses to a single space, and the result is capped
|
|
116
|
+
* to a bounded length so one pathological node can't blow out an otherwise
|
|
117
|
+
* one-line reason either.
|
|
118
|
+
*/
|
|
119
|
+
export declare function briefNodeText(node: ts.Node, maxLength?: number): string;
|
|
108
120
|
export declare function computedPropertyNameMessage(node: ts.PropertyName): string;
|
|
109
121
|
export declare function dynamicElementAccessMessage(keyNode: ts.Expression): string;
|
|
110
122
|
export declare const UNSUPPORTED_OBJECT_MEMBER_MESSAGE = "unsupported object member";
|
|
111
123
|
export declare const UNSUPPORTED_UNARY_MESSAGE = "unsupported unary";
|
|
112
124
|
export declare function unsupportedBinaryMessage(opKind: ts.SyntaxKind): string;
|
|
125
|
+
/**
|
|
126
|
+
* chant #1054 — the ONE wording for "a bare function/method call used where
|
|
127
|
+
* chant needs a value it can fold." Before this, `fold()` (this message) and
|
|
128
|
+
* `../discovery/fold-import.ts`'s `resolveCallExpression` (a top-level
|
|
129
|
+
* export's own call-as-a-value check) had each grown their own hand-written
|
|
130
|
+
* copy — "function call as a value" here, "call expression as a value"
|
|
131
|
+
* there — for the identical rejection, which meant a tool grouping fallback
|
|
132
|
+
* reasons by text had to match both to avoid silently under-counting one of
|
|
133
|
+
* them. `resolveCallExpression` now calls this function directly instead of
|
|
134
|
+
* building its own string.
|
|
135
|
+
*/
|
|
113
136
|
export declare function callExpressionMessage(node: ts.CallExpression): string;
|
|
114
137
|
export declare function unsupportedExpressionMessage(node: ts.Node): string;
|
|
115
138
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subset.d.ts","sourceRoot":"","sources":["../../src/fold/subset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAEH,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/C,uGAAuG;AACvG,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,0FAA0F;AAC1F,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,EAAE,CAAC,UAAU,CAchE,CAAC;AAEH,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,EAAE,CAAC,UAAU,CAG/D,CAAC;AAEH,gHAAgH;AAChH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,EAAE,CAAC,YAAY,GACpB,IAAI,IAAI,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,cAAc,CAE9D;AAED,6GAA6G;AAC7G,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,cAAc,CAErG;AAQD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,GAAG,MAAM,CAEzE;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,GAAG,MAAM,CAE1E;AAED,eAAO,MAAM,iCAAiC,8BAA8B,CAAC;AAE7E,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAE7D,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,MAAM,CAEtE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,GAAG,MAAM,CAErE;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,CAElE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,EAAE,CAAC,wBAAwB,EACjC,UAAU,CAAC,EAAE,SAAS,YAAY,EAAE,GACnC,eAAe,GAAG,SAAS,CAc7B;AAWD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,UAAU,CAAC,EAAE,SAAS,YAAY,EAAE,GACnC,eAAe,GAAG,SAAS,CAiK7B"}
|
|
1
|
+
{"version":3,"file":"subset.d.ts","sourceRoot":"","sources":["../../src/fold/subset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAEH,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/C,uGAAuG;AACvG,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,0FAA0F;AAC1F,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,EAAE,CAAC,UAAU,CAchE,CAAC;AAEH,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,EAAE,CAAC,UAAU,CAG/D,CAAC;AAEH,gHAAgH;AAChH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,EAAE,CAAC,YAAY,GACpB,IAAI,IAAI,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,cAAc,CAE9D;AAED,6GAA6G;AAC7G,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,cAAc,CAErG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,SAAS,SAAK,GAAG,MAAM,CAGnE;AAQD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,GAAG,MAAM,CAEzE;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,GAAG,MAAM,CAE1E;AAED,eAAO,MAAM,iCAAiC,8BAA8B,CAAC;AAE7E,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAE7D,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,MAAM,CAEtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,GAAG,MAAM,CAErE;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,CAElE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,EAAE,CAAC,wBAAwB,EACjC,UAAU,CAAC,EAAE,SAAS,YAAY,EAAE,GACnC,eAAe,GAAG,SAAS,CAc7B;AAWD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,UAAU,CAAC,EAAE,SAAS,YAAY,EAAE,GACnC,eAAe,GAAG,SAAS,CAiK7B"}
|
|
@@ -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
|
});
|
|
@@ -201,6 +201,14 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
201
201
|
// any), hence the guard.
|
|
202
202
|
const intrinsics = options.plugins?.flatMap((plugin) => plugin.intrinsics?.() ?? []) ?? [];
|
|
203
203
|
|
|
204
|
+
// #1063 — the same loaded plugins, by NAME, are this build's allowlist for
|
|
205
|
+
// following a bare import specifier into a lexicon package (so `Azure`,
|
|
206
|
+
// `GCP`, `S3Actions`, `CI` fold as values). A plugin's `name` is the
|
|
207
|
+
// lexicon name `loadPlugin()` was called with, which is exactly what
|
|
208
|
+
// `@intentius/chant-lexicon-<name>` was imported from — see
|
|
209
|
+
// ../plugins.ts and fold-import.ts's `lexiconPackageName`.
|
|
210
|
+
const lexicons = options.plugins?.map((plugin) => plugin.name) ?? [];
|
|
211
|
+
|
|
204
212
|
// Run the build
|
|
205
213
|
const result = await build(infraPath, options.serializers, undefined, {
|
|
206
214
|
ownership,
|
|
@@ -208,6 +216,7 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
208
216
|
fold,
|
|
209
217
|
sandbox,
|
|
210
218
|
intrinsics,
|
|
219
|
+
lexicons,
|
|
211
220
|
buildParams: paramsResolution.provenance,
|
|
212
221
|
});
|
|
213
222
|
|
|
@@ -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,
|
|
@@ -7,6 +7,7 @@ import { tryFoldFile, createFoldSession } from "./fold-import";
|
|
|
7
7
|
import { isDeclarable } from "../declarable";
|
|
8
8
|
import { isCompositeInstance } from "../composite";
|
|
9
9
|
import { isAttrRefLike } from "../utils";
|
|
10
|
+
import { isIntrinsic } from "../intrinsic";
|
|
10
11
|
import { params as sharedParams } from "../params";
|
|
11
12
|
import type { AttrRef } from "../attrref";
|
|
12
13
|
import type { IntrinsicDef } from "../lexicon";
|
|
@@ -165,7 +166,28 @@ describe("tryFoldFile", () => {
|
|
|
165
166
|
|
|
166
167
|
expect(result.ok).toBe(false);
|
|
167
168
|
if (result.ok) return;
|
|
168
|
-
|
|
169
|
+
// chant #1054 — locks in the ONE wording for "a call used as a value,
|
|
170
|
+
// not foldable" (../fold/subset.ts's `callExpressionMessage`), which
|
|
171
|
+
// `resolveCallExpression` now reuses instead of its own hand-written
|
|
172
|
+
// "call expression as a value" copy.
|
|
173
|
+
expect(result.reason).toBe('"stack" is not foldable: function call as a value is not foldable: LocalStack(...)');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("falls back when a top-level export's callee isn't a plain identifier (chant #1054: same wording as any other call-as-a-value rejection)", async () => {
|
|
177
|
+
const file = join(testDir, "main.ts");
|
|
178
|
+
await writeFile(
|
|
179
|
+
file,
|
|
180
|
+
`
|
|
181
|
+
const builder = { build: (props: { name: string }) => ({ name: props.name }) };
|
|
182
|
+
export const stack = builder.build({ name: "x" });
|
|
183
|
+
`,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const result = await tryFoldFile(file);
|
|
187
|
+
|
|
188
|
+
expect(result.ok).toBe(false);
|
|
189
|
+
if (result.ok) return;
|
|
190
|
+
expect(result.reason).toBe('"stack" is not foldable: function call as a value is not foldable: builder.build(...)');
|
|
169
191
|
});
|
|
170
192
|
|
|
171
193
|
test("falls back when a prop value is not foldable", async () => {
|
|
@@ -286,6 +308,77 @@ describe("tryFoldFile", () => {
|
|
|
286
308
|
DependsOn: "otherResource",
|
|
287
309
|
});
|
|
288
310
|
});
|
|
311
|
+
|
|
312
|
+
// chant #1054 — a destructured composite export's fallback reason used to
|
|
313
|
+
// embed `decl.node.getText()`: the ENTIRE call expression, which for a
|
|
314
|
+
// real multi-prop composite call is many lines, burying the real error
|
|
315
|
+
// after all of it. These lock in the fix: the reason names the binding(s)
|
|
316
|
+
// and the callee briefly instead, and stays on one line.
|
|
317
|
+
test("a destructured composite export whose source fails to resolve reports a brief, single-line reason naming the binding names and the callee", async () => {
|
|
318
|
+
await writeFile(
|
|
319
|
+
join(testDir, "composites.ts"),
|
|
320
|
+
`
|
|
321
|
+
import { createResource } from ${JSON.stringify(runtimePath)};
|
|
322
|
+
import { Composite } from ${JSON.stringify(compositePath)};
|
|
323
|
+
const Cluster = createResource("Test::Cluster", "gcp", {});
|
|
324
|
+
const NodePool = createResource("Test::NodePool", "gcp", {});
|
|
325
|
+
export const GkeCluster = Composite<{ name: string; location: string }>((props) => {
|
|
326
|
+
const cluster = new Cluster({ name: props.name });
|
|
327
|
+
const nodePool = new NodePool({ location: props.location });
|
|
328
|
+
return { cluster, nodePool };
|
|
329
|
+
}, "GkeCluster");
|
|
330
|
+
`,
|
|
331
|
+
);
|
|
332
|
+
const file = join(testDir, "cluster.ts");
|
|
333
|
+
await writeFile(
|
|
334
|
+
file,
|
|
335
|
+
`
|
|
336
|
+
import { GkeCluster } from "./composites";
|
|
337
|
+
export const { cluster, nodePool } = GkeCluster({
|
|
338
|
+
name: config.clusterName,
|
|
339
|
+
location: config.region,
|
|
340
|
+
});
|
|
341
|
+
`,
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
const result = await tryFoldFile(file);
|
|
345
|
+
|
|
346
|
+
expect(result.ok).toBe(false);
|
|
347
|
+
if (result.ok) return;
|
|
348
|
+
expect(result.reason).not.toContain("\n");
|
|
349
|
+
expect(result.reason).toContain('"cluster, nodePool" (destructured from GkeCluster(...))');
|
|
350
|
+
expect(result.reason).toContain("unresolved identifier: config");
|
|
351
|
+
// The whole multi-line source is gone — the reason never reproduces the
|
|
352
|
+
// call's own argument list verbatim.
|
|
353
|
+
expect(result.reason).not.toContain("clusterName");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("a destructured export whose source resolves but isn't an object still reports a brief, single-line reason", async () => {
|
|
357
|
+
await writeFile(
|
|
358
|
+
join(testDir, "factory.ts"),
|
|
359
|
+
`
|
|
360
|
+
export function StringFactory(props: { text: string }): string {
|
|
361
|
+
return props.text;
|
|
362
|
+
}
|
|
363
|
+
`,
|
|
364
|
+
);
|
|
365
|
+
const file = join(testDir, "main.ts");
|
|
366
|
+
await writeFile(
|
|
367
|
+
file,
|
|
368
|
+
`
|
|
369
|
+
import { StringFactory } from "./factory";
|
|
370
|
+
export const { length } = StringFactory({ text: "hello" });
|
|
371
|
+
`,
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const result = await tryFoldFile(file);
|
|
375
|
+
|
|
376
|
+
expect(result.ok).toBe(false);
|
|
377
|
+
if (result.ok) return;
|
|
378
|
+
expect(result.reason).toBe(
|
|
379
|
+
'"length" (destructured from StringFactory(...)) is not foldable: not a composite call or object',
|
|
380
|
+
);
|
|
381
|
+
});
|
|
289
382
|
});
|
|
290
383
|
|
|
291
384
|
// chant #1020 (epic #1019) — cross-file resolution: an imported binding now
|
|
@@ -1596,3 +1689,237 @@ describe("tryFoldFile — registered call-form intrinsics (#1044)", () => {
|
|
|
1596
1689
|
expect(result.reason).toContain("same-file resource reference");
|
|
1597
1690
|
});
|
|
1598
1691
|
});
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* chant #1063 — cross-file references into an ACTIVE LEXICON PACKAGE's
|
|
1695
|
+
* exports.
|
|
1696
|
+
*
|
|
1697
|
+
* #1020 taught fold to follow an identifier into another module, but only
|
|
1698
|
+
* through a relative/absolute specifier — a bare package specifier was
|
|
1699
|
+
* skipped outright, so `Azure`, `GCP`, `S3Actions` and gitlab's `CI` all
|
|
1700
|
+
* failed as "unresolved identifier" even though every one of them is a plain
|
|
1701
|
+
* `as const` object the folder already handles. These tests install a real
|
|
1702
|
+
* package into the fixture tree's own `node_modules` and drive the identical
|
|
1703
|
+
* resolution a real `@intentius/chant-lexicon-azure` import takes: a genuine
|
|
1704
|
+
* bare specifier, resolved by {@link fastResolveBareSpecifier} walking up to
|
|
1705
|
+
* `<testDir>/node_modules`, then really imported.
|
|
1706
|
+
*
|
|
1707
|
+
* Every fixture package gets a UNIQUE name. `bareSpecifierPathCache` is
|
|
1708
|
+
* process-wide and keyed by specifier alone (deliberately — see its doc), so
|
|
1709
|
+
* two tests reusing one name would have the second silently resolve to the
|
|
1710
|
+
* first's already-deleted directory.
|
|
1711
|
+
*/
|
|
1712
|
+
describe("tryFoldFile — active lexicon package exports (#1063)", () => {
|
|
1713
|
+
let testDir: string;
|
|
1714
|
+
let seq = 0;
|
|
1715
|
+
|
|
1716
|
+
beforeEach(async () => {
|
|
1717
|
+
testDir = join(tmpdir(), `chant-fold-import-lexpkg-test-${Date.now()}-${Math.random()}`);
|
|
1718
|
+
await mkdir(testDir, { recursive: true });
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
afterEach(async () => {
|
|
1722
|
+
await rm(testDir, { recursive: true, force: true });
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* Install a package into `<testDir>/node_modules/<name>` and return its
|
|
1727
|
+
* bare specifier. `lexiconName` is what a build would list in
|
|
1728
|
+
* `chant.config.ts`'s `lexicons`; the package name follows chant's one
|
|
1729
|
+
* naming convention (`@intentius/chant-lexicon-<name>`), same as
|
|
1730
|
+
* `loadPlugin()` uses.
|
|
1731
|
+
*/
|
|
1732
|
+
async function installLexiconPackage(source: string): Promise<{ lexicon: string; specifier: string }> {
|
|
1733
|
+
const lexicon = `fold1063x${seq++}${Date.now().toString(36)}`;
|
|
1734
|
+
const specifier = `@intentius/chant-lexicon-${lexicon}`;
|
|
1735
|
+
const dir = join(testDir, "node_modules", specifier);
|
|
1736
|
+
await mkdir(dir, { recursive: true });
|
|
1737
|
+
await writeFile(
|
|
1738
|
+
join(dir, "package.json"),
|
|
1739
|
+
JSON.stringify({ name: specifier, version: "0.0.0", type: "module", exports: { ".": "./index.js" } }),
|
|
1740
|
+
);
|
|
1741
|
+
await writeFile(join(dir, "index.js"), source);
|
|
1742
|
+
return { lexicon, specifier };
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/** A pseudo-parameter namespace shaped exactly like `lexicons/azure/src/pseudo.ts`'s `Azure`, plus a plain-data constants object and a callable export. */
|
|
1746
|
+
const LEXICON_SOURCE = `
|
|
1747
|
+
const INTRINSIC_MARKER = Symbol.for("chant.intrinsic");
|
|
1748
|
+
class ArmPseudoParameter {
|
|
1749
|
+
constructor(expression) {
|
|
1750
|
+
this[INTRINSIC_MARKER] = true;
|
|
1751
|
+
this.expression = expression;
|
|
1752
|
+
}
|
|
1753
|
+
toJSON() { return this.expression; }
|
|
1754
|
+
}
|
|
1755
|
+
export const ResourceGroupLocation = new ArmPseudoParameter("[resourceGroup().location]");
|
|
1756
|
+
export const Azure = { ResourceGroupLocation };
|
|
1757
|
+
export const CI = { CommitBranch: "$CI_COMMIT_BRANCH", DefaultBranch: "$CI_DEFAULT_BRANCH" };
|
|
1758
|
+
export const S3Actions = { ReadOnly: ["s3:GetObject", "s3:ListBucket"] };
|
|
1759
|
+
export function defaultTags(tags) { return tags; }
|
|
1760
|
+
`;
|
|
1761
|
+
|
|
1762
|
+
test("a lexicon package's plain-data export resolves and folds when referenced as a value", async () => {
|
|
1763
|
+
const { lexicon, specifier } = await installLexiconPackage(LEXICON_SOURCE);
|
|
1764
|
+
const file = join(testDir, "main.ts");
|
|
1765
|
+
await writeFile(
|
|
1766
|
+
file,
|
|
1767
|
+
`
|
|
1768
|
+
import { CI, S3Actions } from ${JSON.stringify(specifier)};
|
|
1769
|
+
throw new Error("must never execute — sentinel for #1063 fold verification");
|
|
1770
|
+
export const branch = CI.CommitBranch;
|
|
1771
|
+
export const rule = \`\${CI.CommitBranch} == \${CI.DefaultBranch}\`;
|
|
1772
|
+
export const actions = S3Actions.ReadOnly;
|
|
1773
|
+
`,
|
|
1774
|
+
);
|
|
1775
|
+
|
|
1776
|
+
const result = await tryFoldFile(file, [], createFoldSession([], undefined, [lexicon]));
|
|
1777
|
+
|
|
1778
|
+
expect(result.ok).toBe(true);
|
|
1779
|
+
if (!result.ok) return;
|
|
1780
|
+
expect(result.exportedValues.get("branch")).toBe("$CI_COMMIT_BRANCH");
|
|
1781
|
+
// A template literal is the real test that the VALUE arrived, not a
|
|
1782
|
+
// symbolic envelope: an envelope would stringify to "[object Object]".
|
|
1783
|
+
expect(result.exportedValues.get("rule")).toBe("$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH");
|
|
1784
|
+
expect(result.exportedValues.get("actions")).toEqual(["s3:GetObject", "s3:ListBucket"]);
|
|
1785
|
+
});
|
|
1786
|
+
|
|
1787
|
+
test("a pseudo-parameter reached through the namespace stays the REAL live Intrinsic instance, prototype intact", async () => {
|
|
1788
|
+
const { lexicon, specifier } = await installLexiconPackage(LEXICON_SOURCE);
|
|
1789
|
+
await writeFile(
|
|
1790
|
+
join(testDir, "resources.ts"),
|
|
1791
|
+
`
|
|
1792
|
+
import { createResource } from ${JSON.stringify(runtimePath)};
|
|
1793
|
+
export const Group = createResource("Test::Group", "azure", {});
|
|
1794
|
+
`,
|
|
1795
|
+
);
|
|
1796
|
+
const file = join(testDir, "main.ts");
|
|
1797
|
+
await writeFile(
|
|
1798
|
+
file,
|
|
1799
|
+
`
|
|
1800
|
+
import { Azure } from ${JSON.stringify(specifier)};
|
|
1801
|
+
import { Group } from "./resources";
|
|
1802
|
+
export const group = new Group({ location: Azure.ResourceGroupLocation });
|
|
1803
|
+
`,
|
|
1804
|
+
);
|
|
1805
|
+
|
|
1806
|
+
const result = await tryFoldFile(file, [], createFoldSession([], undefined, [lexicon]));
|
|
1807
|
+
|
|
1808
|
+
expect(result.ok).toBe(true);
|
|
1809
|
+
if (!result.ok) return;
|
|
1810
|
+
const [, entity] = result.entities[0];
|
|
1811
|
+
const location = (entity as unknown as { props: { location: unknown } }).props.location;
|
|
1812
|
+
// Not a plain-object copy: the generic revive walk would have rebuilt it
|
|
1813
|
+
// as `{ expression: … }` and dropped `toJSON`, which is what actually
|
|
1814
|
+
// reaches the serializer.
|
|
1815
|
+
expect(isIntrinsic(location)).toBe(true);
|
|
1816
|
+
expect((location as { toJSON(): unknown }).toJSON()).toBe("[resourceGroup().location]");
|
|
1817
|
+
// Identity: the very object the module exports, not a reconstruction —
|
|
1818
|
+
// the same one a run-path import of this package would hand the file.
|
|
1819
|
+
const mod = (await import(join(testDir, "node_modules", specifier, "index.js"))) as {
|
|
1820
|
+
Azure: { ResourceGroupLocation: unknown };
|
|
1821
|
+
};
|
|
1822
|
+
expect(location).toBe(mod.Azure.ResourceGroupLocation);
|
|
1823
|
+
});
|
|
1824
|
+
|
|
1825
|
+
test("a lexicon package NOT active for this build is not resolved", async () => {
|
|
1826
|
+
const { specifier } = await installLexiconPackage(LEXICON_SOURCE);
|
|
1827
|
+
const file = join(testDir, "main.ts");
|
|
1828
|
+
await writeFile(
|
|
1829
|
+
file,
|
|
1830
|
+
`
|
|
1831
|
+
import { CI } from ${JSON.stringify(specifier)};
|
|
1832
|
+
export const branch = CI.CommitBranch;
|
|
1833
|
+
`,
|
|
1834
|
+
);
|
|
1835
|
+
|
|
1836
|
+
// The package is installed and importable — the ONLY thing missing is
|
|
1837
|
+
// this build having loaded it as a lexicon.
|
|
1838
|
+
const result = await tryFoldFile(file, [], createFoldSession([], undefined, ["aws"]));
|
|
1839
|
+
|
|
1840
|
+
expect(result.ok).toBe(false);
|
|
1841
|
+
if (result.ok) return;
|
|
1842
|
+
expect(result.reason).toContain("unresolved identifier: CI");
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
test("with no active lexicons at all, a bare specifier is skipped exactly as before", async () => {
|
|
1846
|
+
const { specifier } = await installLexiconPackage(LEXICON_SOURCE);
|
|
1847
|
+
const file = join(testDir, "main.ts");
|
|
1848
|
+
await writeFile(
|
|
1849
|
+
file,
|
|
1850
|
+
`
|
|
1851
|
+
import { CI } from ${JSON.stringify(specifier)};
|
|
1852
|
+
export const branch = CI.CommitBranch;
|
|
1853
|
+
`,
|
|
1854
|
+
);
|
|
1855
|
+
|
|
1856
|
+
const result = await tryFoldFile(file);
|
|
1857
|
+
|
|
1858
|
+
expect(result.ok).toBe(false);
|
|
1859
|
+
if (result.ok) return;
|
|
1860
|
+
expect(result.reason).toContain("unresolved identifier: CI");
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
test("a non-lexicon package is never followed, even when it is installed right next to one that is", async () => {
|
|
1864
|
+
const { lexicon } = await installLexiconPackage(LEXICON_SOURCE);
|
|
1865
|
+
const vendorDir = join(testDir, "node_modules", "some-vendor-pkg");
|
|
1866
|
+
await mkdir(vendorDir, { recursive: true });
|
|
1867
|
+
await writeFile(
|
|
1868
|
+
join(vendorDir, "package.json"),
|
|
1869
|
+
JSON.stringify({ name: "some-vendor-pkg", version: "0.0.0", type: "module", exports: { ".": "./index.js" } }),
|
|
1870
|
+
);
|
|
1871
|
+
await writeFile(join(vendorDir, "index.js"), `export const SETTINGS = { region: "us-east-1" };`);
|
|
1872
|
+
|
|
1873
|
+
const file = join(testDir, "main.ts");
|
|
1874
|
+
await writeFile(
|
|
1875
|
+
file,
|
|
1876
|
+
`
|
|
1877
|
+
import { SETTINGS } from "some-vendor-pkg";
|
|
1878
|
+
export const region = SETTINGS.region;
|
|
1879
|
+
`,
|
|
1880
|
+
);
|
|
1881
|
+
|
|
1882
|
+
const result = await tryFoldFile(file, [], createFoldSession([], undefined, [lexicon]));
|
|
1883
|
+
|
|
1884
|
+
expect(result.ok).toBe(false);
|
|
1885
|
+
if (result.ok) return;
|
|
1886
|
+
expect(result.reason).toContain("unresolved identifier: SETTINGS");
|
|
1887
|
+
});
|
|
1888
|
+
|
|
1889
|
+
test("a lexicon package's CALLABLE export is not bound as an identifier value", async () => {
|
|
1890
|
+
const { lexicon, specifier } = await installLexiconPackage(LEXICON_SOURCE);
|
|
1891
|
+
const file = join(testDir, "main.ts");
|
|
1892
|
+
await writeFile(
|
|
1893
|
+
file,
|
|
1894
|
+
`
|
|
1895
|
+
import { defaultTags } from ${JSON.stringify(specifier)};
|
|
1896
|
+
export const tags = defaultTags;
|
|
1897
|
+
`,
|
|
1898
|
+
);
|
|
1899
|
+
|
|
1900
|
+
const result = await tryFoldFile(file, [], createFoldSession([], undefined, [lexicon]));
|
|
1901
|
+
|
|
1902
|
+
expect(result.ok).toBe(false);
|
|
1903
|
+
if (result.ok) return;
|
|
1904
|
+
expect(result.reason).toContain("unresolved identifier: defaultTags");
|
|
1905
|
+
});
|
|
1906
|
+
|
|
1907
|
+
test("process.env still rejects, with the build-parameter guidance, even with an active lexicon package in scope", async () => {
|
|
1908
|
+
const { lexicon, specifier } = await installLexiconPackage(LEXICON_SOURCE);
|
|
1909
|
+
const file = join(testDir, "main.ts");
|
|
1910
|
+
await writeFile(
|
|
1911
|
+
file,
|
|
1912
|
+
`
|
|
1913
|
+
import { CI } from ${JSON.stringify(specifier)};
|
|
1914
|
+
export const branch = CI.CommitBranch;
|
|
1915
|
+
export const region = process.env.AWS_REGION;
|
|
1916
|
+
`,
|
|
1917
|
+
);
|
|
1918
|
+
|
|
1919
|
+
const result = await tryFoldFile(file, [], createFoldSession([], undefined, [lexicon]));
|
|
1920
|
+
|
|
1921
|
+
expect(result.ok).toBe(false);
|
|
1922
|
+
if (result.ok) return;
|
|
1923
|
+
expect(result.reason).toContain(`ambient "process" read is not foldable`);
|
|
1924
|
+
});
|
|
1925
|
+
});
|
|
@@ -7,6 +7,7 @@ import { createRequire } from "node:module";
|
|
|
7
7
|
import { isDeclarable, type Declarable } from "../declarable";
|
|
8
8
|
import { isCompositeInstance, type CompositeInstance } from "../composite";
|
|
9
9
|
import { isAttrRefLike } from "../utils";
|
|
10
|
+
import { isIntrinsic } from "../intrinsic";
|
|
10
11
|
import {
|
|
11
12
|
collectConsts,
|
|
12
13
|
foldResource,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
type SymbolicValue,
|
|
21
22
|
} from "../fold/fold";
|
|
22
23
|
import { isChantOwnedSpecifier } from "../fold/foldable-helpers";
|
|
24
|
+
import { briefNodeText, callExpressionMessage } from "../fold/subset";
|
|
23
25
|
import { importModule } from "./import";
|
|
24
26
|
import type { IntrinsicDef } from "../lexicon";
|
|
25
27
|
import type { BuildParamValue } from "../build-params";
|
|
@@ -171,14 +173,58 @@ export interface FoldSession {
|
|
|
171
173
|
* doesn't use build-time parameters pays nothing extra here.
|
|
172
174
|
*/
|
|
173
175
|
readonly buildParams?: Readonly<Record<string, BuildParamValue>>;
|
|
176
|
+
/**
|
|
177
|
+
* chant #1063 — the exact package specifiers of the lexicons LOADED for
|
|
178
|
+
* this build (`@intentius/chant-lexicon-aws`, …), derived from the lexicon
|
|
179
|
+
* names the build already resolved (`resolveProjectLexicons` ->
|
|
180
|
+
* `loadPlugins`, see ../cli/plugins.ts). This is the entire allowlist
|
|
181
|
+
* {@link buildExternals} will follow a bare import specifier into — see
|
|
182
|
+
* {@link activeLexiconPackage} for why the set is matched by TEXT and
|
|
183
|
+
* built from names the build already knows, rather than by resolving
|
|
184
|
+
* specifiers to find out what they are.
|
|
185
|
+
*
|
|
186
|
+
* Empty when the caller supplied no lexicon list, which disables
|
|
187
|
+
* lexicon-package resolution entirely rather than falling back to
|
|
188
|
+
* something more permissive: "an active lexicon of this build" is the
|
|
189
|
+
* boundary, and a build that can't say what its lexicons are hasn't
|
|
190
|
+
* established one.
|
|
191
|
+
*/
|
|
192
|
+
readonly lexiconPackages: ReadonlySet<string>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* chant #1063 — the package specifier a lexicon NAME (`"aws"`, `"gitlab"`)
|
|
197
|
+
* is installed under. The one naming convention the whole CLI already
|
|
198
|
+
* depends on: `loadPlugin(name)` imports exactly this
|
|
199
|
+
* (../cli/plugins.ts), `detectLexicons` scans source for exactly this
|
|
200
|
+
* (../detectLexicon.ts), and `chant init` writes exactly this into
|
|
201
|
+
* package.json.
|
|
202
|
+
*/
|
|
203
|
+
export function lexiconPackageName(lexiconName: string): string {
|
|
204
|
+
return `@intentius/chant-lexicon-${lexiconName}`;
|
|
174
205
|
}
|
|
175
206
|
|
|
176
|
-
/**
|
|
207
|
+
/**
|
|
208
|
+
* Create a fresh, empty {@link FoldSession}.
|
|
209
|
+
*
|
|
210
|
+
* @param lexicons - chant #1063: the lexicon NAMES active for this build
|
|
211
|
+
* (`["aws", "k8s"]`). Converted to package specifiers via
|
|
212
|
+
* {@link lexiconPackageName}; see {@link FoldSession.lexiconPackages}.
|
|
213
|
+
*/
|
|
177
214
|
export function createFoldSession(
|
|
178
215
|
intrinsics: readonly IntrinsicDef[] = [],
|
|
179
216
|
buildParams?: Readonly<Record<string, BuildParamValue>>,
|
|
217
|
+
lexicons: readonly string[] = [],
|
|
180
218
|
): FoldSession {
|
|
181
|
-
return {
|
|
219
|
+
return {
|
|
220
|
+
intrinsics,
|
|
221
|
+
cache: new Map(),
|
|
222
|
+
stack: [],
|
|
223
|
+
importCache: new Map(),
|
|
224
|
+
resolvePathCache: new Map(),
|
|
225
|
+
buildParams,
|
|
226
|
+
lexiconPackages: new Set(lexicons.map(lexiconPackageName)),
|
|
227
|
+
};
|
|
182
228
|
}
|
|
183
229
|
|
|
184
230
|
/**
|
|
@@ -805,6 +851,100 @@ function paramsModulePath(): string | null {
|
|
|
805
851
|
return paramsModulePathMemo;
|
|
806
852
|
}
|
|
807
853
|
|
|
854
|
+
/**
|
|
855
|
+
* chant #1063 — is `specifier` a bare import of a package that is an ACTIVE
|
|
856
|
+
* LEXICON of this build? Returns the specifier itself when so, `undefined`
|
|
857
|
+
* otherwise.
|
|
858
|
+
*
|
|
859
|
+
* Three deliberate restrictions, each of which is the point rather than an
|
|
860
|
+
* omission:
|
|
861
|
+
*
|
|
862
|
+
* - **Text only, no resolution.** The answer is a `Set.has` on a set built
|
|
863
|
+
* from the lexicon names the build ALREADY resolved before discovery ran
|
|
864
|
+
* (`resolveProjectLexicons` -> `loadPlugins`). Nothing is probed, read, or
|
|
865
|
+
* resolved to decide whether a specifier is in scope — so an import of
|
|
866
|
+
* some unrelated package costs a string comparison and is then left alone,
|
|
867
|
+
* never resolved "just to find out what it is". That matters here more
|
|
868
|
+
* than anywhere: `resolveModulePath`'s bare branch can fall through to
|
|
869
|
+
* `createRequire(fromFile).resolve(specifier)`, measured at up to ~361s
|
|
870
|
+
* for the first resolution of a genuinely new bare specifier in a process
|
|
871
|
+
* (see {@link bareSpecifierPathCache}/{@link fastResolveBareSpecifier} —
|
|
872
|
+
* this exact class of cost regressed twice during chant#1020).
|
|
873
|
+
*
|
|
874
|
+
* - **Exact package specifier, no subpaths.** `@intentius/chant-lexicon-aws`
|
|
875
|
+
* matches; `@intentius/chant-lexicon-aws/actions` does not. Every lexicon
|
|
876
|
+
* re-exports its whole public surface from its barrel and every example
|
|
877
|
+
* imports it that way, so subpaths buy nothing — and they would cost
|
|
878
|
+
* something real: {@link fastResolveBareSpecifier} only recognizes a
|
|
879
|
+
* package ROOT (it looks for `<node_modules>/<specifier>/package.json`),
|
|
880
|
+
* so a subpath specifier falls through to exactly the slow
|
|
881
|
+
* `createRequire().resolve()` path this restriction exists to avoid.
|
|
882
|
+
*
|
|
883
|
+
* - **Lexicons of THIS build only.** Not "any `@intentius/chant-lexicon-*`
|
|
884
|
+
* package on disk", and emphatically not "any bare specifier". A lexicon
|
|
885
|
+
* the build did not load is as out of scope as `node:fs`.
|
|
886
|
+
*/
|
|
887
|
+
function activeLexiconPackage(specifier: string, lexiconPackages: ReadonlySet<string>): string | undefined {
|
|
888
|
+
return lexiconPackages.has(specifier) ? specifier : undefined;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* chant #1063 — resolve one named import binding against an active lexicon
|
|
893
|
+
* package's REAL exports, for {@link buildExternals}.
|
|
894
|
+
*
|
|
895
|
+
* The lexicon module is imported (through the session-wide
|
|
896
|
+
* {@link FoldSession.importCache}, so at most one real `import()` per package
|
|
897
|
+
* per build) and the requested export read straight off it. That is the same
|
|
898
|
+
* module object the run path gets — `importModule` is a plain dynamic
|
|
899
|
+
* `import()` with no cache-busting — so what fold captures here is not a
|
|
900
|
+
* reconstruction of the lexicon's data but the identical value, down to
|
|
901
|
+
* object identity for `Azure.ResourceGroupLocation`-style singletons. It is
|
|
902
|
+
* also the same two-step (resolve path, then import) that
|
|
903
|
+
* `resolveImportedExport` has always used to reach a lexicon's constructors
|
|
904
|
+
* and intrinsic functions; the only thing new is that a plain DATA export is
|
|
905
|
+
* now reachable too.
|
|
906
|
+
*
|
|
907
|
+
* Callable exports are deliberately excluded. A lexicon's functions — its
|
|
908
|
+
* resource classes, composite factories, intrinsic implementations — already
|
|
909
|
+
* have dedicated resolution paths that know how to INVOKE them
|
|
910
|
+
* (`resolveResourceEntity`, `resolveCallExpression`, `reviveFoldedValue`), and
|
|
911
|
+
* binding them as plain identifier values here would widen what folds in ways
|
|
912
|
+
* this issue neither needs nor measured: none of the references #1063 exists
|
|
913
|
+
* to unblock (`Azure`, `GCP`, `S3Actions`, gitlab's `CI`) is a function.
|
|
914
|
+
*
|
|
915
|
+
* Returns `undefined` — never throws — for a package that isn't an active
|
|
916
|
+
* lexicon, an export the package doesn't have, a callable export, or an
|
|
917
|
+
* import that fails. The binding is then simply absent from `externals`,
|
|
918
|
+
* exactly as before, and `fold()`'s ordinary "unresolved identifier" failure
|
|
919
|
+
* still fires if the name is actually referenced.
|
|
920
|
+
*/
|
|
921
|
+
async function resolveActiveLexiconExport(
|
|
922
|
+
binding: ImportBinding,
|
|
923
|
+
fromFile: string,
|
|
924
|
+
session: FoldSession,
|
|
925
|
+
): Promise<{ value: unknown } | undefined> {
|
|
926
|
+
if (!activeLexiconPackage(binding.specifier, session.lexiconPackages)) return undefined;
|
|
927
|
+
|
|
928
|
+
let modulePath: string;
|
|
929
|
+
try {
|
|
930
|
+
modulePath = resolveModulePathMemoized(binding.specifier, fromFile, session.resolvePathCache);
|
|
931
|
+
} catch {
|
|
932
|
+
return undefined;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
let mod: Record<string, unknown>;
|
|
936
|
+
try {
|
|
937
|
+
mod = await importModuleMemoized(modulePath, session.importCache);
|
|
938
|
+
} catch {
|
|
939
|
+
return undefined;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (!(binding.imported in mod)) return undefined;
|
|
943
|
+
const value = mod[binding.imported];
|
|
944
|
+
if (typeof value === "function") return undefined;
|
|
945
|
+
return { value };
|
|
946
|
+
}
|
|
947
|
+
|
|
808
948
|
// ─────────────────────────────────────────────────────────────────────────
|
|
809
949
|
// Resolution: given the scan + import map, compute the REAL runtime value
|
|
810
950
|
// (Declarable | CompositeInstance) each foldable export would have had if
|
|
@@ -962,13 +1102,18 @@ async function resolveLiveValue(node: ts.Expression, ctx: ResolveCtx): Promise<L
|
|
|
962
1102
|
* `isDeclarable`/`isCompositeInstance` value for a top-level export).
|
|
963
1103
|
*/
|
|
964
1104
|
async function resolveCallExpression(node: ts.CallExpression, ctx: ResolveCtx): Promise<unknown> {
|
|
1105
|
+
// chant #1054 — reuses ../fold/subset's `callExpressionMessage` (the SAME
|
|
1106
|
+
// builder `fold()` throws with for a call used as a prop value) rather
|
|
1107
|
+
// than a hand-written "call expression as a value" copy: the two used to
|
|
1108
|
+
// say different things for the identical rejection, which silently broke
|
|
1109
|
+
// any tooling grouping fallback reasons by text.
|
|
965
1110
|
if (!ts.isIdentifier(node.expression)) {
|
|
966
|
-
throw cheapError(
|
|
1111
|
+
throw cheapError(callExpressionMessage(node));
|
|
967
1112
|
}
|
|
968
1113
|
const calleeName = node.expression.text;
|
|
969
1114
|
const binding = ctx.imports.get(calleeName);
|
|
970
1115
|
if (!binding) {
|
|
971
|
-
throw cheapError(
|
|
1116
|
+
throw cheapError(callExpressionMessage(node));
|
|
972
1117
|
}
|
|
973
1118
|
|
|
974
1119
|
let modulePath: string;
|
|
@@ -1161,7 +1306,17 @@ async function reviveFoldedValue(value: FoldedValue, ctx: ResolveCtx, requireLiv
|
|
|
1161
1306
|
// destroying the very identity #1020 exists to preserve. Passed through
|
|
1162
1307
|
// completely unchanged, exactly like `resolveCallExpression`'s own
|
|
1163
1308
|
// `live.value` passthrough for a composite-call argument.
|
|
1164
|
-
|
|
1309
|
+
// chant #1063 adds the third kind of real object cross-file resolution can
|
|
1310
|
+
// now put here: a live `Intrinsic` instance read off an active lexicon
|
|
1311
|
+
// package (`Azure.ResourceGroupLocation`, `GCP.ProjectId` — see
|
|
1312
|
+
// `resolveActiveLexiconExport`). Same hazard, same fix: the generic walk
|
|
1313
|
+
// below would rebuild it as a plain `{}` copy, dropping the prototype that
|
|
1314
|
+
// carries `toJSON()` and so serializing `{}` where the run path emits
|
|
1315
|
+
// `[resourceGroup().location]`. `isIntrinsic` keys off
|
|
1316
|
+
// `Symbol.for("chant.intrinsic")` (../intrinsic.ts), a GLOBAL symbol, so it
|
|
1317
|
+
// holds across separately-loaded copies of chant-core the way a bare
|
|
1318
|
+
// `instanceof` would not.
|
|
1319
|
+
if (isAttrRefLike(value) || isDeclarable(value) || isCompositeInstance(value) || isIntrinsic(value)) {
|
|
1165
1320
|
return value;
|
|
1166
1321
|
}
|
|
1167
1322
|
|
|
@@ -1443,6 +1598,18 @@ function describeFoldFailure(err: unknown, ctx: ResolveCtx): string {
|
|
|
1443
1598
|
return err.message;
|
|
1444
1599
|
}
|
|
1445
1600
|
|
|
1601
|
+
/**
|
|
1602
|
+
* chant #1054 — a short, single-line label for a destructured export's
|
|
1603
|
+
* source expression, for a fold fallback reason: the callee plus `(...)`
|
|
1604
|
+
* for the common composite-call source (`GkeCluster(...)`), or a brief,
|
|
1605
|
+
* bounded rendering of whatever else it is otherwise. Never the source's own
|
|
1606
|
+
* `getText()` — for a real composite call that's the entire multi-line
|
|
1607
|
+
* argument list.
|
|
1608
|
+
*/
|
|
1609
|
+
function describeDestructureSource(node: ts.Expression): string {
|
|
1610
|
+
return ts.isCallExpression(node) ? `${briefNodeText(node.expression)}(...)` : briefNodeText(node);
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1446
1613
|
/** Build a located `FoldError`'s formatted "line:col - message" string anchored at `node` — for a cross-file failure detected here in fold-import.ts (an import cycle, a name genuinely absent from the target module's exports) rather than inside `fold()` itself. */
|
|
1447
1614
|
function locatedMessage(node: ts.Node, message: string): string {
|
|
1448
1615
|
const { line, column } = locate(node);
|
|
@@ -1585,10 +1752,36 @@ async function buildExternals(
|
|
|
1585
1752
|
}
|
|
1586
1753
|
|
|
1587
1754
|
if (!isProjectFileSpecifier(binding.specifier)) {
|
|
1588
|
-
//
|
|
1589
|
-
//
|
|
1590
|
-
//
|
|
1591
|
-
//
|
|
1755
|
+
// chant #1063 — a bare specifier naming one of THIS BUILD's active
|
|
1756
|
+
// lexicon packages resolves to that package's real export, so a plain
|
|
1757
|
+
// data export a lexicon publishes (`Azure`/`GCP`'s pseudo-parameter
|
|
1758
|
+
// namespaces, AWS's `S3Actions`, gitlab's `CI`) is an ordinary
|
|
1759
|
+
// identifier value here rather than fold's most common remaining
|
|
1760
|
+
// "unresolved identifier" failure. See
|
|
1761
|
+
// {@link resolveActiveLexiconExport} for the allowlist, the no-cold-
|
|
1762
|
+
// resolution rule, and why callable exports stay out.
|
|
1763
|
+
//
|
|
1764
|
+
// No `liveSources` edge is recorded for what comes back, unlike the
|
|
1765
|
+
// project-file case just below. `liveSources` (chant #1044) exists so
|
|
1766
|
+
// that a folded file which captured ANOTHER FILE's objects is
|
|
1767
|
+
// invalidated when that file is forced back to run — a fold/run
|
|
1768
|
+
// disagreement about identity. A lexicon package has no such duality:
|
|
1769
|
+
// it is not a discovered source file, `planFoldTaint` never considers
|
|
1770
|
+
// it (it filters to the discovered `files` set), it never falls back to
|
|
1771
|
+
// run, and both paths reach it through the identical un-cache-busted
|
|
1772
|
+
// `import()` of the identical resolved path — so the object fold
|
|
1773
|
+
// captures IS the object the run path holds. There is nothing for the
|
|
1774
|
+
// two sides to disagree about, hence nothing to taint.
|
|
1775
|
+
const lexiconExport = await resolveActiveLexiconExport(binding, file, session);
|
|
1776
|
+
if (lexiconExport) {
|
|
1777
|
+
externals.set(localName, lexiconExport.value);
|
|
1778
|
+
continue;
|
|
1779
|
+
}
|
|
1780
|
+
// Every other bare specifier (a non-lexicon vendor package, a lexicon
|
|
1781
|
+
// this build didn't load) is left alone here, exactly as before #1064:
|
|
1782
|
+
// it resolves lazily, through the pre-existing `importModule`
|
|
1783
|
+
// mechanism, only once a constructor/composite-factory/intrinsic tag
|
|
1784
|
+
// actually consumes it.
|
|
1592
1785
|
continue;
|
|
1593
1786
|
}
|
|
1594
1787
|
let targetPath: string;
|
|
@@ -1736,18 +1929,26 @@ async function tryFoldFileCore(file: string, session: FoldSession): Promise<Fold
|
|
|
1736
1929
|
|
|
1737
1930
|
if (decl.kind === "destructure") {
|
|
1738
1931
|
let value: unknown;
|
|
1932
|
+
// chant #1054 — identify the destructured export by its BINDING
|
|
1933
|
+
// NAMES and the source's callee (`"cluster, nodePool" (destructured
|
|
1934
|
+
// from GkeCluster(...))`), never `decl.node.getText()`: for a real
|
|
1935
|
+
// composite call that's the entire multi-line source, which buries
|
|
1936
|
+
// the actual error after it and breaks any line-oriented consumer of
|
|
1937
|
+
// `[fold:run]` output.
|
|
1938
|
+
const boundNames = decl.elements.map((el) => el.bindingName).join(", ");
|
|
1939
|
+
const source = describeDestructureSource(decl.node);
|
|
1739
1940
|
try {
|
|
1740
1941
|
value = (await resolveDeclaratorValue(decl.node, ctx)).value;
|
|
1741
1942
|
} catch (err) {
|
|
1742
1943
|
return {
|
|
1743
1944
|
ok: false,
|
|
1744
|
-
reason: `destructured
|
|
1945
|
+
reason: `"${boundNames}" (destructured from ${source}) is not foldable: ${describeFoldFailure(err, ctx)}`,
|
|
1745
1946
|
};
|
|
1746
1947
|
}
|
|
1747
1948
|
if (!isIndexableObject(value)) {
|
|
1748
1949
|
return {
|
|
1749
1950
|
ok: false,
|
|
1750
|
-
reason: `destructured
|
|
1951
|
+
reason: `"${boundNames}" (destructured from ${source}) is not foldable: not a composite call or object`,
|
|
1751
1952
|
};
|
|
1752
1953
|
}
|
|
1753
1954
|
for (const { propKey, bindingName } of decl.elements) {
|
package/src/discovery/index.ts
CHANGED
|
@@ -56,6 +56,19 @@ export interface DiscoveryOptions {
|
|
|
56
56
|
*/
|
|
57
57
|
intrinsics?: IntrinsicDef[];
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* chant #1063 — the lexicon NAMES loaded for this build (`["aws", "k8s"]`),
|
|
61
|
+
* i.e. what `resolveProjectLexicons()` returned and `loadPlugins()` then
|
|
62
|
+
* imported. Threaded into the fold session as the ALLOWLIST of packages a
|
|
63
|
+
* bare import specifier may be resolved into, so a lexicon's plain data
|
|
64
|
+
* exports (`Azure`/`GCP`'s pseudo-parameter namespaces, AWS's `S3Actions`,
|
|
65
|
+
* gitlab's `CI`) fold as identifier values instead of failing the file.
|
|
66
|
+
* Only meaningful when {@link fold} is set. Default: none — a caller that
|
|
67
|
+
* doesn't say which lexicons are active gets no bare-specifier resolution
|
|
68
|
+
* at all, rather than a looser fallback.
|
|
69
|
+
*/
|
|
70
|
+
lexicons?: readonly string[];
|
|
71
|
+
|
|
59
72
|
/**
|
|
60
73
|
* chant #1045 Phase 2 — opt-in: whatever would otherwise reach the
|
|
61
74
|
* in-process `importModule` step (every file, when {@link fold} isn't set;
|
|
@@ -156,7 +169,9 @@ export async function discover(path: string, options?: DiscoveryOptions): Promis
|
|
|
156
169
|
// a project file imported by several others is folded exactly once, so
|
|
157
170
|
// every referrer shares the identical constructed Declarable/
|
|
158
171
|
// CompositeInstance objects rather than each building its own copy.
|
|
159
|
-
const foldSession = options?.fold
|
|
172
|
+
const foldSession = options?.fold
|
|
173
|
+
? createFoldSession(options.intrinsics, buildParamValuesMap, options.lexicons)
|
|
174
|
+
: undefined;
|
|
160
175
|
if (options?.fold) {
|
|
161
176
|
for (const file of files) {
|
|
162
177
|
foldAttempts.set(file, await tryFoldFile(file, options.intrinsics, foldSession));
|
package/src/fold/fold.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
SUPPORTED_UNARY_OPERATORS,
|
|
6
6
|
UNSUPPORTED_OBJECT_MEMBER_MESSAGE,
|
|
7
7
|
UNSUPPORTED_UNARY_MESSAGE,
|
|
8
|
+
briefNodeText,
|
|
8
9
|
callExpressionMessage,
|
|
9
10
|
computedPropertyNameMessage,
|
|
10
11
|
dynamicElementAccessMessage,
|
|
@@ -396,7 +397,7 @@ function foldTaggedTemplate(
|
|
|
396
397
|
const tagName = node.tag.getText();
|
|
397
398
|
const isRegistered = intrinsics.some((i) => i.name === tagName && intrinsicTagFolds(i));
|
|
398
399
|
if (!isRegistered) {
|
|
399
|
-
throw foldError(node, `unregistered tagged template intrinsic: ${
|
|
400
|
+
throw foldError(node, `unregistered tagged template intrinsic: ${briefNodeText(node.tag)}\`...\``);
|
|
400
401
|
}
|
|
401
402
|
|
|
402
403
|
const template = node.template;
|
|
@@ -635,7 +636,10 @@ export function fold(
|
|
|
635
636
|
// Reject so the file falls back to run, which constructs and serializes it
|
|
636
637
|
// correctly. EVL permits this statically — it's a documented fold/EVL
|
|
637
638
|
// divergence, like identifier resolution and spread runtime type.
|
|
638
|
-
throw foldError(
|
|
639
|
+
throw foldError(
|
|
640
|
+
node,
|
|
641
|
+
`nested \`new ${briefNodeText(node.expression)}(...)\` as a value is not foldable — falls back to run`,
|
|
642
|
+
);
|
|
639
643
|
}
|
|
640
644
|
|
|
641
645
|
if (ts.isCallExpression(node)) {
|
package/src/fold/subset.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import * as ts from "typescript";
|
|
3
3
|
import { fold, foldResource, collectConsts, FoldError } from "./fold";
|
|
4
|
-
import { findSubsetViolation } from "./subset";
|
|
4
|
+
import { briefNodeText, callExpressionMessage, findSubsetViolation } from "./subset";
|
|
5
5
|
import { evl001NonLiteralExpressionRule } from "../lint/rules/evl001-non-literal-expression";
|
|
6
6
|
import { evl003DynamicPropertyAccessRule } from "../lint/rules/evl003-dynamic-property-access";
|
|
7
7
|
import { evl004SpreadNonConstRule } from "../lint/rules/evl004-spread-non-const";
|
|
@@ -350,3 +350,69 @@ describe("findSubsetViolation — optional intrinsic registry (#1044)", () => {
|
|
|
350
350
|
expect(evl001NonLiteralExpressionRule.check(context).length).toBeGreaterThan(0);
|
|
351
351
|
});
|
|
352
352
|
});
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* chant #1054 — `briefNodeText` is what keeps every fold fallback reason
|
|
356
|
+
* that used to embed a node's raw `getText()` down to one bounded line. A
|
|
357
|
+
* real composite call's source is many lines; a fold reason that reproduces
|
|
358
|
+
* it verbatim buries the actual error after all of it (the bug this issue
|
|
359
|
+
* reports) and breaks any line-oriented consumer of `[fold:run]` output.
|
|
360
|
+
*/
|
|
361
|
+
describe("briefNodeText — single-line, bounded diagnostic text (chant #1054)", () => {
|
|
362
|
+
function initializerOf(source: string): ts.Expression {
|
|
363
|
+
const sourceFile = ts.createSourceFile("t.ts", source, ts.ScriptTarget.Latest, true);
|
|
364
|
+
const consts = collectConsts(sourceFile);
|
|
365
|
+
const init = consts.get("x");
|
|
366
|
+
if (!init) throw new Error(`fixture error: "x" did not parse in ${JSON.stringify(source)}`);
|
|
367
|
+
return init;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
test("a short, single-line node passes through unchanged", () => {
|
|
371
|
+
expect(briefNodeText(initializerOf(`const x = GkeCluster;`))).toBe("GkeCluster");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("a multi-line node's newlines collapse to spaces — the result is always one line", () => {
|
|
375
|
+
const text = briefNodeText(
|
|
376
|
+
initializerOf(`
|
|
377
|
+
const x = GkeCluster({
|
|
378
|
+
name: config.clusterName,
|
|
379
|
+
location: config.region,
|
|
380
|
+
});
|
|
381
|
+
`),
|
|
382
|
+
);
|
|
383
|
+
expect(text).not.toContain("\n");
|
|
384
|
+
expect(text.split("\n")).toHaveLength(1);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test("text over the length cap is truncated with a trailing marker rather than left unbounded", () => {
|
|
388
|
+
const text = briefNodeText(
|
|
389
|
+
initializerOf(`const x = { aVeryLongPropertyNameNumberOne: 1, aVeryLongPropertyNameNumberTwo: 2 };`),
|
|
390
|
+
20,
|
|
391
|
+
);
|
|
392
|
+
expect(text.length).toBe(20);
|
|
393
|
+
expect(text.endsWith("...")).toBe(true);
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
describe("callExpressionMessage — one line regardless of the call's own argument list (chant #1054)", () => {
|
|
398
|
+
test("only the callee is embedded — a multi-line argument list never leaks into the message", () => {
|
|
399
|
+
const sourceFile = ts.createSourceFile(
|
|
400
|
+
"t.ts",
|
|
401
|
+
`
|
|
402
|
+
const x = GkeCluster({
|
|
403
|
+
name: config.clusterName,
|
|
404
|
+
location: config.region,
|
|
405
|
+
machineType: "n2-standard-2",
|
|
406
|
+
});
|
|
407
|
+
`,
|
|
408
|
+
ts.ScriptTarget.Latest,
|
|
409
|
+
true,
|
|
410
|
+
);
|
|
411
|
+
const consts = collectConsts(sourceFile);
|
|
412
|
+
const call = consts.get("x") as ts.CallExpression;
|
|
413
|
+
|
|
414
|
+
const message = callExpressionMessage(call);
|
|
415
|
+
|
|
416
|
+
expect(message).toBe("function call as a value is not foldable: GkeCluster(...)");
|
|
417
|
+
});
|
|
418
|
+
});
|
package/src/fold/subset.ts
CHANGED
|
@@ -141,6 +141,22 @@ export function isLiteralElementKey(node: ts.Expression): node is ts.StringLiter
|
|
|
141
141
|
return ts.isStringLiteral(node) || ts.isNumericLiteral(node);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
/**
|
|
145
|
+
* A short, single-line rendering of `node`'s source text for embedding in a
|
|
146
|
+
* diagnostic message — never the raw `getText()`, which reproduces the
|
|
147
|
+
* node's ENTIRE source verbatim and can span dozens of lines for a real
|
|
148
|
+
* composite call or object literal (chant #1054: a fold fallback reason that
|
|
149
|
+
* embeds one of these buries the actual error after it, and breaks any
|
|
150
|
+
* line-oriented consumer of `[fold:run]` output). Internal whitespace
|
|
151
|
+
* (including newlines) collapses to a single space, and the result is capped
|
|
152
|
+
* to a bounded length so one pathological node can't blow out an otherwise
|
|
153
|
+
* one-line reason either.
|
|
154
|
+
*/
|
|
155
|
+
export function briefNodeText(node: ts.Node, maxLength = 60): string {
|
|
156
|
+
const collapsed = node.getText().replace(/\s+/g, " ").trim();
|
|
157
|
+
return collapsed.length > maxLength ? `${collapsed.slice(0, maxLength - 3)}...` : collapsed;
|
|
158
|
+
}
|
|
159
|
+
|
|
144
160
|
// ---------------------------------------------------------------------------
|
|
145
161
|
// Shared message builders — `fold()` and `findSubsetViolation` both call
|
|
146
162
|
// these so the diagnostic text for the same violation kind is the same
|
|
@@ -148,11 +164,11 @@ export function isLiteralElementKey(node: ts.Expression): node is ts.StringLiter
|
|
|
148
164
|
// ---------------------------------------------------------------------------
|
|
149
165
|
|
|
150
166
|
export function computedPropertyNameMessage(node: ts.PropertyName): string {
|
|
151
|
-
return `computed/dynamic property name not foldable: ${node
|
|
167
|
+
return `computed/dynamic property name not foldable: ${briefNodeText(node)}`;
|
|
152
168
|
}
|
|
153
169
|
|
|
154
170
|
export function dynamicElementAccessMessage(keyNode: ts.Expression): string {
|
|
155
|
-
return `dynamic property access — computed key must be a string or numeric literal: ${keyNode
|
|
171
|
+
return `dynamic property access — computed key must be a string or numeric literal: ${briefNodeText(keyNode)}`;
|
|
156
172
|
}
|
|
157
173
|
|
|
158
174
|
export const UNSUPPORTED_OBJECT_MEMBER_MESSAGE = "unsupported object member";
|
|
@@ -163,8 +179,19 @@ export function unsupportedBinaryMessage(opKind: ts.SyntaxKind): string {
|
|
|
163
179
|
return `unsupported binary operator: ${ts.SyntaxKind[opKind]}`;
|
|
164
180
|
}
|
|
165
181
|
|
|
182
|
+
/**
|
|
183
|
+
* chant #1054 — the ONE wording for "a bare function/method call used where
|
|
184
|
+
* chant needs a value it can fold." Before this, `fold()` (this message) and
|
|
185
|
+
* `../discovery/fold-import.ts`'s `resolveCallExpression` (a top-level
|
|
186
|
+
* export's own call-as-a-value check) had each grown their own hand-written
|
|
187
|
+
* copy — "function call as a value" here, "call expression as a value"
|
|
188
|
+
* there — for the identical rejection, which meant a tool grouping fallback
|
|
189
|
+
* reasons by text had to match both to avoid silently under-counting one of
|
|
190
|
+
* them. `resolveCallExpression` now calls this function directly instead of
|
|
191
|
+
* building its own string.
|
|
192
|
+
*/
|
|
166
193
|
export function callExpressionMessage(node: ts.CallExpression): string {
|
|
167
|
-
return `function call as a value is not foldable: ${node.expression
|
|
194
|
+
return `function call as a value is not foldable: ${briefNodeText(node.expression)}(...)`;
|
|
168
195
|
}
|
|
169
196
|
|
|
170
197
|
export function unsupportedExpressionMessage(node: ts.Node): string {
|
package/src/serializer-walker.ts
CHANGED
|
@@ -61,6 +61,20 @@ export function walkValue(
|
|
|
61
61
|
if (name) {
|
|
62
62
|
return visitor.resourceRef(name);
|
|
63
63
|
}
|
|
64
|
+
// A resource-kind Declarable constructed inline rather than exported as
|
|
65
|
+
// its own top-level entity (e.g. K8s `new PersistentVolumeClaim({...})`
|
|
66
|
+
// embedded directly in a StatefulSet's `volumeClaimTemplates`) has no
|
|
67
|
+
// logical name to Ref — it was never a key in `entities`, so
|
|
68
|
+
// resolveAttrRefs() never assigns one. Falling through to the generic
|
|
69
|
+
// "object" branch below would walk the Declarable's own enumerable
|
|
70
|
+
// properties, which for a Declarable are its self-referencing attribute
|
|
71
|
+
// accessors (AttrRef instances whose parent is the Declarable itself,
|
|
72
|
+
// still unresolved) — never its authored `.props`. That either threw
|
|
73
|
+
// "logical name not set" (when the resource type declares any
|
|
74
|
+
// attributes) or silently serialized as `{}` (when it doesn't), instead
|
|
75
|
+
// of the embedded spec the caller wrote. Embed its own props inline
|
|
76
|
+
// instead, exactly like a property-kind Declarable already does.
|
|
77
|
+
return visitor.propertyDeclarable(decl, (v) => walkValue(v, entityNames, visitor));
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
// Handle arrays
|