@tsonic/frontend 0.0.66 → 0.0.68

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 (200) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/generic-function-values.d.ts.map +1 -1
  3. package/dist/generic-function-values.js +4 -2
  4. package/dist/generic-function-values.js.map +1 -1
  5. package/dist/graph/extraction/imports.d.ts.map +1 -1
  6. package/dist/graph/extraction/imports.js +5 -1
  7. package/dist/graph/extraction/imports.js.map +1 -1
  8. package/dist/ir/binding/binding-factory.d.ts.map +1 -1
  9. package/dist/ir/binding/binding-factory.js +239 -26
  10. package/dist/ir/binding/binding-factory.js.map +1 -1
  11. package/dist/ir/binding/binding-types.d.ts +1 -0
  12. package/dist/ir/binding/binding-types.d.ts.map +1 -1
  13. package/dist/ir/binding-resolution.test.js +172 -1
  14. package/dist/ir/binding-resolution.test.js.map +1 -1
  15. package/dist/ir/builder/imports.d.ts.map +1 -1
  16. package/dist/ir/builder/imports.js +13 -1
  17. package/dist/ir/builder/imports.js.map +1 -1
  18. package/dist/ir/builder.test.js +1350 -7
  19. package/dist/ir/builder.test.js.map +1 -1
  20. package/dist/ir/converters/anonymous-synthesis.d.ts +3 -2
  21. package/dist/ir/converters/anonymous-synthesis.d.ts.map +1 -1
  22. package/dist/ir/converters/anonymous-synthesis.js +88 -67
  23. package/dist/ir/converters/anonymous-synthesis.js.map +1 -1
  24. package/dist/ir/converters/expressions/access/access-converter.d.ts.map +1 -1
  25. package/dist/ir/converters/expressions/access/access-converter.js +44 -36
  26. package/dist/ir/converters/expressions/access/access-converter.js.map +1 -1
  27. package/dist/ir/converters/expressions/access/binding-resolution.d.ts.map +1 -1
  28. package/dist/ir/converters/expressions/access/binding-resolution.js.map +1 -1
  29. package/dist/ir/converters/expressions/access/member-resolution.d.ts.map +1 -1
  30. package/dist/ir/converters/expressions/access/member-resolution.js +15 -3
  31. package/dist/ir/converters/expressions/access/member-resolution.js.map +1 -1
  32. package/dist/ir/converters/expressions/calls/call-converter.d.ts +2 -2
  33. package/dist/ir/converters/expressions/calls/call-converter.d.ts.map +1 -1
  34. package/dist/ir/converters/expressions/calls/call-converter.js +108 -4
  35. package/dist/ir/converters/expressions/calls/call-converter.js.map +1 -1
  36. package/dist/ir/converters/expressions/calls/new-converter.d.ts +2 -1
  37. package/dist/ir/converters/expressions/calls/new-converter.d.ts.map +1 -1
  38. package/dist/ir/converters/expressions/calls/new-converter.js +4 -1
  39. package/dist/ir/converters/expressions/calls/new-converter.js.map +1 -1
  40. package/dist/ir/converters/expressions/collections.d.ts.map +1 -1
  41. package/dist/ir/converters/expressions/collections.js +425 -121
  42. package/dist/ir/converters/expressions/collections.js.map +1 -1
  43. package/dist/ir/converters/expressions/dynamic-import.d.ts +6 -0
  44. package/dist/ir/converters/expressions/dynamic-import.d.ts.map +1 -0
  45. package/dist/ir/converters/expressions/dynamic-import.js +121 -0
  46. package/dist/ir/converters/expressions/dynamic-import.js.map +1 -0
  47. package/dist/ir/converters/expressions/functions.d.ts.map +1 -1
  48. package/dist/ir/converters/expressions/functions.js +28 -13
  49. package/dist/ir/converters/expressions/functions.js.map +1 -1
  50. package/dist/ir/converters/expressions/import-meta.d.ts +9 -0
  51. package/dist/ir/converters/expressions/import-meta.d.ts.map +1 -0
  52. package/dist/ir/converters/expressions/import-meta.js +93 -0
  53. package/dist/ir/converters/expressions/import-meta.js.map +1 -0
  54. package/dist/ir/converters/expressions/literals.d.ts +2 -1
  55. package/dist/ir/converters/expressions/literals.d.ts.map +1 -1
  56. package/dist/ir/converters/expressions/literals.js +73 -0
  57. package/dist/ir/converters/expressions/literals.js.map +1 -1
  58. package/dist/ir/converters/flow-narrowing.d.ts.map +1 -1
  59. package/dist/ir/converters/flow-narrowing.js +100 -0
  60. package/dist/ir/converters/flow-narrowing.js.map +1 -1
  61. package/dist/ir/converters/statements/control/loops.d.ts.map +1 -1
  62. package/dist/ir/converters/statements/control/loops.js +72 -8
  63. package/dist/ir/converters/statements/control/loops.js.map +1 -1
  64. package/dist/ir/converters/statements/declarations/variables.d.ts.map +1 -1
  65. package/dist/ir/converters/statements/declarations/variables.js +22 -6
  66. package/dist/ir/converters/statements/declarations/variables.js.map +1 -1
  67. package/dist/ir/expression-converter.d.ts.map +1 -1
  68. package/dist/ir/expression-converter.js +13 -4
  69. package/dist/ir/expression-converter.js.map +1 -1
  70. package/dist/ir/program-context.d.ts +27 -0
  71. package/dist/ir/program-context.d.ts.map +1 -1
  72. package/dist/ir/program-context.js +12 -0
  73. package/dist/ir/program-context.js.map +1 -1
  74. package/dist/ir/type-system/internal/handle-types.d.ts +1 -0
  75. package/dist/ir/type-system/internal/handle-types.d.ts.map +1 -1
  76. package/dist/ir/type-system/internal/type-converter/references.d.ts.map +1 -1
  77. package/dist/ir/type-system/internal/type-converter/references.js +37 -1
  78. package/dist/ir/type-system/internal/type-converter/references.js.map +1 -1
  79. package/dist/ir/type-system/type-system-call-resolution.d.ts +1 -1
  80. package/dist/ir/type-system/type-system-call-resolution.d.ts.map +1 -1
  81. package/dist/ir/type-system/type-system-call-resolution.js +158 -26
  82. package/dist/ir/type-system/type-system-call-resolution.js.map +1 -1
  83. package/dist/ir/type-system/type-system-inference.d.ts +1 -1
  84. package/dist/ir/type-system/type-system-inference.d.ts.map +1 -1
  85. package/dist/ir/type-system/type-system-inference.js +189 -31
  86. package/dist/ir/type-system/type-system-inference.js.map +1 -1
  87. package/dist/ir/type-system/type-system-state.d.ts +14 -0
  88. package/dist/ir/type-system/type-system-state.d.ts.map +1 -1
  89. package/dist/ir/type-system/type-system-state.js +1 -0
  90. package/dist/ir/type-system/type-system-state.js.map +1 -1
  91. package/dist/ir/type-system/type-system.d.ts +5 -1
  92. package/dist/ir/type-system/type-system.d.ts.map +1 -1
  93. package/dist/ir/type-system/type-system.js +5 -2
  94. package/dist/ir/type-system/type-system.js.map +1 -1
  95. package/dist/ir/types/expressions.d.ts +55 -1
  96. package/dist/ir/types/expressions.d.ts.map +1 -1
  97. package/dist/ir/types/index.d.ts +1 -1
  98. package/dist/ir/types/index.d.ts.map +1 -1
  99. package/dist/ir/types/index.js.map +1 -1
  100. package/dist/ir/types/module.d.ts +2 -0
  101. package/dist/ir/types/module.d.ts.map +1 -1
  102. package/dist/ir/types/type-ops.d.ts.map +1 -1
  103. package/dist/ir/types/type-ops.js +1 -0
  104. package/dist/ir/types/type-ops.js.map +1 -1
  105. package/dist/ir/types.d.ts +1 -1
  106. package/dist/ir/types.d.ts.map +1 -1
  107. package/dist/ir/types.js.map +1 -1
  108. package/dist/ir/validation/anonymous-type-lowering-pass.d.ts.map +1 -1
  109. package/dist/ir/validation/anonymous-type-lowering-pass.js +97 -9
  110. package/dist/ir/validation/anonymous-type-lowering-pass.js.map +1 -1
  111. package/dist/ir/validation/arrow-return-finalization-pass.js +3 -0
  112. package/dist/ir/validation/arrow-return-finalization-pass.js.map +1 -1
  113. package/dist/ir/validation/char-validation-pass.d.ts.map +1 -1
  114. package/dist/ir/validation/char-validation-pass.js +10 -0
  115. package/dist/ir/validation/char-validation-pass.js.map +1 -1
  116. package/dist/ir/validation/numeric-coercion-pass.d.ts.map +1 -1
  117. package/dist/ir/validation/numeric-coercion-pass.js +6 -0
  118. package/dist/ir/validation/numeric-coercion-pass.js.map +1 -1
  119. package/dist/ir/validation/numeric-invariants.test.js +180 -0
  120. package/dist/ir/validation/numeric-invariants.test.js.map +1 -1
  121. package/dist/ir/validation/numeric-proof-pass.d.ts.map +1 -1
  122. package/dist/ir/validation/numeric-proof-pass.js +188 -2
  123. package/dist/ir/validation/numeric-proof-pass.js.map +1 -1
  124. package/dist/ir/validation/soundness-gate.d.ts.map +1 -1
  125. package/dist/ir/validation/soundness-gate.js +29 -10
  126. package/dist/ir/validation/soundness-gate.js.map +1 -1
  127. package/dist/ir/validation/soundness-gate.test.js +127 -4
  128. package/dist/ir/validation/soundness-gate.test.js.map +1 -1
  129. package/dist/ir/validation/yield-lowering-pass.d.ts.map +1 -1
  130. package/dist/ir/validation/yield-lowering-pass.js +26 -2
  131. package/dist/ir/validation/yield-lowering-pass.js.map +1 -1
  132. package/dist/object-literal-method-runtime.d.ts +15 -0
  133. package/dist/object-literal-method-runtime.d.ts.map +1 -0
  134. package/dist/object-literal-method-runtime.js +279 -0
  135. package/dist/object-literal-method-runtime.js.map +1 -0
  136. package/dist/program/bindings.test.js +44 -0
  137. package/dist/program/bindings.test.js.map +1 -1
  138. package/dist/program/creation.d.ts.map +1 -1
  139. package/dist/program/creation.js +239 -50
  140. package/dist/program/creation.js.map +1 -1
  141. package/dist/program/creation.test.js +485 -18
  142. package/dist/program/creation.test.js.map +1 -1
  143. package/dist/program/dependency-graph.d.ts.map +1 -1
  144. package/dist/program/dependency-graph.js +49 -22
  145. package/dist/program/dependency-graph.js.map +1 -1
  146. package/dist/program/dependency-graph.test.d.ts +2 -0
  147. package/dist/program/dependency-graph.test.d.ts.map +1 -0
  148. package/dist/program/dependency-graph.test.js +118 -0
  149. package/dist/program/dependency-graph.test.js.map +1 -0
  150. package/dist/resolver/dynamic-import.d.ts +33 -0
  151. package/dist/resolver/dynamic-import.d.ts.map +1 -0
  152. package/dist/resolver/dynamic-import.js +142 -0
  153. package/dist/resolver/dynamic-import.js.map +1 -0
  154. package/dist/resolver/dynamic-import.test.d.ts +2 -0
  155. package/dist/resolver/dynamic-import.test.d.ts.map +1 -0
  156. package/dist/resolver/dynamic-import.test.js +150 -0
  157. package/dist/resolver/dynamic-import.test.js.map +1 -0
  158. package/dist/resolver/import-resolution.d.ts +2 -0
  159. package/dist/resolver/import-resolution.d.ts.map +1 -1
  160. package/dist/resolver/import-resolution.js +20 -3
  161. package/dist/resolver/import-resolution.js.map +1 -1
  162. package/dist/resolver/source-package-resolution.d.ts +11 -0
  163. package/dist/resolver/source-package-resolution.d.ts.map +1 -0
  164. package/dist/resolver/source-package-resolution.js +188 -0
  165. package/dist/resolver/source-package-resolution.js.map +1 -0
  166. package/dist/resolver/source-package-resolution.test.d.ts +2 -0
  167. package/dist/resolver/source-package-resolution.test.d.ts.map +1 -0
  168. package/dist/resolver/source-package-resolution.test.js +77 -0
  169. package/dist/resolver/source-package-resolution.test.js.map +1 -0
  170. package/dist/resolver/types.d.ts +1 -0
  171. package/dist/resolver/types.d.ts.map +1 -1
  172. package/dist/resolver.test.js +83 -0
  173. package/dist/resolver.test.js.map +1 -1
  174. package/dist/surface/profiles.d.ts +2 -0
  175. package/dist/surface/profiles.d.ts.map +1 -1
  176. package/dist/surface/profiles.js +15 -9
  177. package/dist/surface/profiles.js.map +1 -1
  178. package/dist/surface/profiles.test.js +63 -26
  179. package/dist/surface/profiles.test.js.map +1 -1
  180. package/dist/types/module.d.ts +1 -1
  181. package/dist/types/module.d.ts.map +1 -1
  182. package/dist/validation/features.d.ts +1 -1
  183. package/dist/validation/features.d.ts.map +1 -1
  184. package/dist/validation/features.js +37 -6
  185. package/dist/validation/features.js.map +1 -1
  186. package/dist/validation/features.test.js +137 -23
  187. package/dist/validation/features.test.js.map +1 -1
  188. package/dist/validation/imports.d.ts.map +1 -1
  189. package/dist/validation/imports.js +2 -8
  190. package/dist/validation/imports.js.map +1 -1
  191. package/dist/validation/imports.test.js +13 -16
  192. package/dist/validation/imports.test.js.map +1 -1
  193. package/dist/validation/static-safety.d.ts.map +1 -1
  194. package/dist/validation/static-safety.js +118 -67
  195. package/dist/validation/static-safety.js.map +1 -1
  196. package/dist/validator.maximus.test.js +225 -33
  197. package/dist/validator.maximus.test.js.map +1 -1
  198. package/dist/validator.test.js +87 -6
  199. package/dist/validator.test.js.map +1 -1
  200. package/package.json +1 -1
