@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
|
@@ -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
|
+
});
|