@scriptc/compiler 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +5947 -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 +10087 -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 +64 -0
  61. package/dist/coverage/report.js +226 -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 +6281 -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 +483 -0
  94. package/dist/frontend/lowering/lower-containers.js +6281 -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 +8099 -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 +1069 -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 +6256 -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 +2172 -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 +60 -0
  166. package/dist/frontend/shared.js +232 -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 +424 -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 +4884 -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,2172 @@
1
+ /* THE program lifecycle: load → preflight — tsconfig adoption, the tsc
2
+ * gate with its project-world second chance, the import/export form
3
+ * fences, CommonJS require discipline, and the module evaluation order —
4
+ * on the native TypeScript compiler (one spawned tsgo server behind
5
+ * Ts7Host, through the ts7/ adapter) with scriptc's OWN resolver
6
+ * (resolve.ts) standing in for ts.resolveModuleName /
7
+ * ts.resolveTypeReferenceDirective, which typescript@7.0.2 no longer
8
+ * ships.
9
+ *
10
+ * HISTORY. This began as a deliberately MECHANICAL PORT of the
11
+ * typescript@5.9.3 checkPreflight (the adapter's design: swap the ts
12
+ * import, keep every spelling), shipped alongside it for phases 2-3 with
13
+ * the order-parity suite holding both lanes to identical diagnostics and
14
+ * module order over the whole corpus. The phase-4 flip deleted the 5.9.3
15
+ * pipeline; its recorded verdicts live on as the canary baselines
16
+ * (test/ts7/baselines/order-parity.json), and the rationale prose the old
17
+ * program.ts carried was folded in here where behavior demands it.
18
+ *
19
+ * Differences from the retired 5.9.3 lane, each deliberate and each
20
+ * pinned by the canary baselines:
21
+ * - Module resolution is resolve.ts (answer-parity proven by its suite).
22
+ * - Diagnostics carry pos/end and flat text + messageChain; toPassthrough
23
+ * renders through the adapter's flattenDiagnosticMessageText, whose
24
+ * output shape matches 5.9.3's flattener byte for byte.
25
+ * - Finding 5 (tsgo models CommonJS strictly): TS2309 joins the JS-file
26
+ * relaxation here — 5.9.3 never fires it on JS CJS files, tsgo does on
27
+ * the table-then-member pattern. The EXPORT-IDENTITY fences do not live
28
+ * in tsc codes at all: cjsExportDiscardReason (lowering) names every
29
+ * genuinely discarded export, so relaxing the checker's stricter
30
+ * modeling loses nothing.
31
+ * - isNodeEsmFile: 5.9.3 under bundler sets impliedNodeFormat ONLY for
32
+ * .mjs/.cjs-family files (probed) and falls back to syntactic module
33
+ * detection for .js/.ts; 7's client SourceFile has no impliedNodeFormat,
34
+ * so this lane spells exactly that observed behavior: extensions first,
35
+ * then externalModuleIndicator.
36
+ * - Malformed tsconfig.json: tsgo's parseConfigFile swallows JSONC syntax
37
+ * errors (recovers to empty options) where 5.9.3 reported a passthrough
38
+ * diagnostic, so this lane validates the config's JSONC syntax itself
39
+ * (jsoncSyntaxError) and fails preflight like 5.9.3 does — with
40
+ * JSON.parse's wording rather than tsc's, the one message-text delta on
41
+ * that path (no snapshot pins it). */
42
+ import { builtinModules } from "node:module";
43
+ import { dirname, resolve } from "node:path";
44
+ import * as ts from "./ts7/adapter.js";
45
+ import { strictNullChecksFloorDiag, tscPassthroughDiag, unsupportedDiag, } from "../diagnostics/diagnostic.js";
46
+ import { isNodeModulesPath, nearestPkgJsonPath, projectDtsRuntimeSibling, resolveBareModule, resolveProjectImport, resolveRelativeModule, resolveTypeDirective, setProjectRealm } from "./resolve.js";
47
+ import { probeNodeImportRefusal, probeNodeRequireRefusal } from "./npm.js";
48
+ import { isNpmStaticPackage, npmStaticActive, npmStaticFsShadow, npmStaticPackageOfPath, reportNpmStaticOffender, setNpmStaticPackages } from "./npm-static.js";
49
+ import { provenanceEntryFor, provenancePaths } from "./provenance-registry.js";
50
+ import { cjsLexerVisibleNames } from "./cjs-lexer.js";
51
+ import { ADOPTED_OPTIONS, ambientDtsPath, builtinDefaultImportModule, canonicalBuiltinModule, clearWorkspacePackages, fallbackDtsPath, isJsSourceFileName, isNodeTypesPath, JS_ANY_OPERATOR_CODES, JS_RELAXED_TSC_CODES, overridesDtsPath, registerWorkspacePackage, SUPPORTED_NODE_MODULES, } from "./shared.js";
52
+ const BASE_OPTIONS = {
53
+ strict: true,
54
+ };
55
+ const FORCED_OPTIONS = {
56
+ target: ts.ScriptTarget.ESNext,
57
+ module: ts.ModuleKind.ESNext,
58
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
59
+ lib: ["lib.es2025.d.ts"],
60
+ types: [],
61
+ allowImportingTsExtensions: true,
62
+ allowJs: true,
63
+ checkJs: true,
64
+ resolveJsonModule: true,
65
+ noEmit: true,
66
+ };
67
+ /** JSONC syntax validation for a tsconfig text: tsgo's parseConfigFile
68
+ * RECOVERS silently over syntax errors (probed: a hard-broken file answers
69
+ * empty options, no diagnostic), where 5.9.3's readConfigFile reported the
70
+ * first parse error — a preflight-visible difference (broken config: fail
71
+ * loudly, never adopt silently). tsconfig's grammar is JSON plus comments
72
+ * and trailing commas, so stripping exactly those and handing the rest to
73
+ * JSON.parse decides validity without either TypeScript's parser. The
74
+ * MESSAGE is JSON.parse's, not 5.9.3's ("'}' expected.") — the one
75
+ * remaining wording delta on this path, unpinned by any snapshot. */
76
+ function jsoncSyntaxError(text) {
77
+ let out = "";
78
+ for (let i = 0; i < text.length; i++) {
79
+ const ch = text[i];
80
+ if (ch === '"') {
81
+ const from = i;
82
+ i++;
83
+ while (i < text.length && text[i] !== '"')
84
+ i += text[i] === "\\" ? 2 : 1;
85
+ out += text.slice(from, i + 1);
86
+ continue;
87
+ }
88
+ if (ch === "/" && text[i + 1] === "/") {
89
+ while (i < text.length && text[i] !== "\n")
90
+ i++;
91
+ out += "\n";
92
+ continue;
93
+ }
94
+ if (ch === "/" && text[i + 1] === "*") {
95
+ const close = text.indexOf("*/", i + 2);
96
+ i = close < 0 ? text.length : close + 1;
97
+ continue;
98
+ }
99
+ out += ch;
100
+ }
101
+ // Trailing commas: `,` followed only by whitespace before } or ].
102
+ out = out.replace(/,(\s*[}\]])/g, "$1");
103
+ if (out.trim() === "")
104
+ return null; // an empty config file is legal
105
+ try {
106
+ JSON.parse(out);
107
+ return null;
108
+ }
109
+ catch (e) {
110
+ return e instanceof Error ? e.message : String(e);
111
+ }
112
+ }
113
+ /** The project's tsconfig adoption in the 7 world: tsgo's own config parser
114
+ * (extends chains resolved server-side), the ADOPTED_OPTIONS subset taken,
115
+ * the rest forced, the strictNullChecks floor enforced. */
116
+ function adoptProjectConfig7(host, entryPath) {
117
+ const configFile = ts.findConfigFile(dirname(entryPath), ts.sys.fileExists) ?? null;
118
+ if (!configFile) {
119
+ return { configFile, options: { ...BASE_OPTIONS, ...FORCED_OPTIONS }, diags: [] };
120
+ }
121
+ const diags = [];
122
+ const syntaxError = jsoncSyntaxError(ts.sys.readFile(configFile) ?? "");
123
+ if (syntaxError !== null) {
124
+ diags.push(tscPassthroughDiag(syntaxError, { file: configFile, start: 0, end: 0 }));
125
+ return { configFile, options: { ...BASE_OPTIONS, ...FORCED_OPTIONS }, diags };
126
+ }
127
+ const parsed = host.parseConfigFile(configFile);
128
+ const adopted = {};
129
+ for (const key of ADOPTED_OPTIONS) {
130
+ const value = parsed.options[key];
131
+ if (value !== undefined)
132
+ adopted[key] = value;
133
+ }
134
+ const nullChecks = adopted["strictNullChecks"] ?? adopted["strict"] ?? false;
135
+ if (nullChecks !== true) {
136
+ diags.push(strictNullChecksFloorDiag(configFile));
137
+ adopted["strictNullChecks"] = true;
138
+ }
139
+ return { configFile, options: { ...BASE_OPTIONS, ...adopted, ...FORCED_OPTIONS }, diags };
140
+ }
141
+ /** The target project's @types/node, resolved with the OWN resolver's
142
+ * type-directive lookup anchored at the ENTRY (typeRoots-free — exactly the
143
+ * secondary lookup 5.9.3 ran with typeRoots emptied). */
144
+ function resolveNodeTypes7(entryPath) {
145
+ const file = resolveTypeDirective("node", entryPath);
146
+ return file !== null && isNodeTypesPath(file) ? file : null;
147
+ }
148
+ function loadProgram7(host, entryPath) {
149
+ const config = adoptProjectConfig7(host, entryPath);
150
+ const nodeTypes = config.configFile ? resolveNodeTypes7(entryPath) : null;
151
+ // skipLibCheck is FORCED with @types/node in the program: checking a
152
+ // third-party lib's internals against OUR lib choice (es2025, no DOM) is
153
+ // not scriptc's fence and drowns real diagnostics in hundreds of
154
+ // .d.ts-internal errors. Fence discipline never depended on it: the
155
+ // lowerer checks provenance and forms at every use site.
156
+ let options = nodeTypes ? { ...config.options, skipLibCheck: true } : { ...config.options };
157
+ // --npm-static: opted-in packages' shipped JS must be TYPE-INCLUDED (not
158
+ // just resolved) — without maxNodeModuleJsDepth, node_modules JS types as
159
+ // an implicit-any module (TS7016) and nothing infers. Only flagged
160
+ // compiles pay this; flagless builds keep the exact historical options.
161
+ if (npmStaticActive())
162
+ options.maxNodeModuleJsDepth = 4;
163
+ // --provenance-sources: the registered entries become tsconfig "paths"
164
+ // so tsgo's OWN resolution of the bare specifiers lands on the same
165
+ // source files the preflight resolver answers — the checker types the
166
+ // driver against the package's real TypeScript, not its shipped .d.ts.
167
+ const paths = provenancePaths();
168
+ if (paths !== null)
169
+ options = { ...options, paths };
170
+ const coreRoots = [entryPath, ambientDtsPath(), nodeTypes ?? fallbackDtsPath()];
171
+ const program = ts.createProgram([...coreRoots, overridesDtsPath()], options, host);
172
+ const entry = program.getSourceFile(entryPath);
173
+ if (!entry)
174
+ throw new Error(`could not load ${entryPath}`);
175
+ let projectWorld = null;
176
+ return {
177
+ program,
178
+ entry,
179
+ moduleOrder: [],
180
+ configDiags: config.diags,
181
+ projectWorld: () => (projectWorld ??= ts.createProgram(coreRoots, options, host)),
182
+ disposeAll: () => {
183
+ projectWorld?.dispose();
184
+ program.dispose();
185
+ },
186
+ };
187
+ }
188
+ /** The pipeline surface: ONE tsgo program serves preflight AND the
189
+ * lowering. The caller runs checkPreflight, hands program/entry/
190
+ * moduleOrder straight to lowerToIr, and MUST dispose() when done — the
191
+ * spawned tsgo server must not outlive the compile. */
192
+ export function loadProgram(entryPath, opts) {
193
+ // Absolute from the start: tsgo's world is absolute-path-keyed (the CLI
194
+ // resolves before calling; this covers direct API callers too).
195
+ entryPath = resolve(entryPath);
196
+ setNpmStaticPackages(opts?.npmStatic ?? []);
197
+ // Workspace-package registrations reset per load (same discipline as the
198
+ // npm-static set), then the opted-in names are probed UP FRONT: a
199
+ // workspace-linked opt-in resolves to files whose realpaths carry no
200
+ // node_modules segment, and the tsgo host's fs shadow must already
201
+ // recognize those directories while the PROGRAM is created (hiding the
202
+ // package's declaration twins along its internal relative edges).
203
+ // Non-opted workspace packages register lazily as preflight's own
204
+ // resolver discovers their import edges.
205
+ clearWorkspacePackages();
206
+ for (const pkg of opts?.npmStatic ?? []) {
207
+ const probe = resolveBareModule(entryPath, pkg);
208
+ if (probe?.workspaceDir !== undefined)
209
+ registerWorkspacePackage(probe.packageName, probe.workspaceDir);
210
+ }
211
+ // Declaration-twin hiding is scoped to the entry's own package realm
212
+ // (see projectDtsRuntimeSibling) — set before the host exists so the
213
+ // program never sees an out-of-realm twin hidden.
214
+ setProjectRealm(entryPath);
215
+ // Two fs shadows compose: --npm-static's per-package hiding, and the
216
+ // always-on project declaration-TWIN hiding (a .d.ts beside runtime JS
217
+ // outside node_modules — prettier's src/index.d.ts — must not exist for
218
+ // the checker, so its resolution lands on the JS Node actually loads;
219
+ // resolve.ts answers the same sibling for scriptc's own edges).
220
+ const npmShadow = npmStaticFsShadow();
221
+ const fsShadow = {
222
+ readFile: (path) => npmShadow?.readFile(path),
223
+ hideFile: (path) => (npmShadow?.hideFile(path) ?? false) || projectDtsRuntimeSibling(path) !== null,
224
+ };
225
+ const host = new ts.Ts7Host({ cwd: dirname(entryPath), fsShadow });
226
+ const load = loadProgram7(host, entryPath);
227
+ return {
228
+ ...load,
229
+ dispose: () => {
230
+ load.disposeAll();
231
+ host.close();
232
+ },
233
+ };
234
+ }
235
+ /** tsc diagnostics (syntax + types), the supported-import fence, and the
236
+ * module evaluation order: fills load.moduleOrder (the SourceFiles
237
+ * themselves — the lowering consumes them directly) and returns the
238
+ * preflight diagnostics. The lowerer runs only on programs that pass. */
239
+ export function checkPreflight(load) {
240
+ const { diags, moduleOrder, startupCrash } = preflight7(load);
241
+ load.moduleOrder = moduleOrder;
242
+ load.startupCrash = startupCrash;
243
+ return diags;
244
+ }
245
+ /* Finding 5: tsgo's strict CommonJS modeling fires checker codes on JS
246
+ * shapes 5.9.3 accepted, and the real hazards those shapes carry are fenced
247
+ * by scriptc's OWN analysis, never by these codes — so for JS FILES ONLY
248
+ * they join the relaxation (TS sources keep the full gate; the order-parity
249
+ * harness holds both lanes to identical verdicts, so a case where 5.9.3
250
+ * itself raises one of these on a JS file would fail there, not slip by):
251
+ * - TS2309 ("export assignment with other exported elements") on the
252
+ * table-then-member pattern — cjsExportDiscardReason names every export
253
+ * Node actually discards.
254
+ * - TS2323 ("cannot redeclare exported variable") on symbol-keyed expando
255
+ * assignments in CJS classes (`this[kSym] = v` under `module.exports =
256
+ * Class`) — 5.9.3 synthesized expando symbols, tsgo trips its
257
+ * redeclaration check instead (the corpus's countdown fixture). */
258
+ const TS7_JS_RELAXED_EXTRA = new Set([2309, 2323]);
259
+ function suppressedJsStrictness7(d) {
260
+ const file = d.fileName;
261
+ if (!file || !isJsSourceFileName(file))
262
+ return false;
263
+ if (JS_RELAXED_TSC_CODES.has(d.code) || TS7_JS_RELAXED_EXTRA.has(d.code))
264
+ return true;
265
+ // The 8xxx range is TypeScript's JSDoc-specific diagnostic family
266
+ // (malformed tags, signature/tag mismatches): comment semantics with no
267
+ // runtime meaning in a JS file.
268
+ if (d.code >= 8000 && d.code <= 8999)
269
+ return true;
270
+ if (JS_ANY_OPERATOR_CODES.has(d.code)) {
271
+ return ts.flattenDiagnosticMessageText(d, "\n").includes("'any'");
272
+ }
273
+ return false;
274
+ }
275
+ /** The specifier of a `require("...")` call: the callee is the bare
276
+ * identifier `require` and the single argument a string literal. The one
277
+ * recognizer preflight edges, global collection, and statement lowering
278
+ * share — anything require-shaped it does NOT match (computed specifiers,
279
+ * extra arguments) is not a module edge and fences at its use site. */
280
+ function requireSpecOf7(node) {
281
+ if (!ts.isCallExpression(node))
282
+ return null;
283
+ if (!ts.isIdentifier(node.expression) || node.expression.text !== "require")
284
+ return null;
285
+ if (node.arguments.length !== 1)
286
+ return null;
287
+ const arg = node.arguments[0];
288
+ return ts.isStringLiteral(arg) ? arg.text : null;
289
+ }
290
+ /** True for statements that are CommonJS require IMPORTS at a module's top
291
+ * level: `const x = require("s")` / `const { a, b } = require("s")`
292
+ * (every declarator a require), and the bare side-effect `require("s");`.
293
+ * These lower to NOTHING — the bindings are alias plumbing (tsc models
294
+ * them as import aliases) and the module edge lives in the order walk. */
295
+ function isRequireStatement7(stmt) {
296
+ if (ts.isExpressionStatement(stmt))
297
+ return requireSpecOf7(stmt.expression) !== null;
298
+ if (!ts.isVariableStatement(stmt))
299
+ return false;
300
+ const decls = stmt.declarationList.declarations;
301
+ return decls.length > 0 && decls.every((d) => d.initializer !== undefined && requireSpecOf7(d.initializer) !== null);
302
+ }
303
+ /** The require() occurrences of one top-level statement: the declaration
304
+ * forms and the bare side-effect call. Empty for everything else. */
305
+ function requiresOf7(stmt) {
306
+ if (ts.isExpressionStatement(stmt)) {
307
+ const spec = requireSpecOf7(stmt.expression);
308
+ return spec !== null ? [{ spec, node: stmt.expression, decl: null }] : [];
309
+ }
310
+ if (!ts.isVariableStatement(stmt))
311
+ return [];
312
+ const out = [];
313
+ for (const decl of stmt.declarationList.declarations) {
314
+ const spec = decl.initializer !== undefined ? requireSpecOf7(decl.initializer) : null;
315
+ if (spec !== null)
316
+ out.push({ spec, node: decl, decl });
317
+ }
318
+ return out;
319
+ }
320
+ /** True for top-level statements that cannot run user code: directives,
321
+ * empty statements, hoisted declarations, require statements themselves,
322
+ * and literal-initialized variables. A require preceded ONLY by these can
323
+ * never have its bindings observed early — nothing above it executes. */
324
+ function purePrefixStmt7(s) {
325
+ if (ts.isEmptyStatement(s) || ts.isFunctionDeclaration(s))
326
+ return true;
327
+ if (ts.isExpressionStatement(s) && ts.isStringLiteral(s.expression))
328
+ return true; // directive
329
+ if (isRequireStatement7(s))
330
+ return true;
331
+ if (ts.isVariableStatement(s)) {
332
+ return s.declarationList.declarations.every((d) => d.initializer === undefined ||
333
+ requireSpecOf7(d.initializer) !== null ||
334
+ ts.isStringLiteralLike(d.initializer) ||
335
+ ts.isNumericLiteral(d.initializer) ||
336
+ d.initializer.kind === ts.SyntaxKind.TrueKeyword ||
337
+ d.initializer.kind === ts.SyntaxKind.FalseKeyword ||
338
+ d.initializer.kind === ts.SyntaxKind.NullKeyword);
339
+ }
340
+ return false;
341
+ }
342
+ /** Can code that runs BEFORE the require statement at index `k` reach one
343
+ * of the require's bindings? Node initializes them AT the require (TDZ —
344
+ * earlier access is a ReferenceError), but the lowering aliases reads
345
+ * through to the exporter's storage with no TDZ state, so a reachable
346
+ * early read would silently diverge. Conservative reachability: earlier
347
+ * statements' whole subtrees (arrows and function expressions included)
348
+ * read the binding directly, or reference a hoisted function/class whose
349
+ * body (transitively through other hoisted declarations) reads it — a
350
+ * referenced function value may be invoked immediately by whatever takes
351
+ * it. Asynchronously-scheduled callbacks run after the whole module body,
352
+ * hence after the require: never an early read. Returns the first
353
+ * reachable binding's name, or null when position is provably free.
354
+ * (The CheckerFacade memoizes symbol queries, and 7's client dedupes
355
+ * symbol identity by server handle, so the Map-keyed-by-Symbol discipline
356
+ * carries over from the 5.9.3 original unchanged.) */
357
+ function requireTdzRisk7(program, sf, k, decl) {
358
+ const checker = program.getTypeChecker();
359
+ const bound = [];
360
+ if (ts.isIdentifier(decl.name))
361
+ bound.push(decl.name);
362
+ else if (ts.isObjectBindingPattern(decl.name)) {
363
+ for (const el of decl.name.elements)
364
+ if (el.name !== undefined && ts.isIdentifier(el.name))
365
+ bound.push(el.name);
366
+ }
367
+ const bindings = new Map();
368
+ for (const id of bound) {
369
+ const sym = checker.getSymbolAtLocation(id);
370
+ if (sym)
371
+ bindings.set(sym, id.text);
372
+ }
373
+ if (bindings.size === 0)
374
+ return null;
375
+ const stmts = sf.statements;
376
+ const hoisted = new Map();
377
+ for (const s of stmts) {
378
+ if ((ts.isFunctionDeclaration(s) || ts.isClassDeclaration(s)) && s.name) {
379
+ const sym = checker.getSymbolAtLocation(s.name);
380
+ if (sym)
381
+ hoisted.set(sym, s);
382
+ }
383
+ }
384
+ let hit = null;
385
+ const scanned = new Set();
386
+ const work = [];
387
+ const scan = (root) => {
388
+ // Iterative walk (walkPreorder): a statement's subtree can be
389
+ // pathologically deep (the binderBinaryExpressionStress chains), and
390
+ // this scan must survive it so the nesting fence can answer later.
391
+ ts.walkPreorder(root, (n) => {
392
+ if (ts.isIdentifier(n)) {
393
+ const sym = checker.getSymbolAtLocation(n);
394
+ if (sym) {
395
+ const name = bindings.get(sym);
396
+ if (name !== undefined) {
397
+ hit = name;
398
+ return "stop";
399
+ }
400
+ const target = hoisted.get(sym);
401
+ if (target && !scanned.has(target)) {
402
+ scanned.add(target);
403
+ work.push(target);
404
+ }
405
+ }
406
+ }
407
+ return undefined;
408
+ });
409
+ };
410
+ for (let i = 0; i < k && hit === null; i++) {
411
+ const s = stmts[i];
412
+ if (ts.isFunctionDeclaration(s))
413
+ continue;
414
+ scan(s);
415
+ }
416
+ while (hit === null && work.length > 0) {
417
+ scan(work.pop());
418
+ }
419
+ return hit;
420
+ }
421
+ /** Bare `require("...")` EXPRESSION STATEMENTS anywhere below the top
422
+ * level of a file — inside top-level ifs/blocks/loops and inside function
423
+ * bodies (the lazy-require idiom). No bindings, pure side-effect loads. */
424
+ function nestedBareRequiresOf7(sf) {
425
+ const out = [];
426
+ // Iterative walk (walkPreorder): this sweep sees every JS file whole,
427
+ // including pathologically deep expression chains that must live to be
428
+ // fenced by name, not crash a recursive visit.
429
+ ts.walkPreorder(sf, (n) => {
430
+ if (ts.isExpressionStatement(n) &&
431
+ !ts.isSourceFile(n.parent) &&
432
+ ts.isCallExpression(n.expression) &&
433
+ requireSpecOf7(n.expression) !== null) {
434
+ out.push(n.expression);
435
+ }
436
+ });
437
+ return out;
438
+ }
439
+ /** True when Node would treat this file as an ES MODULE (never defining
440
+ * require/__dirname there): .mjs always, .cjs never, and .js/.ts by
441
+ * syntactic module detection — spelled to 5.9.3's OBSERVED bundler-mode
442
+ * behavior (it set impliedNodeFormat only for the mjs/cjs families; 7's
443
+ * client SourceFile has no impliedNodeFormat at all). */
444
+ function isNodeEsmFile7(sf) {
445
+ if (sf.fileName.endsWith(".cjs") || sf.fileName.endsWith(".cts"))
446
+ return false;
447
+ if (sf.fileName.endsWith(".mjs") || sf.fileName.endsWith(".mts"))
448
+ return true;
449
+ return ts.isExternalModule(sf);
450
+ }
451
+ /** A JavaScript source file Node treats as CommonJS (not an ES module):
452
+ * ESM namespace/default machinery must not claim it — its export surface
453
+ * is module.exports, read through the CJS interop paths and their own
454
+ * fences. */
455
+ function isCjsJsFile7(sf) {
456
+ return isJsSourceFileName(sf.fileName) && !isNodeEsmFile7(sf);
457
+ }
458
+ /** The exported spelling of isCjsJsFile7 (the lowering's gate for
459
+ * module.exports surfaces — `ts.isExternalModule` is NOT the CJS test:
460
+ * tsgo marks CJS files with export assignments as external modules). */
461
+ export function isCjsJsFile(sf) {
462
+ return isCjsJsFile7(sf);
463
+ }
464
+ /** The self-import TDZ fences: for `import { x } from "<self>"` (default
465
+ * and namespace forms included), any TOP-LEVEL reference lexically above
466
+ * the aliased declaration observes Node's temporal dead zone — the direct
467
+ * spelling of the same read is tsc's own TS2448, which the import
468
+ * indirection dodges, so preflight restores the bar with SC1030. Function
469
+ * aliases hoist (initialized at link — readable anywhere); references
470
+ * inside function-like bodies follow the direct spelling's existing
471
+ * policy (tsc accepts those too) and are not fenced here. Namespace
472
+ * bindings fence per MEMBER access (`self.x` above x's declaration);
473
+ * the bare namespace binding itself is never TDZ. */
474
+ function selfImportTdzFences7(program, sf, clause, diags) {
475
+ const checker = program.getTypeChecker();
476
+ const insideFunction = (node) => {
477
+ for (let p = node.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
478
+ if (ts.isFunctionLike(p))
479
+ return true;
480
+ }
481
+ return false;
482
+ };
483
+ // The aliased declaration's TDZ horizon: the position after which reads
484
+ // are initialized. Hoisted functions have none.
485
+ const tdzEndOf = (aliased) => {
486
+ const d = checker.valueDeclarationOf(aliased) ?? checker.declarationsOf(aliased)[0];
487
+ if (d === undefined || d.getSourceFile() !== sf)
488
+ return null;
489
+ if (ts.isFunctionDeclaration(d))
490
+ return null;
491
+ return d.getEnd();
492
+ };
493
+ const fence = (ref, name) => {
494
+ diags.push(unsupportedDiag("SC1030", { file: sf.fileName, start: ref.getStart(sf), end: ref.getEnd() }, `the self-import binding '${name}' read above its declaration (the import aliases this module's OWN binding — Node initializes it when the declaration executes, so this read throws ReferenceError; move the read below the declaration)`));
495
+ };
496
+ const checkNamed = (bindingName) => {
497
+ const sym = checker.getSymbolAtLocation(bindingName);
498
+ if (sym === undefined || !(sym.flags & ts.SymbolFlags.Alias))
499
+ return;
500
+ const end = tdzEndOf(checker.getAliasedSymbol(sym));
501
+ if (end === null)
502
+ return;
503
+ const visit = (node) => {
504
+ if (ts.isIdentifier(node) &&
505
+ node !== bindingName &&
506
+ node.text === bindingName.text &&
507
+ node.getStart(sf) < end &&
508
+ !insideFunction(node) &&
509
+ checker.getSymbolAtLocation(node) === sym) {
510
+ fence(node, bindingName.text);
511
+ }
512
+ ts.forEachChild(node, visit);
513
+ };
514
+ visit(sf);
515
+ };
516
+ if (clause.name !== undefined)
517
+ checkNamed(clause.name);
518
+ if (clause.namedBindings !== undefined && ts.isNamedImports(clause.namedBindings)) {
519
+ for (const el of clause.namedBindings.elements) {
520
+ if (!el.isTypeOnly)
521
+ checkNamed(el.name);
522
+ }
523
+ }
524
+ if (clause.namedBindings !== undefined && ts.isNamespaceImport(clause.namedBindings)) {
525
+ const nsName = clause.namedBindings.name;
526
+ const nsSym = checker.getSymbolAtLocation(nsName);
527
+ if (nsSym === undefined)
528
+ return;
529
+ const visit = (node) => {
530
+ if (ts.isPropertyAccessExpression(node) &&
531
+ ts.isIdentifier(node.expression) &&
532
+ node.expression.text === nsName.text &&
533
+ ts.isIdentifier(node.name) &&
534
+ !insideFunction(node) &&
535
+ checker.getSymbolAtLocation(node.expression) === nsSym) {
536
+ const memberSym = checker.getSymbolAtLocation(node.name);
537
+ if (memberSym !== undefined) {
538
+ const end = tdzEndOf(memberSym);
539
+ if (end !== null && node.getStart(sf) < end)
540
+ fence(node.expression, `${nsName.text}.${node.name.text}`);
541
+ }
542
+ }
543
+ ts.forEachChild(node, visit);
544
+ };
545
+ visit(sf);
546
+ }
547
+ }
548
+ /** True when every reference to the namespace-import binding `nsName`
549
+ * (outside its own declaration) is exactly a bare expression statement's
550
+ * expression — the shape whose lowering is Node's no-op. A self-import's
551
+ * references all live in the importing file, so one file-local scan
552
+ * answers completely. */
553
+ function nsBindingUsesAreBareStatements7(program, sf, nsName) {
554
+ const checker = program.getTypeChecker();
555
+ const bindingSym = checker.getSymbolAtLocation(nsName);
556
+ if (bindingSym === undefined)
557
+ return false;
558
+ let bareOnly = true;
559
+ const visit = (node) => {
560
+ if (!bareOnly)
561
+ return;
562
+ if (ts.isIdentifier(node) &&
563
+ node !== nsName &&
564
+ node.text === nsName.text &&
565
+ checker.getSymbolAtLocation(node) === bindingSym) {
566
+ if (!(node.parent !== undefined && ts.isExpressionStatement(node.parent) && node.parent.expression === node)) {
567
+ bareOnly = false;
568
+ return;
569
+ }
570
+ }
571
+ ts.forEachChild(node, visit);
572
+ };
573
+ visit(sf);
574
+ return bareOnly;
575
+ }
576
+ /** True when the reference at `node` can only evaluate AFTER the module
577
+ * graph finished initializing: inside a function-like BODY, inside a
578
+ * parameter (default initializers evaluate at call time), or inside an
579
+ * INSTANCE field initializer (runs at construction, and no admitted top
580
+ * level constructs). Static field initializers, static blocks, heritage
581
+ * clauses, computed names, and decorators all run at the class statement
582
+ * itself — not deferred. */
583
+ function inDeferredPosition7(node) {
584
+ let child = node;
585
+ for (let p = node.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
586
+ if (p.kind === ts.SyntaxKind.Decorator)
587
+ return false;
588
+ if (ts.isClassStaticBlockDeclaration(p))
589
+ return false;
590
+ if (ts.isFunctionLike(p)) {
591
+ if (p.body === child)
592
+ return true;
593
+ if (ts.isParameter(child))
594
+ return true;
595
+ }
596
+ if (ts.isPropertyDeclaration(p) &&
597
+ p.initializer === child &&
598
+ (ts.getCombinedModifierFlags(p) & ts.ModifierFlags.Static) === 0) {
599
+ return true;
600
+ }
601
+ child = p;
602
+ }
603
+ return false;
604
+ }
605
+ /** True for identifiers in pure TYPE positions (annotations, `typeof`
606
+ * queries, interface/alias bodies) — erased at runtime, never a read. A
607
+ * CLASS `extends` heritage expression is the one TypeNode-shaped position
608
+ * that IS a runtime read. */
609
+ function inTypePosition7(node) {
610
+ for (let p = node.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
611
+ if (ts.isTypeNode(p)) {
612
+ const heritage = p.parent;
613
+ const isClassExtends = p.kind === ts.SyntaxKind.ExpressionWithTypeArguments &&
614
+ heritage !== undefined &&
615
+ ts.isHeritageClause(heritage) &&
616
+ heritage.token === ts.SyntaxKind.ExtendsKeyword &&
617
+ (ts.isClassDeclaration(heritage.parent) || ts.isClassExpression(heritage.parent));
618
+ if (!isClassExtends)
619
+ return true;
620
+ }
621
+ if (ts.isTypeAliasDeclaration(p) || ts.isInterfaceDeclaration(p))
622
+ return true;
623
+ }
624
+ return false;
625
+ }
626
+ /** The first top-level construct of `sf` that could execute USER code
627
+ * during a cycle's init window, or null when the module's top level is
628
+ * declaration-only ("inert"). The expression whitelist is deliberately
629
+ * conservative — anything that could CALL user code (directly, through a
630
+ * callback-invoking builtin, a getter, an iterator, or an implicit
631
+ * coercion of a user object) disqualifies the module's cycles. */
632
+ function nonInertTopLevel7(program, sf) {
633
+ const checker = program.getTypeChecker();
634
+ const PRIM = ts.TypeFlags.StringLike |
635
+ ts.TypeFlags.NumberLike |
636
+ ts.TypeFlags.BooleanLike |
637
+ ts.TypeFlags.BigIntLike |
638
+ ts.TypeFlags.ESSymbolLike |
639
+ ts.TypeFlags.EnumLike |
640
+ ts.TypeFlags.Undefined |
641
+ ts.TypeFlags.Null |
642
+ ts.TypeFlags.Void |
643
+ ts.TypeFlags.Never;
644
+ /** Coercion-safe: the expression's type is primitive in every arm, so
645
+ * ToString/ToNumber/ToPrimitive can never reach a user valueOf. */
646
+ const primitiveTyped = (e) => {
647
+ const t = checker.getTypeAtLocation(e);
648
+ const parts = t.isUnionType() ? t.getTypes() : [t];
649
+ return parts.every((p) => (p.flags & PRIM) !== 0);
650
+ };
651
+ /** The identifier's symbol lives entirely in declaration files — a
652
+ * global builtin (process, WeakSet, Object, …) whose property reads and
653
+ * calls are runtime-implemented, never user code. */
654
+ const dtsRooted = (e) => {
655
+ let root = e;
656
+ while (ts.isPropertyAccessExpression(root) || ts.isElementAccessExpression(root))
657
+ root = root.expression;
658
+ if (ts.isMetaProperty(root))
659
+ return true; // import.meta
660
+ if (!ts.isIdentifier(root))
661
+ return false;
662
+ let sym = checker.getSymbolAtLocation(root);
663
+ if (sym === undefined)
664
+ return false;
665
+ if (sym.flags & ts.SymbolFlags.Alias)
666
+ sym = checker.getAliasedSymbol(sym);
667
+ const decls = checker.declarationsOf(sym);
668
+ return decls.length > 0 && decls.every((d) => d.getSourceFile().isDeclarationFile);
669
+ };
670
+ const hasDecorator = (n) => (n.modifiers ?? []).some((m) => m.kind === ts.SyntaxKind.Decorator);
671
+ /** No function-like node anywhere in the subtree — arguments to builtin
672
+ * calls must not smuggle a callback the builtin could invoke. */
673
+ const containsFunctionLike = (n) => {
674
+ let found = false;
675
+ ts.walkPreorder(n, (c) => {
676
+ if (ts.isFunctionLike(c) || ts.isClassExpression(c) || ts.isClassDeclaration(c)) {
677
+ found = true;
678
+ return "stop";
679
+ }
680
+ return undefined;
681
+ });
682
+ return found;
683
+ };
684
+ const inertClass = (c) => {
685
+ if (hasDecorator(c))
686
+ return c;
687
+ for (const h of c.heritageClauses ?? []) {
688
+ if (h.token !== ts.SyntaxKind.ExtendsKeyword)
689
+ continue;
690
+ for (const t of h.types)
691
+ if (!inert(t.expression))
692
+ return t.expression;
693
+ }
694
+ for (const m of c.members) {
695
+ if (ts.isClassStaticBlockDeclaration(m))
696
+ return m;
697
+ if (hasDecorator(m))
698
+ return m;
699
+ const memberName = m.name;
700
+ if (memberName !== undefined && ts.isComputedPropertyName(memberName)) {
701
+ if (!inert(memberName.expression) || !primitiveTyped(memberName.expression))
702
+ return memberName;
703
+ }
704
+ if (ts.isPropertyDeclaration(m) &&
705
+ (ts.getCombinedModifierFlags(m) & ts.ModifierFlags.Static) !== 0 &&
706
+ m.initializer !== undefined &&
707
+ !inert(m.initializer)) {
708
+ return m.initializer;
709
+ }
710
+ }
711
+ return null;
712
+ };
713
+ const COERCING_FREE = new Set([
714
+ ts.SyntaxKind.EqualsEqualsEqualsToken,
715
+ ts.SyntaxKind.ExclamationEqualsEqualsToken,
716
+ ts.SyntaxKind.AmpersandAmpersandToken,
717
+ ts.SyntaxKind.BarBarToken,
718
+ ts.SyntaxKind.QuestionQuestionToken,
719
+ ts.SyntaxKind.CommaToken,
720
+ ts.SyntaxKind.InstanceOfKeyword,
721
+ ]);
722
+ const inert = (e) => {
723
+ if (ts.isStringLiteral(e) ||
724
+ ts.isNumericLiteral(e) ||
725
+ ts.isBigIntLiteral(e) ||
726
+ ts.isRegularExpressionLiteral(e) ||
727
+ ts.isNoSubstitutionTemplateLiteral(e) ||
728
+ e.kind === ts.SyntaxKind.TrueKeyword ||
729
+ e.kind === ts.SyntaxKind.FalseKeyword ||
730
+ e.kind === ts.SyntaxKind.NullKeyword ||
731
+ ts.isIdentifier(e) ||
732
+ ts.isMetaProperty(e) ||
733
+ ts.isArrowFunction(e) ||
734
+ ts.isFunctionExpression(e)) {
735
+ return true;
736
+ }
737
+ if (ts.isClassExpression(e))
738
+ return inertClass(e) === null;
739
+ if (ts.isParenthesizedExpression(e) || ts.isAsExpression(e) || ts.isSatisfiesExpression(e) || ts.isNonNullExpression(e)) {
740
+ return inert(e.expression);
741
+ }
742
+ if (ts.isPropertyAccessExpression(e))
743
+ return dtsRooted(e);
744
+ if (ts.isElementAccessExpression(e)) {
745
+ return dtsRooted(e) && inert(e.argumentExpression) && primitiveTyped(e.argumentExpression);
746
+ }
747
+ if (ts.isTemplateExpression(e)) {
748
+ return e.templateSpans.every((s) => inert(s.expression) && primitiveTyped(s.expression));
749
+ }
750
+ if (ts.isVoidExpression(e) || ts.isTypeOfExpression(e))
751
+ return inert(e.expression);
752
+ if (ts.isPrefixUnaryExpression(e)) {
753
+ if (e.operator === ts.SyntaxKind.ExclamationToken)
754
+ return inert(e.operand);
755
+ if (e.operator === ts.SyntaxKind.PlusToken ||
756
+ e.operator === ts.SyntaxKind.MinusToken ||
757
+ e.operator === ts.SyntaxKind.TildeToken) {
758
+ return inert(e.operand) && primitiveTyped(e.operand);
759
+ }
760
+ return false;
761
+ }
762
+ if (ts.isBinaryExpression(e)) {
763
+ if (!inert(e.left) || !inert(e.right))
764
+ return false;
765
+ if (COERCING_FREE.has(e.operatorToken.kind))
766
+ return true;
767
+ return primitiveTyped(e.left) && primitiveTyped(e.right);
768
+ }
769
+ if (ts.isConditionalExpression(e))
770
+ return inert(e.condition) && inert(e.whenTrue) && inert(e.whenFalse);
771
+ if (ts.isArrayLiteralExpression(e))
772
+ return e.elements.every((el) => !ts.isSpreadElement(el) && inert(el));
773
+ if (ts.isObjectLiteralExpression(e)) {
774
+ return e.properties.every((p) => {
775
+ if (ts.isShorthandPropertyAssignment(p))
776
+ return true;
777
+ if (!ts.isPropertyAssignment(p) || ts.isComputedPropertyName(p.name))
778
+ return false;
779
+ return inert(p.initializer);
780
+ });
781
+ }
782
+ if (ts.isCallExpression(e) || ts.isNewExpression(e)) {
783
+ if (!dtsRooted(e.expression) || !ts.isIdentifier(chainRoot7(e.expression)))
784
+ return false;
785
+ const args = e.arguments ?? [];
786
+ return args.every((a) => inert(a) &&
787
+ !containsFunctionLike(a) &&
788
+ checker.getCallSignatures(checker.getTypeAtLocation(a)).length === 0);
789
+ }
790
+ return false;
791
+ };
792
+ for (const stmt of sf.statements) {
793
+ if (ts.isImportDeclaration(stmt) ||
794
+ ts.isExportDeclaration(stmt) ||
795
+ ts.isImportEqualsDeclaration(stmt) ||
796
+ ts.isFunctionDeclaration(stmt) ||
797
+ ts.isInterfaceDeclaration(stmt) ||
798
+ ts.isTypeAliasDeclaration(stmt) ||
799
+ ts.isEmptyStatement(stmt) ||
800
+ (ts.getCombinedModifierFlags(stmt) & ts.ModifierFlags.Ambient) !== 0) {
801
+ continue;
802
+ }
803
+ if (ts.isEnumDeclaration(stmt)) {
804
+ const bad = stmt.members.find((m) => m.initializer !== undefined && !inert(m.initializer));
805
+ if (bad !== undefined)
806
+ return bad;
807
+ continue;
808
+ }
809
+ if (ts.isClassDeclaration(stmt)) {
810
+ const bad = inertClass(stmt);
811
+ if (bad !== null)
812
+ return bad;
813
+ continue;
814
+ }
815
+ if (ts.isVariableStatement(stmt)) {
816
+ for (const d of stmt.declarationList.declarations) {
817
+ if (!ts.isIdentifier(d.name))
818
+ return d.name; // patterns can invoke getters/iterators
819
+ if (d.initializer !== undefined && !inert(d.initializer))
820
+ return d.initializer;
821
+ }
822
+ continue;
823
+ }
824
+ if (ts.isExpressionStatement(stmt)) {
825
+ if (!inert(stmt.expression))
826
+ return stmt;
827
+ continue;
828
+ }
829
+ if (ts.isExportAssignment(stmt) && !stmt.isExportEquals) {
830
+ if (!inert(stmt.expression))
831
+ return stmt;
832
+ continue;
833
+ }
834
+ return stmt;
835
+ }
836
+ return null;
837
+ }
838
+ /** The head of a property/element-access chain. */
839
+ function chainRoot7(e) {
840
+ let root = e;
841
+ while (ts.isPropertyAccessExpression(root) || ts.isElementAccessExpression(root))
842
+ root = root.expression;
843
+ return root;
844
+ }
845
+ /** The first use of a binding this cycle-closing import introduces that
846
+ * sits OUTSIDE a deferred position (a read there can observe the
847
+ * partially-initialized exporter), or null when every use defers.
848
+ * Re-export statements bind nothing locally — always null: the alias is
849
+ * link-time plumbing, never an evaluation-time read. Export specifiers
850
+ * of an imported binding are the same alias story, not reads. */
851
+ function backEdgeUseOffence7(program, sf, stmt) {
852
+ if (!ts.isImportDeclaration(stmt) || stmt.importClause === undefined)
853
+ return null;
854
+ const checker = program.getTypeChecker();
855
+ const clause = stmt.importClause;
856
+ const bindingNames = [];
857
+ if (clause.name !== undefined)
858
+ bindingNames.push(clause.name);
859
+ if (clause.namedBindings !== undefined) {
860
+ if (ts.isNamespaceImport(clause.namedBindings))
861
+ bindingNames.push(clause.namedBindings.name);
862
+ else
863
+ for (const el of clause.namedBindings.elements)
864
+ if (!el.isTypeOnly)
865
+ bindingNames.push(el.name);
866
+ }
867
+ for (const bindingName of bindingNames) {
868
+ const sym = checker.getSymbolAtLocation(bindingName);
869
+ if (sym === undefined)
870
+ continue;
871
+ let offence = null;
872
+ const visit = (node) => {
873
+ if (offence !== null || ts.isImportDeclaration(node))
874
+ return;
875
+ if (ts.isIdentifier(node) &&
876
+ node.text === bindingName.text &&
877
+ !(node.parent !== undefined && ts.isExportSpecifier(node.parent)) &&
878
+ checker.getSymbolAtLocation(node) === sym &&
879
+ !inTypePosition7(node) &&
880
+ !inDeferredPosition7(node)) {
881
+ offence = { name: bindingName.text, node };
882
+ return;
883
+ }
884
+ ts.forEachChild(node, visit);
885
+ };
886
+ visit(sf);
887
+ if (offence !== null)
888
+ return offence;
889
+ }
890
+ return null;
891
+ }
892
+ /** Resolves an import specifier from `from` to a source file of the
893
+ * program, or null (unresolvable / outside the program). Exported (as
894
+ * resolveImport) for the lowering: CommonJS require statements lower to
895
+ * guarded %init calls of exactly the module preflight resolved here. */
896
+ function resolveImport7(program, from, specifier) {
897
+ const resolved = resolveRelativeModule(from.fileName, specifier);
898
+ if (resolved === null)
899
+ return null;
900
+ return program.getSourceFile(resolved) ?? null;
901
+ }
902
+ /** An import that resolves into node_modules: the package's shipped .d.ts
903
+ * is the type surface, and the package's shipped JS runs in the dynamic
904
+ * island under --dynamic. Resolution rides the own resolver (resolve.ts).
905
+ * Null for relative and node: specifiers, and for anything that doesn't
906
+ * resolve into node_modules. */
907
+ function resolveNpmImport7(fromFileName, specifier) {
908
+ if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("node:")) {
909
+ return null;
910
+ }
911
+ // --provenance-sources: a registered specifier is NOT an npm import —
912
+ // its attested source compiles as program modules (resolveProjectImport
913
+ // answers the entry), so no island embed and no .d.ts type surface.
914
+ if (provenanceEntryFor(specifier) !== null)
915
+ return null;
916
+ const resolved = resolveBareModule(fromFileName, specifier);
917
+ if (!resolved)
918
+ return null;
919
+ if (!isNodeModulesPath(resolved.typesFile)) {
920
+ // A workspace-linked package (the node_modules entry is a symlink into
921
+ // the project — every monorepo tool's internal-package install): Node
922
+ // resolves and runs it exactly like any installed package, so it IS an
923
+ // npm import; the realpath'd answer escaping node_modules must not
924
+ // refuse the edge. Registered so path-keyed package attribution
925
+ // (shared.ts) recognizes the package's real files. Anything else
926
+ // whose answer left node_modules stays refused.
927
+ if (resolved.workspaceDir === undefined)
928
+ return null;
929
+ registerWorkspacePackage(resolved.packageName, resolved.workspaceDir);
930
+ }
931
+ return {
932
+ packageName: resolved.packageName,
933
+ ...(resolved.version !== undefined ? { version: resolved.version } : {}),
934
+ typesFile: resolved.typesFile,
935
+ };
936
+ }
937
+ function locOf7(node) {
938
+ const sf = node.getSourceFile();
939
+ return { file: sf.fileName, start: node.getStart(sf), end: node.getEnd() };
940
+ }
941
+ /** The PROGRAM source file an opted-in --npm-static package's resolved
942
+ * entry maps to, or null with the package marked an offender (resolution
943
+ * still answering declarations, or the file missing from the type-checked
944
+ * program — a path-identity mismatch between the two resolvers). */
945
+ function npmStaticProgramDep(program, packageName, resolvedFile) {
946
+ if (!isJsSourceFileName(resolvedFile)) {
947
+ reportNpmStaticOffender(packageName, `its import resolves to '${resolvedFile}', not to runtime JavaScript`);
948
+ return null;
949
+ }
950
+ const dep = program.getSourceFile(resolvedFile) ?? null;
951
+ if (dep === null) {
952
+ reportNpmStaticOffender(packageName, `its entry '${resolvedFile}' did not join the type-checked program`);
953
+ return null;
954
+ }
955
+ return dep;
956
+ }
957
+ /** `const process = require('node:process')` (identifier binding) and the
958
+ * bare side-effect `require('node:process')`: Node's process module IS
959
+ * the global process object, so the binding is a stdlib-global alias
960
+ * (surfaces.ts registers it; reads lower through the process surface) and
961
+ * the bare load is a no-op. These forms are exempt from the SC1010
962
+ * builtin fence; every other shape (destructuring, subpath) keeps it. */
963
+ function processModuleAliasRequire7(spec, decl) {
964
+ if (spec !== "process" && spec !== "node:process")
965
+ return false;
966
+ return decl === null || ts.isIdentifier(decl.name);
967
+ }
968
+ /** The whole TS7-lane lifecycle for one entry: spawn (or share) a tsgo
969
+ * host, build the lowering-world program, run the ported preflight, and
970
+ * dispose EVERYTHING before returning — the CLI process must exit promptly,
971
+ * so no snapshot or tsgo child outlives the call. */
972
+ export function checkPreflightTs7(entryPath, sharedHost) {
973
+ const host = sharedHost ?? new ts.Ts7Host({ cwd: dirname(entryPath) });
974
+ try {
975
+ const load = loadProgram7(host, entryPath);
976
+ try {
977
+ const { diags, moduleOrder } = preflight7(load);
978
+ return { diags, moduleOrder: moduleOrder.map((sf) => sf.fileName) };
979
+ }
980
+ finally {
981
+ load.disposeAll();
982
+ }
983
+ }
984
+ finally {
985
+ if (!sharedHost)
986
+ host.close();
987
+ }
988
+ }
989
+ function preflight7(load) {
990
+ const { program, entry } = load;
991
+ const diags = [...load.configDiags];
992
+ const toPassthrough = (d) => {
993
+ const message = ts.flattenDiagnosticMessageText(d, "\n");
994
+ // File-less (global/options) diagnostics anchor at the entry with a
995
+ // zero span, exactly like the 5.9.3 lane's `d.start ?? 0` fallback.
996
+ const file = d.fileName ?? entry.fileName;
997
+ const start = d.fileName !== undefined ? d.pos : 0;
998
+ const end = d.fileName !== undefined ? d.end : 0;
999
+ return tscPassthroughDiag(message, { file, start, end });
1000
+ };
1001
+ /* TS7 checker change (not finding 5, same discipline): tsgo types a
1002
+ * NAMESPACE import as the spec's non-callable module namespace object
1003
+ * even when the module is declared with the callable `export =` shape —
1004
+ * 5.9.3 bound the callable value directly, so `import * as a from
1005
+ * "node:assert"; a(x)` typechecked there and draws TS2349 here. Those
1006
+ * callable module objects are scriptc's OWN declared surface, and calls
1007
+ * through the namespace binding are fenced per site by the lowering (the
1008
+ * assert-fences fixture pins the wording), so tsgo's extra TS2349 on
1009
+ * exactly those callees — an identifier that is a namespace-import
1010
+ * binding of a supported builtin module — is suppressed to the 5.9.3
1011
+ * verdict. Nothing else rides this: any other non-callable call keeps
1012
+ * tsc's voice in both lanes. */
1013
+ const builtinNamespaceNames = new Map();
1014
+ const namespaceCalleeSuppressed = (p, d) => {
1015
+ if (d.code !== 2349 || d.fileName === undefined)
1016
+ return false;
1017
+ let names = builtinNamespaceNames.get(d.fileName);
1018
+ if (!names) {
1019
+ names = new Set();
1020
+ builtinNamespaceNames.set(d.fileName, names);
1021
+ const sf = p.getSourceFile(d.fileName);
1022
+ for (const stmt of sf?.statements ?? []) {
1023
+ if (!ts.isImportDeclaration(stmt) || !ts.isStringLiteral(stmt.moduleSpecifier))
1024
+ continue;
1025
+ if (canonicalBuiltinModule(stmt.moduleSpecifier.text) === null)
1026
+ continue;
1027
+ const bindings = stmt.importClause?.namedBindings;
1028
+ if (bindings && ts.isNamespaceImport(bindings))
1029
+ names.add(bindings.name.text);
1030
+ }
1031
+ }
1032
+ if (names.size === 0)
1033
+ return false;
1034
+ const sf = p.getSourceFile(d.fileName);
1035
+ return sf !== undefined && names.has(sf.text.slice(d.pos, d.end));
1036
+ };
1037
+ /* --npm-static: checker errors INSIDE an opted-in package's files never
1038
+ * gate the build — the package's author checked that JS under a foreign
1039
+ * tsconfig (their own lib choices, their own strictness), the program's
1040
+ * author cannot fix it, and the JS design already names where inference
1041
+ * gaps land: the per-statement runtime fences (trust-but-verify — a
1042
+ * statement the checker could not prove compiles to its honest trap,
1043
+ * never to a silent guess). scriptc's OWN SC1xxx fences in those files
1044
+ * still count — preflight structure problems mark the package an
1045
+ * offender and it falls back to the island. */
1046
+ const npmStaticFileSuppressed = (d) => d.fileName !== undefined && npmStaticPackageOfPath(d.fileName) !== null;
1047
+ /* The same doctrine for node_modules JS the opt-in never NAMED:
1048
+ * maxNodeModuleJsDepth (set only on --npm-static loads) admits ANY
1049
+ * node_modules JavaScript the checker's resolution touches — e.g. an
1050
+ * ambient `declare module "punycode"` in @types/node now loses to an
1051
+ * INSTALLED punycode package's JS, which checkJs then checks. Those
1052
+ * files' errors are the identical foreign-tsconfig story (third-party
1053
+ * shipped JS the program's author cannot fix), they were invisible to
1054
+ * every flagless compile (depth 0 keeps node_modules JS out of the
1055
+ * program), and the packages themselves still island — so their checker
1056
+ * errors never gate a build. */
1057
+ const nodeModulesJsSuppressed = (d) => d.fileName !== undefined && isNodeModulesPath(d.fileName) && isJsSourceFileName(d.fileName);
1058
+ /* JSDoc TYPE positions in JS files are documentation Node never reads:
1059
+ * a name-resolution failure THERE (2304/2552 — prettier's utilities.js
1060
+ * spells a mapped type over a @template name it never declared) types
1061
+ * as the error-any and the per-site fences apply, same stance as the
1062
+ * strictness families above. A 2300 duplicate-identifier PAIR formed by
1063
+ * a @typedef and a same-named class/function is the same story — the
1064
+ * typedef half sits inside the comment, and the code half is suppressed
1065
+ * exactly when its partner (same file, same name) was comment-side, so
1066
+ * a REAL duplicate declaration (both halves in code — Node's own
1067
+ * SyntaxError) keeps tsc's voice. */
1068
+ const insideBlockComment = (text, pos) => {
1069
+ const open = text.lastIndexOf("/*", pos);
1070
+ if (open < 0)
1071
+ return false;
1072
+ const close = text.indexOf("*/", open + 2);
1073
+ return close === -1 || close >= pos;
1074
+ };
1075
+ const jsdocTypeSuppressed = (p, d, commentDup) => {
1076
+ if (d.fileName === undefined || !isJsSourceFileName(d.fileName))
1077
+ return false;
1078
+ if (d.code !== 2304 && d.code !== 2552 && d.code !== 2300 && d.code !== 1003)
1079
+ return false;
1080
+ if (d.pos === undefined)
1081
+ return false;
1082
+ const sf = p.getSourceFile(d.fileName);
1083
+ if (!sf)
1084
+ return false;
1085
+ if (insideBlockComment(sf.text, d.pos))
1086
+ return true;
1087
+ return d.code === 2300 && commentDup.has(`${d.fileName}:${sf.text.slice(d.pos, d.end)}`);
1088
+ };
1089
+ const errorsOf = (p) => {
1090
+ const all = ts.getPreEmitDiagnostics(p);
1091
+ // First pass: every comment-side 2300's (file, name) — the partners
1092
+ // the second pass forgives.
1093
+ const commentDup = new Set();
1094
+ for (const d of all) {
1095
+ if (d.code !== 2300 || d.fileName === undefined || d.pos === undefined || d.end === undefined)
1096
+ continue;
1097
+ if (!isJsSourceFileName(d.fileName))
1098
+ continue;
1099
+ const sf = p.getSourceFile(d.fileName);
1100
+ if (sf && insideBlockComment(sf.text, d.pos))
1101
+ commentDup.add(`${d.fileName}:${sf.text.slice(d.pos, d.end)}`);
1102
+ }
1103
+ return all.filter((d) => d.category === ts.DiagnosticCategory.Error &&
1104
+ !suppressedJsStrictness7(d) &&
1105
+ !npmStaticFileSuppressed(d) &&
1106
+ !nodeModulesJsSuppressed(d) &&
1107
+ !namespaceCalleeSuppressed(p, d) &&
1108
+ !jsdocTypeSuppressed(p, d, commentDup));
1109
+ };
1110
+ // The tsc gate, with a second chance: the LOWERING world's checker
1111
+ // includes the divergence overrides (JSON.parse(): unknown, the Promise
1112
+ // executor shape, ...), which are TIGHTER than the standard lib — a
1113
+ // project that typechecks clean under its own tsc can still error here
1114
+ // (any-typed JSON.parse results, most commonly). Those errors are OURS,
1115
+ // not the project's, so they must not fail preflight: when the lowering
1116
+ // world has errors, the PROJECT world (same program without the override
1117
+ // declarations) is built and consulted. Clean there → preflight passes
1118
+ // and the override-affected sites meet the lowerer's honest fences
1119
+ // instead (the SC1100 checked-cast family). Dirty there → the project-
1120
+ // world errors are the ones reported: they are reproducible with the
1121
+ // project's own tsc, which is what "fix type errors first" asks for.
1122
+ const tscErrors = errorsOf(program);
1123
+ if (tscErrors.length > 0) {
1124
+ for (const d of errorsOf(load.projectWorld()))
1125
+ diags.push(toPassthrough(d));
1126
+ }
1127
+ const ambient = ambientDtsPath();
1128
+ // node_modules JS that no --npm-static opt-in claims is NOT program
1129
+ // source even when maxNodeModuleJsDepth pulled it into the checker's
1130
+ // program (see nodeModulesJsSuppressed above): its execution home is the
1131
+ // island, so preflight's statement walks skip it — no import fences, no
1132
+ // module edges, no statement counts from files the lowering never lowers.
1133
+ const userFiles = program
1134
+ .getSourceFiles()
1135
+ .filter((sf) => sf.fileName !== ambient &&
1136
+ !sf.isDeclarationFile &&
1137
+ !sf.fileName.endsWith(".json") &&
1138
+ (!isNodeModulesPath(sf.fileName) || npmStaticPackageOfPath(sf.fileName) !== null));
1139
+ const ambientModules = new Set(SUPPORTED_NODE_MODULES);
1140
+ // Ambient `declare module "name"` declarations anywhere in the program —
1141
+ // exact names and `*` patterns — so the import fence can say "type
1142
+ // surface only, no runtime module" instead of the generic package story.
1143
+ // Computed lazily: the scan only runs when a bare-import fence is about
1144
+ // to fire.
1145
+ let ambientModuleDecls = null;
1146
+ const ambientDeclared = (spec) => {
1147
+ if (ambientModuleDecls === null) {
1148
+ ambientModuleDecls = { names: new Set(), patterns: [] };
1149
+ for (const f of program.getSourceFiles()) {
1150
+ for (const s of f.statements) {
1151
+ if (!ts.isModuleDeclaration(s) || !ts.isStringLiteral(s.name))
1152
+ continue;
1153
+ const name = s.name.text;
1154
+ if (name.includes("*"))
1155
+ ambientModuleDecls.patterns.push(name);
1156
+ else
1157
+ ambientModuleDecls.names.add(name);
1158
+ }
1159
+ }
1160
+ }
1161
+ if (ambientModuleDecls.names.has(spec))
1162
+ return true;
1163
+ return ambientModuleDecls.patterns.some((p) => {
1164
+ const star = p.indexOf("*");
1165
+ return (spec.length >= p.length - 1 &&
1166
+ spec.startsWith(p.slice(0, star)) &&
1167
+ spec.endsWith(p.slice(star + 1)));
1168
+ });
1169
+ };
1170
+ // Module edges, each carrying its BACK-EDGE admissibility: an edge that
1171
+ // closes a cycle is admissible when nothing can OBSERVE the partially-
1172
+ // initialized module through it — the importing statement binds nothing
1173
+ // (side-effect import) or binds only NAMESPACE objects (initialized at
1174
+ // link, never TDZ) whose every use is a bare expression statement (the
1175
+ // no-op lowering; no member is ever read). Node evaluates such cycles
1176
+ // benignly (the revisited module is a cache hit, and no binding read
1177
+ // can hit TDZ or a stale slot), so the guarded %init calls reproduce
1178
+ // its order exactly. Named/default bindings and `export ... from`
1179
+ // re-exports get the SECOND admission chance when they close a cycle —
1180
+ // the declaration-only init window analysis (see the benign-cycle
1181
+ // admission block above): cycles that fail it keep the SC1016 fence,
1182
+ // because a read through them mid-cycle observes TDZ (let/const) or
1183
+ // undefined (var) in Node — runtime evaluation-order semantics the
1184
+ // static story does not model.
1185
+ const edges = new Map();
1186
+ const refusals = new Map();
1187
+ const depPositions = new Map();
1188
+ for (const sf of userFiles) {
1189
+ const deps = [];
1190
+ edges.set(sf, deps);
1191
+ refusals.set(sf, []);
1192
+ depPositions.set(sf, []);
1193
+ for (const stmt of sf.statements) {
1194
+ if (ts.isImportEqualsDeclaration(stmt)) {
1195
+ // The ENTITY form (`import a = A`, `import x = N.y`) is namespace
1196
+ // alias plumbing — the lowerer resolves references through it and
1197
+ // fences what it cannot honor (lower-namespaces.ts). Only the
1198
+ // require form is a module edge, and its RUNTIME form stays fenced
1199
+ // (under the forced ESNext module world tsc itself rejects it, so
1200
+ // only nested/namespace-internal sites can reach the lowering's
1201
+ // fence); `import type x = require(...)` is pure type surface and
1202
+ // lowers to nothing.
1203
+ if (ts.isExternalModuleReference(stmt.moduleReference) && !stmt.isTypeOnly) {
1204
+ diags.push(unsupportedDiag("SC1013", locOf7(stmt), "import = require(...) assignments"));
1205
+ }
1206
+ continue;
1207
+ }
1208
+ if (ts.isExportAssignment(stmt)) {
1209
+ if (stmt.isExportEquals) {
1210
+ diags.push(unsupportedDiag("SC1012", locOf7(stmt), "export = assignments"));
1211
+ }
1212
+ continue;
1213
+ }
1214
+ if (ts.isExportDeclaration(stmt)) {
1215
+ if (stmt.isTypeOnly || erasedTypeOnlyReexport(stmt))
1216
+ continue;
1217
+ if (!stmt.moduleSpecifier)
1218
+ continue;
1219
+ const fromSpec = ts.isStringLiteral(stmt.moduleSpecifier) ? stmt.moduleSpecifier.text : "";
1220
+ if (!fromSpec.startsWith("./") && !fromSpec.startsWith("../")) {
1221
+ // NAMED re-exports from a SUPPORTED builtin pass (`export { ok }
1222
+ // from "node:assert"` — prettier's universal/assert facade): the
1223
+ // statement binds nothing locally and evaluates nothing (builtins
1224
+ // have no %init), and importers' references chase the alias to
1225
+ // the builtin's own declarations exactly as if they imported the
1226
+ // builtin directly — per-member support stays the lowering's
1227
+ // per-site business. Star and namespace re-exports and package
1228
+ // specifiers keep the fence.
1229
+ if (canonicalBuiltinModule(fromSpec) !== null &&
1230
+ stmt.exportClause !== undefined &&
1231
+ ts.isNamedExports(stmt.exportClause)) {
1232
+ continue;
1233
+ }
1234
+ diags.push(unsupportedDiag("SC1014", locOf7(stmt), "re-exports from packages or builtin modules"));
1235
+ continue;
1236
+ }
1237
+ const reDep = resolveImport7(program, sf, fromSpec);
1238
+ // `export * as ns from "./m"` re-exports the module NAMESPACE
1239
+ // object under a name: importers' `x.ns.member` reads resolve
1240
+ // statically through the same alias machinery as `import * as ns`
1241
+ // (moduleNsSymbolOf follows the chain to the module symbol), so a
1242
+ // RESOLVED user module lowers; everything else keeps the fence —
1243
+ // there is no namespace object to materialize.
1244
+ if (stmt.exportClause && ts.isNamespaceExport(stmt.exportClause)) {
1245
+ if (reDep === null || reDep.fileName.endsWith(".json") || isCjsJsFile7(reDep)) {
1246
+ diags.push(unsupportedDiag("SC1013", locOf7(stmt), "namespace re-exports (export * as ns) of this module form"));
1247
+ continue;
1248
+ }
1249
+ }
1250
+ if (reDep && !reDep.fileName.endsWith(".json")) {
1251
+ deps.push({ dep: reDep, backAdmissible: () => false, stmt });
1252
+ depPositions.get(sf).push({ dep: reDep, pos: stmt.getStart(sf) });
1253
+ }
1254
+ continue;
1255
+ }
1256
+ if (!ts.isImportDeclaration(stmt))
1257
+ continue;
1258
+ if (erasedTypeOnlyImport(stmt))
1259
+ continue;
1260
+ // Import-like nodes whose specifier the parse could not shape into a
1261
+ // string literal — the jsdoc `/** @import x = require(...) */`
1262
+ // materialization and `import defer type * as ns` both arrive here
1263
+ // with no specifier text. Neither is a runtime module edge (both are
1264
+ // type-only forms), and where the form is malformed TS the SC0001
1265
+ // passthrough above already names it — never a crash here.
1266
+ const specNode = stmt.moduleSpecifier;
1267
+ if (specNode === undefined || !ts.isStringLiteral(specNode))
1268
+ continue;
1269
+ const spec = specNode.text;
1270
+ const isRelative = spec.startsWith("./") || spec.startsWith("../");
1271
+ const isBare = !isRelative && !ambientModules.has(spec);
1272
+ // An import edge Node's own resolution refuses BEFORE any module
1273
+ // evaluates: recorded as a startup-crash candidate (the Node-order
1274
+ // walk below picks the first one Node would report; the program
1275
+ // compiles to exactly that crash — buildMain) with a compile-fence
1276
+ // fallback for candidates the entry never reaches. The bindings of a
1277
+ // refused edge never link, so every clause fence below is skipped —
1278
+ // their uses meet the lowering's own fences if the code demands
1279
+ // values from them.
1280
+ const refuse = (message, className, fallbackFeature) => {
1281
+ refusals.get(sf).push({
1282
+ crash: { message, className, loc: locOf7(stmt) },
1283
+ fallback: unsupportedDiag("SC1010", locOf7(stmt), fallbackFeature),
1284
+ pos: stmt.getStart(sf),
1285
+ });
1286
+ };
1287
+ // "#" specifiers can never name an npm package — they are the
1288
+ // imports-field family, resolved below.
1289
+ const npm = isBare && !spec.startsWith("#") ? resolveNpmImport7(sf.fileName, spec) : null;
1290
+ if (npm && isNodeTypesPath(npm.typesFile)) {
1291
+ diags.push(unsupportedDiag("SC1010", locOf7(stmt), `the '${spec}' module`));
1292
+ continue;
1293
+ }
1294
+ // --npm-static: an opted-in package's import is a PROGRAM-MODULE
1295
+ // edge — its resolution (types stripped) answered the shipped JS,
1296
+ // which allowJs + maxNodeModuleJsDepth pulled into the program, so
1297
+ // the entry joins the module order like any user module and the
1298
+ // clause fences below apply to it as to program JS. A miss (the
1299
+ // resolution still answers declarations, or the file never joined
1300
+ // the program) marks the package an OFFENDER: the frontend's
1301
+ // fallback loop rebuilds without it and the import islands as ever.
1302
+ let npmStaticDep = null;
1303
+ if (npm !== null) {
1304
+ if (isNpmStaticPackage(npm.packageName)) {
1305
+ npmStaticDep = npmStaticProgramDep(program, npm.packageName, npm.typesFile);
1306
+ if (npmStaticDep === null)
1307
+ continue;
1308
+ }
1309
+ else {
1310
+ // Types resolved, but Node's RUNTIME resolution can still refuse
1311
+ // the edge (a types-only package whose exports target ships no JS,
1312
+ // most commonly): that program is Node's startup crash, not an
1313
+ // island candidate — there is nothing to execute anywhere.
1314
+ const refusal = probeNodeImportRefusal(sf.fileName, spec);
1315
+ if (refusal !== null) {
1316
+ refuse(refusal.message, "%Error", `the '${spec}' package (its runtime resolution fails: ${refusal.message})`);
1317
+ }
1318
+ continue;
1319
+ }
1320
+ }
1321
+ // PROJECT imports: package.json-mediated specifiers that resolve to
1322
+ // the program's own sources — `#alias` (the imports field) and
1323
+ // self-name references (the nearest package.json's name through its
1324
+ // exports). A source answer is an ordinary user-module edge, exactly
1325
+ // like a relative import (the checker resolved the bindings the same
1326
+ // way); a refused resolution is Node's startup crash with Node's
1327
+ // exact message; what keeps a compile fence: the unsupported
1328
+ // builtin and the types-only resolution (Node-hostable or ambiguous
1329
+ // — scriptc's own limitations, named as such).
1330
+ let projDep = null;
1331
+ if (isBare && npmStaticDep === null) {
1332
+ const resolved = resolveProjectImport(sf.fileName, spec);
1333
+ projDep = resolved !== null ? (program.getSourceFile(resolved) ?? null) : null;
1334
+ if (projDep !== null && projDep.isDeclarationFile) {
1335
+ diags.push(unsupportedDiag("SC1010", locOf7(stmt), `the '${spec}' import (it resolves only to type declarations — there is no runtime implementation to compile)`));
1336
+ continue;
1337
+ }
1338
+ if (projDep === null) {
1339
+ const nodeBuiltin = spec.startsWith("node:") || nodeBuiltinNames.has(spec);
1340
+ if (nodeBuiltin) {
1341
+ diags.push(unsupportedDiag("SC1010", locOf7(stmt), `the '${spec}' module`));
1342
+ continue;
1343
+ }
1344
+ if (spec === "#") {
1345
+ refuse(`Invalid module "#" is not a valid internal imports specifier name imported from ${sf.fileName}`, "%TypeError", `the '#' import (Node rejects the bare '#' specifier as invalid)`);
1346
+ continue;
1347
+ }
1348
+ if (spec.startsWith("#")) {
1349
+ const pkgJson = nearestPkgJsonPath(sf.fileName);
1350
+ refuse(`Package import specifier "${spec}" is not defined${pkgJson !== null ? ` in package ${pkgJson}` : ""} imported from ${sf.fileName}`, "%TypeError", `the '${spec}' import (no package.json imports-field entry maps it)`);
1351
+ continue;
1352
+ }
1353
+ const refusal = probeNodeImportRefusal(sf.fileName, spec);
1354
+ if (refusal !== null) {
1355
+ const ambientNote = ambientDeclared(spec)
1356
+ ? `the '${spec}' module (it exists only as an ambient 'declare module' type surface — there is no runtime module to import)`
1357
+ : `the '${spec}' package (nothing installed resolves it)`;
1358
+ refuse(refusal.message, "%Error", ambientNote);
1359
+ continue;
1360
+ }
1361
+ // Runtime-resolvable (or a shape the probe stays conservative
1362
+ // about) with no compilable types answer: scriptc's fence.
1363
+ diags.push(unsupportedDiag("SC1010", locOf7(stmt), ambientDeclared(spec)
1364
+ ? `the '${spec}' module (it exists only as an ambient 'declare module' type surface — there is no runtime module to import)`
1365
+ : `the '${spec}' package (nothing installed resolves it)`));
1366
+ continue;
1367
+ }
1368
+ }
1369
+ const clause = stmt.importClause;
1370
+ const dep = isRelative ? resolveImport7(program, sf, spec) : (npmStaticDep ?? projDep);
1371
+ const isJson = dep !== null && dep.fileName.endsWith(".json");
1372
+ // SELF-imports: the bindings alias this module's OWN declarations,
1373
+ // so a TOP-LEVEL read lexically above the aliased declaration is
1374
+ // Node's TDZ ReferenceError at runtime (exactly what tsc's TS2448
1375
+ // refuses for the DIRECT spelling — imports are exempt from that
1376
+ // check, so the fence restores the same bar). Hoisted function
1377
+ // aliases initialize at link and read fine anywhere; reads inside
1378
+ // function bodies keep the direct spelling's existing policy.
1379
+ if (dep === sf && clause !== undefined) {
1380
+ selfImportTdzFences7(program, sf, clause, diags);
1381
+ }
1382
+ if (clause) {
1383
+ // Default imports of SUPPORTED builtins in JS sources pass: Node's
1384
+ // default export of a CJS builtin IS the module object, so the
1385
+ // binding exposes exactly the namespace-import surface and the
1386
+ // lowering keys the same tables (builtinNamespaceModuleOf's
1387
+ // default-import twin). TS sources keep the fence — the checker
1388
+ // itself refuses the spelling without interop flags, and the
1389
+ // SC1012 wording beats the raw TS1259. The callable module
1390
+ // objects (assert, events, test) stay allowed everywhere.
1391
+ const defaultOk = builtinDefaultImportModule(spec) !== null ||
1392
+ (isJsSourceFileName(sf.fileName) && canonicalBuiltinModule(spec) !== null);
1393
+ if (clause.name && !isJson && dep === null && !defaultOk) {
1394
+ diags.push(unsupportedDiag("SC1012", locOf7(clause.name)));
1395
+ }
1396
+ if (clause.namedBindings && ts.isNamedImports(clause.namedBindings) && isJson) {
1397
+ diags.push(unsupportedDiag("SC1012", locOf7(clause.namedBindings), "named imports from JSON modules"));
1398
+ }
1399
+ if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) {
1400
+ // `import * as ns from "./m"` of a RESOLVED user module lowers:
1401
+ // ns.member accesses resolve statically through the checker to
1402
+ // the exporter's own registrations (lower-namespaces.ts's
1403
+ // module-namespace paths); the ns OBJECT as a first-class value
1404
+ // fences per site. What keeps a fence here: JSON modules (their
1405
+ // namespace object has no static story — default imports bake
1406
+ // the data), CommonJS JS modules (Node builds their namespace
1407
+ // from its lexer's static analysis, a different surface),
1408
+ // and unresolved specifiers. Supported builtins pass through as
1409
+ // before (their namespace members are the declared surface).
1410
+ if (isJson) {
1411
+ diags.push(unsupportedDiag("SC1013", locOf7(clause.namedBindings), "namespace imports of JSON modules"));
1412
+ }
1413
+ else if (dep !== null && isCjsJsFile7(dep)) {
1414
+ // A CJS namespace binding whose every use is a bare expression
1415
+ // statement (`cjs;` — the corpus's "the import linked"
1416
+ // assertion) carries no surface question: the statement lowers
1417
+ // to Node's no-op, the module still evaluates through its
1418
+ // ordinary init edge, and no member is ever read through the
1419
+ // lexer facade (self-imports of a CJS module included — the
1420
+ // partially-built exports are never observed). Any OTHER use
1421
+ // keeps the fence.
1422
+ if (!nsBindingUsesAreBareStatements7(program, sf, clause.namedBindings.name)) {
1423
+ diags.push(unsupportedDiag("SC1013", locOf7(clause.namedBindings), "namespace imports of CommonJS modules"));
1424
+ }
1425
+ }
1426
+ else if (dep === null && !(!isRelative && ambientModules.has(spec))) {
1427
+ diags.push(unsupportedDiag("SC1013", locOf7(clause.namedBindings)));
1428
+ }
1429
+ }
1430
+ }
1431
+ if (dep && !isJson) {
1432
+ // Back-edge admissibility (see the edges declaration): no clause,
1433
+ // or namespace-only bindings whose uses are all bare statements.
1434
+ // Lazy — the file scan only runs if this edge ever closes a cycle.
1435
+ const importClause = clause;
1436
+ const importSf = sf;
1437
+ deps.push({
1438
+ dep,
1439
+ backAdmissible: () => importClause === undefined ||
1440
+ (importClause.name === undefined &&
1441
+ (importClause.namedBindings === undefined ||
1442
+ (ts.isNamespaceImport(importClause.namedBindings) &&
1443
+ nsBindingUsesAreBareStatements7(program, importSf, importClause.namedBindings.name)))),
1444
+ stmt,
1445
+ });
1446
+ depPositions.get(sf).push({ dep, pos: stmt.getStart(sf) });
1447
+ }
1448
+ }
1449
+ if (isJsSourceFileName(sf.fileName)) {
1450
+ const stmts = sf.statements;
1451
+ let firstRunnable = -1;
1452
+ stmts.forEach((s, i) => {
1453
+ if (firstRunnable < 0 && !purePrefixStmt7(s))
1454
+ firstRunnable = i;
1455
+ });
1456
+ for (let k = 0; k < stmts.length; k++) {
1457
+ const stmt = stmts[k];
1458
+ for (const req of requiresOf7(stmt)) {
1459
+ const loc = { file: sf.fileName, start: req.node.getStart(sf), end: req.node.getEnd() };
1460
+ if (isNodeEsmFile7(sf)) {
1461
+ diags.push(unsupportedDiag("SC1013", loc, "require() in an ES module (Node throws ReferenceError — use import)"));
1462
+ continue;
1463
+ }
1464
+ if (req.decl && ts.isArrayBindingPattern(req.decl.name)) {
1465
+ diags.push(unsupportedDiag("SC1012", loc, "array-destructuring require() bindings"));
1466
+ continue;
1467
+ }
1468
+ if (req.decl && ts.isObjectBindingPattern(req.decl.name)) {
1469
+ const bad = req.decl.name.elements.find((e) => e.name === undefined || !ts.isIdentifier(e.name) || e.initializer !== undefined || e.dotDotDotToken !== undefined);
1470
+ if (bad) {
1471
+ diags.push(unsupportedDiag("SC1012", loc, "require() destructuring with defaults, rest, or nested patterns"));
1472
+ continue;
1473
+ }
1474
+ }
1475
+ const isRelative = req.spec.startsWith("./") || req.spec.startsWith("../");
1476
+ if (!isRelative) {
1477
+ if (canonicalBuiltinModule(req.spec) === null &&
1478
+ !processModuleAliasRequire7(req.spec, req.decl)) {
1479
+ // A binding-less require of a package NOTHING resolves:
1480
+ // Node throws MODULE_NOT_FOUND at the require site — the
1481
+ // lowering compiles exactly that catchable throw (the
1482
+ // optional-dependency try/require pattern), so no fence.
1483
+ // Binding forms keep the fence: their downstream reads
1484
+ // would need the module that never loads.
1485
+ if (req.decl !== null || probeNodeRequireRefusal(sf.fileName, req.spec) === null) {
1486
+ diags.push(unsupportedDiag("SC1010", loc, `the '${req.spec}' module`));
1487
+ }
1488
+ }
1489
+ continue;
1490
+ }
1491
+ const dep = resolveImport7(program, sf, req.spec);
1492
+ if (dep && dep.fileName.endsWith(".json")) {
1493
+ diags.push(unsupportedDiag("SC1012", loc, "require() of JSON modules"));
1494
+ continue;
1495
+ }
1496
+ const tdzName = firstRunnable >= 0 && firstRunnable < k && req.decl
1497
+ ? requireTdzRisk7(program, sf, k, req.decl)
1498
+ : null;
1499
+ if (tdzName !== null) {
1500
+ diags.push(unsupportedDiag("SC1013", loc, `require() below code that can reach its binding '${tdzName}' (Node initializes the binding AT the require and throws ReferenceError on any earlier access, which is not modeled — move the require above the code that can run first)`));
1501
+ continue;
1502
+ }
1503
+ if (dep)
1504
+ deps.push({ dep, backAdmissible: () => false });
1505
+ }
1506
+ }
1507
+ if (!ts.isExternalModule(sf)) {
1508
+ for (const call of nestedBareRequiresOf7(sf)) {
1509
+ const spec = requireSpecOf7(call);
1510
+ const loc = { file: sf.fileName, start: call.getStart(sf), end: call.getEnd() };
1511
+ if (!spec.startsWith("./") && !spec.startsWith("../")) {
1512
+ if (canonicalBuiltinModule(spec) === null && !processModuleAliasRequire7(spec, null)) {
1513
+ // Binding-less by construction — same require-site throw
1514
+ // channel as the statement-level form above.
1515
+ if (probeNodeRequireRefusal(sf.fileName, spec) === null) {
1516
+ diags.push(unsupportedDiag("SC1010", loc, `the '${spec}' module`));
1517
+ }
1518
+ }
1519
+ continue;
1520
+ }
1521
+ const dep = resolveImport7(program, sf, spec);
1522
+ if (dep && dep.fileName.endsWith(".json")) {
1523
+ diags.push(unsupportedDiag("SC1012", loc, "require() of JSON modules"));
1524
+ continue;
1525
+ }
1526
+ if (dep)
1527
+ deps.push({ dep, backAdmissible: () => false });
1528
+ }
1529
+ }
1530
+ }
1531
+ }
1532
+ // Node's evaluation order: depth-first postorder from the entry, each
1533
+ // module once, back-edges answered from the cache (a module already
1534
+ // evaluating is skipped) — which the guarded %init calls reproduce
1535
+ // exactly. Cycles are admitted only where Node's partially-initialized
1536
+ // semantics are UNOBSERVABLE (JS's partial-initialization is otherwise a
1537
+ // silent-misbehavior trap, so SC1016 stays for the rest):
1538
+ // - a DIRECT SELF-IMPORT (the package self-name reference resolving to
1539
+ // the importing module itself — Node's self-reference rule): the
1540
+ // imported bindings alias the module's OWN top-level bindings with
1541
+ // identical timing, evaluation happens once — always benign;
1542
+ // - a longer cycle whose closing edge is backAdmissible (see the edges
1543
+ // declaration: nothing binds, or only namespace objects bound to
1544
+ // bare-statement uses — no read can hit TDZ or a stale slot);
1545
+ // - a cycle passing the DECLARATION-ONLY INIT WINDOW analysis (the
1546
+ // benign-cycle admission block): every module of the cycle's
1547
+ // strongly-connected component has an inert top level and the
1548
+ // closing edge's bindings are used only in deferred positions, so no
1549
+ // read can execute before every member initialized.
1550
+ // The cycle cluster (SCC) memo: computed once, on the first back edge
1551
+ // the cheap per-edge rule does not admit. Tarjan over the same edges
1552
+ // the order walk uses (self-edges skipped — Node's self-reference rule
1553
+ // handles those above).
1554
+ let sccOf = null;
1555
+ const computeSccs = () => {
1556
+ const out = new Map();
1557
+ const index = new Map();
1558
+ const low = new Map();
1559
+ const onStack = new Set();
1560
+ const tstack = [];
1561
+ let next = 0;
1562
+ const strongconnect = (v) => {
1563
+ index.set(v, next);
1564
+ low.set(v, next);
1565
+ next++;
1566
+ tstack.push(v);
1567
+ onStack.add(v);
1568
+ for (const e of edges.get(v) ?? []) {
1569
+ const w = e.dep;
1570
+ if (w === v)
1571
+ continue;
1572
+ if (!index.has(w)) {
1573
+ strongconnect(w);
1574
+ low.set(v, Math.min(low.get(v), low.get(w)));
1575
+ }
1576
+ else if (onStack.has(w)) {
1577
+ low.set(v, Math.min(low.get(v), index.get(w)));
1578
+ }
1579
+ }
1580
+ if (low.get(v) === index.get(v)) {
1581
+ const comp = [];
1582
+ for (;;) {
1583
+ const w = tstack.pop();
1584
+ onStack.delete(w);
1585
+ comp.push(w);
1586
+ if (w === v)
1587
+ break;
1588
+ }
1589
+ for (const w of comp)
1590
+ out.set(w, comp);
1591
+ }
1592
+ };
1593
+ for (const sf of edges.keys())
1594
+ if (!index.has(sf))
1595
+ strongconnect(sf);
1596
+ return out;
1597
+ };
1598
+ const lineOf = (node) => {
1599
+ const nsf = node.getSourceFile();
1600
+ return `${nsf.fileName}:${ts.getLineAndCharacterOfPosition(nsf, node.getStart(nsf)).line + 1}`;
1601
+ };
1602
+ // Cluster verdict memo (keyed by the component array identity): the
1603
+ // reason the cluster's cycles stay fenced, or null when its every
1604
+ // member passes the inert-top-level bar.
1605
+ const sccVerdict = new Map();
1606
+ const cycleAdmissionReason = (importer, e) => {
1607
+ if (e.stmt === undefined)
1608
+ return "the cycle closes through a require() edge";
1609
+ sccOf ??= computeSccs();
1610
+ const comp = sccOf.get(e.dep);
1611
+ if (comp === undefined || comp.length < 2 || !comp.includes(importer)) {
1612
+ return "the cycle's module cluster could not be analyzed";
1613
+ }
1614
+ if (!sccVerdict.has(comp)) {
1615
+ let reason = null;
1616
+ for (const m of comp) {
1617
+ if (isCjsJsFile7(m)) {
1618
+ reason = `${m.fileName} is a CommonJS module — admission covers ES-module cycles only`;
1619
+ break;
1620
+ }
1621
+ const off = nonInertTopLevel7(program, m);
1622
+ if (off !== null) {
1623
+ reason = `top-level code at ${lineOf(off)} can run user code during the cycle's init window — only declaration-only module bodies are admitted`;
1624
+ break;
1625
+ }
1626
+ }
1627
+ sccVerdict.set(comp, reason);
1628
+ }
1629
+ const clusterReason = sccVerdict.get(comp);
1630
+ if (clusterReason !== null)
1631
+ return clusterReason;
1632
+ const use = backEdgeUseOffence7(program, importer, e.stmt);
1633
+ if (use !== null) {
1634
+ return `the cycle-crossing binding '${use.name}' is read at ${lineOf(use.node)}, outside any function body — a read during the init window observes the partially-initialized module (Node's TDZ ReferenceError / stale var), which is not modeled`;
1635
+ }
1636
+ return null;
1637
+ };
1638
+ const order = [];
1639
+ const state = new Map();
1640
+ const stack = [];
1641
+ const visit = (sf) => {
1642
+ state.set(sf, "visiting");
1643
+ stack.push(sf.fileName);
1644
+ for (const e of edges.get(sf) ?? []) {
1645
+ if (e.dep === sf)
1646
+ continue; // self-import: a Node cache hit, not a cycle
1647
+ const s = state.get(e.dep);
1648
+ if (s === "done")
1649
+ continue;
1650
+ if (s === "visiting") {
1651
+ // A back-edge: Node answers it from the cache (the module is
1652
+ // already evaluating) — benign exactly when nothing can observe
1653
+ // the partial initialization through this edge's bindings, by the
1654
+ // cheap per-edge rule or the declaration-only init window rule.
1655
+ if (!e.backAdmissible()) {
1656
+ const reason = cycleAdmissionReason(sf, e);
1657
+ if (reason !== null) {
1658
+ const cycleStart = stack.indexOf(e.dep.fileName);
1659
+ const cycle = [...stack.slice(cycleStart), e.dep.fileName].join(" → ");
1660
+ diags.push(unsupportedDiag("SC1016", { file: e.dep.fileName, start: 0, end: 0 }, `circular imports (${cycle}; ${reason})`));
1661
+ }
1662
+ }
1663
+ continue;
1664
+ }
1665
+ visit(e.dep);
1666
+ }
1667
+ stack.pop();
1668
+ state.set(sf, "done");
1669
+ order.push(sf);
1670
+ };
1671
+ visit(entry);
1672
+ // The startup crash Node's RESOLUTION phase reports: modules resolve
1673
+ // their request specifiers in source order, children linking depth-first
1674
+ // as each resolves (ModuleJob.syncLink) — a preorder walk over the
1675
+ // resolved edges, statement-interleaved, so the first refusal found here
1676
+ // is the one Node reports. Refusal candidates in modules the entry never
1677
+ // reaches keep their compile-fence fallback instead (Node never resolves
1678
+ // them). A found resolution crash wins over the CJS link check below:
1679
+ // Node's fetch/resolve phase runs before instantiate.
1680
+ let resolveCrash = null;
1681
+ {
1682
+ const seen = new Set();
1683
+ const walk = (sf) => {
1684
+ if (seen.has(sf))
1685
+ return null;
1686
+ seen.add(sf);
1687
+ const steps = [
1688
+ ...(depPositions.get(sf) ?? []),
1689
+ ...(refusals.get(sf) ?? []).map((r) => ({ pos: r.pos, crash: r.crash })),
1690
+ ].sort((a, b) => a.pos - b.pos);
1691
+ for (const step of steps) {
1692
+ if (step.crash !== undefined)
1693
+ return step.crash;
1694
+ if (step.dep !== undefined && step.dep !== sf) {
1695
+ const hit = walk(step.dep);
1696
+ if (hit !== null)
1697
+ return hit;
1698
+ }
1699
+ }
1700
+ return null;
1701
+ };
1702
+ resolveCrash = walk(entry);
1703
+ // The crash channel engages only for programs that OTHERWISE pass
1704
+ // preflight: when any other diagnostic already fails the build, every
1705
+ // refusal keeps its compile-fence fallback instead — the report names
1706
+ // each import's story rather than silently dropping edges whose crash
1707
+ // will never compile. (No reachable refusal → same: fallbacks only.)
1708
+ if (resolveCrash === null || diags.length > 0) {
1709
+ resolveCrash = null;
1710
+ for (const cands of refusals.values()) {
1711
+ for (const c of cands)
1712
+ diags.push(c.fallback);
1713
+ }
1714
+ }
1715
+ }
1716
+ const linkCrash = resolveCrash !== null ? null : cjsNamedImportLinkCheck(program, entry, order, diags);
1717
+ return { diags, moduleOrder: order, startupCrash: resolveCrash ?? linkCrash };
1718
+ }
1719
+ /** Walks the entry's ESM instantiate graph exactly as V8 does — children
1720
+ * in request order, each module's own imports checked AFTER its children
1721
+ * (postorder), regular imports sorted by LOCAL name (code units), then
1722
+ * `export ... from` re-exports in source order — and answers the first
1723
+ * named import of a lexer-INVISIBLE CommonJS export as Node's link-time
1724
+ * SyntaxError (the caller compiles the program to that startup crash).
1725
+ * ESM modules OUTSIDE the instantiate graph (reached only through
1726
+ * require() of an ES module from CommonJS) instantiate mid-evaluation in
1727
+ * Node, where the SyntaxError interleaves with output already produced —
1728
+ * not modeled: those get a pointed fence instead. */
1729
+ function cjsNamedImportLinkCheck(program, entry, moduleOrder, diags) {
1730
+ const lexMemo = new Map();
1731
+ const isRelative = (spec) => spec.startsWith("./") || spec.startsWith("../");
1732
+ // Relative specifiers resolve as ever; PROJECT imports (#alias/self-name
1733
+ // — the same package.json-mediated edges preflight admits) join them so
1734
+ // a named import THROUGH one of a CommonJS module keeps Node's lexer
1735
+ // check. Builtin/npm targets contribute nothing here — except opted-in
1736
+ // --npm-static packages, whose CJS entries face Node's lexer exactly
1737
+ // like program CJS files (their JS IS the program now).
1738
+ const resolveEdge = (from, spec) => {
1739
+ if (isRelative(spec))
1740
+ return resolveImport7(program, from, spec);
1741
+ const npmStatic = npmStaticDepSf7(program, from, spec);
1742
+ if (npmStatic !== null)
1743
+ return npmStatic;
1744
+ const p = resolveProjectImport(from.fileName, spec);
1745
+ return p !== null ? (program.getSourceFile(p) ?? null) : null;
1746
+ };
1747
+ // Reexport targets union in only when they resolve to CommonJS program
1748
+ // files (Node's cjsPreparseModuleExports rule).
1749
+ const resolveCjsDep = (from, spec) => {
1750
+ const dep = resolveEdge(from, spec);
1751
+ return dep !== null && isCjsJsFile7(dep) ? dep : null;
1752
+ };
1753
+ const visible = (dep, name) =>
1754
+ // `default` is the module.exports binding itself — always provided.
1755
+ // (cjs-lexer.ts lexes SOURCE TEXT — only strings cross into the
1756
+ // typescript5 island; the SourceFile is just the memo/resolve handle.)
1757
+ name === "default" || cjsLexerVisibleNames(dep, (d) => d.text, resolveCjsDep, lexMemo).has(name);
1758
+ /** The statement's resolved LOCAL CommonJS dependency, when it is an
1759
+ * import/re-export from one. */
1760
+ const cjsDepOf = (sf, spec) => {
1761
+ const dep = resolveEdge(sf, spec);
1762
+ return dep !== null && isCjsJsFile7(dep) ? dep : null;
1763
+ };
1764
+ /** The first lexer-invisible CJS name request of ONE module, in V8's
1765
+ * check order: all regular named imports sorted by local binding name,
1766
+ * then `export { x } from` elements in source order. */
1767
+ const firstInvisibleOf = (sf) => {
1768
+ const entries = [];
1769
+ for (const stmt of sf.statements) {
1770
+ if (!ts.isImportDeclaration(stmt) || stmt.moduleSpecifier === undefined || !ts.isStringLiteral(stmt.moduleSpecifier))
1771
+ continue;
1772
+ const clause = stmt.importClause;
1773
+ if (!clause || clause.phaseModifier === ts.SyntaxKind.TypeKeyword)
1774
+ continue;
1775
+ if (!clause.namedBindings || !ts.isNamedImports(clause.namedBindings))
1776
+ continue;
1777
+ const spec = stmt.moduleSpecifier.text;
1778
+ const dep = cjsDepOf(sf, spec);
1779
+ if (dep === null)
1780
+ continue;
1781
+ for (const e of clause.namedBindings.elements) {
1782
+ if (e.isTypeOnly)
1783
+ continue;
1784
+ const nameNode = e.propertyName ?? e.name;
1785
+ entries.push({ local: e.name.text, exportName: nameNode.text, nameNode, spec, dep });
1786
+ }
1787
+ }
1788
+ entries.sort((a, b) => (a.local < b.local ? -1 : a.local > b.local ? 1 : 0));
1789
+ for (const e of entries) {
1790
+ if (!visible(e.dep, e.exportName))
1791
+ return { exportName: e.exportName, spec: e.spec, nameNode: e.nameNode, sf };
1792
+ }
1793
+ for (const stmt of sf.statements) {
1794
+ if (!ts.isExportDeclaration(stmt) || stmt.isTypeOnly)
1795
+ continue;
1796
+ if (stmt.moduleSpecifier === undefined || !ts.isStringLiteral(stmt.moduleSpecifier))
1797
+ continue;
1798
+ if (!stmt.exportClause || !ts.isNamedExports(stmt.exportClause))
1799
+ continue;
1800
+ const spec = stmt.moduleSpecifier.text;
1801
+ const dep = cjsDepOf(sf, spec);
1802
+ if (dep === null)
1803
+ continue;
1804
+ for (const e of stmt.exportClause.elements) {
1805
+ if (e.isTypeOnly)
1806
+ continue;
1807
+ const nameNode = e.propertyName ?? e.name;
1808
+ if (!visible(dep, nameNode.text))
1809
+ return { exportName: nameNode.text, spec, nameNode, sf };
1810
+ }
1811
+ }
1812
+ return null;
1813
+ };
1814
+ // The instantiate graph: from an ESM entry, every import/re-export
1815
+ // request in source order, recursing into ESM dependencies only (CJS
1816
+ // and JSON modules are leaves — their own requires never join V8's
1817
+ // linking). A CommonJS ENTRY has no instantiate phase at all.
1818
+ const visited = new Set();
1819
+ const dfs = (sf) => {
1820
+ if (visited.has(sf))
1821
+ return null;
1822
+ visited.add(sf);
1823
+ for (const stmt of sf.statements) {
1824
+ if (!ts.isImportDeclaration(stmt) && !(ts.isExportDeclaration(stmt) && !stmt.isTypeOnly))
1825
+ continue;
1826
+ if (ts.isImportDeclaration(stmt) && stmt.importClause?.phaseModifier === ts.SyntaxKind.TypeKeyword)
1827
+ continue;
1828
+ const specNode = stmt.moduleSpecifier;
1829
+ if (specNode === undefined || !ts.isStringLiteral(specNode))
1830
+ continue;
1831
+ const spec = specNode.text;
1832
+ const dep = resolveEdge(sf, spec);
1833
+ if (dep === null || dep.fileName.endsWith(".json") || !isNodeEsmFile7(dep))
1834
+ continue;
1835
+ const bad = dfs(dep);
1836
+ if (bad !== null)
1837
+ return bad;
1838
+ }
1839
+ return firstInvisibleOf(sf);
1840
+ };
1841
+ const bad = isNodeEsmFile7(entry) ? dfs(entry) : null;
1842
+ // ESM modules only require() reaches: same invisibility, but Node's
1843
+ // SyntaxError fires mid-evaluation at the require — a pointed fence
1844
+ // instead of a mismodeled crash position.
1845
+ for (const sf of moduleOrder) {
1846
+ if (!isNodeEsmFile7(sf) || visited.has(sf))
1847
+ continue;
1848
+ const b = firstInvisibleOf(sf);
1849
+ if (b !== null) {
1850
+ diags.push(unsupportedDiag("SC1013", locOf7(b.nameNode), `a named import of a CommonJS export Node's lexer cannot detect, reached through require() of an ES module (Node throws its SyntaxError mid-evaluation at that require; import the CommonJS module's default and read members off it instead)`));
1851
+ }
1852
+ }
1853
+ if (bad === null)
1854
+ return null;
1855
+ // Node's message: V8's generic missing-export SyntaxError, rewritten to
1856
+ // the CommonJS hint form only when the failing SPECIFIER STRING is also
1857
+ // one of the ENTRY module's own requests and the entry's resolution of
1858
+ // it is CommonJS (module_job.js consults the ROOT job's moduleRequests/
1859
+ // commonJsDeps — a deep module's failure keeps the generic wording).
1860
+ let flavored = false;
1861
+ for (const stmt of entry.statements) {
1862
+ if (!ts.isImportDeclaration(stmt) && !(ts.isExportDeclaration(stmt) && !stmt.isTypeOnly))
1863
+ continue;
1864
+ if (ts.isImportDeclaration(stmt) && stmt.importClause?.phaseModifier === ts.SyntaxKind.TypeKeyword)
1865
+ continue;
1866
+ const specNode = stmt.moduleSpecifier;
1867
+ if (specNode === undefined || !ts.isStringLiteral(specNode) || specNode.text !== bad.spec)
1868
+ continue;
1869
+ if (cjsDepOf(entry, bad.spec) !== null) {
1870
+ flavored = true;
1871
+ break;
1872
+ }
1873
+ }
1874
+ const loc = locOf7(bad.nameNode);
1875
+ if (!flavored) {
1876
+ return { message: `The requested module '${bad.spec}' does not provide an export named '${bad.exportName}'`, className: "%SyntaxError", loc };
1877
+ }
1878
+ // The destructuring hint: Node regexes the failing name's SOURCE LINE
1879
+ // for its brace group (greedy, first `{` to last `}` on the line) and
1880
+ // swaps ` as ` for `: ` — reproduced byte for byte, quirks included
1881
+ // (multi-line import statements simply lose the hint).
1882
+ const text = bad.sf.text;
1883
+ const pos = bad.nameNode.getStart(bad.sf);
1884
+ const lineStart = text.lastIndexOf("\n", pos - 1) + 1;
1885
+ const lineEnd = text.indexOf("\n", pos);
1886
+ let line = text.slice(lineStart, lineEnd < 0 ? text.length : lineEnd);
1887
+ if (line.endsWith("\r"))
1888
+ line = line.slice(0, -1);
1889
+ const braces = /{.*}/.exec(line);
1890
+ const destructuring = braces === null ? null : braces[0].replace(/\s+as\s+/g, ": ");
1891
+ return {
1892
+ message: `Named export '${bad.exportName}' not found. The requested module '${bad.spec}' is a CommonJS module, which may not support all module.exports as named exports.\n` +
1893
+ "CommonJS modules can always be imported via the default export, for example using:\n\n" +
1894
+ `import pkg from '${bad.spec}';\n` +
1895
+ (destructuring !== null ? `const ${destructuring} = pkg;\n` : ""),
1896
+ className: "%SyntaxError",
1897
+ loc,
1898
+ };
1899
+ }
1900
+ /* node's builtin-module name list, for the SC1010 wording decision ("the
1901
+ * 'fs' module" vs the generic package message) — shared verbatim with the
1902
+ * retired 5.9.3 lane's builtinModules use. */
1903
+ const nodeBuiltinNames = new Set(builtinModules);
1904
+ /* ── the lowering-facing surface ─────────────────────────────────────────
1905
+ * A family of world-typed helpers the LOWERING imports (require
1906
+ * recognizers, the CJS export-identity analysis, ordered imports,
1907
+ * resolution, locations), exported under the names the 5.9.3 program.ts
1908
+ * established (the phase-2 port kept every spelling); the world-neutral
1909
+ * helpers re-export from shared.ts. */
1910
+ export { ambientDtsPath, builtinDefaultImportModule, canonicalBuiltinModule, fallbackDtsPath, isNodeTypesPath, npmPackageNameOf, overridesDtsPath, SUPPORTED_BUILTIN_MODULES, workspacePackageOfPath, } from "./shared.js";
1911
+ export { requireSpecOf7 as requireSpecOf, isRequireStatement7 as isRequireStatement, requiresOf7 as requiresOf, isNodeEsmFile7 as isNodeEsmFile, resolveImport7 as resolveImport, resolveNpmImport7 as resolveNpmImport, locOf7 as locOf, };
1912
+ /** True for JavaScript source files (.js/.mjs/.cjs) — the files whose
1913
+ * types come entirely from inference (checkJs + JSDoc) and whose classes
1914
+ * declare fields by constructor assignment. */
1915
+ export function isJsSourceFile(sf) {
1916
+ return isJsSourceFileName(sf.fileName);
1917
+ }
1918
+ /** `import type …` declarations and named clauses whose every element is
1919
+ * `type`-qualified are ERASED by tsc at emit — Node's module graph has no
1920
+ * edge there, so module order, cycle detection, init calls, and the
1921
+ * startup-refusal probe must not count them either (zod v4's core modules
1922
+ * are "cyclic" only through `import type * as ns` namespaces — 27 phantom
1923
+ * SC1016 sites against 1 real value-level cycle; and a type-only import
1924
+ * of a module Node cannot resolve is NOT a startup crash — Node never
1925
+ * resolves it). Originated as the provenance-sources pilot's flag-gated
1926
+ * prototype (SCRIPTC_TYPE_ONLY_EDGES), promoted to always-on with the
1927
+ * full-suite differential green. Side-effect imports (no clause) stay
1928
+ * real edges. */
1929
+ function erasedTypeOnlyImport(stmt) {
1930
+ const clause = stmt.importClause;
1931
+ if (clause === undefined)
1932
+ return false; // side-effect import: a real edge
1933
+ if (clause.phaseModifier === ts.SyntaxKind.TypeKeyword)
1934
+ return true;
1935
+ return (clause.name === undefined &&
1936
+ clause.namedBindings !== undefined &&
1937
+ ts.isNamedImports(clause.namedBindings) &&
1938
+ clause.namedBindings.elements.length > 0 &&
1939
+ clause.namedBindings.elements.every((e) => e.isTypeOnly));
1940
+ }
1941
+ /** The re-export twin: `export { type X } from "./m"` with every element
1942
+ * `type`-qualified erases at emit exactly like `export type { X } from`
1943
+ * (which stmt.isTypeOnly already covers) — no module edge. */
1944
+ function erasedTypeOnlyReexport(stmt) {
1945
+ return (stmt.exportClause !== undefined &&
1946
+ ts.isNamedExports(stmt.exportClause) &&
1947
+ stmt.exportClause.elements.length > 0 &&
1948
+ stmt.exportClause.elements.every((e) => e.isTypeOnly));
1949
+ }
1950
+ /** A file's import declarations — and value re-exports with a specifier
1951
+ * (`export { x } from "./y.js"`, `export * from "./y.js"`), which Node
1952
+ * evaluates exactly like imports — in SOURCE ORDER, each with its resolved
1953
+ * user-module dependency (null for npm/builtin/JSON specifiers — those are
1954
+ * not module-order edges). The per-file %init header (lowerFileInit) uses
1955
+ * this to interleave island imports with user-module init calls exactly
1956
+ * where Node would evaluate them. CommonJS require statements are
1957
+ * deliberately NOT here: Node evaluates a require AT ITS STATEMENT, inline
1958
+ * in the module body, so the statement lowering emits their guarded %init
1959
+ * calls in place instead of hoisting them. */
1960
+ export function orderedImportsOf(program, sf) {
1961
+ const out = [];
1962
+ for (const stmt of sf.statements) {
1963
+ if (ts.isExportDeclaration(stmt) && (stmt.isTypeOnly || erasedTypeOnlyReexport(stmt) || !stmt.moduleSpecifier))
1964
+ continue;
1965
+ if (!ts.isImportDeclaration(stmt) && !ts.isExportDeclaration(stmt))
1966
+ continue;
1967
+ if (ts.isImportDeclaration(stmt) && erasedTypeOnlyImport(stmt))
1968
+ continue;
1969
+ if (!stmt.moduleSpecifier || !ts.isStringLiteral(stmt.moduleSpecifier))
1970
+ continue;
1971
+ const spec = stmt.moduleSpecifier.text;
1972
+ const isRelative = spec.startsWith("./") || spec.startsWith("../");
1973
+ // Relative edges as ever; PROJECT imports (#alias/self-name — the
1974
+ // package.json-mediated specifiers preflight admits as user-module
1975
+ // edges) resolve to the same dep so the importer's %init header calls
1976
+ // theirs. Builtin and npm bare specifiers stay null (resolveProjectImport
1977
+ // answers only inside the project) — EXCEPT opted-in --npm-static
1978
+ // packages, whose entries are program modules the header must init.
1979
+ const dep = isRelative
1980
+ ? resolveImport7(program, sf, spec)
1981
+ : (npmStaticDepSf7(program, sf, spec) ?? resolveProjectImportSf7(program, sf, spec));
1982
+ const isJson = dep !== null && dep.fileName.endsWith(".json");
1983
+ out.push({ stmt, dep: isJson ? null : dep });
1984
+ }
1985
+ return out;
1986
+ }
1987
+ /** The program source file a bare specifier reaches when it names an
1988
+ * opted-in --npm-static package (the shipped-JS entry preflight admitted
1989
+ * as a module edge), else null. No offender reporting here — preflight
1990
+ * already classified the import; this is the lookup the module-order and
1991
+ * lowering paths share. */
1992
+ function npmStaticDepSf7(program, sf, spec) {
1993
+ if (!npmStaticActive() || spec.startsWith("./") || spec.startsWith("../"))
1994
+ return null;
1995
+ if (spec.startsWith("node:") || spec.startsWith("#"))
1996
+ return null;
1997
+ const npm = resolveNpmImport7(sf.fileName, spec);
1998
+ if (npm === null || !isNpmStaticPackage(npm.packageName))
1999
+ return null;
2000
+ if (!isJsSourceFileName(npm.typesFile))
2001
+ return null;
2002
+ return program.getSourceFile(npm.typesFile) ?? null;
2003
+ }
2004
+ /** resolveProjectImport lifted to SourceFile answers: the program's file
2005
+ * for a `#alias`/self-name resolution, or null (unresolved, or resolved
2006
+ * outside the program — declaration files included; callers that admit
2007
+ * d.ts answers probe resolveProjectImport directly). */
2008
+ function resolveProjectImportSf7(program, sf, spec) {
2009
+ if (spec.startsWith("node:"))
2010
+ return null;
2011
+ const p = resolveProjectImport(sf.fileName, spec);
2012
+ if (p === null)
2013
+ return null;
2014
+ const dep = program.getSourceFile(p) ?? null;
2015
+ return dep !== null && !dep.isDeclarationFile ? dep : null;
2016
+ }
2017
+ /** True when `expr` is the `module.exports` property access. */
2018
+ export function isModuleExportsAccess(expr) {
2019
+ return (ts.isPropertyAccessExpression(expr) &&
2020
+ !expr.questionDotToken &&
2021
+ ts.isIdentifier(expr.expression) &&
2022
+ expr.expression.text === "module" &&
2023
+ expr.name.text === "exports");
2024
+ }
2025
+ /** Classifies a statement as a CommonJS EXPORT assignment:
2026
+ * - table: `module.exports = { ... }` — the whole export table at once
2027
+ * (obj is null when the RHS is not an object literal — the
2028
+ * recognized-but-unsupported form, fenced by the caller);
2029
+ * - member: `exports.f = <expr>` / `module.exports.f = <expr>`.
2030
+ * Null for everything else. Callers gate on top-level position in a JS
2031
+ * module file — these forms anywhere else keep their generic fences. */
2032
+ export function cjsExportAssignmentOf(stmt) {
2033
+ if (!ts.isExpressionStatement(stmt))
2034
+ return null;
2035
+ const e = stmt.expression;
2036
+ if (!ts.isBinaryExpression(e) || e.operatorToken.kind !== ts.SyntaxKind.EqualsToken)
2037
+ return null;
2038
+ if (isModuleExportsAccess(e.left)) {
2039
+ return { kind: "table", obj: ts.isObjectLiteralExpression(e.right) ? e.right : null, expr: e };
2040
+ }
2041
+ if (ts.isPropertyAccessExpression(e.left) && !e.left.questionDotToken) {
2042
+ const recv = e.left.expression;
2043
+ const isExports = ts.isIdentifier(recv) && recv.text === "exports";
2044
+ if (isExports || isModuleExportsAccess(recv)) {
2045
+ return { kind: "member", name: e.left.name, value: e.right, expr: e };
2046
+ }
2047
+ }
2048
+ return null;
2049
+ }
2050
+ /** Why Node would DISCARD this CommonJS export statement, or null when it
2051
+ * survives (Node's export-object identity rules — checker-free, pure
2052
+ * AST): `module.exports = {...}` REPLACES the export object, so members
2053
+ * attached before it (either spelling), any earlier table, and
2054
+ * `exports.f =` after it (exports still references the replaced object)
2055
+ * are never seen by importers. Lowering fences these instead of shipping
2056
+ * the checker's everything-is-live answer — the checker/runtime split is
2057
+ * exactly the silent-divergence trap. */
2058
+ export function cjsExportDiscardReason(stmt) {
2059
+ const sf = stmt.parent;
2060
+ if (!ts.isSourceFile(sf))
2061
+ return null;
2062
+ const mine = cjsExportAssignmentOf(stmt);
2063
+ if (!mine)
2064
+ return null;
2065
+ const stmts = sf.statements;
2066
+ let lastTable = -1;
2067
+ for (let i = 0; i < stmts.length; i++) {
2068
+ if (cjsExportAssignmentOf(stmts[i])?.kind === "table")
2069
+ lastTable = i;
2070
+ }
2071
+ if (lastTable < 0)
2072
+ return null;
2073
+ const myIdx = stmts.indexOf(stmt);
2074
+ if (mine.kind === "table") {
2075
+ return myIdx < lastTable
2076
+ ? "multiple 'module.exports =' assignments (Node keeps only the last one — this table is discarded)"
2077
+ : null;
2078
+ }
2079
+ if (myIdx < lastTable) {
2080
+ return "export members attached before a 'module.exports =' assignment (Node replaces the export object — importers never see this member; attach it to module.exports AFTER the table)";
2081
+ }
2082
+ const left = mine.expr.left;
2083
+ return ts.isIdentifier(left.expression) && left.expression.text === "exports"
2084
+ ? "'exports.f =' after a 'module.exports =' assignment ('exports' still references the replaced object — Node discards this member; write module.exports.f instead)"
2085
+ : null;
2086
+ }
2087
+ /** The object literal a `module.exports = <expr>` statement's RHS
2088
+ * RESOLVES to (syntactically): the literal itself; `new Proxy(target,
2089
+ * handler)` — Node programs export identity-forwarding proxies over a
2090
+ * table, and the TARGET is the table (traps cannot run in a compiled
2091
+ * program: every member access is statically resolved against the
2092
+ * target's own type); or an identifier naming a top-level const whose
2093
+ * initializer is the literal. Null for everything else. */
2094
+ export function cjsExportTargetLiteral(e, sf) {
2095
+ while (ts.isParenthesizedExpression(e))
2096
+ e = e.expression;
2097
+ if (ts.isObjectLiteralExpression(e))
2098
+ return e;
2099
+ if (ts.isNewExpression(e) &&
2100
+ ts.isIdentifier(e.expression) &&
2101
+ e.expression.text === "Proxy" &&
2102
+ (e.arguments?.length ?? 0) >= 1) {
2103
+ return cjsExportTargetLiteral(e.arguments[0], sf);
2104
+ }
2105
+ if (ts.isIdentifier(e)) {
2106
+ for (const stmt of sf.statements) {
2107
+ if (!ts.isVariableStatement(stmt))
2108
+ continue;
2109
+ if ((stmt.declarationList.flags & ts.NodeFlags.Const) === 0)
2110
+ continue;
2111
+ for (const d of stmt.declarationList.declarations) {
2112
+ if (d.name !== undefined && ts.isIdentifier(d.name) && d.name.text === e.text && d.initializer) {
2113
+ let init = d.initializer;
2114
+ while (ts.isParenthesizedExpression(init))
2115
+ init = init.expression;
2116
+ return ts.isObjectLiteralExpression(init) ? init : null;
2117
+ }
2118
+ }
2119
+ }
2120
+ }
2121
+ return null;
2122
+ }
2123
+ /** The class expression a JS module's WHOLE export is (`module.exports =
2124
+ * class …{}`): the kept (last) top-level `module.exports =` statement's
2125
+ * paren-unwrapped RHS when it is a class expression, together with the
2126
+ * statement it sits in (in-file `module.exports` reads gate on source
2127
+ * order against it). Null when the kept export is anything else. */
2128
+ export function cjsClassExprWholeExportOf(sf) {
2129
+ let last = null;
2130
+ for (const stmt of sf.statements) {
2131
+ const cjs = cjsExportAssignmentOf(stmt);
2132
+ if (!cjs || cjs.kind !== "table")
2133
+ continue;
2134
+ let rhs = cjs.expr.right;
2135
+ while (ts.isParenthesizedExpression(rhs))
2136
+ rhs = rhs.expression;
2137
+ // Any LATER table replaces the export object — a non-class one takes
2138
+ // the class answer back off the table.
2139
+ last = ts.isClassExpression(rhs) ? { classExpr: rhs, stmt } : null;
2140
+ }
2141
+ return last;
2142
+ }
2143
+ /** True when `obj` is the effective export-table literal of a JS module:
2144
+ * the object literal of a top-level `module.exports = { ... }` statement
2145
+ * directly, OR the RESOLVED TARGET of the module's kept `module.exports =`
2146
+ * statement (the identifier/Proxy forms — see cjsExportTargetLiteral).
2147
+ * The gate for the CJS export-property alias re-resolution
2148
+ * (resolveValueSymbol) and the accessor-read lift. */
2149
+ export function isCjsExportTableLiteral(obj) {
2150
+ const assign = obj.parent;
2151
+ if (ts.isBinaryExpression(assign) &&
2152
+ assign.right === obj &&
2153
+ isModuleExportsAccess(assign.left) &&
2154
+ ts.isExpressionStatement(assign.parent) &&
2155
+ ts.isSourceFile(assign.parent.parent)) {
2156
+ return isJsSourceFile(assign.parent.parent);
2157
+ }
2158
+ const decl = obj.parent;
2159
+ if (!ts.isVariableDeclaration(decl) || decl.initializer !== obj)
2160
+ return false;
2161
+ const sf = obj.getSourceFile();
2162
+ if (!isJsSourceFile(sf))
2163
+ return false;
2164
+ for (const stmt of sf.statements) {
2165
+ const cjs = cjsExportAssignmentOf(stmt);
2166
+ if (cjs?.kind === "table" && cjsExportDiscardReason(stmt) === null) {
2167
+ return cjsExportTargetLiteral(cjs.expr.right, sf) === obj;
2168
+ }
2169
+ }
2170
+ return false;
2171
+ }
2172
+ //# sourceMappingURL=program.js.map