xbintsc 0.5.52 → 0.6.8

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 (156) hide show
  1. package/README.md +8 -2
  2. package/README.zh-CN.md +9 -3
  3. package/dist/scripts/fetch-tarball.d.ts +28 -0
  4. package/dist/scripts/fetch-tarball.js +57 -0
  5. package/dist/scripts/fetch-tarball.js.map +1 -0
  6. package/dist/scripts/gui-build-options.d.ts +77 -0
  7. package/dist/scripts/gui-build-options.js +134 -0
  8. package/dist/scripts/gui-build-options.js.map +1 -0
  9. package/dist/src/cli/help.d.ts +1 -1
  10. package/dist/src/cli/hints.d.ts +6 -2
  11. package/dist/src/cli/hints.js +16 -2
  12. package/dist/src/cli/hints.js.map +1 -1
  13. package/dist/src/cli/main.js +9 -0
  14. package/dist/src/cli/main.js.map +1 -1
  15. package/dist/src/driver/app-artifacts.d.ts +2 -0
  16. package/dist/src/driver/app-artifacts.js +3 -3
  17. package/dist/src/driver/app-artifacts.js.map +1 -1
  18. package/dist/src/driver/compiler.js +147 -52
  19. package/dist/src/driver/compiler.js.map +1 -1
  20. package/dist/src/driver/icon.d.ts +1 -1
  21. package/dist/src/driver/icon.js +3 -3
  22. package/dist/src/driver/icon.js.map +1 -1
  23. package/dist/src/driver/macos-sdk.d.ts +32 -0
  24. package/dist/src/driver/macos-sdk.js +50 -0
  25. package/dist/src/driver/macos-sdk.js.map +1 -0
  26. package/dist/src/driver/options.d.ts +6 -0
  27. package/dist/src/driver/platform-support.d.ts +45 -0
  28. package/dist/src/driver/platform-support.js +66 -0
  29. package/dist/src/driver/platform-support.js.map +1 -0
  30. package/dist/src/driver/prebuilt.d.ts +26 -0
  31. package/dist/src/driver/prebuilt.js +58 -0
  32. package/dist/src/driver/prebuilt.js.map +1 -0
  33. package/dist/src/driver/runtime-objects.d.ts +11 -0
  34. package/dist/src/driver/runtime-objects.js +14 -5
  35. package/dist/src/driver/runtime-objects.js.map +1 -1
  36. package/dist/src/driver/toolchain-provider.d.ts +8 -0
  37. package/dist/src/driver/toolchain-provider.js +8 -0
  38. package/dist/src/driver/toolchain-provider.js.map +1 -1
  39. package/dist/src/driver/toolchain.d.ts +20 -1
  40. package/dist/src/driver/toolchain.js +19 -0
  41. package/dist/src/driver/toolchain.js.map +1 -1
  42. package/dist/src/driver/version.d.ts +1 -1
  43. package/dist/src/driver/version.js +1 -1
  44. package/dist/src/driver/version.js.map +1 -1
  45. package/dist/src/driver/win-entry.d.ts +1 -1
  46. package/dist/src/driver/win-entry.js +4 -4
  47. package/dist/src/driver/win-entry.js.map +1 -1
  48. package/dist/src/extensions/gui/html.d.ts +10 -7
  49. package/dist/src/extensions/gui/html.js +53 -29
  50. package/dist/src/extensions/gui/html.js.map +1 -1
  51. package/dist/src/extensions/node/child_process/index.d.ts +4 -3
  52. package/dist/src/extensions/node/child_process/index.js +4 -3
  53. package/dist/src/extensions/node/child_process/index.js.map +1 -1
  54. package/dist/tests/cli/hints.test.js +9 -1
  55. package/dist/tests/cli/hints.test.js.map +1 -1
  56. package/dist/tests/cli/main.test.js +9 -0
  57. package/dist/tests/cli/main.test.js.map +1 -1
  58. package/dist/tests/driver/compiler.test.js +107 -0
  59. package/dist/tests/driver/compiler.test.js.map +1 -1
  60. package/dist/tests/driver/cxx-compile-flags.test.d.ts +1 -0
  61. package/dist/tests/driver/cxx-compile-flags.test.js +24 -0
  62. package/dist/tests/driver/cxx-compile-flags.test.js.map +1 -0
  63. package/dist/tests/driver/macos-sdk.test.d.ts +1 -0
  64. package/dist/tests/driver/macos-sdk.test.js +83 -0
  65. package/dist/tests/driver/macos-sdk.test.js.map +1 -0
  66. package/dist/tests/driver/platform-support.test.d.ts +1 -0
  67. package/dist/tests/driver/platform-support.test.js +41 -0
  68. package/dist/tests/driver/platform-support.test.js.map +1 -0
  69. package/dist/tests/driver/prebuilt.test.d.ts +1 -0
  70. package/dist/tests/driver/prebuilt.test.js +44 -0
  71. package/dist/tests/driver/prebuilt.test.js.map +1 -0
  72. package/dist/tests/driver/toolchain-provider.test.js +24 -0
  73. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  74. package/dist/tests/driver/toolchain.test.js +23 -0
  75. package/dist/tests/driver/toolchain.test.js.map +1 -1
  76. package/dist/tests/e2e/async-await.test.js +4 -2
  77. package/dist/tests/e2e/async-await.test.js.map +1 -1
  78. package/dist/tests/e2e/compile-node.test.js +21 -0
  79. package/dist/tests/e2e/compile-node.test.js.map +1 -1
  80. package/dist/tests/e2e/fetch.test.js +34 -6
  81. package/dist/tests/e2e/fetch.test.js.map +1 -1
  82. package/dist/tests/e2e/gui-example-pelican3d.test.d.ts +10 -0
  83. package/dist/tests/e2e/gui-example-pelican3d.test.js +94 -0
  84. package/dist/tests/e2e/gui-example-pelican3d.test.js.map +1 -0
  85. package/dist/tests/e2e/gui-script.test.js +48 -0
  86. package/dist/tests/e2e/gui-script.test.js.map +1 -1
  87. package/dist/tests/e2e/unhandled-rejection.test.d.ts +14 -0
  88. package/dist/tests/e2e/unhandled-rejection.test.js +107 -0
  89. package/dist/tests/e2e/unhandled-rejection.test.js.map +1 -0
  90. package/dist/tests/extensions/gui.test.js +44 -0
  91. package/dist/tests/extensions/gui.test.js.map +1 -1
  92. package/dist/tests/scripts/fetch-tarball.test.d.ts +1 -0
  93. package/dist/tests/scripts/fetch-tarball.test.js +70 -0
  94. package/dist/tests/scripts/fetch-tarball.test.js.map +1 -0
  95. package/doc/ai/cli.md +6 -0
  96. package/doc/ai/language-support.md +1 -0
  97. package/doc/ai/troubleshooting.md +32 -2
  98. package/doc/ai/zh-CN/cli.md +5 -0
  99. package/doc/ai/zh-CN/language-support.md +1 -0
  100. package/doc/ai/zh-CN/troubleshooting.md +27 -2
  101. package/doc/gui/build.md +7 -3
  102. package/doc/gui/dom.md +3 -1
  103. package/doc/gui/webgl.md +4 -0
  104. package/doc/gui-scripts.md +33 -17
  105. package/doc/gui.md +17 -1
  106. package/doc/implemented/async.md +1 -0
  107. package/doc/implemented/modules.md +9 -6
  108. package/doc/implemented/node/child-process.md +12 -4
  109. package/doc/implemented/runtime-core.md +1 -1
  110. package/doc/npm-package.md +23 -2
  111. package/doc/post/make-js-great-again.md +30 -0
  112. package/doc/requirements.md +31 -0
  113. package/doc/unimplemented/deviations.md +1 -0
  114. package/doc/zh-CN/gui/build.md +4 -2
  115. package/doc/zh-CN/gui/dom.md +3 -1
  116. package/doc/zh-CN/gui/webgl.md +1 -0
  117. package/doc/zh-CN/gui-scripts.md +23 -11
  118. package/doc/zh-CN/gui.md +11 -1
  119. package/doc/zh-CN/implemented/async.md +1 -0
  120. package/doc/zh-CN/implemented/modules.md +8 -4
  121. package/doc/zh-CN/implemented/node/child-process.md +10 -3
  122. package/doc/zh-CN/implemented/runtime-core.md +1 -1
  123. package/doc/zh-CN/npm-package.md +18 -1
  124. package/doc/zh-CN/post/make-js-great-again.md +32 -0
  125. package/doc/zh-CN/requirements.md +25 -0
  126. package/doc/zh-CN/unimplemented/deviations.md +1 -0
  127. package/package.json +7 -7
  128. package/runtime/ext_node/child_process/child_process.c +149 -7
  129. package/runtime/rt.h +4 -0
  130. package/runtime/xt_async.c +4 -0
  131. package/runtime/xt_generator.c +2 -0
  132. package/runtime/xt_loop.c +8 -4
  133. package/runtime/xt_promise.c +103 -0
  134. package/scripts/build-gui.ts +17 -5
  135. package/scripts/build-media.ts +89 -10
  136. package/scripts/build-runtime.ts +7 -2
  137. package/scripts/fetch-tarball.ts +71 -0
  138. package/scripts/gui-build-cache.ts +28 -0
  139. package/scripts/gui-build-options.ts +26 -2
  140. package/scripts/package-release.ts +8 -0
  141. package/src/cli/hints.ts +19 -2
  142. package/src/cli/main.ts +11 -0
  143. package/src/driver/app-artifacts.ts +5 -3
  144. package/src/driver/compiler.ts +167 -56
  145. package/src/driver/icon.ts +3 -2
  146. package/src/driver/macos-sdk.ts +52 -0
  147. package/src/driver/options.ts +6 -0
  148. package/src/driver/platform-support.ts +69 -0
  149. package/src/driver/prebuilt.ts +61 -0
  150. package/src/driver/runtime-objects.ts +26 -5
  151. package/src/driver/toolchain-provider.ts +11 -0
  152. package/src/driver/toolchain.ts +26 -1
  153. package/src/driver/version.ts +1 -1
  154. package/src/driver/win-entry.ts +4 -3
  155. package/src/extensions/gui/html.ts +61 -31
  156. package/src/extensions/node/child_process/index.ts +4 -3
