@scriptc/compiler 0.0.0 → 0.0.2

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.
Files changed (206) hide show
  1. package/LICENSE +202 -0
  2. package/ambient/package.json +3 -0
  3. package/ambient/scriptc-node-fallback.d.ts +3001 -0
  4. package/ambient/scriptc-overrides.d.ts +95 -0
  5. package/ambient/scriptc.d.ts +74 -0
  6. package/dist/backend/cc.d.ts +169 -0
  7. package/dist/backend/cc.js +897 -0
  8. package/dist/backend/cc.js.map +1 -0
  9. package/dist/backend/emission/emit-async.d.ts +145 -0
  10. package/dist/backend/emission/emit-async.js +996 -0
  11. package/dist/backend/emission/emit-async.js.map +1 -0
  12. package/dist/backend/emission/emit-exprs.d.ts +3 -0
  13. package/dist/backend/emission/emit-exprs.js +5949 -0
  14. package/dist/backend/emission/emit-exprs.js.map +1 -0
  15. package/dist/backend/emission/emit-island.d.ts +45 -0
  16. package/dist/backend/emission/emit-island.js +271 -0
  17. package/dist/backend/emission/emit-island.js.map +1 -0
  18. package/dist/backend/emission/emit-shapes.d.ts +142 -0
  19. package/dist/backend/emission/emit-shapes.js +575 -0
  20. package/dist/backend/emission/emit-shapes.js.map +1 -0
  21. package/dist/backend/emission/emit-stmts.d.ts +78 -0
  22. package/dist/backend/emission/emit-stmts.js +960 -0
  23. package/dist/backend/emission/emit-stmts.js.map +1 -0
  24. package/dist/backend/emission/emit-types.d.ts +65 -0
  25. package/dist/backend/emission/emit-types.js +652 -0
  26. package/dist/backend/emission/emit-types.js.map +1 -0
  27. package/dist/backend/emission/emit-walkers.d.ts +154 -0
  28. package/dist/backend/emission/emit-walkers.js +1587 -0
  29. package/dist/backend/emission/emit-walkers.js.map +1 -0
  30. package/dist/backend/emission/emitter.d.ts +452 -0
  31. package/dist/backend/emission/emitter.js +1249 -0
  32. package/dist/backend/emission/emitter.js.map +1 -0
  33. package/dist/backend/emission/may-throw.d.ts +15 -0
  34. package/dist/backend/emission/may-throw.js +250 -0
  35. package/dist/backend/emission/may-throw.js.map +1 -0
  36. package/dist/backend/llvm/blocks.d.ts +21 -0
  37. package/dist/backend/llvm/blocks.js +68 -0
  38. package/dist/backend/llvm/blocks.js.map +1 -0
  39. package/dist/backend/llvm/classes.d.ts +84 -0
  40. package/dist/backend/llvm/classes.js +438 -0
  41. package/dist/backend/llvm/classes.js.map +1 -0
  42. package/dist/backend/llvm/dyn.d.ts +123 -0
  43. package/dist/backend/llvm/dyn.js +2624 -0
  44. package/dist/backend/llvm/dyn.js.map +1 -0
  45. package/dist/backend/llvm/emitter.d.ts +3 -0
  46. package/dist/backend/llvm/emitter.js +10102 -0
  47. package/dist/backend/llvm/emitter.js.map +1 -0
  48. package/dist/backend/llvm/shapes.d.ts +78 -0
  49. package/dist/backend/llvm/shapes.js +754 -0
  50. package/dist/backend/llvm/shapes.js.map +1 -0
  51. package/dist/backend/llvm/unsupported.d.ts +6 -0
  52. package/dist/backend/llvm/unsupported.js +12 -0
  53. package/dist/backend/llvm/unsupported.js.map +1 -0
  54. package/dist/backend/llvm/walkers.d.ts +58 -0
  55. package/dist/backend/llvm/walkers.js +793 -0
  56. package/dist/backend/llvm/walkers.js.map +1 -0
  57. package/dist/backend/mangle.d.ts +124 -0
  58. package/dist/backend/mangle.js +232 -0
  59. package/dist/backend/mangle.js.map +1 -0
  60. package/dist/coverage/report.d.ts +65 -0
  61. package/dist/coverage/report.js +238 -0
  62. package/dist/coverage/report.js.map +1 -0
  63. package/dist/diagnostics/diagnostic.d.ts +140 -0
  64. package/dist/diagnostics/diagnostic.js +458 -0
  65. package/dist/diagnostics/diagnostic.js.map +1 -0
  66. package/dist/diagnostics/render.d.ts +11 -0
  67. package/dist/diagnostics/render.js +58 -0
  68. package/dist/diagnostics/render.js.map +1 -0
  69. package/dist/frontend/cjs-lexer.d.ts +20 -0
  70. package/dist/frontend/cjs-lexer.js +813 -0
  71. package/dist/frontend/cjs-lexer.js.map +1 -0
  72. package/dist/frontend/lowering/http2-constants.d.ts +1 -0
  73. package/dist/frontend/lowering/http2-constants.js +251 -0
  74. package/dist/frontend/lowering/http2-constants.js.map +1 -0
  75. package/dist/frontend/lowering/lib-boundary.d.ts +6 -0
  76. package/dist/frontend/lowering/lib-boundary.js +143 -0
  77. package/dist/frontend/lowering/lib-boundary.js.map +1 -0
  78. package/dist/frontend/lowering/lower-assert.d.ts +18 -0
  79. package/dist/frontend/lowering/lower-assert.js +1269 -0
  80. package/dist/frontend/lowering/lower-assert.js.map +1 -0
  81. package/dist/frontend/lowering/lower-builtins.d.ts +511 -0
  82. package/dist/frontend/lowering/lower-builtins.js +5249 -0
  83. package/dist/frontend/lowering/lower-builtins.js.map +1 -0
  84. package/dist/frontend/lowering/lower-calls.d.ts +581 -0
  85. package/dist/frontend/lowering/lower-calls.js +6286 -0
  86. package/dist/frontend/lowering/lower-calls.js.map +1 -0
  87. package/dist/frontend/lowering/lower-classes.d.ts +727 -0
  88. package/dist/frontend/lowering/lower-classes.js +4701 -0
  89. package/dist/frontend/lowering/lower-classes.js.map +1 -0
  90. package/dist/frontend/lowering/lower-comptime.d.ts +53 -0
  91. package/dist/frontend/lowering/lower-comptime.js +244 -0
  92. package/dist/frontend/lowering/lower-comptime.js.map +1 -0
  93. package/dist/frontend/lowering/lower-containers.d.ts +486 -0
  94. package/dist/frontend/lowering/lower-containers.js +6345 -0
  95. package/dist/frontend/lowering/lower-containers.js.map +1 -0
  96. package/dist/frontend/lowering/lower-dgram.d.ts +20 -0
  97. package/dist/frontend/lowering/lower-dgram.js +319 -0
  98. package/dist/frontend/lowering/lower-dgram.js.map +1 -0
  99. package/dist/frontend/lowering/lower-emitter.d.ts +10 -0
  100. package/dist/frontend/lowering/lower-emitter.js +688 -0
  101. package/dist/frontend/lowering/lower-emitter.js.map +1 -0
  102. package/dist/frontend/lowering/lower-enums.d.ts +22 -0
  103. package/dist/frontend/lowering/lower-enums.js +235 -0
  104. package/dist/frontend/lowering/lower-enums.js.map +1 -0
  105. package/dist/frontend/lowering/lower-expando.d.ts +35 -0
  106. package/dist/frontend/lowering/lower-expando.js +276 -0
  107. package/dist/frontend/lowering/lower-expando.js.map +1 -0
  108. package/dist/frontend/lowering/lower-exprs.d.ts +524 -0
  109. package/dist/frontend/lowering/lower-exprs.js +8111 -0
  110. package/dist/frontend/lowering/lower-exprs.js.map +1 -0
  111. package/dist/frontend/lowering/lower-generators.d.ts +50 -0
  112. package/dist/frontend/lowering/lower-generators.js +421 -0
  113. package/dist/frontend/lowering/lower-generators.js.map +1 -0
  114. package/dist/frontend/lowering/lower-inspect.d.ts +20 -0
  115. package/dist/frontend/lowering/lower-inspect.js +1074 -0
  116. package/dist/frontend/lowering/lower-inspect.js.map +1 -0
  117. package/dist/frontend/lowering/lower-island.d.ts +108 -0
  118. package/dist/frontend/lowering/lower-island.js +650 -0
  119. package/dist/frontend/lowering/lower-island.js.map +1 -0
  120. package/dist/frontend/lowering/lower-mixins.d.ts +96 -0
  121. package/dist/frontend/lowering/lower-mixins.js +534 -0
  122. package/dist/frontend/lowering/lower-mixins.js.map +1 -0
  123. package/dist/frontend/lowering/lower-modules.d.ts +135 -0
  124. package/dist/frontend/lowering/lower-modules.js +1613 -0
  125. package/dist/frontend/lowering/lower-modules.js.map +1 -0
  126. package/dist/frontend/lowering/lower-namespaces.d.ts +168 -0
  127. package/dist/frontend/lowering/lower-namespaces.js +706 -0
  128. package/dist/frontend/lowering/lower-namespaces.js.map +1 -0
  129. package/dist/frontend/lowering/lower-server.d.ts +90 -0
  130. package/dist/frontend/lowering/lower-server.js +3471 -0
  131. package/dist/frontend/lowering/lower-server.js.map +1 -0
  132. package/dist/frontend/lowering/lower-stmts.d.ts +361 -0
  133. package/dist/frontend/lowering/lower-stmts.js +5341 -0
  134. package/dist/frontend/lowering/lower-stmts.js.map +1 -0
  135. package/dist/frontend/lowering/lower-stream.d.ts +88 -0
  136. package/dist/frontend/lowering/lower-stream.js +1533 -0
  137. package/dist/frontend/lowering/lower-stream.js.map +1 -0
  138. package/dist/frontend/lowering/lower-test.d.ts +27 -0
  139. package/dist/frontend/lowering/lower-test.js +353 -0
  140. package/dist/frontend/lowering/lower-test.js.map +1 -0
  141. package/dist/frontend/lowering/lowerer.d.ts +1748 -0
  142. package/dist/frontend/lowering/lowerer.js +6257 -0
  143. package/dist/frontend/lowering/lowerer.js.map +1 -0
  144. package/dist/frontend/lowering/surfaces.d.ts +272 -0
  145. package/dist/frontend/lowering/surfaces.js +1096 -0
  146. package/dist/frontend/lowering/surfaces.js.map +1 -0
  147. package/dist/frontend/npm-static.d.ts +55 -0
  148. package/dist/frontend/npm-static.js +300 -0
  149. package/dist/frontend/npm-static.js.map +1 -0
  150. package/dist/frontend/npm.d.ts +330 -0
  151. package/dist/frontend/npm.js +1259 -0
  152. package/dist/frontend/npm.js.map +1 -0
  153. package/dist/frontend/program.d.ts +186 -0
  154. package/dist/frontend/program.js +2255 -0
  155. package/dist/frontend/program.js.map +1 -0
  156. package/dist/frontend/provenance-registry.d.ts +48 -0
  157. package/dist/frontend/provenance-registry.js +87 -0
  158. package/dist/frontend/provenance-registry.js.map +1 -0
  159. package/dist/frontend/provenance.d.ts +6 -0
  160. package/dist/frontend/provenance.js +459 -0
  161. package/dist/frontend/provenance.js.map +1 -0
  162. package/dist/frontend/resolve.d.ts +56 -0
  163. package/dist/frontend/resolve.js +682 -0
  164. package/dist/frontend/resolve.js.map +1 -0
  165. package/dist/frontend/shared.d.ts +67 -0
  166. package/dist/frontend/shared.js +241 -0
  167. package/dist/frontend/shared.js.map +1 -0
  168. package/dist/frontend/ts7/adapter.d.ts +7 -0
  169. package/dist/frontend/ts7/adapter.js +54 -0
  170. package/dist/frontend/ts7/adapter.js.map +1 -0
  171. package/dist/frontend/ts7/ast.d.ts +50 -0
  172. package/dist/frontend/ts7/ast.js +211 -0
  173. package/dist/frontend/ts7/ast.js.map +1 -0
  174. package/dist/frontend/ts7/census-check.d.ts +1 -0
  175. package/dist/frontend/ts7/census-check.js +12 -0
  176. package/dist/frontend/ts7/census-check.js.map +1 -0
  177. package/dist/frontend/ts7/checker.d.ts +140 -0
  178. package/dist/frontend/ts7/checker.js +544 -0
  179. package/dist/frontend/ts7/checker.js.map +1 -0
  180. package/dist/frontend/ts7/enums.d.ts +18 -0
  181. package/dist/frontend/ts7/enums.js +43 -0
  182. package/dist/frontend/ts7/enums.js.map +1 -0
  183. package/dist/frontend/ts7/program.d.ts +99 -0
  184. package/dist/frontend/ts7/program.js +278 -0
  185. package/dist/frontend/ts7/program.js.map +1 -0
  186. package/dist/frontend/ts7/world-check.d.ts +3 -0
  187. package/dist/frontend/ts7/world-check.js +34 -0
  188. package/dist/frontend/ts7/world-check.js.map +1 -0
  189. package/dist/frontend/types.d.ts +205 -0
  190. package/dist/frontend/types.js +2485 -0
  191. package/dist/frontend/types.js.map +1 -0
  192. package/dist/index.d.ts +85 -0
  193. package/dist/index.js +429 -0
  194. package/dist/index.js.map +1 -0
  195. package/dist/ir/nodes.d.ts +4334 -0
  196. package/dist/ir/nodes.js +1906 -0
  197. package/dist/ir/nodes.js.map +1 -0
  198. package/dist/ir/serialize.d.ts +4 -0
  199. package/dist/ir/serialize.js +34 -0
  200. package/dist/ir/serialize.js.map +1 -0
  201. package/dist/ir/validate.d.ts +36 -0
  202. package/dist/ir/validate.js +4885 -0
  203. package/dist/ir/validate.js.map +1 -0
  204. package/package.json +30 -6
  205. package/README.md +0 -3
  206. package/index.js +0 -2