@@ -6,6 +6,7 @@ import { expect } from "chai";
6
6
  import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
+ import * as ts from "typescript";
9
10
  import { createCompilerOptions, createProgram } from "./creation.js";
10
11
  describe("Program Creation", () => {
11
12
  it("should keep noLib mode in js surface mode", () => {
@@ -17,14 +18,51 @@ describe("Program Creation", () => {
17
18
  });
18
19
  expect(options.noLib).to.equal(true);
19
20
  });
20
- it("should keep noLib mode in nodejs surface mode", () => {
21
- const options = createCompilerOptions({
22
- projectRoot: "/tmp/app",
23
- sourceRoot: "/tmp/app/src",
24
- rootNamespace: "App",
25
- surface: "@tsonic/nodejs",
26
- });
27
- expect(options.noLib).to.equal(true);
21
+ it("should widen rootDir to the nearest common ancestor when sourceRoot is outside projectRoot", () => {
22
+ const tempSourceRoot = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-rootdir-"));
23
+ try {
24
+ const projectRoot = path.resolve("/home/jester/repos/tsoniclang/tsonic");
25
+ const options = createCompilerOptions({
26
+ projectRoot,
27
+ sourceRoot: tempSourceRoot,
28
+ rootNamespace: "App",
29
+ surface: "@tsonic/js",
30
+ });
31
+ expect(typeof options.rootDir).to.equal("string");
32
+ if (typeof options.rootDir !== "string")
33
+ return;
34
+ const relativeToProject = path.relative(options.rootDir, projectRoot);
35
+ const relativeToSource = path.relative(options.rootDir, tempSourceRoot);
36
+ expect(relativeToProject.startsWith("..")).to.equal(false);
37
+ expect(path.isAbsolute(relativeToProject)).to.equal(false);
38
+ expect(relativeToSource.startsWith("..")).to.equal(false);
39
+ expect(path.isAbsolute(relativeToSource)).to.equal(false);
40
+ }
41
+ finally {
42
+ fs.rmSync(tempSourceRoot, { recursive: true, force: true });
43
+ }
44
+ });
45
+ it("should widen rootDir to the workspace node_modules root for installed source packages", () => {
46
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-rootdir-node-modules-"));
47
+ try {
48
+ const projectRoot = path.join(tempDir, "packages", "app");
49
+ const sourceRoot = path.join(projectRoot, "src");
50
+ fs.mkdirSync(path.join(tempDir, "node_modules"), { recursive: true });
51
+ fs.mkdirSync(sourceRoot, { recursive: true });
52
+ const options = createCompilerOptions({
53
+ projectRoot,
54
+ sourceRoot,
55
+ rootNamespace: "App",
56
+ surface: "@tsonic/js",
57
+ });
58
+ expect(typeof options.rootDir).to.equal("string");
59
+ if (typeof options.rootDir !== "string")
60
+ return;
61
+ expect(path.resolve(options.rootDir)).to.equal(path.resolve(tempDir));
62
+ }
63
+ finally {
64
+ fs.rmSync(tempDir, { recursive: true, force: true });
65
+ }
28
66
  });
29
67
  it("should allow mutable array index writes in clr surface mode", () => {
30
68
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-clr-array-write-"));
@@ -49,19 +87,19 @@ describe("Program Creation", () => {
49
87
  fs.rmSync(tempDir, { recursive: true, force: true });
50
88
  }
51
89
  });
52
- it("should resolve @tsonic/* imports from the project root (global install)", () => {
90
+ it("should resolve project-local @tsonic/* imports when no authoritative package exists", () => {
53
91
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-creation-"));
54
92
  try {
55
93
  fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
56
94
  const srcDir = path.join(tempDir, "src");
57
95
  fs.mkdirSync(srcDir, { recursive: true });
58
- const fakeDotnetRoot = path.join(tempDir, "node_modules/@tsonic/dotnet");
59
- fs.mkdirSync(fakeDotnetRoot, { recursive: true });
60
- fs.writeFileSync(path.join(fakeDotnetRoot, "package.json"), JSON.stringify({ name: "@tsonic/dotnet", version: "0.0.0", type: "module" }, null, 2));
61
- fs.writeFileSync(path.join(fakeDotnetRoot, "System.d.ts"), "export const Marker: unique symbol;\n");
62
- fs.writeFileSync(path.join(fakeDotnetRoot, "System.js"), "export const Marker = Symbol('marker');\n");
96
+ const fakePkgRoot = path.join(tempDir, "node_modules/@tsonic/custom");
97
+ fs.mkdirSync(fakePkgRoot, { recursive: true });
98
+ fs.writeFileSync(path.join(fakePkgRoot, "package.json"), JSON.stringify({ name: "@tsonic/custom", version: "0.0.0", type: "module" }, null, 2));
99
+ fs.writeFileSync(path.join(fakePkgRoot, "System.d.ts"), "export const Marker: unique symbol;\n");
100
+ fs.writeFileSync(path.join(fakePkgRoot, "System.js"), "export const Marker = Symbol('marker');\n");
63
101
  const entryPath = path.join(srcDir, "index.ts");
64
- fs.writeFileSync(entryPath, 'import { Marker } from "@tsonic/dotnet/System.js";\nexport const ok = Marker;\n');
102
+ fs.writeFileSync(entryPath, 'import { Marker } from "@tsonic/custom/System.js";\nexport const ok = Marker;\n');
65
103
  const result = createProgram([entryPath], {
66
104
  projectRoot: tempDir,
67
105
  sourceRoot: srcDir,
@@ -72,7 +110,7 @@ describe("Program Creation", () => {
72
110
  expect(result.ok).to.equal(true);
73
111
  if (!result.ok)
74
112
  return;
75
- const expectedDts = path.resolve(path.join(fakeDotnetRoot, "System.d.ts"));
113
+ const expectedDts = path.resolve(path.join(fakePkgRoot, "System.d.ts"));
76
114
  expect(result.value.program.getSourceFile(expectedDts)).to.not.equal(undefined);
77
115
  }
78
116
  finally {
@@ -112,8 +150,8 @@ describe("Program Creation", () => {
112
150
  projectRoot: tempDir,
113
151
  sourceRoot: srcDir,
114
152
  rootNamespace: "Test",
115
- surface: "@tsonic/nodejs",
116
- typeRoots: [],
153
+ surface: "@tsonic/js",
154
+ typeRoots: ["node_modules/@tsonic/nodejs"],
117
155
  });
118
156
  expect(result.ok).to.equal(true);
119
157
  if (!result.ok)
@@ -128,6 +166,55 @@ describe("Program Creation", () => {
128
166
  fs.rmSync(tempDir, { recursive: true, force: true });
129
167
  }
130
168
  });
169
+ it("should remap root-namespace internal imports to package index internals", () => {
170
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-root-namespace-internal-"));
171
+ try {
172
+ fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
173
+ const srcDir = path.join(tempDir, "src");
174
+ fs.mkdirSync(srcDir, { recursive: true });
175
+ const jsRoot = path.join(tempDir, "node_modules/@tsonic/js-temp");
176
+ fs.mkdirSync(path.join(jsRoot, "index", "internal"), {
177
+ recursive: true,
178
+ });
179
+ fs.writeFileSync(path.join(jsRoot, "package.json"), JSON.stringify({ name: "@tsonic/js-temp", version: "0.0.0", type: "module" }, null, 2));
180
+ fs.writeFileSync(path.join(jsRoot, "index", "bindings.json"), JSON.stringify({ namespace: "Acme.JsRuntime", types: [] }, null, 2));
181
+ fs.writeFileSync(path.join(jsRoot, "index", "internal", "index.d.ts"), "export interface Date$instance { toISOString(): string; }\nexport type Date = Date$instance;\n");
182
+ fs.writeFileSync(path.join(jsRoot, "index.js"), "export {};\n");
183
+ const nodeRoot = path.join(tempDir, "node_modules/@tsonic/node-temp");
184
+ fs.mkdirSync(path.join(nodeRoot, "index", "internal"), {
185
+ recursive: true,
186
+ });
187
+ fs.writeFileSync(path.join(nodeRoot, "package.json"), JSON.stringify({ name: "@tsonic/node-temp", version: "0.0.0", type: "module" }, null, 2));
188
+ fs.writeFileSync(path.join(nodeRoot, "index", "bindings.json"), JSON.stringify({ namespace: "acme.node", types: [] }, null, 2));
189
+ fs.writeFileSync(path.join(nodeRoot, "index", "internal", "index.d.ts"), [
190
+ 'import type { Date } from "@tsonic/js-temp/Acme.JsRuntime/internal/index.js";',
191
+ "export interface Stats$instance {",
192
+ " mtime: Date;",
193
+ "}",
194
+ "export type Stats = Stats$instance;",
195
+ ].join("\n"));
196
+ fs.writeFileSync(path.join(nodeRoot, "index.js"), "export {};\n");
197
+ const entryPath = path.join(srcDir, "index.ts");
198
+ fs.writeFileSync(entryPath, 'import type { Stats } from "@tsonic/node-temp/index/internal/index.js";\nexport type Result = Stats;\n');
199
+ const result = createProgram([entryPath], {
200
+ projectRoot: tempDir,
201
+ sourceRoot: srcDir,
202
+ rootNamespace: "Test",
203
+ useStandardLib: true,
204
+ typeRoots: [
205
+ "node_modules/@tsonic/node-temp",
206
+ "node_modules/@tsonic/js-temp",
207
+ ],
208
+ });
209
+ expect(result.ok).to.equal(true);
210
+ if (!result.ok)
211
+ return;
212
+ expect(result.value.program.getSourceFile(path.join(jsRoot, "index", "internal", "index.d.ts"))).to.not.equal(undefined);
213
+ }
214
+ finally {
215
+ fs.rmSync(tempDir, { recursive: true, force: true });
216
+ }
217
+ });
131
218
  it("should include declaration files from custom non-@tsonic surface packages", () => {
132
219
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-custom-surface-"));
133
220
  try {
@@ -163,6 +250,45 @@ describe("Program Creation", () => {
163
250
  fs.rmSync(tempDir, { recursive: true, force: true });
164
251
  }
165
252
  });
253
+ it("should load imported source-package modules into the program graph", () => {
254
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-source-package-"));
255
+ try {
256
+ fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
257
+ const srcDir = path.join(tempDir, "src");
258
+ fs.mkdirSync(srcDir, { recursive: true });
259
+ const packageRoot = path.join(tempDir, "node_modules", "@acme", "math");
260
+ fs.mkdirSync(path.join(packageRoot, "tsonic"), { recursive: true });
261
+ fs.mkdirSync(path.join(packageRoot, "src"), { recursive: true });
262
+ fs.writeFileSync(path.join(packageRoot, "package.json"), JSON.stringify({ name: "@acme/math", version: "1.0.0", type: "module" }, null, 2));
263
+ fs.writeFileSync(path.join(packageRoot, "tsonic", "package-manifest.json"), JSON.stringify({
264
+ schemaVersion: 1,
265
+ kind: "tsonic-source-package",
266
+ surfaces: ["@tsonic/js"],
267
+ source: {
268
+ exports: {
269
+ ".": "./src/index.ts",
270
+ },
271
+ },
272
+ }, null, 2));
273
+ const packageEntry = path.join(packageRoot, "src", "index.ts");
274
+ fs.writeFileSync(packageEntry, "export function clamp(x: number, min: number, max: number): number { return x < min ? min : x > max ? max : x; }\n");
275
+ const entryPath = path.join(srcDir, "index.ts");
276
+ fs.writeFileSync(entryPath, 'import { clamp } from "@acme/math";\nexport const x = clamp(10, 0, 5);\n');
277
+ const result = createProgram([entryPath], {
278
+ projectRoot: tempDir,
279
+ sourceRoot: srcDir,
280
+ rootNamespace: "Test",
281
+ surface: "@tsonic/js",
282
+ });
283
+ expect(result.ok).to.equal(true);
284
+ if (!result.ok)
285
+ return;
286
+ expect(result.value.program.getSourceFile(packageEntry)).to.not.equal(undefined);
287
+ }
288
+ finally {
289
+ fs.rmSync(tempDir, { recursive: true, force: true });
290
+ }
291
+ });
166
292
  it("should load js-surface extension bindings without explicit typeRoots", () => {
167
293
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-js-extensions-"));
168
294
  try {
@@ -174,6 +300,13 @@ describe("Program Creation", () => {
174
300
  fs.writeFileSync(path.join(jsRoot, "package.json"), JSON.stringify({ name: "@tsonic/js", version: "0.0.0", type: "module" }, null, 2));
175
301
  fs.writeFileSync(path.join(jsRoot, "index.d.ts"), "declare global { interface String { trim(): string; } }\nexport {};\n");
176
302
  fs.writeFileSync(path.join(jsRoot, "index.js"), "export {};\n");
303
+ fs.writeFileSync(path.join(jsRoot, "tsonic.surface.json"), JSON.stringify({
304
+ schemaVersion: 1,
305
+ id: "@tsonic/js",
306
+ extends: [],
307
+ requiredTypeRoots: ["."],
308
+ useStandardLib: false,
309
+ }, null, 2));
177
310
  fs.writeFileSync(path.join(jsRoot, "index", "bindings.json"), JSON.stringify({
178
311
  namespace: "Tsonic.JSRuntime",
179
312
  types: [
@@ -212,6 +345,159 @@ describe("Program Creation", () => {
212
345
  fs.rmSync(tempDir, { recursive: true, force: true });
213
346
  }
214
347
  });
348
+ it("should keep @tsonic module type queries on the authoritative typeRoot package graph", () => {
349
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-node-authoritative-"));
350
+ try {
351
+ const authoritativeRoot = path.resolve(process.cwd(), "../../../nodejs/versions/10");
352
+ expect(fs.existsSync(path.join(authoritativeRoot, "package.json"))).to.equal(true);
353
+ fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
354
+ const srcDir = path.join(tempDir, "src");
355
+ fs.mkdirSync(srcDir, { recursive: true });
356
+ const projectNodejsRoot = path.join(tempDir, "node_modules/@tsonic/nodejs");
357
+ fs.mkdirSync(projectNodejsRoot, { recursive: true });
358
+ fs.writeFileSync(path.join(projectNodejsRoot, "package.json"), JSON.stringify({
359
+ name: "@tsonic/nodejs",
360
+ version: "9.9.9",
361
+ type: "module",
362
+ types: "./index.d.ts",
363
+ }, null, 2));
364
+ fs.writeFileSync(path.join(projectNodejsRoot, "index.js"), "export {};\n");
365
+ fs.writeFileSync(path.join(projectNodejsRoot, "index.d.ts"), [
366
+ '/// <reference path="./node-aliases.d.ts" />',
367
+ "export declare const path: {",
368
+ " join(...parts: string[]): any;",
369
+ "};",
370
+ "export declare const process: {",
371
+ " cwd(): any;",
372
+ "};",
373
+ ].join("\n"));
374
+ fs.writeFileSync(path.join(projectNodejsRoot, "node-aliases.d.ts"), [
375
+ 'declare module "node:path" {',
376
+ ' export { path } from "@tsonic/nodejs/index.js";',
377
+ ' export const join: typeof import("@tsonic/nodejs/index.js").path.join;',
378
+ "}",
379
+ 'declare module "node:process" {',
380
+ ' export { process } from "@tsonic/nodejs/index.js";',
381
+ ' export const cwd: typeof import("@tsonic/nodejs/index.js").process.cwd;',
382
+ "}",
383
+ "export {};",
384
+ ].join("\n"));
385
+ const entryPath = path.join(srcDir, "index.ts");
386
+ fs.writeFileSync(entryPath, [
387
+ 'import * as path from "node:path";',
388
+ 'import * as process from "node:process";',
389
+ 'export const joined = path.join("a", "b");',
390
+ "export const cwd = process.cwd();",
391
+ ].join("\n"));
392
+ const result = createProgram([entryPath], {
393
+ projectRoot: tempDir,
394
+ sourceRoot: srcDir,
395
+ rootNamespace: "Test",
396
+ surface: "@tsonic/js",
397
+ typeRoots: [authoritativeRoot],
398
+ });
399
+ expect(result.ok).to.equal(true);
400
+ if (!result.ok)
401
+ return;
402
+ const sourceFile = result.value.program.getSourceFile(entryPath);
403
+ expect(sourceFile).to.not.equal(undefined);
404
+ if (!sourceFile)
405
+ return;
406
+ const checker = result.value.program.getTypeChecker();
407
+ const returnTypes = new Map();
408
+ const declarationFlags = new Map();
409
+ const visit = (node) => {
410
+ if (ts.isCallExpression(node) &&
411
+ ts.isPropertyAccessExpression(node.expression)) {
412
+ const callee = node.expression.getText(sourceFile);
413
+ if (callee === "path.join" || callee === "process.cwd") {
414
+ const signature = checker.getResolvedSignature(node);
415
+ returnTypes.set(callee, checker.typeToString(checker.getTypeAtLocation(node)));
416
+ declarationFlags.set(callee, signature?.declaration !== undefined);
417
+ }
418
+ }
419
+ ts.forEachChild(node, visit);
420
+ };
421
+ visit(sourceFile);
422
+ expect(returnTypes.get("path.join")).to.equal("string");
423
+ expect(returnTypes.get("process.cwd")).to.equal("string");
424
+ expect(declarationFlags.get("path.join")).to.equal(true);
425
+ expect(declarationFlags.get("process.cwd")).to.equal(true);
426
+ }
427
+ finally {
428
+ fs.rmSync(tempDir, { recursive: true, force: true });
429
+ }
430
+ });
431
+ it("should keep direct @tsonic imports on the authoritative package graph", () => {
432
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-direct-authoritative-"));
433
+ try {
434
+ const authoritativeRoot = path.resolve(process.cwd(), "../../../nodejs/versions/10");
435
+ expect(fs.existsSync(path.join(authoritativeRoot, "package.json"))).to.equal(true);
436
+ fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
437
+ const srcDir = path.join(tempDir, "src");
438
+ fs.mkdirSync(srcDir, { recursive: true });
439
+ const projectNodejsRoot = path.join(tempDir, "node_modules/@tsonic/nodejs");
440
+ fs.mkdirSync(projectNodejsRoot, { recursive: true });
441
+ fs.writeFileSync(path.join(projectNodejsRoot, "package.json"), JSON.stringify({
442
+ name: "@tsonic/nodejs",
443
+ version: "9.9.9",
444
+ type: "module",
445
+ types: "./index.d.ts",
446
+ }, null, 2));
447
+ fs.writeFileSync(path.join(projectNodejsRoot, "index.js"), "export {};\n");
448
+ fs.writeFileSync(path.join(projectNodejsRoot, "index.d.ts"), [
449
+ "export declare const path: {",
450
+ " join(...parts: string[]): any;",
451
+ "};",
452
+ "export declare const process: {",
453
+ " cwd(): any;",
454
+ "};",
455
+ ].join("\n"));
456
+ const entryPath = path.join(srcDir, "index.ts");
457
+ fs.writeFileSync(entryPath, [
458
+ 'import { path, process } from "@tsonic/nodejs/index.js";',
459
+ 'export const joined = path.join("a", "b");',
460
+ "export const cwd = process.cwd();",
461
+ ].join("\n"));
462
+ const result = createProgram([entryPath], {
463
+ projectRoot: tempDir,
464
+ sourceRoot: srcDir,
465
+ rootNamespace: "Test",
466
+ surface: "@tsonic/js",
467
+ typeRoots: [authoritativeRoot],
468
+ });
469
+ expect(result.ok).to.equal(true);
470
+ if (!result.ok)
471
+ return;
472
+ const sourceFile = result.value.program.getSourceFile(entryPath);
473
+ expect(sourceFile).to.not.equal(undefined);
474
+ if (!sourceFile)
475
+ return;
476
+ const checker = result.value.program.getTypeChecker();
477
+ const returnTypes = new Map();
478
+ const declarationFlags = new Map();
479
+ const visit = (node) => {
480
+ if (ts.isCallExpression(node) &&
481
+ ts.isPropertyAccessExpression(node.expression)) {
482
+ const callee = node.expression.getText(sourceFile);
483
+ if (callee === "path.join" || callee === "process.cwd") {
484
+ const signature = checker.getResolvedSignature(node);
485
+ returnTypes.set(callee, checker.typeToString(checker.getTypeAtLocation(node)));
486
+ declarationFlags.set(callee, signature?.declaration !== undefined);
487
+ }
488
+ }
489
+ ts.forEachChild(node, visit);
490
+ };
491
+ visit(sourceFile);
492
+ expect(returnTypes.get("path.join")).to.equal("string");
493
+ expect(returnTypes.get("process.cwd")).to.equal("string");
494
+ expect(declarationFlags.get("path.join")).to.equal(true);
495
+ expect(declarationFlags.get("process.cwd")).to.equal(true);
496
+ }
497
+ finally {
498
+ fs.rmSync(tempDir, { recursive: true, force: true });
499
+ }
500
+ });
215
501
  it("should typecheck package-provided js globals in noLib mode", () => {
216
502
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-js-globals-"));
217
503
  try {
@@ -249,6 +535,13 @@ declare global {
249
535
 
250
536
  export {};
251
537
  `);
538
+ fs.writeFileSync(path.join(jsRoot, "tsonic.surface.json"), JSON.stringify({
539
+ schemaVersion: 1,
540
+ id: "@tsonic/js",
541
+ extends: [],
542
+ requiredTypeRoots: ["."],
543
+ useStandardLib: false,
544
+ }, null, 2));
252
545
  const entryPath = path.join(srcDir, "index.ts");
253
546
  fs.writeFileSync(entryPath, [
254
547
  'const m = " hi ".trim().toUpperCase();',
@@ -273,6 +566,180 @@ export {};
273
566
  fs.rmSync(tempDir, { recursive: true, force: true });
274
567
  }
275
568
  });
569
+ it("should load root-level global function bindings from a generic surface package", () => {
570
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-generic-surface-globals-"));
571
+ try {
572
+ fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
573
+ const srcDir = path.join(tempDir, "src");
574
+ fs.mkdirSync(srcDir, { recursive: true });
575
+ const surfaceRoot = path.join(tempDir, "node_modules/@fixture/js");
576
+ fs.mkdirSync(surfaceRoot, { recursive: true });
577
+ fs.writeFileSync(path.join(surfaceRoot, "package.json"), JSON.stringify({ name: "@fixture/js", version: "1.0.0", type: "module" }, null, 2));
578
+ fs.writeFileSync(path.join(surfaceRoot, "index.js"), "export {};\n");
579
+ fs.writeFileSync(path.join(surfaceRoot, "index.d.ts"), [
580
+ 'import type { int, long } from "@tsonic/core/types.js";',
581
+ "",
582
+ "declare global {",
583
+ " const console: {",
584
+ " log(...data: unknown[]): void;",
585
+ " };",
586
+ "",
587
+ " function parseInt(str: string, radix?: int): long | undefined;",
588
+ " function setInterval(handler: (...args: unknown[]) => void, timeout?: int, ...args: unknown[]): int;",
589
+ " function clearInterval(id: int): void;",
590
+ "}",
591
+ "",
592
+ "export {};",
593
+ "",
594
+ ].join("\n"));
595
+ fs.writeFileSync(path.join(surfaceRoot, "bindings.json"), JSON.stringify({
596
+ bindings: {
597
+ console: {
598
+ kind: "global",
599
+ assembly: "Tsonic.JSRuntime",
600
+ type: "Tsonic.JSRuntime.console",
601
+ },
602
+ parseInt: {
603
+ kind: "global",
604
+ assembly: "Tsonic.JSRuntime",
605
+ type: "Tsonic.JSRuntime.Globals",
606
+ csharpName: "Globals.parseInt",
607
+ },
608
+ setInterval: {
609
+ kind: "global",
610
+ assembly: "Tsonic.JSRuntime",
611
+ type: "Tsonic.JSRuntime.Timers",
612
+ csharpName: "Timers.setInterval",
613
+ },
614
+ clearInterval: {
615
+ kind: "global",
616
+ assembly: "Tsonic.JSRuntime",
617
+ type: "Tsonic.JSRuntime.Timers",
618
+ csharpName: "Timers.clearInterval",
619
+ },
620
+ },
621
+ }, null, 2));
622
+ fs.writeFileSync(path.join(surfaceRoot, "tsonic.surface.json"), JSON.stringify({
623
+ schemaVersion: 1,
624
+ id: "@fixture/js",
625
+ extends: [],
626
+ requiredTypeRoots: ["."],
627
+ useStandardLib: false,
628
+ }, null, 2));
629
+ const entryPath = path.join(srcDir, "index.ts");
630
+ fs.writeFileSync(entryPath, [
631
+ 'const parsed = parseInt("42", 10);',
632
+ "const timerId = setInterval(() => {}, 1000);",
633
+ "clearInterval(timerId);",
634
+ "console.log(parsed);",
635
+ ].join("\n"));
636
+ const result = createProgram([entryPath], {
637
+ projectRoot: tempDir,
638
+ sourceRoot: srcDir,
639
+ rootNamespace: "Test",
640
+ surface: "@fixture/js",
641
+ useStandardLib: false,
642
+ });
643
+ expect(result.ok).to.equal(true);
644
+ if (!result.ok)
645
+ return;
646
+ expect(result.value.bindings.getBinding("console")).to.deep.equal({
647
+ kind: "global",
648
+ assembly: "Tsonic.JSRuntime",
649
+ type: "Tsonic.JSRuntime.console",
650
+ });
651
+ expect(result.value.bindings.getBinding("parseInt")).to.deep.equal({
652
+ kind: "global",
653
+ assembly: "Tsonic.JSRuntime",
654
+ type: "Tsonic.JSRuntime.Globals",
655
+ csharpName: "Globals.parseInt",
656
+ });
657
+ expect(result.value.bindings.getBinding("setInterval")).to.deep.equal({
658
+ kind: "global",
659
+ assembly: "Tsonic.JSRuntime",
660
+ type: "Tsonic.JSRuntime.Timers",
661
+ csharpName: "Timers.setInterval",
662
+ });
663
+ expect(result.value.bindings.getBinding("clearInterval")).to.deep.equal({
664
+ kind: "global",
665
+ assembly: "Tsonic.JSRuntime",
666
+ type: "Tsonic.JSRuntime.Timers",
667
+ csharpName: "Timers.clearInterval",
668
+ });
669
+ }
670
+ finally {
671
+ fs.rmSync(tempDir, { recursive: true, force: true });
672
+ }
673
+ });
674
+ it("should provide string index access from compiler-owned core globals", () => {
675
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-core-string-index-"));
676
+ try {
677
+ fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
678
+ const srcDir = path.join(tempDir, "src");
679
+ fs.mkdirSync(srcDir, { recursive: true });
680
+ const entryPath = path.join(srcDir, "index.ts");
681
+ fs.writeFileSync(entryPath, [
682
+ 'const source = "abc";',
683
+ "const first = source[0];",
684
+ "export const ok = first;",
685
+ ].join("\n"));
686
+ const result = createProgram([entryPath], {
687
+ projectRoot: tempDir,
688
+ sourceRoot: srcDir,
689
+ rootNamespace: "Test",
690
+ });
691
+ expect(result.ok).to.equal(true);
692
+ }
693
+ finally {
694
+ fs.rmSync(tempDir, { recursive: true, force: true });
695
+ }
696
+ });
697
+ it("should typecheck core IArguments.length in noLib mode", () => {
698
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-core-iarguments-"));
699
+ try {
700
+ fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
701
+ const srcDir = path.join(tempDir, "src");
702
+ fs.mkdirSync(srcDir, { recursive: true });
703
+ const entryPath = path.join(srcDir, "index.ts");
704
+ fs.writeFileSync(entryPath, [
705
+ "export function count(x: number, y: number): number {",
706
+ " return arguments.length + x + y;",
707
+ "}",
708
+ ].join("\n"));
709
+ const result = createProgram([entryPath], {
710
+ projectRoot: tempDir,
711
+ sourceRoot: srcDir,
712
+ rootNamespace: "Test",
713
+ });
714
+ expect(result.ok).to.equal(true);
715
+ }
716
+ finally {
717
+ fs.rmSync(tempDir, { recursive: true, force: true });
718
+ }
719
+ });
720
+ it("should typecheck core IArguments index access in noLib mode", () => {
721
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-core-iarguments-index-"));
722
+ try {
723
+ fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ name: "app", version: "1.0.0", type: "module" }, null, 2));
724
+ const srcDir = path.join(tempDir, "src");
725
+ fs.mkdirSync(srcDir, { recursive: true });
726
+ const entryPath = path.join(srcDir, "index.ts");
727
+ fs.writeFileSync(entryPath, [
728
+ "export function first(x: number, y: number): number {",
729
+ " return (arguments[0] as number) + y;",
730
+ "}",
731
+ ].join("\n"));
732
+ const result = createProgram([entryPath], {
733
+ projectRoot: tempDir,
734
+ sourceRoot: srcDir,
735
+ rootNamespace: "Test",
736
+ });
737
+ expect(result.ok).to.equal(true);
738
+ }
739
+ finally {
740
+ fs.rmSync(tempDir, { recursive: true, force: true });
741
+ }
742
+ });
276
743
  it("should keep JS surface free of CLR string members", () => {
277
744
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-program-js-no-clr-"));
278
745
  try {