@@ -299,12 +299,25 @@ export function optimizationFlags(
299
299
  return flags;
300
300
  }
301
301
 
302
+ /**
303
+ * `-isysroot` for a resolved macOS SDK, or nothing off macOS.
304
+ *
305
+ * The sysroot travels on the command line and not as `SDKROOT` in the
306
+ * environment because the self-hosted compiler's `spawnSync` ignores
307
+ * `options.env`; flags survive that. See `macos-sdk.ts`.
308
+ */
309
+ export function sysrootFlags(sdkRoot: string | undefined): string[] {
310
+ return sdkRoot === undefined ? [] : ["-isysroot", sdkRoot];
311
+ }
312
+
302
313
  export interface CompileIrOptions {
303
314
  readonly clang: string;
304
315
  readonly irPath: string;
305
316
  readonly objectPath: string;
306
317
  readonly optimize: string;
307
318
  readonly env?: Record<string, string>;
319
+ /** macOS SDK for `-isysroot`; see {@link sysrootFlags}. */
320
+ readonly sdkRoot?: string;
308
321
  /** Emit debug info (`-g`) and keep frame pointers for crash backtraces. */
309
322
  readonly debug?: boolean;
310
323
  /** Compile to LTO bitcode and let the linker optimize across units. */
@@ -332,6 +345,7 @@ export function compileIr(runner: Runner, options: CompileIrOptions): void {
332
345
  ...SECTION_FLAGS,
333
346
  ...debugFlags,
334
347
  ...profileFlags,
348
+ ...sysrootFlags(options.sdkRoot),
335
349
  "-c",
336
350
  options.irPath,
337
351
  "-o",
@@ -349,6 +363,9 @@ export interface LinkOptions {
349
363
  readonly linkerFlags: readonly string[];
350
364
  readonly optimize: string;
351
365
  readonly env?: Record<string, string>;
366
+ /** macOS SDK for `-isysroot`; the linker's stubs (`libm`, `libSystem`) come
367
+ * from it, so a link without one fails with `library 'm' not found`. */
368
+ readonly sdkRoot?: string;
352
369
  readonly lto?: boolean;
353
370
  readonly pgo?: PgoMode;
354
371
  readonly pgoDir?: string;
@@ -361,7 +378,13 @@ function deadStripFlag(): string[] {
361
378
  return ["-Wl,--gc-sections"];
362
379
  }
363
380
 
364
- export function link(runner: Runner, options: LinkOptions): void {
381
+ /**
382
+ * Link the program and return the command result. A successful link can still
383
+ * carry a warning (for example ld64's "object file was built for newer macOS
384
+ * version"); the driver inspects `stderr` to decide whether a prebuilt archive
385
+ * should be rebuilt from source.
386
+ */
387
+ export function link(runner: Runner, options: LinkOptions): CommandResult {
365
388
  const profileFlags = optimizationFlags(options.lto, options.pgo, options.pgoDir);
366
389
  const args = [
367
390
  `-O${options.optimize}`,
@@ -370,6 +393,7 @@ export function link(runner: Runner, options: LinkOptions): void {
370
393
  * understands both ELF/Mach-O and COFF; without it MSVC link.exe rejects
371
394
  * the bitcode object on Windows. */
372
395
  ...(options.lto ? ["-fuse-ld=lld"] : []),
396
+ ...sysrootFlags(options.sdkRoot),
373
397
  ...options.objectPaths,
374
398
  "-o",
375
399
  options.outputPath,
@@ -378,6 +402,7 @@ export function link(runner: Runner, options: LinkOptions): void {
378
402
  ];
379
403
  const result = runner.run(options.clang, args, { env: options.env });
380
404
  if (result.status !== 0) throw new ToolchainError(`${options.clang} link`, result.status, result.stderr);
405
+ return result;
381
406
  }
382
407
 
383
408
  /**
@@ -9,4 +9,4 @@
9
9
  * `main`/`master` only (see `.githooks/pre-commit`), so feature branches never
10
10
  * change this line and merging two of them cannot conflict here.
11
11
  */
12
- export const COMPILER_VERSION = "0.5.52";
12
+ export const COMPILER_VERSION = "0.6.8";
@@ -15,7 +15,7 @@
15
15
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
16
16
  import { join } from "node:path";
17
17
  import { hashParts } from "./cache.js";
18
- import { compileC, type Runner } from "./toolchain.js";
18
+ import { compileC, sysrootFlags, type Runner } from "./toolchain.js";
19
19
 
20
20
  /** The bridge source. `main` is the entry point the runtime emits. */
21
21
  export const WINDOWS_ENTRY_SOURCE = `/* Generated by xbintsc - Windows GUI subsystem entry point. Do not edit. */
@@ -46,14 +46,15 @@ export function ensureWindowsEntryObject(
46
46
  clang: string,
47
47
  cacheDir: string,
48
48
  env?: Record<string, string>,
49
+ sdkRoot?: string,
49
50
  ): string {
50
- const key = hashParts(["win-gui-entry", process.platform, clang, WINDOWS_ENTRY_SOURCE]);
51
+ const key = hashParts(["win-gui-entry", process.platform, clang, sdkRoot ?? "", WINDOWS_ENTRY_SOURCE]);
51
52
  const sourcePath = join(cacheDir, `winentry-${key}.c`);
52
53
  const objectPath = join(cacheDir, `winentry-${key}.o`);
53
54
  if (!existsSync(objectPath)) {
54
55
  mkdirSync(cacheDir, { recursive: true });
55
56
  writeFileSync(sourcePath, WINDOWS_ENTRY_SOURCE);
56
- compileC(runner, clang, sourcePath, objectPath, cacheDir, env);
57
+ compileC(runner, clang, sourcePath, objectPath, cacheDir, env, sysrootFlags(sdkRoot));
57
58
  }
58
59
  return objectPath;
59
60
  }
@@ -12,13 +12,16 @@
12
12
  * This is how xbintsc supports `<script>` without a JavaScript engine: the body
13
13
  * is ordinary TypeScript compiled by the existing front end, ahead of time.
14
14
  *
15
- * Inline `<script lang="ts">body</script>` bodies are wrapped directly. External
16
- * `<script src="./app.ts">` files are read, their top-level `import`/`export
17
- * … from` statements are hoisted to the generated module (with relative
18
- * specifiers rewritten so they resolve from the HTML file), and the remaining
19
- * statements are wrapped in the script function. All scripts run in document
20
- * order after parsing (there is no "eager" execution point), i.e. every script
21
- * is effectively `defer`red.
15
+ * Both forms are compiled the same way: an inline
16
+ * `<script lang="ts">body</script>` body and an external `<script src="./app.ts">`
17
+ * file are parsed as a module, their top-level `import`/`export … from`
18
+ * statements are hoisted to the generated module (with relative specifiers
19
+ * rewritten so they resolve from the HTML file), and the remaining statements
20
+ * are wrapped in the script function. An inline body therefore resolves imports
21
+ * relative to the page that contains it, exactly like `<script type="module">`
22
+ * in a browser — including a `node:`/bare specifier, which links like any other
23
+ * module import. All scripts run in document order after parsing (there is no
24
+ * "eager" execution point), i.e. every script is effectively `defer`red.
22
25
  */
23
26
 
24
27
  import { existsSync, readFileSync } from "node:fs";
@@ -161,12 +164,18 @@ function deferredPrelude(alias: string, declaration: ImportDeclaration): string[
161
164
  return lines;
162
165
  }
163
166
 
164
- /** Split an external script module into hoisted imports and a body. */
165
- function compileModuleSource(htmlPath: string, scriptPath: string, scriptIndex: number): CompiledModule {
167
+ /** Split a script module into hoisted imports and a body. */
168
+ function compileModuleSource(
169
+ htmlPath: string,
170
+ scriptPath: string,
171
+ scriptSource: string,
172
+ scriptIndex: number,
173
+ ): CompiledModule {
166
174
  /* Mask the normalized text: the parser indexes the `SourceFile` text, where
167
175
  * `\r\n` already became `\n`, so blanking statement spans in the raw file
168
- * would remove the wrong characters on a CRLF checkout. */
169
- const source = new SourceFile(scriptPath, readFileSync(scriptPath, "utf8")).text;
176
+ * would remove the wrong characters on a CRLF checkout. An inline body is
177
+ * already normalized (the page was), so this is the identity for it. */
178
+ const source = new SourceFile(scriptPath, scriptSource).text;
170
179
  const bag = new DiagnosticBag();
171
180
  const parsed = new Parser(new SourceFile(scriptPath, source), bag).parseSourceFile();
172
181
  const chars = source.split("");
@@ -272,6 +281,35 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
272
281
  const importedText = new Set<string>();
273
282
  const bound = new Set<string>();
274
283
 
284
+ /* Every script's hoisted imports live at module scope, so they are collected
285
+ * here and placed in front of the script functions. `element` and `hint` only
286
+ * shape the error for an eager binding claimed by two scripts. */
287
+ const hoistImports = (compiled: CompiledModule, element: string, hint: string): void => {
288
+ for (const text of compiled.imports) {
289
+ if (!importedText.has(text)) {
290
+ importedText.add(text);
291
+ imports.push(text);
292
+ }
293
+ }
294
+ lazyImports.push(...compiled.lazyImports);
295
+ for (const name of compiled.localNames) {
296
+ if (bound.has(name)) {
297
+ throw new Error(
298
+ `gui: ${element} imports '${name}', already imported by another <script>; ${hint}`,
299
+ );
300
+ }
301
+ bound.add(name);
302
+ }
303
+ };
304
+ const register = (compiled: CompiledModule, file?: string): string => {
305
+ /* Hash the wrapped body, prelude included: the engine keys the registry by
306
+ * id, so two scripts with the same statements but different deferred
307
+ * imports must not collapse into one entry. */
308
+ const id = hashString([...compiled.prelude, compiled.body].join("\n"));
309
+ scripts.push({ id, body: compiled.body, prelude: compiled.prelude, ...(file ? { file } : {}) });
310
+ return id;
311
+ };
312
+
275
313
  /* The engine's cascade only reads `<style>` elements, so a linked stylesheet
276
314
  * would be dropped at runtime. Inline it (and its `@import`s) now. */
277
315
  const styles = inlineStylesheets(path, page);
@@ -284,32 +322,24 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
284
322
  if (!existsSync(scriptPath)) {
285
323
  throw new Error(`gui: <script src="${src}"> not found (looked for ${scriptPath})`);
286
324
  }
287
- const compiled = compileModuleSource(path, scriptPath, scripts.length);
288
- for (const text of compiled.imports) {
289
- if (!importedText.has(text)) {
290
- importedText.add(text);
291
- imports.push(text);
292
- }
293
- }
294
- lazyImports.push(...compiled.lazyImports);
295
- for (const name of compiled.localNames) {
296
- if (bound.has(name)) {
297
- throw new Error(
298
- `gui: <script src="${src}"> imports '${name}', already imported by another <script src>; alias it in ${scriptPath}`,
299
- );
300
- }
301
- bound.add(name);
302
- }
303
- const id = hashString([...compiled.prelude, compiled.body].join("\n"));
304
- scripts.push({ id, body: compiled.body, prelude: compiled.prelude, file: scriptPath });
325
+ const compiled = compileModuleSource(path, scriptPath, readFileSync(scriptPath, "utf8"), scripts.length);
326
+ hoistImports(compiled, `<script src="${src}">`, `alias it in ${scriptPath}`);
327
+ const id = register(compiled, scriptPath);
305
328
  return `<script data-xt-id="${id}"></script>`;
306
329
  }
307
330
 
308
331
  /* A `src` URL, or a data block: leave the element untouched. */
309
332
  if (src !== undefined) return match;
310
333
  if (inlineBody.trim() === "") return match;
311
- const id = hashString(inlineBody);
312
- scripts.push({ id, body: inlineBody, prelude: [] });
334
+ /* An inline body is compiled exactly like a `<script src>` file, so an
335
+ * `import` links the same way in both: a bare or `node:` specifier is
336
+ * hoisted to the generated module, a relative one is deferred, and both
337
+ * resolve from the HTML file as a browser module script would. Wrapping the
338
+ * body verbatim used to leave an inline `import` inside the script function,
339
+ * where codegen cannot link its binding (TS5001). */
340
+ const compiled = compileModuleSource(path, path, inlineBody, scripts.length);
341
+ hoistImports(compiled, "inline <script>", "alias it");
342
+ const id = register(compiled);
313
343
  return `<script data-xt-id="${id}"></script>`;
314
344
  });
315
345
 
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Node's `child_process` module: `spawnSync(command, args, options)`, backed by
3
3
  * `runtime/ext_node/child_process`. Captures stdout/stderr as UTF-8 strings,
4
- * honours `options.cwd`, and supports `options.stdio: "inherit"` (hand the
5
- * child the parent's stdout/stderr) — which is all the compiler's toolchain
6
- * wrapper and `xbintsc run` need.
4
+ * honours `options.cwd` and `options.env` (replace semantics, like Node), and
5
+ * supports `options.stdio: "inherit"` (hand the child the parent's
6
+ * stdout/stderr) — which is all the compiler's toolchain wrapper and
7
+ * `xbintsc run` need.
7
8
  */
8
9
 
9
10
  import type { NodeModule } from "../module.js";