@@ -0,0 +1,1613 @@
1
+ /* Module-graph lowering: splitting each source file into its parts, the
2
+ * program-collection pass (signatures, classes, globals — reachability
3
+ * seeds), npm/JSON import collection, per-file %init functions, %main, and
4
+ * the module artifacts (globals, embedded npm tables) the IR module carries. */
5
+ import * as ts from "../ts7/adapter.js";
6
+ import { dirname as dirnamePath, resolve as resolvePath } from "node:path";
7
+ import { NpmGraphBuilder, packageNameOfPath, probeNodeImportRefusal } from "../npm.js";
8
+ import { isNpmStaticPackage } from "../npm-static.js";
9
+ import { isJsSourceFileName, isRelativeSpecifier } from "../shared.js";
10
+ import { cjsExportAssignmentOf, cjsExportDiscardReason, isCjsJsFile, isJsSourceFile, isRequireStatement, locOf, orderedImportsOf, resolveImport, resolveNpmImport } from "../program.js";
11
+ import { invalidJsonModuleDiag, npmEmbedFailedDiag, requiresDynamicImportDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
12
+ import { BOOL, DYN, JSVAL, RUNTIME_ERROR_CLASSES, STRING, VOID, canConvertToDyn, isUnitType } from "../../ir/nodes.js";
13
+ import { ENTRY_NAME, PoisonError, boundIdentifiersOf, dynFallbackType, dynUndefinedExpr, importCallHandleType, newFnCtx, uncheckedOverloadHandleCall } from "./lowerer.js";
14
+ import { builtinMemberRequireDecl, builtinNamespaceDestructureModuleOf, isPromisifyCall } from "./lower-builtins.js";
15
+ import { bindingGenericFnInfoOf, bindingGenericFnNodeOf, implicitLocalFnInfoOf, implicitLocalFnNodeOf } from "./lower-calls.js";
16
+ import { isVarDeclared, provenanceElidedConstDecl } from "./lower-stmts.js";
17
+ import { streamClassAliasDecl } from "./lower-stream.js";
18
+ import { stdlibGlobalAliasDecl } from "./surfaces.js";
19
+ import { collectNamespaceStmt, nsPathPrefix } from "./lower-namespaces.js";
20
+ import { collectExpandoMembers } from "./lower-expando.js";
21
+ import { isUnitOnlyTsType, unitOnlyUnion } from "../types.js";
22
+ import { decoratorNodesOf, guaranteedDecorationThrow } from "./lower-classes.js";
23
+ import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
24
+ /** Splits each file into function/class declarations and the top-level
25
+ * statements that form its init body. Node's evaluation order (depth-first
26
+ * postorder, entry last), computed by preflight. Single-file programs
27
+ * (tests, coverage on broken files) may arrive with an empty order — fall
28
+ * back to the entry alone. */
29
+ export function splitFiles(L) {
30
+ const files = L.moduleOrder.length > 0 ? L.moduleOrder : [L.entry];
31
+ return files.map((sf) => {
32
+ const fp = { sf, fnDecls: [], classDecls: [], topStmts: [] };
33
+ for (const stmt of sf.statements) {
34
+ if (ts.isFunctionDeclaration(stmt))
35
+ fp.fnDecls.push(stmt);
36
+ else if (ts.isClassDeclaration(stmt))
37
+ fp.classDecls.push(stmt);
38
+ // Namespaces: ambient/type-only ones are zero-runtime and skip;
39
+ // instantiated bodies FLATTEN into this file's parts (functions/
40
+ // classes hoist under namespace-qualified names, statements join
41
+ // the init body in source order) — lower-namespaces.ts.
42
+ else if (ts.isModuleDeclaration(stmt))
43
+ collectNamespaceStmt(L, stmt, fp);
44
+ else if (ts.isInterfaceDeclaration(stmt) ||
45
+ ts.isTypeAliasDeclaration(stmt) ||
46
+ ts.isImportDeclaration(stmt) ||
47
+ // Export lists and re-exports are pure alias plumbing (importers
48
+ // resolve through them to the original declarations); their
49
+ // module EDGES live in preflight/buildInitSteps. Nothing runs.
50
+ ts.isExportDeclaration(stmt)) {
51
+ continue; // type-only / already-resolved module plumbing
52
+ }
53
+ else
54
+ fp.topStmts.push(stmt);
55
+ }
56
+ return fp;
57
+ });
58
+ }
59
+ /** A source file dynamic `import("<relative>")` can host as a COMPILED
60
+ * module namespace (lowerOwnModuleImport): a non-declaration program file
61
+ * that is not JSON and not CommonJS-flavored (a CJS namespace is built
62
+ * from module.exports through Node's lexer — a different surface with no
63
+ * static story here). Null for everything else. */
64
+ export function dynamicImportProgramTargetOf(program, sf, spec) {
65
+ if (!isRelativeSpecifier(spec) && !spec.startsWith("/"))
66
+ return null;
67
+ const dep = resolveImport(program, sf, spec);
68
+ if (!dep || dep.isDeclarationFile)
69
+ return null;
70
+ if (dep.fileName.endsWith(".json") || dep.fileName.endsWith(".cts"))
71
+ return null;
72
+ if (isCjsJsFile(dep))
73
+ return null;
74
+ return dep;
75
+ }
76
+ /** Every distinct program module a file's `import("literal")` sites name
77
+ * (source order, deduped). */
78
+ function dynamicProgramImportsOf(program, sf) {
79
+ const out = [];
80
+ const seen = new Set();
81
+ ts.walkPreorder(sf, (node) => {
82
+ if (ts.isCallExpression(node) &&
83
+ node.expression.kind === ts.SyntaxKind.ImportKeyword &&
84
+ node.arguments[0] !== undefined &&
85
+ ts.isStringLiteralLike(node.arguments[0])) {
86
+ const dep = dynamicImportProgramTargetOf(program, sf, node.arguments[0].text);
87
+ if (dep && !seen.has(dep)) {
88
+ seen.add(dep);
89
+ out.push(dep);
90
+ }
91
+ }
92
+ });
93
+ return out;
94
+ }
95
+ /** --dynamic builds: program modules reachable ONLY through dynamic
96
+ * `import()` join the compiled module graph — appended to `order` (before
97
+ * the entry, which stays last) in depth-first postorder over their own
98
+ * static import edges, exactly like preflight ordered the static graph.
99
+ * NOTHING calls their %init at startup (%main runs only the entry's init,
100
+ * and no static import header names these), so a dynamic-only module
101
+ * still evaluates exactly when Node evaluates it: the import() site's
102
+ * namespace builder calls the guarded init on the engine microtask.
103
+ * Fixpoint: an added module's own import() sites are scanned too. Static
104
+ * cycles inside the added subgraph get preflight's SC1016 via `onCycle`
105
+ * (preflight only walked the entry's static graph, so it never saw
106
+ * these). Idempotent — re-running on an already-extended order adds
107
+ * nothing (both lowering passes share one array). */
108
+ export function appendDynamicImportModules(program, order, onCycle) {
109
+ if (order.length === 0)
110
+ return; // no preflight order — sites keep their fences
111
+ const state = new Map();
112
+ for (const sf of order)
113
+ state.set(sf, "done");
114
+ const added = [];
115
+ const stack = [];
116
+ const visit = (sf) => {
117
+ const s = state.get(sf);
118
+ if (s === "done")
119
+ return;
120
+ if (s === "visiting") {
121
+ const cycleStart = stack.indexOf(sf.fileName);
122
+ onCycle([...stack.slice(cycleStart), sf.fileName].join(" → "));
123
+ return;
124
+ }
125
+ state.set(sf, "visiting");
126
+ stack.push(sf.fileName);
127
+ for (const { dep } of orderedImportsOf(program, sf))
128
+ if (dep && dep !== sf)
129
+ visit(dep);
130
+ stack.pop();
131
+ state.set(sf, "done");
132
+ added.push(sf);
133
+ };
134
+ const scanned = new Set();
135
+ for (;;) {
136
+ const scan = [...order, ...added].filter((sf) => !scanned.has(sf));
137
+ if (scan.length === 0)
138
+ break;
139
+ for (const sf of scan) {
140
+ scanned.add(sf);
141
+ for (const dep of dynamicProgramImportsOf(program, sf))
142
+ visit(dep);
143
+ }
144
+ }
145
+ if (added.length > 0)
146
+ order.splice(order.length - 1, 0, ...added);
147
+ }
148
+ /** Collect pass over the WHOLE program: signatures, class shapes, and
149
+ * file-scope globals visible everywhere → hoisting, mutual recursion, and
150
+ * cross-module references fall out for free. Runs in both passes —
151
+ * signatures are cheap and calls resolve against them; only BODY lowering
152
+ * is reachability-gated. */
153
+ export function collectProgram(L, parts) {
154
+ L.collecting = true;
155
+ try {
156
+ for (const fp of parts)
157
+ for (const decl of fp.classDecls)
158
+ L.collectClassShape(decl);
159
+ for (const fp of parts)
160
+ for (const decl of fp.fnDecls)
161
+ L.collectSignature(decl);
162
+ }
163
+ finally {
164
+ L.collecting = false;
165
+ }
166
+ // Globals are top-level (always reachable), so their collection reports
167
+ // eagerly — including deferred-class flushes their types trigger. The
168
+ // coverage remainder still needs the registrations but the emit pass
169
+ // already reported the diagnostics; discard the duplicates.
170
+ if (L.remainder) {
171
+ L.diagSink = [];
172
+ try {
173
+ for (const fp of parts)
174
+ L.collectGlobals(fp.sf, fp.topStmts);
175
+ L.collectNpmImports(parts);
176
+ L.collectJsonImports(parts);
177
+ }
178
+ finally {
179
+ L.diagSink = null;
180
+ }
181
+ }
182
+ else {
183
+ for (const fp of parts)
184
+ L.collectGlobals(fp.sf, fp.topStmts);
185
+ L.collectNpmImports(parts);
186
+ L.collectJsonImports(parts);
187
+ }
188
+ }
189
+ /** The npm-import chokepoint, run once per pass during collection (so
190
+ * bodies lowered in ANY pass resolve the bindings). In a static build
191
+ * every value-importing npm import reports the requires-dynamic
192
+ * diagnostic naming the package — the package's shipped JS has exactly
193
+ * one execution home, the embedded engine. Under --dynamic each value
194
+ * binding becomes a jsval GLOBAL, registered by ALIASED symbol (modules
195
+ * importing the same export share one global) and initialized at the
196
+ * top of the importing module's %init by island.import — the engine's
197
+ * module registry caches, so re-imports are lookups, not re-evaluations.
198
+ * The runtime graph (the package entry plus everything it reaches)
199
+ * builds here too; its failures are ordinary diagnostics at the import
200
+ * site. Type-only imports are free either way: the .d.ts is a type
201
+ * surface, not code. */
202
+ export function collectNpmImports(L, parts) {
203
+ const builder = L.dynamic ? new NpmGraphBuilder() : null;
204
+ for (const fp of parts) {
205
+ for (const stmt of fp.sf.statements) {
206
+ if (!ts.isImportDeclaration(stmt) || !ts.isStringLiteral(stmt.moduleSpecifier))
207
+ continue;
208
+ const clause = stmt.importClause;
209
+ if (clause?.phaseModifier === ts.SyntaxKind.TypeKeyword)
210
+ continue;
211
+ if (clause?.namedBindings &&
212
+ ts.isNamedImports(clause.namedBindings) &&
213
+ !clause.name &&
214
+ clause.namedBindings.elements.every((e) => e.isTypeOnly)) {
215
+ continue;
216
+ }
217
+ const spec = stmt.moduleSpecifier.text;
218
+ const npm = resolveNpmImport(fp.sf.fileName, spec);
219
+ // --npm-static: an opted-in package that made it through preflight
220
+ // is a PROGRAM-MODULE dependency — its entry sits in the module
221
+ // order, orderedImportsOf answers it for the %init header, and the
222
+ // checker's alias chains bind the imports to its own exports. The
223
+ // island owns nothing here, in static and --dynamic builds alike.
224
+ if (npm !== null &&
225
+ isNpmStaticPackage(npm.packageName) &&
226
+ isJsSourceFileName(npm.typesFile) &&
227
+ L.program.getSourceFile(npm.typesFile) !== undefined) {
228
+ continue;
229
+ }
230
+ // A RELATIVE specifier resolving INTO node_modules (the e2e driver
231
+ // shape: `import "../node_modules/vercel/dist/index.js"`) is
232
+ // package code too — tsc pulls the FILE into the program under
233
+ // allowJs, but shipped JS has exactly one execution home, the
234
+ // embedded engine, so it takes the same island path as a bare
235
+ // specifier (previously it lowered to NOTHING: not an npm import,
236
+ // and node_modules files never lower as program modules — the
237
+ // import silently dropped).
238
+ const relPkg = npm === null && isRelativeSpecifier(spec)
239
+ ? packageNameOfPath(resolvePath(dirnamePath(fp.sf.fileName), spec))
240
+ : null;
241
+ const relIsJs = relPkg !== null && !/\.(ts|tsx|mts|cts)$/.test(spec);
242
+ // A relative import INTO an opted-in --npm-static package is the
243
+ // program-module path too (preflight resolved the file edge).
244
+ if (relIsJs && isNpmStaticPackage(relPkg))
245
+ continue;
246
+ if (!npm && !relIsJs)
247
+ continue;
248
+ // An edge Node's RUNTIME resolution refuses at startup (types
249
+ // resolved, but the exports target ships no JS — the types-only
250
+ // package shape): preflight registered Node's startup crash for
251
+ // it, %main throws before any init runs, and the bindings never
252
+ // link — nothing to embed, no island requirement, in EITHER mode.
253
+ if (npm !== null && probeNodeImportRefusal(fp.sf.fileName, spec) !== null)
254
+ continue;
255
+ if (!builder) {
256
+ L.pushDiag(requiresDynamicImportDiag(npm?.packageName ?? relPkg, locOf(stmt)));
257
+ continue;
258
+ }
259
+ const before = builder.errors.length;
260
+ const entryKey = npm
261
+ ? (builder.addImport(fp.sf.fileName, spec), builder.entryOf(fp.sf.fileName, spec))
262
+ : builder.addFileImport(fp.sf.fileName, spec);
263
+ for (const err of builder.errors.slice(before)) {
264
+ L.pushDiag(npmEmbedFailedDiag(err.message, locOf(stmt)));
265
+ }
266
+ if (entryKey === null)
267
+ continue; // resolution failed — reported above
268
+ const loc = locOf(stmt);
269
+ const byStmt = L.npmInitActions.get(fp.sf) ?? new Map();
270
+ L.npmInitActions.set(fp.sf, byStmt);
271
+ const actions = byStmt.get(stmt) ?? [];
272
+ byStmt.set(stmt, actions);
273
+ const importExpr = (exportName) => ({
274
+ kind: "libCall",
275
+ fn: "island.import",
276
+ args: [
277
+ { kind: "strLit", value: entryKey, type: STRING, loc },
278
+ { kind: "strLit", value: exportName, type: STRING, loc },
279
+ // The specifier as WRITTEN — Node's missing-export SyntaxError
280
+ // names it ("The requested module 'x' does not provide an
281
+ // export named 'y'"), and the runtime check reproduces that
282
+ // message exactly (scr_jsval_import).
283
+ { kind: "strLit", value: spec, type: STRING, loc },
284
+ ],
285
+ type: JSVAL,
286
+ loc,
287
+ });
288
+ const bind = (nameNode, exportName) => {
289
+ let symbol = L.checker.getSymbolAtLocation(nameNode);
290
+ if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
291
+ symbol = L.checker.getAliasedSymbol(symbol);
292
+ }
293
+ if (!symbol)
294
+ return;
295
+ let g = L.globalsBySymbol.get(symbol);
296
+ if (!g) {
297
+ g = {
298
+ id: `%g.npm.${L.globalsList.length}`,
299
+ name: nameNode.text,
300
+ type: JSVAL,
301
+ mutable: false,
302
+ };
303
+ L.globalsBySymbol.set(symbol, g);
304
+ L.globalsList.push(g);
305
+ }
306
+ actions.push({ kind: "assign", localId: g.id, value: importExpr(exportName), loc });
307
+ };
308
+ if (!clause) {
309
+ // Side-effect import: load for its top-level effects, keep nothing.
310
+ actions.push({ kind: "exprStmt", expr: importExpr("*"), loc });
311
+ continue;
312
+ }
313
+ if (clause.name)
314
+ bind(clause.name, "default");
315
+ if (clause.namedBindings) {
316
+ if (ts.isNamespaceImport(clause.namedBindings)) {
317
+ bind(clause.namedBindings.name, "*");
318
+ }
319
+ else {
320
+ for (const el of clause.namedBindings.elements) {
321
+ if (el.isTypeOnly)
322
+ continue;
323
+ bind(el.name, el.propertyName?.text ?? el.name.text);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ }
329
+ if (builder) {
330
+ for (const fp of parts)
331
+ collectDynamicImports(L, builder, fp.sf);
332
+ const graph = builder.finish();
333
+ if (graph.modules.length > 0) {
334
+ L.npmEmbedded = { modules: graph.modules, edges: graph.edges };
335
+ }
336
+ if (graph.builtins.length > 0)
337
+ L.npmBuiltins = graph.builtins;
338
+ if (graph.lazyTraps.length > 0)
339
+ L.npmLazyTraps = graph.lazyTraps;
340
+ }
341
+ }
342
+ /** The dynamic-import half of the npm chokepoint (--dynamic only): every
343
+ * `import("literal")` in the file — whatever body it sits in — resolves
344
+ * and embeds AT COLLECTION time, so the per-site lowering later just
345
+ * looks its key up (the emitted tables are assembled once, here; a
346
+ * site lowered in any pass finds its module embedded). Resolution
347
+ * failures and unshimmed builtins are diagnostics HERE, at the import
348
+ * expression, exactly like static npm imports at their statements; the
349
+ * lowering poisons those sites without re-reporting. Non-literal
350
+ * specifiers are skipped — the lowering owns that fence (the module
351
+ * graph is a build-time artifact; there is nothing to embed for a
352
+ * runtime-computed name). */
353
+ function collectDynamicImports(L, builder, sf) {
354
+ const visit = (node) => {
355
+ if (ts.isCallExpression(node) &&
356
+ node.expression.kind === ts.SyntaxKind.ImportKeyword &&
357
+ node.arguments.length >= 1 &&
358
+ node.arguments[0] !== undefined &&
359
+ ts.isStringLiteralLike(node.arguments[0])) {
360
+ const spec = node.arguments[0].text;
361
+ const mapKey = `${sf.fileName}\u0000${spec}`;
362
+ if (!L.dynImports.has(mapKey)) {
363
+ // The program's OWN modules first, by the checker's resolution
364
+ // (tsc already resolved "./helper.js" to helper.ts): a compiled
365
+ // module has no runtime namespace object — the per-site fence
366
+ // says so. Only non-declaration source files count; a sibling
367
+ // .d.mts typing shipped JS is the EMBED case, not this one.
368
+ const modSym = L.checker.getSymbolAtLocation(node.arguments[0]);
369
+ const ownModule = modSym !== undefined && L.checker.declarationsOf(modSym).some((d) => ts.isSourceFile(d) && !d.isDeclarationFile);
370
+ const res = ownModule
371
+ ? { kind: "program-module" }
372
+ : builder.addDynamicImport(sf.fileName, spec);
373
+ L.dynImports.set(mapKey, res);
374
+ if (res.kind === "unsupported-builtin") {
375
+ L.pushDiag(npmEmbedFailedDiag(`'${spec}' is a Node builtin the island does not provide a shim for`, locOf(node)));
376
+ }
377
+ else if (res.kind === "unresolved") {
378
+ L.pushDiag(npmEmbedFailedDiag(res.message, locOf(node)));
379
+ }
380
+ }
381
+ }
382
+ ts.forEachChild(node, visit);
383
+ };
384
+ visit(sf);
385
+ }
386
+ /** JSON module default imports (`import pkg from "../package.json"`):
387
+ * the document is DATA known at build time, so the binding bakes into a
388
+ * record global — the checker's structural type for the module (thanks
389
+ * to resolveJsonModule) maps to a record shape, the JSON text parses in
390
+ * the compiler, and comptimeValueToIr turns the value into literal IR
391
+ * assigned in the importing module's %init prelude (evaluation order:
392
+ * imports before the importer's body, like npm bindings). Two importers
393
+ * of the same document share one global (keyed by the ALIASED symbol).
394
+ * Shapes outside the bakeable surface (null-valued fields, mixed
395
+ * arrays, ...) report the standard unsupported-type diagnostic at the
396
+ * import site. Preflight already fenced named/namespace JSON imports
397
+ * and kept .json files out of the module order. */
398
+ export function collectJsonImports(L, parts) {
399
+ for (const fp of parts) {
400
+ for (const stmt of fp.sf.statements) {
401
+ if (!ts.isImportDeclaration(stmt) || !ts.isStringLiteral(stmt.moduleSpecifier))
402
+ continue;
403
+ const clause = stmt.importClause;
404
+ if (!clause?.name || clause.phaseModifier === ts.SyntaxKind.TypeKeyword)
405
+ continue;
406
+ const nameSym = L.checker.getSymbolAtLocation(clause.name);
407
+ if (!nameSym || !(nameSym.flags & ts.SymbolFlags.Alias))
408
+ continue;
409
+ const target = L.checker.getAliasedSymbol(nameSym);
410
+ const jsonSf = L.checker.declarationsOf(target)[0]?.getSourceFile();
411
+ if (!jsonSf || !jsonSf.fileName.endsWith(".json"))
412
+ continue;
413
+ try {
414
+ const tsType = L.typeOf(clause.name);
415
+ const mapped = L.mapTypeOf(tsType);
416
+ if (!mapped || !L.comptimeBakeable(mapped)) {
417
+ L.badType(clause.name, tsType);
418
+ }
419
+ // tsgo tolerates JSON shapes strict JSON.parse rejects (a leading
420
+ // `//` comment — importAttributes11), so no SC0001 guarantees a
421
+ // clean document: a failing parse gates at the import statement
422
+ // (Node refuses to import the module at runtime too).
423
+ let parsed;
424
+ try {
425
+ parsed = JSON.parse(jsonSf.text);
426
+ }
427
+ catch (e) {
428
+ L.pushDiag(invalidJsonModuleDiag(jsonSf.fileName, e instanceof Error ? e.message : String(e), locOf(stmt)));
429
+ throw new PoisonError();
430
+ }
431
+ const value = L.comptimeValueToIr(parsed, mapped, "$", clause.name);
432
+ let g = L.globalsBySymbol.get(target);
433
+ if (!g) {
434
+ g = {
435
+ id: `%g.json.${L.globalsList.length}`,
436
+ name: clause.name.text,
437
+ type: mapped,
438
+ mutable: false,
439
+ };
440
+ L.globalsBySymbol.set(target, g);
441
+ L.globalsList.push(g);
442
+ }
443
+ const actions = L.jsonInitActions.get(fp.sf) ?? [];
444
+ L.jsonInitActions.set(fp.sf, actions);
445
+ actions.push({ kind: "assign", localId: g.id, value, loc: locOf(stmt) });
446
+ }
447
+ catch (e) {
448
+ if (!(e instanceof PoisonError))
449
+ throw e;
450
+ // diagnostic already recorded; uses of the binding poison too
451
+ }
452
+ }
453
+ }
454
+ }
455
+ /** The classes, records, and unions the emitted module carries: exactly
456
+ * what the lowered functions and globals reference, closed transitively
457
+ * over record fields, union arms, class field types, base chains, and
458
+ * WHOLE hierarchies (hierarchy membership decides object layout:
459
+ * dropping an unused subclass must not turn its base into a standalone
460
+ * class at emit time). Registry entries interned while collecting
461
+ * signatures nothing reaches stay out — an unreached type leaves no
462
+ * trace. A referenced class with no registration had its collection
463
+ * deferred: the reference flushes those diagnostics. Methods whose
464
+ * bodies were not lowered leave the def's method list, so vtable slots
465
+ * exist exactly for emitted overrides. */
466
+ export function moduleArtifacts(L, functions) {
467
+ const classNames = new Set();
468
+ const shapeIds = new Set();
469
+ const unionIds = new Set();
470
+ const pendingClasses = [];
471
+ const pendingShapes = [];
472
+ const pendingUnions = [];
473
+ const visit = (node) => {
474
+ if (Array.isArray(node)) {
475
+ for (const item of node)
476
+ visit(item);
477
+ return;
478
+ }
479
+ if (node === null || typeof node !== "object")
480
+ return;
481
+ const rec = node;
482
+ if (typeof rec["className"] === "string" && !classNames.has(rec["className"])) {
483
+ classNames.add(rec["className"]);
484
+ pendingClasses.push(rec["className"]);
485
+ }
486
+ if (typeof rec["shapeId"] === "string" && !shapeIds.has(rec["shapeId"])) {
487
+ shapeIds.add(rec["shapeId"]);
488
+ pendingShapes.push(rec["shapeId"]);
489
+ }
490
+ if (typeof rec["unionId"] === "string" && !unionIds.has(rec["unionId"])) {
491
+ unionIds.add(rec["unionId"]);
492
+ pendingUnions.push(rec["unionId"]);
493
+ }
494
+ for (const key of Object.keys(rec)) {
495
+ if (key !== "loc")
496
+ visit(rec[key]);
497
+ }
498
+ };
499
+ visit(functions);
500
+ visit(L.globalsList);
501
+ // The builtin error classes ride EVERY module: the runtime's own throws
502
+ // (JSON/dynCheck/regex failures) mint instances of them whether or not
503
+ // user code mentions Error, and the uncaught printer tells Error
504
+ // payloads apart by their preorder interval — which must therefore be
505
+ // part of this program's numbering (backends stamp it into the
506
+ // runtime's vtables at main).
507
+ for (const name of RUNTIME_ERROR_CLASSES.keys()) {
508
+ if (!classNames.has(name)) {
509
+ classNames.add(name);
510
+ pendingClasses.push(name);
511
+ }
512
+ }
513
+ while (pendingClasses.length > 0 ||
514
+ pendingShapes.length > 0 ||
515
+ pendingUnions.length > 0) {
516
+ while (pendingShapes.length > 0) {
517
+ // Declared fields AND the index-signature value type: an
518
+ // overflow-valued shape (`{ [key: string]: {script?: string} }`)
519
+ // references its value shape through indexValue alone.
520
+ const shape = L.shapes.get(pendingShapes.pop());
521
+ visit(shape?.fields);
522
+ if (shape?.indexValue)
523
+ visit(shape.indexValue);
524
+ }
525
+ while (pendingUnions.length > 0)
526
+ visit(L.unions.get(pendingUnions.pop())?.arms);
527
+ while (pendingClasses.length > 0) {
528
+ const name = pendingClasses.pop();
529
+ const info = L.classes.get(name);
530
+ if (!info) {
531
+ // Referenced by an emitted type but never registered: collection
532
+ // deferred its diagnostics — a reached reference makes them count.
533
+ L.flushDeferredClass(name);
534
+ continue;
535
+ }
536
+ visit(info.def.fields);
537
+ if (info.base)
538
+ visit([{ className: info.base.def.name }]);
539
+ if (L.inHierarchy(info)) {
540
+ let root = info;
541
+ while (root.base)
542
+ root = root.base;
543
+ const wholeTree = (c) => {
544
+ visit([{ className: c.def.name }]);
545
+ for (const s of c.subclasses)
546
+ wholeTree(s);
547
+ };
548
+ wholeTree(root);
549
+ }
550
+ }
551
+ }
552
+ const reachable = L.reachable;
553
+ return {
554
+ classes: [...L.classes.values()]
555
+ .map((c) => c.def)
556
+ .filter((def) => classNames.has(def.name))
557
+ .map((def) => {
558
+ // Generic-class instantiations bypass the reachability gate
559
+ // (demand-driven — every member of a demanded instantiation
560
+ // lowers; see lowerClassMembers), so their defs keep every
561
+ // method: the emitted functions all exist. ABSTRACT entries ride
562
+ // the same rule (noteVirtualEdge marks the abstract declarer, so
563
+ // a dispatched slot keeps its declaration; an unreferenced one
564
+ // drops and the root-most CONCRETE declaration owns the slot).
565
+ const methods = reachable === null || def.genericOf !== undefined
566
+ ? (def.methods ?? [])
567
+ : (def.methods?.filter((m) => reachable.has(`%${def.name}.${m}`)) ?? []);
568
+ const abstractMethods = def.abstractMethods?.filter((m) => methods.includes(m)) ?? [];
569
+ const rest = { ...def };
570
+ delete rest.methods;
571
+ delete rest.abstractMethods;
572
+ return {
573
+ ...rest,
574
+ ...(methods.length > 0 ? { methods } : {}),
575
+ ...(abstractMethods.length > 0 ? { abstractMethods } : {}),
576
+ };
577
+ }),
578
+ records: L.shapes.shapes.filter((r) => shapeIds.has(r.id)),
579
+ unions: L.unions.unions.filter((u) => unionIds.has(u.id)),
580
+ };
581
+ }
582
+ /** Registers every DIRECT top-level `const`/`let` of a file as a module
583
+ * global (stable storage: cross-module live bindings, and functions can
584
+ * reference them without capture). Block-scoped vars inside nested
585
+ * statements keep their different symbols and stay locals. */
586
+ /** JS collection failures defer to runtime fences: the registration's
587
+ * diagnostics move off the build (into L.runtimeFences) — the declaring
588
+ * statement re-fails during init lowering and compiles to the
589
+ * runtimeFence trap there, and every use site cascades to its own trap.
590
+ * TypeScript files keep eager collection reports. */
591
+ function deferJsCollectionDiags(L, sf, diagsBefore) {
592
+ if (!isJsSourceFile(sf))
593
+ return;
594
+ const captured = L.diags.splice(diagsBefore);
595
+ const ice = captured.filter((d) => d.code === "SC9001");
596
+ if (ice.length > 0)
597
+ L.diags.push(...ice);
598
+ L.runtimeFences.push(...captured.filter((d) => d.code !== "SC9001"));
599
+ }
600
+ /** A scalar-literal expression a single-value `module.exports =` can carry
601
+ * without any declaration to alias to: string/number/boolean literals,
602
+ * substitution-free templates, and negated numbers. */
603
+ function cjsScalarLiteral(e) {
604
+ if (ts.isNumericLiteral(e) || ts.isStringLiteral(e) || ts.isNoSubstitutionTemplateLiteral(e))
605
+ return true;
606
+ if (e.kind === ts.SyntaxKind.TrueKeyword || e.kind === ts.SyntaxKind.FalseKeyword)
607
+ return true;
608
+ return (ts.isPrefixUnaryExpression(e) &&
609
+ e.operator === ts.SyntaxKind.MinusToken &&
610
+ ts.isNumericLiteral(e.operand));
611
+ }
612
+ /** True when a JS file-scope declaration's initializer provably LOWERS to
613
+ * a checked-dynamic (or unit) value, so a DYN module global can hold it:
614
+ * no initializer, a unit literal, or a checker-`any` call/member/
615
+ * identifier read (any-typed operations lower through the DOM in JS).
616
+ * `new` expressions stay OUT even when checker-any (`new Anon.Sub()` over
617
+ * an expando class member constructs a TYPED instance — corpus 2032), as
618
+ * does everything else (object/array literals have their own rules;
619
+ * typed-but-unmappable initializers keep the %init-local adoption). */
620
+ function jsDynHoldableInitializer(L, init) {
621
+ if (init === undefined)
622
+ return true;
623
+ let e = init;
624
+ while (ts.isParenthesizedExpression(e))
625
+ e = e.expression;
626
+ if (e.kind === ts.SyntaxKind.NullKeyword)
627
+ return true;
628
+ if (ts.isIdentifier(e) && e.text === "undefined")
629
+ return true;
630
+ if ((ts.isCallExpression(e) || ts.isPropertyAccessExpression(e) || ts.isIdentifier(e)) &&
631
+ (L.typeOf(e).flags & ts.TypeFlags.Any) !== 0) {
632
+ return true;
633
+ }
634
+ // A CALL whose inferred type has no static mapping and whose residue
635
+ // falls to the checked-dynamic kind (`const exec = common.mustCall(() =>
636
+ // ... exec ...)` — the wrapper's rest-args type is not checker-`any`,
637
+ // but the VALUE lowers through the DOM the same way): registering the
638
+ // dyn global BEFORE the initializer lowers is what lets the callback
639
+ // capture the binding itself — the Node-suite self-referential-const
640
+ // idiom. Non-dyn residues (pure single-signature function types, typed
641
+ // arrays) keep their static stories.
642
+ if (ts.isCallExpression(e)) {
643
+ const t = L.typeOf(e);
644
+ if (L.mapTypeOf(t) === null && dynFallbackType(L, e, t)?.kind === "dyn")
645
+ return true;
646
+ }
647
+ return false;
648
+ }
649
+ export function collectGlobals(L, sf, topStmts) {
650
+ // Segment-tagged so mangled names can't collide across files (user
651
+ // identifiers cannot contain '.'): entry globals "e.<name>", others
652
+ // "m<i>.<name>".
653
+ const rawTag = L.fileTag.get(sf) ?? "";
654
+ const tag = rawTag === "" ? "e." : rawTag.replace(/^%/, "");
655
+ // Expando function members (`foo.bar = 12` anywhere in the file)
656
+ // register their module globals first — reads inside function bodies
657
+ // collected earlier in this pass must resolve them (lower-expando.ts).
658
+ collectExpandoMembers(L, sf);
659
+ for (const stmt of topStmts) {
660
+ // `export default <expr>`: the module's `default` binding is a const
661
+ // module global (registered under the checker's default-export
662
+ // symbol — the symbol a default IMPORT's alias chain terminates at,
663
+ // so importers share the storage). The init body assigns it at the
664
+ // statement's position (lowerDefaultExport) — which IS Node's
665
+ // semantics for expression defaults: the binding snapshots the value
666
+ // once, when the export statement evaluates.
667
+ //
668
+ // ENTITY-NAME defaults (`export default x`, `export default N.a`)
669
+ // are different: the checker binds the default symbol as an ALIAS to
670
+ // the target, and importers resolve straight through to the target's
671
+ // own storage — pure plumbing, like `export { x as default }`. That
672
+ // is honest exactly when the target cannot be reassigned (consts,
673
+ // function/class declarations — reassignment of those is fenced
674
+ // elsewhere): Node's default binding would NOT see a later write
675
+ // (snapshot), the alias-resolved read would (live). Mutable targets
676
+ // fence by name instead of diverging silently.
677
+ if (ts.isExportAssignment(stmt) && !stmt.isExportEquals) {
678
+ const symbol = defaultExportSymbolOf(L, sf);
679
+ if (!symbol)
680
+ continue;
681
+ if (symbol.flags & ts.SymbolFlags.Alias) {
682
+ const target = L.checker.getAliasedSymbol(symbol);
683
+ const vd = L.checker.valueDeclarationOf(target);
684
+ if (vd !== undefined &&
685
+ ts.isVariableDeclaration(vd) &&
686
+ !vd.getSourceFile().isDeclarationFile &&
687
+ (ts.getCombinedNodeFlags(vd) & ts.NodeFlags.Const) === 0) {
688
+ L.pushDiag(unsupportedDiag("SC1012", locOf(stmt), `default-exporting the mutable binding '${stmt.expression.getText()}'`, "Node exports a SNAPSHOT taken when the export statement runs, which is not modeled for let/var targets — export a const, or use a named export (export { x }) for a live binding"));
689
+ }
690
+ continue; // alias plumbing — importers resolve to the target's storage
691
+ }
692
+ // A default whose expression is a decorated class expression with
693
+ // a PROVABLY-THROWING decoration (the ambient-decorator shape):
694
+ // evaluating the export statement throws before the binding could
695
+ // snapshot anything, so no storage registers — the statement
696
+ // lowering emits the throw itself (lowerDefaultExport), and no
697
+ // importer's read can ever run (the module init unwound).
698
+ {
699
+ let inner = stmt.expression;
700
+ while (ts.isParenthesizedExpression(inner))
701
+ inner = inner.expression;
702
+ if (ts.isClassExpression(inner) && !isJsSourceFile(sf) &&
703
+ inner.typeParameters === undefined &&
704
+ (decoratorNodesOf(inner).length > 0 || inner.members.some((m) => decoratorNodesOf(m).length > 0)) &&
705
+ guaranteedDecorationThrow(L, inner) !== null) {
706
+ continue;
707
+ }
708
+ }
709
+ try {
710
+ let tsType = L.typeOf(stmt.expression);
711
+ // tsgo answers `any` at some export-assignment EXPRESSION
712
+ // positions (a bare literal default) — the SYMBOL's type is the
713
+ // declared truth and serves as the fallback.
714
+ if (tsType.flags & ts.TypeFlags.Any) {
715
+ const symType = L.checker.getTypeOfSymbol(symbol);
716
+ if (!(symType.flags & ts.TypeFlags.Any))
717
+ tsType = symType;
718
+ }
719
+ let type = L.mapTypeOf(tsType) ?? dynFallbackType(L, stmt.expression, tsType) ?? L.badType(stmt.expression, tsType);
720
+ // `export default undefined` — the unit-only union, like any
721
+ // unit-only binding (`export default null` maps via mapType).
722
+ if (type.kind === "void" && isUnitOnlyTsType(tsType)) {
723
+ type = unitOnlyUnion(L.unions);
724
+ }
725
+ if (type.kind === "void")
726
+ L.badType(stmt.expression, tsType);
727
+ const g = { id: `%g.${tag}default`, name: "default", type, mutable: false };
728
+ L.globalsBySymbol.set(symbol, g);
729
+ L.globalsList.push(g);
730
+ }
731
+ catch (e) {
732
+ if (!(e instanceof PoisonError))
733
+ throw e;
734
+ // diagnostic already recorded; lowering the statement poisons too
735
+ }
736
+ continue;
737
+ }
738
+ // CommonJS EXPORT statements (JS files): `exports.f = <expr>` /
739
+ // `module.exports.f = <expr>` register a module global keyed by the
740
+ // export symbol (the symbol importer bindings alias to), assigned by
741
+ // the statement's lowering at its source position — the `export
742
+ // default` machinery's shape. `module.exports = { ... }` registers
743
+ // one for each EXPRESSION-valued property; identifier/shorthand
744
+ // properties are pure alias plumbing (resolveValueSymbol re-resolves
745
+ // importers to the referenced declarations — no storage needed).
746
+ if (isJsSourceFile(sf)) {
747
+ // Statements Node would DISCARD (a replaced export object) never
748
+ // register storage — importer reads must not bind to values Node's
749
+ // importers never see. The statement lowering owns the diagnostic.
750
+ const cjs = cjsExportDiscardReason(stmt) === null ? cjsExportAssignmentOf(stmt) : null;
751
+ const registerExport = (nameNode, name, typeNode) => {
752
+ const diagsBefore = L.diags.length;
753
+ try {
754
+ // tsgo answers no symbol at the attachment site's NAME (no
755
+ // expando synthesis); the module symbol's exports table still
756
+ // names the member — the same identity use sites resolve.
757
+ const symbol = L.checker.getSymbolAtLocation(nameNode) ?? L.cjsModuleExportSymbol(sf, name);
758
+ if (!symbol || L.globalsBySymbol.has(symbol))
759
+ return;
760
+ // JS exports whose strict type has no mapping take the
761
+ // checked-dynamic fallback (implicit-any function exports —
762
+ // common/tls's `exports.check = function (certs) {...}` —
763
+ // keep func-ness with dyn pieces; everything else is dyn).
764
+ const strict = L.checker.getTypeOfSymbol(symbol);
765
+ const t = L.mapTypeOf(strict) ?? dynFallbackType(L, nameNode, strict);
766
+ if (!t || t.kind === "void")
767
+ L.badType(typeNode, strict);
768
+ // `module.exports.Strings = Strings` naming a DOM-HOLDING
769
+ // const (the JS file-scope object-literal identity story): the
770
+ // export ALIASES the const's own dyn global — one storage, one
771
+ // owner, identity by construction (a record-typed second slot
772
+ // would either copy or mismatch the dyn value — that mismatch
773
+ // was an ICE; a second same-named global double-released at
774
+ // teardown). The statement's assign degenerates to a harmless
775
+ // self-assign. Mutable (`let`) sources keep the registration
776
+ // below: Node copies the VALUE at this statement, and separate
777
+ // storage is exactly that copy.
778
+ {
779
+ let rhs = typeNode;
780
+ while (ts.isParenthesizedExpression(rhs))
781
+ rhs = rhs.expression;
782
+ if (ts.isIdentifier(rhs)) {
783
+ const vSym = L.checker.getSymbolAtLocation(rhs);
784
+ const vG = vSym && L.globalsBySymbol.get(vSym);
785
+ if (vG?.type.kind === "dyn") {
786
+ if (!vG.mutable) {
787
+ L.globalsBySymbol.set(symbol, vG);
788
+ for (const d of L.checker.declarationsOf(symbol))
789
+ L.globalsByDeclNode.set(d, vG);
790
+ return;
791
+ }
792
+ // A mutable (`let`) DOM source: separate DYN storage IS
793
+ // Node's copy-of-the-reference at this statement (later
794
+ // reassignments of the let stay invisible through the
795
+ // export). The id must not collide with the let's own
796
+ // `%g.<tag><name>` global.
797
+ const g = { id: `%g.${tag}%export.${name}`, name, type: DYN, mutable: false };
798
+ L.globalsBySymbol.set(symbol, g);
799
+ for (const d of L.checker.declarationsOf(symbol))
800
+ L.globalsByDeclNode.set(d, g);
801
+ L.globalsList.push(g);
802
+ return;
803
+ }
804
+ }
805
+ }
806
+ const g = { id: `%g.${tag}${name}`, name, type: t, mutable: false };
807
+ L.globalsBySymbol.set(symbol, g);
808
+ // Importer aliases resolve to a DISTINCT late-bound symbol with
809
+ // the same declaration — key the node too (globalOf's fallback).
810
+ for (const d of L.checker.declarationsOf(symbol))
811
+ L.globalsByDeclNode.set(d, g);
812
+ L.globalsList.push(g);
813
+ }
814
+ catch (e) {
815
+ if (!(e instanceof PoisonError))
816
+ throw e;
817
+ // diagnostic recorded; the statement lowering poisons too —
818
+ // both defer to the statement's runtime fence in JS files.
819
+ deferJsCollectionDiags(L, sf, diagsBefore);
820
+ }
821
+ };
822
+ if (cjs?.kind === "member" && ts.isIdentifier(cjs.name)) {
823
+ // `exports.C = C` naming a CLASS declaration: alias plumbing —
824
+ // no storage (a class VALUE global would fence for
825
+ // builtin-derived classes); importers re-resolve to the class
826
+ // symbol (cjsMemberExportClassSymbol) and the class registry
827
+ // applies unchanged.
828
+ if (L.cjsMemberExportClassSymbol(cjs.expr) !== null)
829
+ continue;
830
+ registerExport(cjs.name, cjs.name.text, cjs.value);
831
+ continue;
832
+ }
833
+ // `module.exports = <scalar literal>` — a single-value replacement
834
+ // export with no declaration for requirer aliases to land on: the
835
+ // checker's `export=` symbol declares AT this statement, so the
836
+ // const export global registers keyed by the statement node
837
+ // (globalOf's declaration-node fallback routes requirer reads
838
+ // here). Identifier values register nothing — alias plumbing, like
839
+ // identifier table entries; other value shapes fence in the
840
+ // statement lowering.
841
+ if (cjs?.kind === "table" && !cjs.obj) {
842
+ let rhs = cjs.expr.right;
843
+ while (ts.isParenthesizedExpression(rhs))
844
+ rhs = rhs.expression;
845
+ // `module.exports = function (…) {…}` (ms's shape — the whole
846
+ // export IS an anonymous function): a FUNC-typed const export
847
+ // global keyed by the statement node, exactly the scalar
848
+ // plumbing — requirer/default-import aliases resolve to the
849
+ // checker's export= symbol, whose declaration IS this statement
850
+ // (globalOf's node fallback), and calls ride the ordinary
851
+ // func-value paths. The inference types the signature (JSDoc
852
+ // included); unmappable pieces take the JS checked-dynamic
853
+ // fallback like every JS binding.
854
+ const fnValued = ts.isFunctionExpression(rhs) || ts.isArrowFunction(rhs);
855
+ if (cjsScalarLiteral(rhs) || fnValued) {
856
+ const diagsBefore = L.diags.length;
857
+ try {
858
+ const strict = L.typeOf(rhs);
859
+ const t = L.mapTypeOf(strict) ?? (fnValued ? dynFallbackType(L, rhs, strict) : null);
860
+ if (t && t.kind !== "void") {
861
+ const g = { id: `%g.${tag}exports`, name: "exports", type: t, mutable: false };
862
+ L.globalsByDeclNode.set(cjs.expr, g);
863
+ L.globalsList.push(g);
864
+ }
865
+ }
866
+ catch (e) {
867
+ if (!(e instanceof PoisonError))
868
+ throw e;
869
+ deferJsCollectionDiags(L, sf, diagsBefore);
870
+ }
871
+ }
872
+ continue;
873
+ }
874
+ if (cjs?.kind === "table" && cjs.obj) {
875
+ for (const prop of cjs.obj.properties) {
876
+ if (ts.isShorthandPropertyAssignment(prop))
877
+ continue; // alias plumbing
878
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.initializer))
879
+ continue;
880
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
881
+ registerExport(prop.name, prop.name.text, prop.initializer);
882
+ }
883
+ // other property forms fence in the statement lowering
884
+ }
885
+ continue;
886
+ }
887
+ }
888
+ // `import x = N.y` where the target is a MUTABLE binding: Node's
889
+ // transform emits `var x = N.y` — a SNAPSHOT taken when the alias
890
+ // statement runs — so the alias gets its own const storage, keyed by
891
+ // the alias's PRE-alias symbol (resolveValueSymbol stops at alias
892
+ // symbols with registered storage instead of chasing them to the
893
+ // live target). lowerImportEquals assigns it at the statement's
894
+ // source position. Immutable targets stay pure plumbing (no storage;
895
+ // the alias-resolved read IS the snapshot when nothing can
896
+ // reassign), and the require/type-only forms are handled at the
897
+ // statement lowering.
898
+ if (ts.isImportEqualsDeclaration(stmt)) {
899
+ if (ts.isExternalModuleReference(stmt.moduleReference) || stmt.isTypeOnly)
900
+ continue;
901
+ const aliasSym = L.checker.getSymbolAtLocation(stmt.name);
902
+ if (!aliasSym || !(aliasSym.flags & ts.SymbolFlags.Alias) || L.globalsBySymbol.has(aliasSym))
903
+ continue;
904
+ const target = L.checker.getAliasedSymbol(aliasSym);
905
+ const vd = L.checker.valueDeclarationOf(target);
906
+ const mutableVar = vd !== undefined &&
907
+ ts.isVariableDeclaration(vd) &&
908
+ !vd.getSourceFile().isDeclarationFile &&
909
+ (ts.getCombinedNodeFlags(vd) & ts.NodeFlags.Const) === 0;
910
+ if (!mutableVar)
911
+ continue;
912
+ const diagsBefore = L.diags.length;
913
+ try {
914
+ let type = L.irTypeOf(stmt.name);
915
+ if (type.kind === "void" && isUnitOnlyTsType(L.typeOf(stmt.name))) {
916
+ type = unitOnlyUnion(L.unions);
917
+ }
918
+ if (type.kind === "void")
919
+ L.badType(stmt.name, L.typeOf(stmt.name));
920
+ const g = {
921
+ id: `%g.${tag}${nsPathPrefix(stmt)}%alias.${stmt.name.text}%${stmt.getStart()}`,
922
+ name: stmt.name.text,
923
+ type,
924
+ mutable: false,
925
+ };
926
+ L.globalsBySymbol.set(aliasSym, g);
927
+ L.globalsList.push(g);
928
+ }
929
+ catch (e) {
930
+ if (!(e instanceof PoisonError))
931
+ throw e;
932
+ deferJsCollectionDiags(L, sf, diagsBefore);
933
+ }
934
+ continue;
935
+ }
936
+ // `var` at module scope hoists across the WHOLE module body, blocks
937
+ // included: `if (c) { var y = 1; }` at top level is a module-scoped
938
+ // binding other functions in the file read. Non-top-level var
939
+ // statements get their globals from the nested walk (top-level
940
+ // statements continue into the main registration below, which also
941
+ // owns the special forms — requires, aliases, promisify).
942
+ if (!ts.isVariableStatement(stmt)) {
943
+ collectNestedVarGlobals(L, sf, stmt, tag);
944
+ continue;
945
+ }
946
+ // CommonJS require declarations are alias plumbing (preflight owns
947
+ // the module edges; resolveValueSymbol routes reads through to the
948
+ // exporter's declarations): no storage, and the statement lowers to
949
+ // nothing. One registration DOES happen here: `const process =
950
+ // require('node:process')` aliases the global process
951
+ // (stdlibGlobalAliasDecl's require form) so reads through the
952
+ // binding lower via the process surface.
953
+ if (isRequireStatement(stmt)) {
954
+ if (ts.isVariableStatement(stmt) &&
955
+ (stmt.declarationList.flags & ts.NodeFlags.Const) !== 0) {
956
+ for (const decl of stmt.declarationList.declarations) {
957
+ stdlibGlobalAliasDecl(L, decl.name, decl.initializer);
958
+ }
959
+ }
960
+ continue;
961
+ }
962
+ // `declare const __VERSION__: string` — AMBIENT: no storage exists
963
+ // (a bundler define in the real pipeline; nothing defines it here,
964
+ // exactly like running the source under Node). Reads lower to
965
+ // Node's ReferenceError at the access (lower-exprs), and the
966
+ // statement itself lowers to nothing.
967
+ if (ts.getCombinedModifierFlags(stmt.declarationList.declarations[0]) & ts.ModifierFlags.Ambient)
968
+ continue;
969
+ const list = stmt.declarationList;
970
+ if ((list.flags & ts.NodeFlags.Using) !== 0)
971
+ continue; // fenced at the statement lowering
972
+ const isConst = (list.flags & ts.NodeFlags.Const) !== 0;
973
+ // `var` registers a mutable module global exactly like `let` — the
974
+ // hoisting difference (binding exists from module entry, undefined
975
+ // until assigned) lives in lowerFileInit's entry inits.
976
+ const isLet = (list.flags & ts.NodeFlags.Let) !== 0 || (list.flags & ts.NodeFlags.BlockScoped) === 0;
977
+ // Namespace members carry their namespace path in the storage id
978
+ // (`%g.e.A%B.x`), so `namespace A { export const x }` and a
979
+ // top-level `const x` never collide; non-exported members add the
980
+ // declaring block's position (block-local bindings).
981
+ const nsPrefix = nsPathPrefix(stmt, list.declarations[0]);
982
+ for (const decl of list.declarations) {
983
+ // --provenance-sources: an elided pure-annotated dead const in a
984
+ // fetched source module registers no global (the statement
985
+ // lowering emits nothing by the same test — see
986
+ // provenanceElidedConstDecl). Checked FIRST: the mixin/promisify
987
+ // probes below would otherwise claim the call-initializer shape
988
+ // and put its fences on the build.
989
+ if (provenanceElidedConstDecl(L, decl))
990
+ continue;
991
+ // `const f = <T>(x: T) => x` at file scope: a generic function
992
+ // value binding — no global exists (the binding is never read;
993
+ // calls and pinned references monomorphize against the
994
+ // initializer like a generic function declaration, registered
995
+ // HERE so hoisted function bodies and cross-module references
996
+ // resolve it). Non-qualifying shapes (reassigned bindings) fence
997
+ // by name; the statement lowering skips (or re-fences — pushDiag
998
+ // dedupes) by the same test.
999
+ {
1000
+ const gfnNode = bindingGenericFnNodeOf(decl);
1001
+ if (gfnNode) {
1002
+ try {
1003
+ bindingGenericFnInfoOf(L, decl, gfnNode);
1004
+ }
1005
+ catch (e) {
1006
+ if (!(e instanceof PoisonError))
1007
+ throw e;
1008
+ }
1009
+ continue;
1010
+ }
1011
+ }
1012
+ // `const f = (x) => ...` at file scope of an npm-static JS file
1013
+ // whose params are implicit-any: the implicit-monomorphization
1014
+ // twin of the generic binding above — no global exists; calls and
1015
+ // value references resolve through genericFnsBySymbol, and the
1016
+ // statement lowering skips by the same test.
1017
+ {
1018
+ const implicitNode = implicitLocalFnNodeOf(L, decl);
1019
+ if (implicitNode) {
1020
+ implicitLocalFnInfoOf(L, decl, implicitNode);
1021
+ continue;
1022
+ }
1023
+ }
1024
+ // `const M = (Base: Ctor) => class extends Base {…}` — a
1025
+ // NON-generic mixin function binding (generic ones took the
1026
+ // branch above): no global exists — calls instantiate the class
1027
+ // inside per site (lower-mixins.ts); the statement lowering skips
1028
+ // by the same test.
1029
+ if (isMixinFnBinding(L, decl))
1030
+ continue;
1031
+ // `const Thing1 = Tagged(Derived)` — a mixin RESULT binding: the
1032
+ // binding provably holds that one call's class object forever
1033
+ // (const), so it registers like a class declaration
1034
+ // (classBySymbol) HERE — before any body lowers — and every
1035
+ // downstream path (new, statics, extends, instanceof, reads)
1036
+ // answers directly. No global exists; the statement lowering
1037
+ // skips by the same test. A poisoned instantiation reported its
1038
+ // own diagnostics — the binding registers nothing and use sites
1039
+ // fence per reference.
1040
+ if (ts.isIdentifier(decl.name) && decl.initializer !== undefined && isConst) {
1041
+ let init = decl.initializer;
1042
+ while (ts.isParenthesizedExpression(init))
1043
+ init = init.expression;
1044
+ if (ts.isCallExpression(init)) {
1045
+ const bindSym = L.checker.getSymbolAtLocation(decl.name);
1046
+ try {
1047
+ if (mixinResultBindingClassOf(L, bindSym))
1048
+ continue;
1049
+ }
1050
+ catch (e) {
1051
+ if (!(e instanceof PoisonError))
1052
+ throw e;
1053
+ continue;
1054
+ }
1055
+ }
1056
+ }
1057
+ // `const execFileAsync = promisify(execFile)` at file scope: no
1058
+ // global exists (the promisified function value never does) — the
1059
+ // symbol registers HERE, before any use site can lower, and the
1060
+ // statement lowering skips the declaration by the same check. A
1061
+ // bad promisify target reports here (recorded once; the statement
1062
+ // lowering poisons too, like every collectGlobals failure).
1063
+ if (ts.isIdentifier(decl.name) && decl.initializer && isPromisifyCall(L, decl.initializer)) {
1064
+ try {
1065
+ L.promisifiedExecFileDecl(decl.name, decl.initializer);
1066
+ }
1067
+ catch (e) {
1068
+ if (!(e instanceof PoisonError))
1069
+ throw e;
1070
+ }
1071
+ continue;
1072
+ }
1073
+ // `const inspect = require("util").inspect` at file scope: a
1074
+ // named import in const clothing (builtinImportOf resolves uses)
1075
+ // — no global storage; the statement lowering skips it by the
1076
+ // same test.
1077
+ if (builtinMemberRequireDecl(decl.name, decl.initializer))
1078
+ continue;
1079
+ // `const { createSign } = crypto` over a builtin NAMESPACE binding
1080
+ // at file scope: alias plumbing like the destructured-require form
1081
+ // — no storage (the statement lowering skips by the same test).
1082
+ if (builtinNamespaceDestructureModuleOf(L, decl) !== null)
1083
+ continue;
1084
+ // `const { NGHTTP2_CANCEL } = http2.constants` at file scope: a
1085
+ // destructure over the baked constants table — alias plumbing, no
1086
+ // global storage (the statement lowering skips it by the same
1087
+ // test).
1088
+ if (L.http2ConstantsDestructureDecl(decl.name, decl.initializer))
1089
+ continue;
1090
+ // `const Writable = stream.Writable` at file scope: a stream
1091
+ // class through the namespace binding — alias plumbing, no
1092
+ // global storage (the statement lowering skips it by the same
1093
+ // test).
1094
+ if (isConst && streamClassAliasDecl(L, decl.name, decl.initializer))
1095
+ continue;
1096
+ // `const process = globalThis.process` at file scope: a stdlib-
1097
+ // global snapshot — alias plumbing, no global storage (see
1098
+ // stdlibGlobalAliasDecl; the statement lowering skips it by the
1099
+ // same test).
1100
+ if (isConst && stdlibGlobalAliasDecl(L, decl.name, decl.initializer))
1101
+ continue;
1102
+ // Destructuring declarations register EVERY bound identifier (the
1103
+ // desugar in the init function assigns the pre-registered globals,
1104
+ // exactly like plain declarations).
1105
+ for (const nameNode of boundIdentifiersOf(decl.name)) {
1106
+ const diagsBefore = L.diags.length;
1107
+ try {
1108
+ // A JS file-scope evolving ARRAY (`const mustCallChecks = [];`
1109
+ // — test/common's exit-accounting ledger): the strict type
1110
+ // (any[]) has no mapping, but the VALUE is the DOM array the
1111
+ // literal builds (lowerArrayLiteral's JS fallback) — register
1112
+ // a checked-dynamic global so separately-declared function
1113
+ // bodies (runCallChecks, _mustCallInner) reach the SAME
1114
+ // storage instead of fencing per reference. Identifier
1115
+ // declarations only: a destructured array literal binds its
1116
+ // pieces, not the array.
1117
+ if (isJsSourceFile(sf) && !L.mapTypeOf(L.typeOf(nameNode)) &&
1118
+ ts.isIdentifier(decl.name) && nameNode === decl.name &&
1119
+ decl.initializer !== undefined && ts.isArrayLiteralExpression(decl.initializer)) {
1120
+ const symbol = L.checker.getSymbolAtLocation(nameNode);
1121
+ if (symbol) {
1122
+ const g = { id: `%g.${tag}${nameNode.text}`, name: nameNode.text, type: DYN, mutable: isLet };
1123
+ L.globalsBySymbol.set(symbol, g);
1124
+ L.globalsList.push(g);
1125
+ }
1126
+ continue;
1127
+ }
1128
+ // A JS file-scope OBJECT LITERAL (`const input = { foo: 'bar' }`
1129
+ // — the Node-suite trace-context idiom): JS object identity is
1130
+ // the literal's contract — the SAME object flows into untyped
1131
+ // callees, gets stamped by them, and is compared back against
1132
+ // the binding (assert.strictEqual(found, input)). A static
1133
+ // record global would re-box at every DOM crossing and lose
1134
+ // that identity, so the binding holds the DOM object itself.
1135
+ // Gated to literals that are PURE DATA WRITTEN INLINE — every
1136
+ // property a non-computed PropertyAssignment whose value is
1137
+ // dyn-representable (unmappable member types are the DOM
1138
+ // fallback's own case) — so shorthand aggregates (test/common's
1139
+ // export object: alias plumbing importers resolve THROUGH),
1140
+ // spreads, accessors, and literals holding unconvertible typed
1141
+ // values (a Map member) keep their static/alias stories.
1142
+ if (isJsSourceFile(sf) &&
1143
+ ts.isIdentifier(decl.name) && nameNode === decl.name &&
1144
+ decl.initializer !== undefined && ts.isObjectLiteralExpression(decl.initializer) &&
1145
+ decl.initializer.properties.every((p) => {
1146
+ if (!ts.isPropertyAssignment(p) || ts.isComputedPropertyName(p.name))
1147
+ return false;
1148
+ const mt = L.mapTypeOf(L.typeOf(p.initializer));
1149
+ return mt === null || mt.kind === "dyn" ||
1150
+ canConvertToDyn(mt, (id) => L.shapes.get(id), (id) => L.unions.get(id));
1151
+ })) {
1152
+ const symbol = L.checker.getSymbolAtLocation(nameNode);
1153
+ if (symbol && !L.globalsBySymbol.has(symbol)) {
1154
+ const g = { id: `%g.${tag}${nameNode.text}`, name: nameNode.text, type: DYN, mutable: isLet };
1155
+ L.globalsBySymbol.set(symbol, g);
1156
+ L.globalsList.push(g);
1157
+ }
1158
+ continue;
1159
+ }
1160
+ // A JS file-scope ANY-RESIDUE binding whose VALUE provably
1161
+ // lives in the DOM: no initializer (`let catchWarning;` —
1162
+ // test/common's warning ledger), a unit initializer (`let
1163
+ // localhostIPv4 = null;` — the lazy-cache idiom), or an
1164
+ // initializer whose own checker type is `any` (`const handler
1165
+ // = common.mustCall(cb)` — a dyn call result). Register a
1166
+ // checked-dynamic global (the evolving-array precedent above)
1167
+ // so separately-declared functions and getters reach the SAME
1168
+ // storage instead of fencing per reference. Typed-but-
1169
+ // unmappable initializers (`new Set(...)` — a real Set the
1170
+ // decl keeps as an %init local) stay OUT: forcing those into
1171
+ // the DOM would trade their working typed representation for
1172
+ // fences.
1173
+ if (isJsSourceFile(sf) && !L.mapTypeOf(L.typeOf(nameNode)) &&
1174
+ ts.isIdentifier(decl.name) && nameNode === decl.name &&
1175
+ jsDynHoldableInitializer(L, decl.initializer) &&
1176
+ dynFallbackType(L, nameNode, L.typeOf(nameNode))?.kind === "dyn") {
1177
+ const symbol = L.checker.getSymbolAtLocation(nameNode);
1178
+ if (symbol && !L.globalsBySymbol.has(symbol)) {
1179
+ const g = { id: `%g.${tag}${nsPrefix}${nameNode.text}`, name: nameNode.text, type: DYN, mutable: isLet };
1180
+ L.globalsBySymbol.set(symbol, g);
1181
+ L.globalsList.push(g);
1182
+ // Mutable dyn globals hold the DOM undefined from module
1183
+ // entry (the main path's rule below): a closure called
1184
+ // above the declaration reads undefined instead of
1185
+ // faulting on NULL.
1186
+ if (g.mutable)
1187
+ noteVarGlobalEntryInit(L, sf, g);
1188
+ }
1189
+ continue;
1190
+ }
1191
+ // JS declarations whose STRICT type has no mapping register no
1192
+ // global at all: the declaration lowers as an %init-body LOCAL
1193
+ // whose type adopts the initializer's (lowerVarDecl's JS
1194
+ // rule — `new Set([...tokens])` becomes a real Set<string>),
1195
+ // and closures created in the init body capture it normally.
1196
+ // References from separately-declared functions cascade to
1197
+ // their own per-site runtime fences.
1198
+ if (isJsSourceFile(sf) && !L.mapTypeOf(L.typeOf(nameNode)))
1199
+ continue;
1200
+ // `var p1 = import("./m")` at file scope: the global holds the
1201
+ // island promise/handle — the import expression's only
1202
+ // production — whatever the checker's namespace type mapped to
1203
+ // (lowerVarDecl's rule at module scope; the init body assigns
1204
+ // it).
1205
+ const handleT = L.dynamic && ts.isIdentifier(decl.name) && nameNode === decl.name
1206
+ ? (importCallHandleType(decl.initializer) ??
1207
+ // An unchecked-overload call result stores the handle,
1208
+ // exactly the local rule (uncheckedOverloadHandleCall).
1209
+ (uncheckedOverloadHandleCall(L, decl.initializer) ? JSVAL : null))
1210
+ : null;
1211
+ let type = handleT ?? L.irTypeOf(nameNode);
1212
+ // A file-scope PATTERN over an ISLAND-bound source (`export
1213
+ // let { toString } = 1;` — the engine reads the wrapper's
1214
+ // prototype member): the bound value follows the island
1215
+ // property-read rule, so the global slot must too — declared
1216
+ // primitives exit eagerly to their static type, everything
1217
+ // else stores the HANDLE (a func-typed slot could never take
1218
+ // the engine value the desugar assigns).
1219
+ if (L.dynamic && !ts.isIdentifier(decl.name) && decl.initializer !== undefined) {
1220
+ const srcT = L.mapTypeOf(L.typeOf(decl.initializer));
1221
+ const island = srcT !== null &&
1222
+ (srcT.kind === "jsval" || srcT.kind === "f64" || srcT.kind === "bool" ||
1223
+ srcT.kind === "string" || isUnitType(srcT));
1224
+ if (island && type.kind !== "f64" && type.kind !== "bool" && type.kind !== "string") {
1225
+ type = JSVAL;
1226
+ }
1227
+ }
1228
+ // File-scope unit-only bindings (`var x: undefined`, `const
1229
+ // y: void = undefined`) ride the unit-only union, exactly
1230
+ // like function-scope locals (lowerVarDecl's rule).
1231
+ if (type.kind === "void" && isUnitOnlyTsType(L.typeOf(nameNode))) {
1232
+ type = unitOnlyUnion(L.unions);
1233
+ }
1234
+ if (type.kind === "void")
1235
+ L.badType(nameNode, L.typeOf(nameNode));
1236
+ // A JS file-scope FUNCTION binding whose unannotated return
1237
+ // infers a record (`const wrapped = function () { return
1238
+ // expectedResult; }`): the closure VALUE lowers with a dyn
1239
+ // return (declaredReturnType's record twin — JS object
1240
+ // literals are DOM values, and a record return would copy
1241
+ // identity away), so the binding's slot must agree or an
1242
+ // adapter would re-copy at the assignment.
1243
+ if (isJsSourceFile(sf) &&
1244
+ type.kind === "func" &&
1245
+ type.ret.kind === "record" &&
1246
+ decl.initializer !== undefined &&
1247
+ (ts.isFunctionExpression(decl.initializer) || ts.isArrowFunction(decl.initializer)) &&
1248
+ decl.initializer.type === undefined &&
1249
+ type.params.every((pt) => pt.kind === "dyn")) {
1250
+ type = { ...type, ret: DYN };
1251
+ }
1252
+ // A file-scope JS `let x = {}`: the same checked-dynamic rule
1253
+ // lowerVarDecl applies to locals — TS's empty-object-literal
1254
+ // type admits ANY later non-nullish assignment, so the static
1255
+ // empty struct cannot hold the binding's future.
1256
+ if (isLet && type.kind === "record" && isJsSourceFile(sf)) {
1257
+ const shape = L.shapes.get(type.shapeId);
1258
+ if (shape && shape.fields.length === 0 && !shape.indexValue && !shape.tuple)
1259
+ type = DYN;
1260
+ }
1261
+ const symbol = L.checker.getSymbolAtLocation(nameNode);
1262
+ // Merged `var` redeclarations (`var y = 1; ...; var y = 2;` —
1263
+ // one symbol) register exactly one global; later declarations
1264
+ // are plain assignments.
1265
+ if (!symbol || L.globalsBySymbol.has(symbol))
1266
+ continue;
1267
+ const g = {
1268
+ id: `%g.${tag}${nsPrefix}${nameNode.text}`,
1269
+ name: nameNode.text,
1270
+ type,
1271
+ mutable: isLet,
1272
+ };
1273
+ L.globalsBySymbol.set(symbol, g);
1274
+ L.globalsList.push(g);
1275
+ // Mutable checked-dynamic LET globals ride the same entry
1276
+ // init: a closure called above the declaration statement
1277
+ // reads the DOM undefined instead of faulting on NULL — the
1278
+ // dyn face of let's documented pre-declaration window (Node
1279
+ // throws the TDZ ReferenceError there).
1280
+ if (isVarDeclared(decl) || (g.type.kind === "dyn" && g.mutable)) {
1281
+ noteVarGlobalEntryInit(L, sf, g);
1282
+ }
1283
+ }
1284
+ catch (e) {
1285
+ if (!(e instanceof PoisonError))
1286
+ throw e;
1287
+ // diagnostic already recorded; lowering the statement poisons
1288
+ // too — and in a JS file BOTH defer to the statement's
1289
+ // runtime fence (deferJsCollectionDiags).
1290
+ deferJsCollectionDiags(L, sf, diagsBefore);
1291
+ }
1292
+ }
1293
+ }
1294
+ }
1295
+ }
1296
+ /** Registers the entry-init note for a `var` module global: JS hoists
1297
+ * module vars to `undefined` at module entry, so an undefined-armed slot
1298
+ * must hold the interned undefined arm BEFORE the body runs (a function
1299
+ * called above the declaration statement may read it — lowerFileInit
1300
+ * emits the assigns right after the run-once guard). Checked-dynamic
1301
+ * slots hold the DOM undefined the same way (a NULL dyn is a trap, not
1302
+ * a value). Other types need nothing: tsc's flow analysis rejects their
1303
+ * direct early reads, and closure reads share let's documented
1304
+ * zero/NULL divergence window. */
1305
+ function noteVarGlobalEntryInit(L, sf, g) {
1306
+ // 'any' globals need the entry init exactly like undefined-armed
1307
+ // unions: tsc never guards `any` reads, so `var x: any;` is readable
1308
+ // before any assignment and its slot must hold its world's undefined
1309
+ // — the ENGINE's for jsval (--dynamic), the DOM's for dyn (static) —
1310
+ // rather than a C-level NULL (an op or validated exit on NULL is
1311
+ // memory-unsafe, not a TypeError).
1312
+ if (g.type.kind !== "union" && g.type.kind !== "jsval" && g.type.kind !== "dyn")
1313
+ return;
1314
+ const inits = L.varGlobalEntryInits.get(sf) ?? [];
1315
+ inits.push(g);
1316
+ L.varGlobalEntryInits.set(sf, inits);
1317
+ }
1318
+ /** The nested half of module-scope `var` hoisting: a `var` inside a
1319
+ * top-level BLOCK (an if arm, a loop body, a try) is module-scoped in
1320
+ * JS — other functions in the file read the same binding — so it must
1321
+ * be a module global like its top-level siblings. Walks a top-level
1322
+ * statement's insides for var-flagged declaration lists (statement and
1323
+ * for/for-in/for-of initializer positions), never descending into
1324
+ * function-likes, classes, or static blocks (each hoists to its own
1325
+ * scope). Registration mirrors the main path's identifier story:
1326
+ * JS-unmappable types register nothing (per-site fences own the
1327
+ * references), everything else gets a mutable global. */
1328
+ function collectNestedVarGlobals(L, sf, stmt, tag) {
1329
+ const lists = [];
1330
+ // Iterative walk (walkPreorder): this sweep sees every non-var top-level
1331
+ // statement whole — the binderBinaryExpressionStress bare ~6500-term
1332
+ // chain sat within ~50 terms of overflowing the recursive form.
1333
+ ts.walkPreorder(stmt, (node) => {
1334
+ if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) ||
1335
+ ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) ||
1336
+ ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node) ||
1337
+ ts.isClassStaticBlockDeclaration(node) || ts.isClassDeclaration(node) || ts.isClassExpression(node) ||
1338
+ ts.isModuleDeclaration(node)) {
1339
+ return "skip";
1340
+ }
1341
+ if (ts.isVariableDeclarationList(node) && (node.flags & ts.NodeFlags.BlockScoped) === 0) {
1342
+ lists.push(node);
1343
+ return "skip"; // initializer expressions hold no further statements outside function bodies
1344
+ }
1345
+ return undefined;
1346
+ });
1347
+ for (const list of lists) {
1348
+ for (const decl of list.declarations) {
1349
+ for (const nameNode of boundIdentifiersOf(decl.name)) {
1350
+ const diagsBefore = L.diags.length;
1351
+ try {
1352
+ if (isJsSourceFile(sf) && !L.mapTypeOf(L.typeOf(nameNode)))
1353
+ continue;
1354
+ let type = L.irTypeOf(nameNode);
1355
+ // Nested-var unit-only bindings take the unit-only union too
1356
+ // (the top-level registration's rule).
1357
+ if (type.kind === "void" && isUnitOnlyTsType(L.typeOf(nameNode))) {
1358
+ type = unitOnlyUnion(L.unions);
1359
+ }
1360
+ if (type.kind === "void")
1361
+ L.badType(nameNode, L.typeOf(nameNode));
1362
+ if (type.kind === "record" && isJsSourceFile(sf)) {
1363
+ const shape = L.shapes.get(type.shapeId);
1364
+ if (shape && shape.fields.length === 0 && !shape.indexValue && !shape.tuple)
1365
+ type = DYN;
1366
+ }
1367
+ const symbol = L.checker.getSymbolAtLocation(nameNode);
1368
+ if (!symbol || L.globalsBySymbol.has(symbol))
1369
+ continue;
1370
+ const g = { id: `%g.${tag}${nameNode.text}`, name: nameNode.text, type, mutable: true };
1371
+ L.globalsBySymbol.set(symbol, g);
1372
+ L.globalsList.push(g);
1373
+ noteVarGlobalEntryInit(L, sf, g);
1374
+ }
1375
+ catch (e) {
1376
+ if (!(e instanceof PoisonError))
1377
+ throw e;
1378
+ deferJsCollectionDiags(L, sf, diagsBefore);
1379
+ }
1380
+ }
1381
+ }
1382
+ }
1383
+ }
1384
+ /** The checker's symbol for a module's default export — the symbol a
1385
+ * default import of this module aliases to (getAliasedSymbol), so keying
1386
+ * the default's global by it makes every importer resolve to the same
1387
+ * storage through the ordinary globalOf path. Null for modules without
1388
+ * a default export (or non-module files). */
1389
+ export function defaultExportSymbolOf(L, sf) {
1390
+ const moduleSym = L.checker.getSymbolAtLocation(sf);
1391
+ return moduleSym?.getExports().get(ts.InternalSymbolName.Default) ?? null;
1392
+ }
1393
+ /** The declaration symbol of a top-level function/class declaration,
1394
+ * covering the one legal NAMELESS form: `export default function () {}` /
1395
+ * `export default class {}`. A named declaration answers through its
1396
+ * name node; the anonymous default IS the module's `default` export
1397
+ * symbol (the binder declares it there), so collection, reachability,
1398
+ * and body lowering all key that one identity — which is also what a
1399
+ * default import's alias chain resolves to. */
1400
+ export function declSymbolOf(L, decl) {
1401
+ if (decl.name)
1402
+ return L.checker.getSymbolAtLocation(decl.name);
1403
+ const isDefault = ts.canHaveModifiers(decl) &&
1404
+ ts.getModifiers(decl)?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword) === true;
1405
+ if (!isDefault)
1406
+ return undefined;
1407
+ const sym = defaultExportSymbolOf(L, decl.getSourceFile());
1408
+ return sym ?? undefined;
1409
+ }
1410
+ /** `export default <expr>` in a module's init body: the first (and only)
1411
+ * assignment of the pre-registered `default` global — evaluated at the
1412
+ * statement's source position, exactly where Node runs it (Node's
1413
+ * expression defaults snapshot exactly once, here). ENTITY-NAME defaults
1414
+ * are alias plumbing with no storage of their own: the statement lowers
1415
+ * to nothing — collection either accepted the alias (const/function/
1416
+ * class targets, where importers' alias-resolved reads equal the
1417
+ * snapshot) or already fenced it (mutable targets), and re-lowering
1418
+ * would double-report. A NON-alias default whose type had no
1419
+ * registration (collection reported the blocker) re-lowers the
1420
+ * expression so the expression's OWN diagnostic wins when it has one,
1421
+ * then poisons. */
1422
+ export function lowerDefaultExport(L, stmt) {
1423
+ const symbol = defaultExportSymbolOf(L, stmt.getSourceFile());
1424
+ if (symbol && symbol.flags & ts.SymbolFlags.Alias)
1425
+ return null;
1426
+ const g = symbol ? L.globalsBySymbol.get(symbol) : undefined;
1427
+ if (!g) {
1428
+ // A provably-throwing decorated class expression registered no
1429
+ // storage (the registration pass skipped it): the statement IS the
1430
+ // throw — lowerClassExpression answers the ambient undefRead.
1431
+ {
1432
+ let inner = stmt.expression;
1433
+ while (ts.isParenthesizedExpression(inner))
1434
+ inner = inner.expression;
1435
+ if (ts.isClassExpression(inner) && !isJsSourceFile(stmt.getSourceFile()) &&
1436
+ inner.typeParameters === undefined &&
1437
+ (decoratorNodesOf(inner).length > 0 || inner.members.some((m) => decoratorNodesOf(m).length > 0)) &&
1438
+ guaranteedDecorationThrow(L, inner) !== null) {
1439
+ return { kind: "exprStmt", expr: L.lowerExpr(stmt.expression), loc: locOf(stmt) };
1440
+ }
1441
+ }
1442
+ L.lowerExpr(stmt.expression);
1443
+ L.badType(stmt.expression, L.typeOf(stmt.expression));
1444
+ }
1445
+ const value = L.lowerExprExpecting(stmt.expression, g.type);
1446
+ return { kind: "assign", localId: g.id, value, loc: locOf(stmt) };
1447
+ }
1448
+ /** One file's top-level statements as its init function — RUN-ONCE like a
1449
+ * Node module body: non-entry inits open with a guard flag test (already
1450
+ * ran → return) and set the flag before anything else, so every caller —
1451
+ * hoisted import headers, inline require statements, diamonds, cache-hit
1452
+ * re-requires — gets Node's module-cache semantics from the same call.
1453
+ * After the guard comes the file's HOISTED module-edge header: one
1454
+ * guarded %init call per imported user module and the island loads for
1455
+ * npm imports, in import order (Node evaluates every imported module
1456
+ * before the importer's body — depth-first postorder falls out of the
1457
+ * nesting). CommonJS requires are NOT in the header: Node runs them at
1458
+ * their statements, so the statement lowering emits their %init calls
1459
+ * inline in the body. JSON-import bindings (pure data) load next; then
1460
+ * the body. Top-level var statements assign the pre-registered globals
1461
+ * instead of declaring locals. */
1462
+ export function lowerFileInit(L, sf, stmts, name) {
1463
+ L.fnStack.push(newFnCtx(false, null, null, VOID));
1464
+ try {
1465
+ const loc0 = { file: sf.fileName, start: 0, end: 0 };
1466
+ const header = [];
1467
+ const guardId = L.moduleGuardOf.get(sf);
1468
+ if (guardId !== undefined) {
1469
+ header.push({
1470
+ kind: "if",
1471
+ cond: { kind: "varRef", localId: guardId, type: BOOL, loc: loc0 },
1472
+ then: [{ kind: "return", value: null, loc: loc0 }],
1473
+ else_: null,
1474
+ loc: loc0,
1475
+ });
1476
+ // The flag sets BEFORE the body runs — Node marks the cache entry
1477
+ // before evaluating too (moot while cycles are fenced, but the
1478
+ // honest order costs nothing).
1479
+ header.push({
1480
+ kind: "assign",
1481
+ localId: guardId,
1482
+ value: { kind: "boolLit", value: true, type: BOOL, loc: loc0 },
1483
+ loc: loc0,
1484
+ });
1485
+ }
1486
+ for (const { stmt, dep } of orderedImportsOf(L.program, sf)) {
1487
+ const npm = L.npmInitActions.get(sf)?.get(stmt);
1488
+ if (npm !== undefined && npm.length > 0) {
1489
+ header.push(...npm);
1490
+ }
1491
+ else if (dep !== null && dep !== sf) {
1492
+ // dep === sf is the self-import (the package self-name resolving
1493
+ // to the importing module): Node answers it from the module
1494
+ // cache mid-evaluation — no re-evaluation, nothing to call (and
1495
+ // the ENTRY has no run-once guard, so a self-call would recurse).
1496
+ const depInit = L.initNameOf.get(dep);
1497
+ if (depInit !== undefined) {
1498
+ const loc = locOf(stmt);
1499
+ header.push({
1500
+ kind: "exprStmt",
1501
+ expr: { kind: "call", callee: depInit, args: [], type: VOID, loc },
1502
+ loc,
1503
+ });
1504
+ }
1505
+ }
1506
+ }
1507
+ // Module-scope `var` hoisting: undefined-armed var globals hold the
1508
+ // interned undefined from module entry (before any body statement —
1509
+ // a function called above the declaration reads `undefined`, exactly
1510
+ // Node); checked-dynamic var globals hold the DOM undefined the same
1511
+ // way. After the guard: a cache-hit revisit must not reset them.
1512
+ for (const g of L.varGlobalEntryInits.get(sf) ?? []) {
1513
+ const wrapped = g.type.kind === "dyn" ? dynUndefinedExpr(loc0) : L.unassignedSlotInit(g.type, loc0);
1514
+ if (wrapped) {
1515
+ header.push({ kind: "assign", localId: g.id, value: wrapped, loc: loc0 });
1516
+ }
1517
+ }
1518
+ const prelude = L.jsonInitActions.get(sf) ?? [];
1519
+ // Class STATIC readonly fields, static BLOCKS, and class DECORATORS
1520
+ // run at their class statement's source position (splitFiles hoisted
1521
+ // the declarations out of `stmts`, so the statements merge back in
1522
+ // by position) — exactly when JS evaluates decorators, static
1523
+ // initializers, and blocks, so code reading an earlier module
1524
+ // binding sees its assigned value.
1525
+ const statics = [...L.classes.values()]
1526
+ .filter((c) =>
1527
+ // Class DECLARATIONS only: expression classes run their static
1528
+ // inits through pendingClassExprInits (below) at the statement
1529
+ // that evaluates them. MIXIN instantiations join by their CALL
1530
+ // SITE's position instead (below) — their inner class node may
1531
+ // even live in another file.
1532
+ c.decl && ts.isClassDeclaration(c.decl) && !c.mixinInstance && c.decl.getSourceFile() === sf &&
1533
+ (c.staticFields.length > 0 || (c.staticBlocks?.length ?? 0) > 0 || c.classDecorators !== undefined))
1534
+ .map((c) => ({ pos: c.decl.getStart(), info: c }));
1535
+ // Statics-bearing MIXIN instantiations whose call evaluates in THIS
1536
+ // file: their declaration-time code runs when the call does — the
1537
+ // top-level statement lower-mixins recorded. Insertion order is
1538
+ // demand order (a nested mixin's base instantiates first, a
1539
+ // heritage base before its derived class), so the stable sort keeps
1540
+ // JS's evaluation order at equal positions.
1541
+ for (const c of L.classes.values()) {
1542
+ const ms = c.mixinInstance?.statics;
1543
+ if (ms && ms.sf === sf)
1544
+ statics.push({ pos: ms.pos, info: c });
1545
+ }
1546
+ statics.sort((a, b) => a.pos - b.pos);
1547
+ const body = [...header, ...prelude];
1548
+ let at = 0;
1549
+ for (const stmt of stmts) {
1550
+ // `<=`: a mixin instantiation's position IS its statement's start
1551
+ // — its statics run mid-statement in JS (the call), before
1552
+ // anything the statement's lowering emits. Hoisted class
1553
+ // declarations never share a kept statement's start, so `<=`
1554
+ // changes nothing for them.
1555
+ while (at < statics.length && statics[at].pos <= stmt.getStart()) {
1556
+ body.push(...L.lowerStaticFieldInits(statics[at].info));
1557
+ at++;
1558
+ }
1559
+ const stmtIr = L.lowerStmts([stmt]);
1560
+ // Class EXPRESSIONS inside this statement queued their static
1561
+ // inits while it lowered: they land immediately before it — JS's
1562
+ // order for the supported whole-initializer positions.
1563
+ body.push(...L.pendingClassExprInits.splice(0), ...stmtIr);
1564
+ }
1565
+ while (at < statics.length) {
1566
+ body.push(...L.lowerStaticFieldInits(statics[at].info));
1567
+ at++;
1568
+ }
1569
+ const loc = { file: sf.fileName, start: 0, end: 0 };
1570
+ return { name, params: [], returnType: VOID, locals: L.ctx.locals, body, loc };
1571
+ }
1572
+ finally {
1573
+ L.fnStack.pop();
1574
+ }
1575
+ }
1576
+ /** %main is just the entry's %init call: Node's whole module evaluation
1577
+ * order lives in the init functions themselves — each file's hoisted
1578
+ * import header runs its dependencies first (depth-first postorder by
1579
+ * nesting), CommonJS require statements call dependency inits inline in
1580
+ * their bodies, and the run-once guards turn every revisit (diamonds,
1581
+ * re-imports, re-requires) into a cache hit. */
1582
+ export function buildMain(L) {
1583
+ const loc = { file: L.entry.fileName, start: 0, end: 0 };
1584
+ const entryInit = L.initNameOf.get(L.entry);
1585
+ const body = entryInit !== undefined
1586
+ ? [{ kind: "exprStmt", expr: { kind: "call", callee: entryInit, args: [], type: VOID, loc }, loc }]
1587
+ : [];
1588
+ // Node's startup refusal (a resolution the graph carries that Node
1589
+ // rejects — preflight's Node-order resolution walk — or the module-
1590
+ // LINK SyntaxError of a named import of a CommonJS export its lexer
1591
+ // cannot detect — cjsNamedImportLinkCheck): the graph is refused
1592
+ // before ANY module evaluates, so %main opens with exactly that throw
1593
+ // (message and error class both Node's) and the entry init below it
1594
+ // never runs. The init still lowers — the program must otherwise
1595
+ // compile, exactly like Node parses every module before its
1596
+ // fetch/instantiate phases refuse the graph.
1597
+ if (L.startupCrash !== null) {
1598
+ const crashLoc = L.startupCrash.loc;
1599
+ body.unshift({
1600
+ kind: "throw",
1601
+ value: {
1602
+ kind: "libCall",
1603
+ fn: "error.new",
1604
+ args: [{ kind: "strLit", value: L.startupCrash.message, type: STRING, loc: crashLoc }],
1605
+ type: { kind: "object", className: L.startupCrash.className },
1606
+ loc: crashLoc,
1607
+ },
1608
+ loc: crashLoc,
1609
+ });
1610
+ }
1611
+ return { name: ENTRY_NAME, params: [], returnType: VOID, locals: [], body, loc };
1612
+ }
1613
+ //# sourceMappingURL=lower-modules.js.map