xbintsc 0.5.52 → 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/README.zh-CN.md +9 -3
- package/dist/scripts/fetch-tarball.d.ts +28 -0
- package/dist/scripts/fetch-tarball.js +57 -0
- package/dist/scripts/fetch-tarball.js.map +1 -0
- package/dist/scripts/gui-build-options.d.ts +77 -0
- package/dist/scripts/gui-build-options.js +134 -0
- package/dist/scripts/gui-build-options.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/hints.d.ts +6 -2
- package/dist/src/cli/hints.js +16 -2
- package/dist/src/cli/hints.js.map +1 -1
- package/dist/src/cli/main.js +9 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/driver/app-artifacts.d.ts +2 -0
- package/dist/src/driver/app-artifacts.js +3 -3
- package/dist/src/driver/app-artifacts.js.map +1 -1
- package/dist/src/driver/compiler.js +147 -52
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/icon.d.ts +1 -1
- package/dist/src/driver/icon.js +3 -3
- package/dist/src/driver/icon.js.map +1 -1
- package/dist/src/driver/macos-sdk.d.ts +32 -0
- package/dist/src/driver/macos-sdk.js +50 -0
- package/dist/src/driver/macos-sdk.js.map +1 -0
- package/dist/src/driver/options.d.ts +6 -0
- package/dist/src/driver/platform-support.d.ts +45 -0
- package/dist/src/driver/platform-support.js +66 -0
- package/dist/src/driver/platform-support.js.map +1 -0
- package/dist/src/driver/prebuilt.d.ts +26 -0
- package/dist/src/driver/prebuilt.js +58 -0
- package/dist/src/driver/prebuilt.js.map +1 -0
- package/dist/src/driver/runtime-objects.d.ts +11 -0
- package/dist/src/driver/runtime-objects.js +14 -5
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/toolchain-provider.d.ts +8 -0
- package/dist/src/driver/toolchain-provider.js +8 -0
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.d.ts +30 -1
- package/dist/src/driver/toolchain.js +88 -8
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/driver/version.js.map +1 -1
- package/dist/src/driver/win-entry.d.ts +1 -1
- package/dist/src/driver/win-entry.js +4 -4
- package/dist/src/driver/win-entry.js.map +1 -1
- package/dist/src/extensions/gui/html.d.ts +10 -7
- package/dist/src/extensions/gui/html.js +53 -29
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/node/child_process/index.d.ts +4 -3
- package/dist/src/extensions/node/child_process/index.js +4 -3
- package/dist/src/extensions/node/child_process/index.js.map +1 -1
- package/dist/tests/cli/hints.test.js +9 -1
- package/dist/tests/cli/hints.test.js.map +1 -1
- package/dist/tests/cli/main.test.js +9 -0
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/compiler.test.js +107 -0
- package/dist/tests/driver/compiler.test.js.map +1 -1
- package/dist/tests/driver/cxx-compile-flags.test.d.ts +1 -0
- package/dist/tests/driver/cxx-compile-flags.test.js +24 -0
- package/dist/tests/driver/cxx-compile-flags.test.js.map +1 -0
- package/dist/tests/driver/macos-sdk.test.d.ts +1 -0
- package/dist/tests/driver/macos-sdk.test.js +83 -0
- package/dist/tests/driver/macos-sdk.test.js.map +1 -0
- package/dist/tests/driver/platform-support.test.d.ts +1 -0
- package/dist/tests/driver/platform-support.test.js +41 -0
- package/dist/tests/driver/platform-support.test.js.map +1 -0
- package/dist/tests/driver/prebuilt.test.d.ts +1 -0
- package/dist/tests/driver/prebuilt.test.js +44 -0
- package/dist/tests/driver/prebuilt.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +24 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +46 -2
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/async-await.test.js +4 -2
- package/dist/tests/e2e/async-await.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +21 -0
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/fetch.test.js +34 -6
- package/dist/tests/e2e/fetch.test.js.map +1 -1
- package/dist/tests/e2e/gui-example-pelican3d.test.d.ts +10 -0
- package/dist/tests/e2e/gui-example-pelican3d.test.js +94 -0
- package/dist/tests/e2e/gui-example-pelican3d.test.js.map +1 -0
- package/dist/tests/e2e/gui-script.test.js +48 -0
- package/dist/tests/e2e/gui-script.test.js.map +1 -1
- package/dist/tests/e2e/unhandled-rejection.test.d.ts +14 -0
- package/dist/tests/e2e/unhandled-rejection.test.js +107 -0
- package/dist/tests/e2e/unhandled-rejection.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +44 -0
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/dist/tests/scripts/fetch-tarball.test.d.ts +1 -0
- package/dist/tests/scripts/fetch-tarball.test.js +70 -0
- package/dist/tests/scripts/fetch-tarball.test.js.map +1 -0
- package/doc/ai/cli.md +6 -0
- package/doc/ai/language-support.md +1 -0
- package/doc/ai/troubleshooting.md +32 -2
- package/doc/ai/zh-CN/cli.md +5 -0
- package/doc/ai/zh-CN/language-support.md +1 -0
- package/doc/ai/zh-CN/troubleshooting.md +27 -2
- package/doc/gui/build.md +7 -3
- package/doc/gui/dom.md +3 -1
- package/doc/gui/webgl.md +4 -0
- package/doc/gui-scripts.md +33 -17
- package/doc/gui.md +17 -1
- package/doc/implemented/async.md +1 -0
- package/doc/implemented/modules.md +9 -6
- package/doc/implemented/node/child-process.md +12 -4
- package/doc/implemented/runtime-core.md +1 -1
- package/doc/npm-package.md +23 -2
- package/doc/post/make-js-great-again.md +30 -0
- package/doc/requirements.md +36 -3
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/build.md +4 -2
- package/doc/zh-CN/gui/dom.md +3 -1
- package/doc/zh-CN/gui/webgl.md +1 -0
- package/doc/zh-CN/gui-scripts.md +23 -11
- package/doc/zh-CN/gui.md +11 -1
- package/doc/zh-CN/implemented/async.md +1 -0
- package/doc/zh-CN/implemented/modules.md +8 -4
- package/doc/zh-CN/implemented/node/child-process.md +10 -3
- package/doc/zh-CN/implemented/runtime-core.md +1 -1
- package/doc/zh-CN/npm-package.md +18 -1
- package/doc/zh-CN/post/make-js-great-again.md +32 -0
- package/doc/zh-CN/requirements.md +30 -2
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- package/package.json +7 -7
- package/runtime/ext_node/child_process/child_process.c +149 -7
- package/runtime/rt.h +4 -0
- package/runtime/xt_async.c +4 -0
- package/runtime/xt_generator.c +2 -0
- package/runtime/xt_loop.c +8 -4
- package/runtime/xt_promise.c +103 -0
- package/scripts/build-gui.ts +17 -5
- package/scripts/build-media.ts +89 -10
- package/scripts/build-runtime.ts +7 -2
- package/scripts/fetch-tarball.ts +71 -0
- package/scripts/gui-build-cache.ts +28 -0
- package/scripts/gui-build-options.ts +26 -2
- package/scripts/package-release.ts +8 -0
- package/src/cli/hints.ts +19 -2
- package/src/cli/main.ts +11 -0
- package/src/driver/app-artifacts.ts +5 -3
- package/src/driver/compiler.ts +167 -56
- package/src/driver/icon.ts +3 -2
- package/src/driver/macos-sdk.ts +52 -0
- package/src/driver/options.ts +6 -0
- package/src/driver/platform-support.ts +69 -0
- package/src/driver/prebuilt.ts +61 -0
- package/src/driver/runtime-objects.ts +26 -5
- package/src/driver/toolchain-provider.ts +11 -0
- package/src/driver/toolchain.ts +105 -9
- package/src/driver/version.ts +1 -1
- package/src/driver/win-entry.ts +4 -3
- package/src/extensions/gui/html.ts +61 -31
- package/src/extensions/node/child_process/index.ts +4 -3
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
xbintsc doctor
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
它会打印平台、将要使用的工具链(以及来源)、编译器版本、运行时目录、运行时库目录、
|
|
10
|
+
支持的平台下限与图标资源编译器;macOS 上还会打印交给 clang 的 SDK。绝大多数
|
|
11
|
+
"跑不起来"的答案都在这里。
|
|
11
12
|
|
|
12
13
|
## 首先确认:clang 够新吗?
|
|
13
14
|
|
|
@@ -100,11 +101,35 @@ clang 自身失败。抛出的消息包含完整的 clang 命令与 stderr——
|
|
|
100
101
|
```
|
|
101
102
|
该脚本会导入 `vcvarsall`,并把 `xbintsc_CLANG` 指向 Visual Studio 自带的
|
|
102
103
|
LLVM。直接执行 `vcvarsall.bat x64` 也仍然可行。
|
|
104
|
+
- macOS 上 clang 报标准头找不到——`fatal error: 'stdio.h' file not found`、
|
|
105
|
+
`'setjmp.h' file not found` 等。macOS 没有 `/usr/include`;这些头文件在 SDK
|
|
106
|
+
里,而 clang 只有拿到 sysroot(`/usr/bin/clang` 的 shim、`-isysroot` 或
|
|
107
|
+
`SDKROOT`)才会去搜索它。xbintsc 会用 `xcrun --show-sdk-path` 解析 SDK 并以
|
|
108
|
+
`-isysroot` 传给 clang(`SDKROOT` 已设置时不覆盖),因此出现这个错误说明
|
|
109
|
+
`xcrun` 无法为选中的 Xcode 解析出 SDK:检查 `xcode-select -p` 和
|
|
110
|
+
`DEVELOPER_DIR`,并确认该 Xcode 是完整安装。消息里出现的头文件名没有意义
|
|
111
|
+
——运行时先 include 哪个系统头,报的就是哪个。同一个缺失的 sysroot 也会在链接
|
|
112
|
+
阶段表现为 `ld: library 'm' not found`。
|
|
103
113
|
- Linux 上链接器不对——设置 `xbintsc_CLANG`,并/或传
|
|
104
114
|
`xbintsc_LINKER_ARGS=-fuse-ld=lld`;
|
|
105
115
|
- `No C compiler found. Set xbintsc_CLANG to a clang binary.` —— `PATH` 里完全没有
|
|
106
116
|
clang。
|
|
107
117
|
|
|
118
|
+
### `Undefined symbols` / `LNK2038` / `was built for newer macOS version`
|
|
119
|
+
|
|
120
|
+
`@xbintsc/<os>-<arch>` 里的预编译归档由比本机更新的 Xcode Command Line
|
|
121
|
+
Tools/SDK 或 Visual Studio C++ 生成工具构建,链接器无法把它与本机目标文件混用。
|
|
122
|
+
驱动会自动改用包内自带的源码重新编译 `core`/`ext_node` 并重试一次链接,同时打印:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
xbintsc: the prebuilt runtime archive does not link with this toolchain (…); compiling the C runtime from source
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
首次重试会编译 C 运行时,所以较慢;之后的构建复用缓存的目标文件。如果仍然失败,
|
|
129
|
+
冲突的就是 `gui.a`/`media.a`:它们内含 SDL3/FFmpeg,npm 包里没有这些源码,无法从
|
|
130
|
+
包里重编。请改用与本机工具链匹配的发布包,或在源码检出中构建(`npm run gui`、
|
|
131
|
+
`npm run media`)。设置 `xbintsc_PREFER_PREBUILT=0` 可以完全跳过这次重试。
|
|
132
|
+
|
|
108
133
|
### `GUI native library not found at …`
|
|
109
134
|
|
|
110
135
|
`gui` 扩展需要各平台预编译的 `gui.a`/`gui.lib`。可选的 `@xbintsc/<os>-<arch>`
|
package/doc/gui/build.md
CHANGED
|
@@ -45,9 +45,13 @@
|
|
|
45
45
|
minimal static build with only the demuxers/parsers/decoders the media
|
|
46
46
|
elements need, compiles `runtime/ext_media/media.cpp` against it and merges
|
|
47
47
|
everything into `runtime/lib/<os>-<arch>/media.a` (`media.lib` with the MSVC
|
|
48
|
-
ABI on Windows).
|
|
49
|
-
|
|
50
|
-
`
|
|
48
|
+
ABI on Windows). The download retries the transient TLS failures the release
|
|
49
|
+
host is prone to, and the tarball is kept at
|
|
50
|
+
`vendor/ffmpeg-<version>.tar.xz` after extraction, so a rebuild (or CI, which
|
|
51
|
+
caches it by `FFMPEG_VERSION`) extracts the local copy instead of fetching it
|
|
52
|
+
again. `gui.a` does **not** link FFmpeg: the compiler selects the `media` link
|
|
53
|
+
feature for a program that may play media and only then links `media.a` (see
|
|
54
|
+
[Link features](#link-features)).
|
|
51
55
|
- `src/extensions/gui/index.ts` exposes the engine archive through
|
|
52
56
|
`nativeObjects()` (via `findRuntimeLibrary`, so `.a`/`.lib` both work) and the
|
|
53
57
|
OS frameworks through `linkerFlags()`. Its `linkFeatures()` (in
|
package/doc/gui/dom.md
CHANGED
|
@@ -201,7 +201,9 @@ The two parameters (`window`, `document`) are ordinary function arguments, so
|
|
|
201
201
|
script locals need no global-object machinery. External scripts work too:
|
|
202
202
|
`<script src="./counter.ts">` is read at compile time, its imports are hoisted
|
|
203
203
|
(rewritten to resolve from the HTML file) and its body is wrapped the same way —
|
|
204
|
-
so a script module can `import` helpers and still see `document`.
|
|
204
|
+
so a script module can `import` helpers and still see `document`. An inline body
|
|
205
|
+
goes through the same split, so `import` works identically there, `node:fs` and
|
|
206
|
+
other `node:` built-ins included once the `node` extension is enabled. Scripts are
|
|
205
207
|
**compile-time assets**: HTML created at runtime (`innerHTML`, fetched over the
|
|
206
208
|
network) never executes, and `src` URLs (`https://…`, `data:…`) are ignored.
|
|
207
209
|
Everything runs in document order after parsing (effectively deferred). See
|
package/doc/gui/webgl.md
CHANGED
|
@@ -337,6 +337,10 @@ SPIRV-Cross, nor any `webgl_*` member (see [Link features](./build.md#link-featu
|
|
|
337
337
|
- `examples/gui/webgl2-cubes` draws 64 instanced cubes with one draw call,
|
|
338
338
|
`#version 300 es` and a VAO; covered by
|
|
339
339
|
`tests/e2e/gui-example-webgl2.test.ts`.
|
|
340
|
+
- `examples/gui/pelican-3d` is a complete lit scene (procedural sky, fog,
|
|
341
|
+
instanced trees/clouds, a rigged pelican and bicycle) split across nine ES
|
|
342
|
+
modules with `AudioContext` sound; covered by
|
|
343
|
+
`tests/e2e/gui-example-pelican3d.test.ts`.
|
|
340
344
|
|
|
341
345
|
These suites run against whichever backend the host selects: Vulkan on Linux
|
|
342
346
|
(and Windows), Metal on macOS, so the SPIRV-Cross -> MSL path is covered by the
|
package/doc/gui-scripts.md
CHANGED
|
@@ -141,9 +141,10 @@ __registerScript("a1b2…", __xt_script_a1b2);
|
|
|
141
141
|
export default __html;
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
- id is a content hash of the wrapped body (sha256, first 32 hex chars; a
|
|
145
|
+
deferred import's prelude is included, so two scripts with identical
|
|
146
|
+
statements but different imports stay distinct); the body is replaced by an
|
|
147
|
+
empty marker element so the engine can find it in document order.
|
|
147
148
|
- `window` and `document` are **function parameters**, so script globals resolve
|
|
148
149
|
as ordinary locals — **no compiler changes**, no global object.
|
|
149
150
|
- `__registerScript(id, fn)` is a new runtime builtin that stores the closure in
|
|
@@ -151,6 +152,13 @@ export default __html;
|
|
|
151
152
|
- Deferred scripts (`<script defer>`, or all inline scripts, as decided in M9c)
|
|
152
153
|
run after the full document is parsed and the first layout is computed, in
|
|
153
154
|
document order. `DOMContentLoaded` then `load` are fired on `window`.
|
|
155
|
+
- An inline body is split into imports and statements by the **same** routine as
|
|
156
|
+
a `<script src>` file (below): top-level `import`/`export … from` statements
|
|
157
|
+
are hoisted into the generated module, relative specifiers resolve from the
|
|
158
|
+
HTML file, and `export` modifiers are stripped. `import { readFileSync } from
|
|
159
|
+
"node:fs"` inside an inline `<script>` therefore links exactly like the same
|
|
160
|
+
import in an external script — one has to enable the `node` extension, but not
|
|
161
|
+
rewrite the page.
|
|
154
162
|
|
|
155
163
|
### 2b. External `<script src>`
|
|
156
164
|
|
|
@@ -162,21 +170,21 @@ are **deferred**, like a browser module script: the loader emits a synthetic
|
|
|
162
170
|
at `loadHTML` time. The bundler wraps the whole local import graph in functions
|
|
163
171
|
that return their exports (memoised by `__xt_defer`), so top-level module code
|
|
164
172
|
runs after the document exists and two scripts importing the same module share
|
|
165
|
-
one instance. Bare specifiers (`gui`, a package) stay eager.
|
|
173
|
+
one instance. Bare specifiers (`gui`, a package, `node:fs`) stay eager. An
|
|
174
|
+
inline `<script>` body goes through the identical split, so an import behaves
|
|
175
|
+
the same in both forms; the only difference is where the body comes from.
|
|
166
176
|
|
|
167
177
|
A missing `src` file is still a clear error (turned into a bundler diagnostic),
|
|
168
178
|
and `src` URLs (`https://…`, `data:…`, `//host`) are left untouched — they are
|
|
169
179
|
not compile-time assets and never run.
|
|
170
180
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
directly: `state.update = …` on an object literal is not a `TS4003`. A page can
|
|
179
|
-
therefore ship its existing scripts unchanged.
|
|
181
|
+
A wrapped `<script src>` body keeps its own file as the origin of every
|
|
182
|
+
statement in it (the loader reports the body's span in the generated module as a
|
|
183
|
+
*source range*), so a diagnostic points at `app.js:12` rather than at a column
|
|
184
|
+
of `index.html`, and a **plain-JavaScript** script stays untyped exactly like a
|
|
185
|
+
`.js` module imported directly: `state.update = …` on an object literal is not a
|
|
186
|
+
`TS4003`. A page can therefore ship its existing scripts unchanged. An inline
|
|
187
|
+
body's origin is the HTML page itself.
|
|
180
188
|
|
|
181
189
|
```html
|
|
182
190
|
<button id="b">0</button>
|
|
@@ -308,6 +316,10 @@ legacy `target` field over the handle.
|
|
|
308
316
|
module wrapper at `loadHTML` time, so the import graph sees a parsed
|
|
309
317
|
document and shares one instance per module. Missing files surface as
|
|
310
318
|
diagnostics. All scripts run in document order. e2e coverage.
|
|
319
|
+
- **M9c′** ✅ — an inline `<script>` body is split by the same routine, so an
|
|
320
|
+
`import` (a `node:`/bare specifier included) links exactly as it does in a
|
|
321
|
+
`<script src>` file. It used to be wrapped verbatim, leaving an inline
|
|
322
|
+
`import` inside the script function where codegen reported TS5001.
|
|
311
323
|
- **M9d** *(optional)* — detect inline HTML in template literals passed to
|
|
312
324
|
`win.loadHTML(...)` and transform them too (fragile; deferred).
|
|
313
325
|
- **M10 — polish** ✅ — `requestAnimationFrame`/`cancelAnimationFrame` on the
|
|
@@ -331,11 +343,12 @@ legacy `target` field over the handle.
|
|
|
331
343
|
network fetches) do not run. Documented.
|
|
332
344
|
- **Template-literal HTML** passed to `win.loadHTML(…)` is not transformed in
|
|
333
345
|
M9b (only imported assets are). M9d addresses this if needed.
|
|
334
|
-
- **Top-level `await`** inside a deferred `<script src>`
|
|
346
|
+
- **Top-level `await`** inside a deferred `<script src>` or inline script is not
|
|
335
347
|
supported: the module body is wrapped in a synchronous init function.
|
|
336
|
-
- **Assets.** `<script src>` is resolved relative to the HTML asset;
|
|
337
|
-
|
|
338
|
-
global `fetch` is a separate
|
|
348
|
+
- **Assets.** `<script src>` is resolved relative to the HTML asset; an inline
|
|
349
|
+
body's imports resolve from the same place. URL `src` values are ignored. The
|
|
350
|
+
loader does not fetch network URLs; the runtime's global `fetch` is a separate
|
|
351
|
+
API.
|
|
339
352
|
|
|
340
353
|
## Testing
|
|
341
354
|
|
|
@@ -347,6 +360,9 @@ legacy `target` field over the handle.
|
|
|
347
360
|
rewrites `*.foo` and records dependencies.
|
|
348
361
|
- **M9b**: an e2e case with an inline `<script>` incrementing a counter on
|
|
349
362
|
click, asserted through `console.log` from the handler.
|
|
363
|
+
- **M9c′**: unit cases asserting an inline body's imports are hoisted (and its
|
|
364
|
+
relative ones deferred), plus an e2e case where an inline script reads a file
|
|
365
|
+
through `node:fs` and calls a deferred helper module.
|
|
350
366
|
- **M10**: e2e cases for `requestAnimationFrame` (runs each frame, re-queues,
|
|
351
367
|
receives a timestamp, honours `cancelAnimationFrame`) and for
|
|
352
368
|
`offsetWidth`/`offsetHeight`/`contains`.
|
package/doc/gui.md
CHANGED
|
@@ -217,7 +217,16 @@ engine were Rust.
|
|
|
217
217
|
- `vendor/` (SDL3/FreeType/HarfBuzz/glslang for the GUI archive, FFmpeg for `media`)
|
|
218
218
|
is cached per OS/arch, keyed by `scripts/build-gui.ts` /
|
|
219
219
|
`scripts/build-media.ts`. Windows x64 gets its own source cache plus the
|
|
220
|
-
installed `vendor/ffmpeg-build-msvc/dist` prefix.
|
|
220
|
+
installed `vendor/ffmpeg-build-msvc/dist` prefix. On Linux/macOS the
|
|
221
|
+
installed `vendor/ffmpeg-build/dist` prefix has its own cache, keyed by
|
|
222
|
+
`FFMPEG_VERSION` and `scripts/build-media.ts` (which owns the configure
|
|
223
|
+
flags), so a hit skips FFmpeg's configure/make instead of rebuilding it on
|
|
224
|
+
every run: `scripts/build-media.ts` stamps the prefix with the release it
|
|
225
|
+
built and discards it when either the release or the macOS deployment
|
|
226
|
+
target changes. The FFmpeg
|
|
227
|
+
release tarball additionally has a cache keyed only by `FFMPEG_VERSION` and
|
|
228
|
+
shared by every job, so ffmpeg.org's intermittent TLS handshake cannot fail
|
|
229
|
+
a run that already downloaded it once.
|
|
221
230
|
- Windows x64 builds both archives in CI and links a `gui` program that
|
|
222
231
|
exercises the `media` link feature (plus an explicit `--feature media`
|
|
223
232
|
build), using the same Visual Studio `vcvarsall` + bundled LLVM toolchain
|
|
@@ -245,6 +254,10 @@ engine were Rust.
|
|
|
245
254
|
through a memoised module wrapper, so imported modules see the parsed
|
|
246
255
|
document and share state. Missing files become diagnostics. Everything
|
|
247
256
|
runs in document order.
|
|
257
|
+
- **M9c′** ✅ — an inline `<script>` body is split by the same routine, so its
|
|
258
|
+
imports are hoisted (a `node:`/bare specifier links like any module import)
|
|
259
|
+
instead of staying inside the wrapped function, where codegen reported
|
|
260
|
+
TS5001. Inline and `<script src>` now behave identically.
|
|
248
261
|
10. **M10 — polish** ✅
|
|
249
262
|
- `win.requestAnimationFrame(fn)` / `win.cancelAnimationFrame(id)`; callbacks
|
|
250
263
|
run at the top of each frame with the frame timestamp and may mutate the
|
|
@@ -454,6 +467,9 @@ engine were Rust.
|
|
|
454
467
|
memoised module wrapper (so module-level DOM work sees the parsed document),
|
|
455
468
|
while missing files are diagnostics. Loader errors are caught by `loadGraph`
|
|
456
469
|
and reported as build errors.
|
|
470
|
+
- **M9c′** ✅ inline `<script>` bodies are split by the same routine as external
|
|
471
|
+
ones, so an inline `import` is hoisted and links identically (including
|
|
472
|
+
`node:fs` with the `node` extension); only the body's origin differs.
|
|
457
473
|
- **M10** ✅ `requestAnimationFrame`/`cancelAnimationFrame` on window handles
|
|
458
474
|
(callbacks run with the frame timestamp before layout each frame) plus
|
|
459
475
|
`offsetWidth`/`offsetHeight`/`contains` on element handles; e2e coverage in
|
package/doc/implemented/async.md
CHANGED
|
@@ -30,6 +30,7 @@ Location: `runtime/xt_async.c`, `runtime/xt_coroutine.c`, `runtime/xt_promise.c`
|
|
|
30
30
|
- Microtask queue (`xt_microtasks`); `xt_promise_ctor/resolve/reject/static` (including `withResolvers`), instance `then` / `catch` / `finally`.
|
|
31
31
|
- `xt_drain_microtasks` empties the queue at program exit, and the event loop drains it between callbacks.
|
|
32
32
|
- `xt_await` queues a microtask for a non-promise or settled value, and suspends the running async coroutine on a pending promise until it settles.
|
|
33
|
+
- Unhandled rejections are fatal, like Node's default `--unhandled-rejections=throw`. A promise that rejects and is never observed — no `.then` / `.catch`, no `await`, not adopted by another promise — is reported as `Unhandled promise rejection` on stderr once the event loop drains, and the process exits with status 1. The case this protects is the entry point: a program's `main()` is usually `async`, so an exception escaping it would otherwise reject an unobserved promise and the process would print nothing and exit 0. Attaching a handler later, even from a timer, still counts as observed.
|
|
33
34
|
|
|
34
35
|
## Timers and the event loop
|
|
35
36
|
|
|
@@ -21,12 +21,15 @@ Location: `src/parser/parser.ts` (syntax), `src/driver/modules.ts` (bundling),
|
|
|
21
21
|
|
|
22
22
|
## Deferred evaluation
|
|
23
23
|
|
|
24
|
-
- The `gui` `.html` asset loader marks relative
|
|
25
|
-
synthetic `__xt_deferred_module` binding.
|
|
26
|
-
and its local import graph in functions that
|
|
27
|
-
generated script calls them after `loadHTML`
|
|
28
|
-
module). This matches a browser module script,
|
|
29
|
-
once the document exists.
|
|
24
|
+
- The `gui` `.html` asset loader marks relative imports of an inline `<script>`
|
|
25
|
+
body or a `<script src>` file with a synthetic `__xt_deferred_module` binding.
|
|
26
|
+
The bundler then wraps that module and its local import graph in functions that
|
|
27
|
+
return their exports, and the generated script calls them after `loadHTML`
|
|
28
|
+
(`__xt_defer` memoises each module). This matches a browser module script,
|
|
29
|
+
where the graph is evaluated once the document exists.
|
|
30
|
+
- Both script forms are split the same way: top-level `import`/`export … from`
|
|
31
|
+
statements are hoisted into the generated module and `export` modifiers are
|
|
32
|
+
stripped, so a bare or `node:` specifier links like any ordinary module import.
|
|
30
33
|
- Modules also reachable through a normal (non-marker) import stay eager and get
|
|
31
34
|
a thin wrapper over their already-initialised exports.
|
|
32
35
|
- The wrapper body is synchronous: top-level `await` inside a deferred script
|
|
@@ -5,17 +5,25 @@
|
|
|
5
5
|
Location: `src/extensions/node/child_process/index.ts`, `runtime/ext_node/child_process/child_process.c`
|
|
6
6
|
|
|
7
7
|
`spawnSync(command, args[, options])` runs a program to completion and returns
|
|
8
|
-
`{ status, stdout, stderr }`. `options.cwd` sets the working directory
|
|
9
|
-
`options.
|
|
10
|
-
|
|
11
|
-
stdout/stderr are captured
|
|
8
|
+
`{ status, stdout, stderr }`. `options.cwd` sets the working directory,
|
|
9
|
+
`options.env` replaces the child's environment, and `options.stdio: "inherit"`
|
|
10
|
+
hands the child the parent's stdout/stderr (used by `xbintsc run`, so a
|
|
11
|
+
compiled program's output streams live); otherwise stdout/stderr are captured
|
|
12
|
+
as UTF-8 strings.
|
|
12
13
|
|
|
13
14
|
| Option | Notes |
|
|
14
15
|
| --- | --- |
|
|
15
16
|
| `cwd` | working directory for the child |
|
|
17
|
+
| `env` | the child's complete environment; omit it to inherit, spread `process.env` into it to extend |
|
|
16
18
|
| `stdio: "inherit"` | share the parent's stdout/stderr instead of capturing |
|
|
17
19
|
| `encoding` | accepted and ignored (output is always decoded as UTF-8) |
|
|
18
20
|
|
|
21
|
+
`env` follows Node's replace semantics: a present object *is* the environment,
|
|
22
|
+
so a variable the caller does not list is absent in the child. That is what lets
|
|
23
|
+
a caller hand a toolchain a specific `SDKROOT`, `INCLUDE` or `LIB` without
|
|
24
|
+
leaking its own shell setup — and what the macOS SDK fix relies on, because a
|
|
25
|
+
self-hosted compiler runs this same code.
|
|
26
|
+
|
|
19
27
|
On Windows the child is launched with `CreateProcess` and its stdout/stderr are
|
|
20
28
|
drained through anonymous pipes by reader threads. No temporary files are used,
|
|
21
29
|
so a locked-down `%TEMP%` cannot break `spawnSync`, and output larger than a pipe
|
|
@@ -28,5 +28,5 @@ standard-library surface have their own pages:
|
|
|
28
28
|
- Operator helpers: `xt_in` (`in`), `xt_delete` (`delete`), `xt_rest_args` (rest parameters / `arguments`).
|
|
29
29
|
- Box: `box_new/get/set` (for closure-captured variables).
|
|
30
30
|
- Functions and closures: `arg`, `closure_new/call/env/arity`.
|
|
31
|
-
- Exceptions: `xt_try_enter` / `xt_try_exception` / `xt_try_leave` maintain the `_setjmp` frame stack; `xt_throw` long-jumps to the nearest `try` when a frame exists, otherwise prints `Uncaught ...` and exits. Codegen also maintains a shadow call stack (function name, source file and line) that `Error.stack` captures (see [Standard library](./stdlib.md)), and a fatal-signal handler dumps the same frames on a native crash. `--debug` additionally emits `!DICompileUnit` / `!DISubprogram` / `!DILocation` metadata and compiles with `-g -fno-omit-frame-pointer`, so gdb/lldb/WinDbg map instructions back to source lines.
|
|
31
|
+
- Exceptions: `xt_try_enter` / `xt_try_exception` / `xt_try_leave` maintain the `_setjmp` frame stack; `xt_throw` long-jumps to the nearest `try` when a frame exists, otherwise prints `Uncaught ...` and exits. A promise rejection nobody observes is fatal too: `xt_promise_report_unhandled` runs once the event loop drains, prints `Unhandled promise rejection` plus the reason and exits with status 1 (see [Async](./async.md)). Codegen also maintains a shadow call stack (function name, source file and line) that `Error.stack` captures (see [Standard library](./stdlib.md)), and a fatal-signal handler dumps the same frames on a native crash. `--debug` additionally emits `!DICompileUnit` / `!DISubprogram` / `!DILocation` metadata and compiles with `-g -fno-omit-frame-pointer`, so gdb/lldb/WinDbg map instructions back to source lines.
|
|
32
32
|
- Objects / functions: `xt_object` with a prototype chain, `xt_function` with a property bag (static members and `prototype`); `xt_new` (instantiation), `xt_instance_of` (prototype chain), `xt_object_freeze/is_frozen/from_entries`; `xt_constructors.c` holds the first-class constructor cache, constructor prototypes and static-value dispatch.
|
package/doc/npm-package.md
CHANGED
|
@@ -216,9 +216,29 @@ necessarily next to the config file); set `xbintsc_CACHE_DIR` to put it
|
|
|
216
216
|
elsewhere. Expect that fallback build to take noticeably longer than the ones
|
|
217
217
|
after it.
|
|
218
218
|
|
|
219
|
+
The prebuilt archives are produced by the release runner's toolchain and target
|
|
220
|
+
the floors in
|
|
221
|
+
[requirements](./requirements.md#supported-platforms) (macOS 11.0, Windows 11,
|
|
222
|
+
glibc 2.39). When that toolchain is newer than the one on this machine — a newer
|
|
223
|
+
Xcode Command Line Tools/SDK or Visual Studio C++ toolset — the linker can
|
|
224
|
+
reject the archive with unresolved symbols (`LNK2038`, `LNK2019`, `Undefined
|
|
225
|
+
symbols`, `undefined reference`) or a `was built for newer macOS version`
|
|
226
|
+
warning. The driver then retries the link **automatically** with
|
|
227
|
+
`core`/`ext_node` compiled from the sources shipped in the package, so a
|
|
228
|
+
mismatched archive costs time, not a failed build:
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
xbintsc: the prebuilt runtime archive does not match this toolchain (…); compiling the C runtime from source
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The retry is not remembered: after a toolchain upgrade a later build links the
|
|
235
|
+
archive again. The bundled `gui.a`/`media.a` are the exception — they contain
|
|
236
|
+
SDL3/FFmpeg, whose sources are not part of the npm package, so they cannot be
|
|
237
|
+
rebuilt this way. If they are the incompatible archive, use a release built for
|
|
238
|
+
this toolchain or a source checkout.
|
|
239
|
+
|
|
219
240
|
To ignore the platform archive on purpose and compile the runtime from the
|
|
220
|
-
sources shipped in the package — for example after patching `runtime
|
|
221
|
-
the prebuilt archive does not match the environment — set:
|
|
241
|
+
sources shipped in the package — for example after patching `runtime/` — set:
|
|
222
242
|
|
|
223
243
|
```bash
|
|
224
244
|
xbintsc_PREFER_PREBUILT=0 npx xbintsc build src/main.ts --out build
|
|
@@ -439,6 +459,7 @@ the same file under `node_modules/xbintsc/doc/ai/`.
|
|
|
439
459
|
| The first build takes a long time | The optional `@xbintsc/<os>-<arch>` package was skipped (or this host has no prebuilt package), so the runtime C sources are compiled and cached. Check `npm ls @xbintsc/<os>-<arch>` and reinstall without `--omit=optional`; `--time` confirms. |
|
|
440
460
|
| Permission errors writing `.xbintsc/` | Set `xbintsc_CACHE_DIR` to a writable directory, or run from a writable working directory. |
|
|
441
461
|
| `module 'fs' is provided by the 'node' extension` | Add `--ext node`, or `"extensions": ["node"]` in the config. |
|
|
462
|
+
| Link fails with `LNK2038`, `LNK2019`, `Undefined symbols` or `was built for newer macOS version` | The prebuilt archive was built by a newer Xcode/Visual Studio toolchain. The driver retries automatically with the runtime's C sources; if the error persists it is `gui.a`/`media.a`, which cannot be rebuilt from the npm package — use a matching release or a source checkout. |
|
|
442
463
|
| `GUI native library not found` / `media native library not found` | The optional platform package that ships `gui.a`/`gui.lib` or `media.a`/`media.lib` was skipped, or this host has no prebuilt GUI/media archive (Windows arm64). Reinstall without `--omit=optional`, use a Linux/macOS/Windows x64 release archive, or build the archive from a source checkout. An auto-detected `media` feature with a missing archive only warns; `--feature media` makes it an error. |
|
|
443
464
|
| `require("xbintsc")` fails | The package is ESM-only; use `import` (or dynamic `import()`). |
|
|
444
465
|
| Windows link errors about missing MSVC libraries | Run from a Native Tools prompt, install the VS C++ build tools, and check `npx xbintsc doctor`. |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# This Might Be the Most Exciting Thing for JS Developers This Year
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
The image above is a 3D pelican riding a bicycle that I built with JS + HTML + WebGL2, and the framerate is excellent too.
|
|
6
|
+
|
|
7
|
+
You might say, "Isn't that just a matter of having hands and an AI these days?"
|
|
8
|
+
|
|
9
|
+
But what if I told you that I compiled that very thing into a native binary with xbintsc — under 10 MB, able to run with no WebView environment at all, and able to mix Node.js and HTML in a single file, like this:
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<html>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="out">loading...</div>
|
|
15
|
+
<script lang="ts">
|
|
16
|
+
import { readFileSync } from "fs";
|
|
17
|
+
|
|
18
|
+
const out = document.getElementById("out");
|
|
19
|
+
const world = readFileSync("data.txt", "utf8");
|
|
20
|
+
out.textContent = "hello " + world;
|
|
21
|
+
</script>
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Keep in mind that Chrome alone used to take at least 100 MB. The xbintsc build is 10 MB and doesn't need a WebView environment — and that means far more than just a massive reduction in the footprint of desktop development, or a more correct implementation path.
|
|
27
|
+
|
|
28
|
+
What is truly epoch-making is this: it is now possible for JS developers to run the applications they want to build on embedded devices.
|
|
29
|
+
|
|
30
|
+
[xbintsc on GitHub](https://github.com/zy445566/xbintsc)
|
package/doc/requirements.md
CHANGED
|
@@ -15,6 +15,32 @@ no requirements on any platform.
|
|
|
15
15
|
Installing the compiler from npm instead of using a release archive? Node.js 22+
|
|
16
16
|
then runs the compiler itself; see [Using xbintsc from npm](./npm-package.md).
|
|
17
17
|
|
|
18
|
+
## Supported platforms
|
|
19
|
+
|
|
20
|
+
Release binaries and the prebuilt runtime archives target the oldest platform
|
|
21
|
+
versions xbintsc supports. CI builds them with the **oldest toolchain that can
|
|
22
|
+
still assemble a release for each host**, so a newer Xcode or Visual Studio on
|
|
23
|
+
your machine can link them — the reverse direction is what fails with `LNK2038`,
|
|
24
|
+
`Undefined symbols` or `was built for newer macOS version`.
|
|
25
|
+
|
|
26
|
+
| Platform | Produced binaries run on | Build baseline (CI) |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| macOS x64 / arm64 | **macOS 11.0** (Big Sur) and newer | the oldest installed Xcode with clang 16+, `MACOSX_DEPLOYMENT_TARGET=11.0` |
|
|
29
|
+
| Windows x64 | **Windows 11** and newer | the `windows-2025` image's toolset (VS 2026) |
|
|
30
|
+
| Windows arm64 | **Windows 11** | the `windows-11-arm` image's toolset (currently VS 2026; no older arm64 image exists) |
|
|
31
|
+
| Linux x64 / arm64 | **glibc 2.39** (Ubuntu 24.04) and newer | clang 16 + lld 16 on `ubuntu-24.04` |
|
|
32
|
+
|
|
33
|
+
The compiler toolchain floors are Xcode 16 (Apple clang 16) on macOS, the
|
|
34
|
+
`windows-2025` image's Visual Studio (currently VS 2026) on Windows and clang 16
|
|
35
|
+
on Linux. Building therefore needs a newer host than the produced binary does:
|
|
36
|
+
Xcode 16 itself requires macOS 14.5+, so a macOS 11–14 build machine needs a
|
|
37
|
+
standalone LLVM 16+ (for example Homebrew's `llvm`). `xbintsc doctor` prints the
|
|
38
|
+
floor for the host it runs on. If the archives were built with a
|
|
39
|
+
newer toolchain than yours, `build` retries with the C runtime's shipped
|
|
40
|
+
sources (see [the npm package guide](./npm-package.md#the-first-build-and-the-cache));
|
|
41
|
+
the bundled `gui.a`/`media.a` cannot be rebuilt that way, because SDL3/FFmpeg
|
|
42
|
+
sources are not part of the package.
|
|
43
|
+
|
|
18
44
|
clang 15 and older fail on the first string literal in any program, with:
|
|
19
45
|
|
|
20
46
|
```
|
|
@@ -112,6 +138,11 @@ xcode-select -p # prints the active developer directory
|
|
|
112
138
|
xcrun --show-sdk-path # prints the SDK path
|
|
113
139
|
```
|
|
114
140
|
|
|
141
|
+
xbintsc runs the command in the second line itself and passes the result to
|
|
142
|
+
clang as `-isysroot`, so a clang selected by absolute path (the CI baseline, or
|
|
143
|
+
`xbintsc_CLANG`) still finds the SDK's C headers and link stubs. Setting
|
|
144
|
+
`SDKROOT` yourself takes precedence and pins a specific SDK.
|
|
145
|
+
|
|
115
146
|
## Linux — install clang and lld
|
|
116
147
|
|
|
117
148
|
A normal glibc-based distribution plus `clang` and `lld` is enough:
|
|
@@ -139,9 +170,11 @@ winget install -e --id Microsoft.VisualStudio.2022.BuildTools `
|
|
|
139
170
|
--override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Llvm.Clang --includeRecommended"
|
|
140
171
|
```
|
|
141
172
|
|
|
142
|
-
The LLVM bundled with Visual Studio
|
|
143
|
-
|
|
144
|
-
|
|
173
|
+
The LLVM bundled with Visual Studio (`VC\Tools\Llvm\x64\bin\clang.exe`) is what
|
|
174
|
+
`scripts/setup-vs-toolchain.ps1` selects. `xbintsc` probes both install layouts —
|
|
175
|
+
the year-named one (`2019`, `2022`) and the version-numbered one used by Visual
|
|
176
|
+
Studio 2026 and later (`18\Community`, `18\Insiders`, …) — so a newer installation
|
|
177
|
+
is found without setting `xbintsc_CLANG`. A standalone LLVM also works for
|
|
145
178
|
`xbintsc` itself (`winget install -e --id LLVM.LLVM`), but the GUI/media archive
|
|
146
179
|
builds and the CI setup use the Visual Studio one so one toolchain produces the
|
|
147
180
|
runtime, the archives and the final binary. Either way the compiler must target
|
|
@@ -16,6 +16,7 @@ These features **compile and run**, but the result does not fully match ECMAScri
|
|
|
16
16
|
| Memory management | Non-moving mark-sweep collector behind `xt_alloc`: explicit roots, subsystem root providers and a conservative C-stack scan. Single-threaded and stop-the-world; no moving/compaction or generational mode |
|
|
17
17
|
| Function `arity` / argument count | Direct calls to a locally visible function are checked by the default [semantic checker](../implemented/checker.md) (`TS4004`); calls through properties, imports, spread arguments or a reassigned binding are not. `fn.length` reports the declared arity |
|
|
18
18
|
| `async` / `await` | Real suspension on a stackful coroutine; `await` always queues a microtask (non-promises and already-settled promises included), so interleaving with pending `.then` callbacks matches the specification. Top-level `await` blocks on the event loop rather than yielding the module body. `fs/promises` and `fetch` offload their blocking work to a four-worker pool and settle on the reactor. Async generators suspend at both `await` and `yield`; `for await...of` awaits one step per iteration |
|
|
19
|
+
| Unhandled rejections | Reported when the program ends rather than at the end of the tick: a rejection no handler observed is printed and makes the process exit 1 once the event loop drains, so a long-running process (a server) keeps running until it exits, and a handler attached later still suppresses the report. Node raises the fatal error right after the microtask queue drains |
|
|
19
20
|
| Async generator cleanup | `for await...of` does not call the iterator's `return()` when the loop exits early. `yield*` inside an async generator delegates to synchronous iterables only. `.return()` at a suspended `yield` completes without running `finally` (the same limitation as synchronous generators) |
|
|
20
21
|
| `super` | `super.x` / `super[x]` read from the parent holder of the class whose body is running (in an instance method: the parent prototype; in a `static` method or static block: the parent constructor), so any inheritance depth is correct |
|
|
21
22
|
| `Error.stack` | Function-level frames only: each frame is `at name (file:line)` with the function's declaration line (updated to the construction line for the innermost frame); no column numbers and no frames for generator / `async` bodies (they switch stacks) |
|
package/doc/zh-CN/gui/build.md
CHANGED
|
@@ -39,8 +39,10 @@
|
|
|
39
39
|
demuxer/parser/decoder 的最小静态构建,用它编译
|
|
40
40
|
`runtime/ext_media/media.cpp`,并把所有内容合并到
|
|
41
41
|
`runtime/lib/<os>-<arch>/media.a`(Windows 上 MSVC ABI 则为 `media.lib`)。
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
下载会重试发布主机常见的瞬时 TLS 故障;解压后 tarball 保留为
|
|
43
|
+
`vendor/ffmpeg-<version>.tar.xz`,因此重建(或按 `FFMPEG_VERSION` 缓存它的
|
|
44
|
+
CI)会解压本地副本而不再下载。`gui.a` **不会**链接 FFmpeg:编译器为可能播放
|
|
45
|
+
媒体的程序选中 `media` 链接特性后才链接 `media.a`(见[链接特性](#链接特性))。
|
|
44
46
|
- `src/extensions/gui/index.ts` 通过 `nativeObjects()` 暴露引擎归档(经由
|
|
45
47
|
`findRuntimeLibrary`,因此 `.a`/`.lib` 都可用),并通过 `linkerFlags()`
|
|
46
48
|
暴露操作系统框架。它的 `linkFeatures()`(位于
|
package/doc/zh-CN/gui/dom.md
CHANGED
|
@@ -182,7 +182,9 @@ run();
|
|
|
182
182
|
这两个参数(`window`、`document`)只是普通的函数参数,因此脚本的局部变量
|
|
183
183
|
无需任何全局对象机制。外部脚本同样可用:`<script src="./counter.ts">` 会在
|
|
184
184
|
编译期读取,其 import 会被提升(重写为从 HTML 文件解析),其主体以同样方式
|
|
185
|
-
包装——因此脚本模块可以 `import` 辅助函数,同时仍能看到 `document
|
|
185
|
+
包装——因此脚本模块可以 `import` 辅助函数,同时仍能看到 `document`。内联主体
|
|
186
|
+
走同一套拆分,因此 `import` 在两种写法中行为一致;开启 `node` 扩展后,
|
|
187
|
+
`node:fs` 等其他 `node:` 内置模块同样可用。脚本是
|
|
186
188
|
**编译期资产**:运行时创建的 HTML(`innerHTML`、网络获取)永不执行,`src`
|
|
187
189
|
URL(`https://…`、`data:…`)会被忽略。一切都按文档顺序在解析后运行(实际上
|
|
188
190
|
是 deferred)。完整设计参见 `gui-scripts.md`。
|
package/doc/zh-CN/gui/webgl.md
CHANGED
|
@@ -133,6 +133,7 @@ transform feedback(入口为空操作:SDL_GPU 没有 transform feedback 阶
|
|
|
133
133
|
- `tests/e2e/gui-media.test.ts` 还会把 `<video>` 的最新解码帧上传为纹理并读回像素。
|
|
134
134
|
- `examples/gui/webgl-cube` 是一个旋转、受光、带纹理的立方体;它在浏览器中原样运行,并由 `tests/e2e/gui-example-webgl.test.ts` 覆盖。
|
|
135
135
|
- `examples/gui/webgl2-cubes` 用一次绘制调用画出 64 个实例化立方体,使用 `#version 300 es` 与 VAO;由 `tests/e2e/gui-example-webgl2.test.ts` 覆盖。
|
|
136
|
+
- `examples/gui/pelican-3d` 是一个完整的光照场景(程序化天空、雾、实例化树木/云、带关节动画的鹈鹕和自行车),拆成九个 ES module,并用 `AudioContext` 合成音效;由 `tests/e2e/gui-example-pelican3d.test.ts` 覆盖。
|
|
136
137
|
|
|
137
138
|
这些用例会在宿主选择的后端上运行:Linux(与 Windows)上是 Vulkan,macOS 上是 Metal,因此 SPIRV-Cross → MSL 路径与 SPIR-V 路径受同一组断言保护。
|
|
138
139
|
|
package/doc/zh-CN/gui-scripts.md
CHANGED
|
@@ -133,8 +133,9 @@ __registerScript("a1b2…", __xt_script_a1b2);
|
|
|
133
133
|
export default __html;
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
- id
|
|
137
|
-
|
|
136
|
+
- id 是包装后主体内容的哈希(sha256,前 32 个十六进制字符;包含延迟 import 的
|
|
137
|
+
prelude,因此语句相同但 import 不同的两个脚本不会合并为同一条注册表记录);
|
|
138
|
+
主体被替换为一个空的标记元素,以便引擎能按文档顺序找到它。
|
|
138
139
|
- `window` 与 `document` 是**函数参数**,因此脚本的全局变量会作为普通局部
|
|
139
140
|
变量解析 —— **无需编译器改动**,也没有全局对象。
|
|
140
141
|
- `__registerScript(id, fn)` 是一个新的运行时内置函数,它把闭包存进全局的
|
|
@@ -142,6 +143,11 @@ export default __html;
|
|
|
142
143
|
- 延迟脚本(`<script defer>`,或 M9c 中决定的所有内联脚本)在完整文档解析
|
|
143
144
|
并计算首次布局之后按文档顺序运行。然后在 `window` 上依次触发
|
|
144
145
|
`DOMContentLoaded` 再 `load`。
|
|
146
|
+
- 内联主体与 `<script src>` 文件走**同一套**拆分逻辑(见下):顶层
|
|
147
|
+
`import`/`export … from` 语句被提升进生成模块,相对说明符相对 HTML 文件
|
|
148
|
+
解析,`export` 修饰符被去掉。因此内联 `<script>` 里的
|
|
149
|
+
`import { readFileSync } from "node:fs"` 与外链脚本里的同一条 import 链接
|
|
150
|
+
方式完全一致 —— 需要开启 `node` 扩展,但不需要改写页面。
|
|
145
151
|
|
|
146
152
|
### 2b. 外部 `<script src>`
|
|
147
153
|
|
|
@@ -151,19 +157,19 @@ import 会像浏览器的模块脚本一样**延迟**:loader 生成一个合
|
|
|
151
157
|
`__xt_deferred_module` 标记,生成的脚本在 `loadHTML` 时调用模块包装函数。
|
|
152
158
|
bundler 把整个本地 import 图包装成返回各自导出的函数(由 `__xt_defer`
|
|
153
159
|
记忆化),因此模块顶层代码在文档就绪后才运行,且两个脚本导入同一模块时共享
|
|
154
|
-
同一个实例。裸说明符(`gui`、npm
|
|
160
|
+
同一个实例。裸说明符(`gui`、npm 包、`node:fs`)仍然立即加载。内联
|
|
161
|
+
`<script>` 主体走完全相同的拆分,因此两种写法里的 import 行为一致;唯一区别
|
|
162
|
+
是主体的来源。
|
|
155
163
|
|
|
156
164
|
缺失的 `src` 文件仍会抛出清晰的错误(被转换为 bundler 诊断);
|
|
157
165
|
`src` URL(`https://…`、`data:…`、`//host`)保持原样 —— 它们不是编译期资产,
|
|
158
166
|
永不运行。
|
|
159
167
|
|
|
160
|
-
|
|
161
|
-
永不运行。
|
|
162
|
-
|
|
163
|
-
被包装的脚本体以**它自己的文件**作为其中每条语句的来源(loader 会把该脚本体在生成模块中的
|
|
168
|
+
被包装的 `<script src>` 主体以**它自己的文件**作为其中每条语句的来源(loader 会把该脚本体在生成模块中的
|
|
164
169
|
区间作为 source range 上报),因此诊断会指向 `app.js:12`,而不是 `index.html` 的某一列;
|
|
165
170
|
同时**纯 JavaScript** 脚本与直接 import 的 `.js` 模块一样不做类型检查:在对象字面量上写
|
|
166
|
-
`state.update = …` 不会再报 `TS4003
|
|
171
|
+
`state.update = …` 不会再报 `TS4003`。页面因此可以原样携带已有的脚本。内联主体的
|
|
172
|
+
来源就是 HTML 页面本身。
|
|
167
173
|
|
|
168
174
|
```html
|
|
169
175
|
<button id="b">0</button>
|
|
@@ -288,6 +294,9 @@ Event 的 `target` 是句柄,其描述符(`toString`)与同一 `div#id.cla
|
|
|
288
294
|
通过带记忆化的模块包装函数延迟到 `loadHTML` 求值,因此 import 图能看到
|
|
289
295
|
已解析的文档,且同一模块只实例化一次。缺失文件作为诊断浮现。所有脚本按
|
|
290
296
|
文档顺序运行。e2e 覆盖。
|
|
297
|
+
- **M9c′** ✅ —— 内联 `<script>` 主体走同一套拆分逻辑,因此 `import`(包括
|
|
298
|
+
`node:`/裸说明符)与外链脚本里的链接方式完全一致。此前内联主体被原样包装,
|
|
299
|
+
其 `import` 留在脚本函数体内,codegen 会报 TS5001。
|
|
291
300
|
- **M9d** *(可选)* —— 检测传给 `win.loadHTML(...)` 的模板字面量中的内联
|
|
292
301
|
HTML 并同样转换它们(脆弱;推迟)。
|
|
293
302
|
- **M10 — 打磨** ✅ —— 窗口上的
|
|
@@ -312,10 +321,11 @@ Event 的 `target` 是句柄,其描述符(`toString`)与同一 `div#id.cla
|
|
|
312
321
|
不会运行。已记录。
|
|
313
322
|
- **模板字面量 HTML** 传给 `win.loadHTML(…)` 时在 M9b 中不会被转换(只有
|
|
314
323
|
导入的资产会)。如有需要,M9d 会处理。
|
|
315
|
-
- **外部 `<script src>`
|
|
324
|
+
- **外部 `<script src>` 与内联脚本之间的 import 绑定冲突。** 会被检测并报告
|
|
316
325
|
(给 import 起别名);之后可以做到带引用重写的按脚本重命名。
|
|
317
|
-
- **资产。** `<script src>` 相对 HTML
|
|
318
|
-
|
|
326
|
+
- **资产。** `<script src>` 相对 HTML 资产解析;内联主体的 import 也从同一位置
|
|
327
|
+
解析。URL `src` 值被忽略。加载器不会请求网络 URL;运行时全局 `fetch` 是
|
|
328
|
+
另一套 API。
|
|
319
329
|
|
|
320
330
|
## 测试
|
|
321
331
|
|
|
@@ -326,6 +336,8 @@ Event 的 `target` 是句柄,其描述符(`toString`)与同一 `div#id.cla
|
|
|
326
336
|
- **M9a**:一个假扩展加载器的单元测试,断言 bundler 重写 `*.foo` 并记录依赖。
|
|
327
337
|
- **M9b**:一个带内联 `<script>`、点击时递增计数器的 e2e 用例,通过处理器的
|
|
328
338
|
`console.log` 断言。
|
|
339
|
+
- **M9c′**:单元用例断言内联主体的 import 被提升(相对 import 被延迟),外加
|
|
340
|
+
一个 e2e 用例:内联脚本通过 `node:fs` 读取文件并调用被延迟的辅助模块。
|
|
329
341
|
- **M10**:`requestAnimationFrame`(每帧运行、重新排队、接收时间戳、遵守
|
|
330
342
|
`cancelAnimationFrame`)与 `offsetWidth`/`offsetHeight`/`contains` 的 e2e
|
|
331
343
|
用例。
|
package/doc/zh-CN/gui.md
CHANGED
|
@@ -157,7 +157,12 @@
|
|
|
157
157
|
- `vendor/`(GUI 归档的 SDL3/FreeType/HarfBuzz/glslang,以及 `media` 的 FFmpeg,
|
|
158
158
|
缓慢的部分)按 OS/arch 缓存,以 `scripts/build-gui.ts` /
|
|
159
159
|
`scripts/build-media.ts` 为键。Windows x64 有独立的源码 + FFmpeg 安装前缀
|
|
160
|
-
|
|
160
|
+
缓存;Linux/macOS 上安装前缀 `vendor/ffmpeg-build/dist` 另有以
|
|
161
|
+
`FFMPEG_VERSION` 和 `scripts/build-media.ts`(决定 configure 参数)为键的
|
|
162
|
+
缓存,命中后跳过 FFmpeg 的 configure/make,不必每次重编:脚本会在前缀里
|
|
163
|
+
记录构建它的发布版本,版本或 macOS 部署目标变化时丢弃重建。FFmpeg 发布
|
|
164
|
+
tarball 另有只以 `FFMPEG_VERSION` 为键、所有 job 共享的缓存:即使
|
|
165
|
+
ffmpeg.org 的 TLS 握手不稳定,也不会让已经下载过它的运行失败。
|
|
161
166
|
- Windows x64 在 CI 中构建两个归档并链接一个会触发 `media` 链接特性的 `gui`
|
|
162
167
|
程序(另有一条显式 `--feature media` 的构建);托管 runner
|
|
163
168
|
没有 GPU/Vulkan 驱动,因此 GUI/媒体示例的*运行*仍为试验性(失败只记录
|
|
@@ -182,6 +187,9 @@
|
|
|
182
187
|
相对 import 通过带记忆化的模块包装函数延迟到 `loadHTML` 求值,因此被导入
|
|
183
188
|
模块能看到已解析的文档并共享状态。缺失文件会成为诊断。一切都按文档顺序
|
|
184
189
|
运行。
|
|
190
|
+
- **M9c′** ✅ —— 内联 `<script>` 主体走同一套拆分逻辑,其 import 被提升
|
|
191
|
+
(`node:`/裸说明符像普通模块 import 一样链接),而不是留在被包装的函数
|
|
192
|
+
体内、由 codegen 报 TS5001。内联与外链现在行为一致。
|
|
185
193
|
10. **M10 — 打磨** ✅
|
|
186
194
|
- `win.requestAnimationFrame(fn)` / `win.cancelAnimationFrame(id)`;回调
|
|
187
195
|
在每帧顶部以帧时间戳运行,并可修改 DOM
|
|
@@ -366,6 +374,8 @@
|
|
|
366
374
|
- **M9c** ✅ 外部 `<script src>` 文件被读取,其主体被包装/注册;相对 import
|
|
367
375
|
通过带记忆化的模块包装函数延迟到 `loadHTML`(模块级 DOM 工作因此能看到已解析
|
|
368
376
|
文档);缺失文件会成为诊断。加载器错误被 `loadGraph` 捕获并报告为构建错误。
|
|
377
|
+
- **M9c′** ✅ 内联 `<script>` 主体与外链主体走同一套拆分逻辑,因此内联 `import`
|
|
378
|
+
被提升并同样链接(开启 `node` 扩展后包括 `node:fs`);只有主体来源不同。
|
|
369
379
|
- **M10** ✅ 窗口句柄上的 `requestAnimationFrame`/`cancelAnimationFrame`
|
|
370
380
|
(回调在每帧布局之前以帧时间戳运行),以及元素句柄上的
|
|
371
381
|
`offsetWidth`/`offsetHeight`/`contains`;e2e 覆盖在
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
- 微任务队列(`xt_microtasks`);`xt_promise_ctor/resolve/reject/static`(含 `withResolvers`)、实例 `then` / `catch` / `finally`。
|
|
31
31
|
- `xt_drain_microtasks` 在程序结束时清空队列,事件循环也会在回调之间清空它。
|
|
32
32
|
- `xt_await` 对非 Promise / 已 settle 值排入一次微任务,对 pending promise 则挂起当前 async 协程直到 settle。
|
|
33
|
+
- 未处理的 rejection 是致命错误,与 Node 默认的 `--unhandled-rejections=throw` 一致。一个 reject 后始终无人观察的 promise——没有 `.then` / `.catch`、没有被 `await`、也没有被另一个 promise 采纳——会在事件循环排空后向 stderr 打印 `Unhandled promise rejection`,并以状态码 1 退出。它主要保护入口函数:程序的 `main()` 通常是 `async`,从其中逃出的异常原本只是 reject 一个无人观察的 promise,进程会静默退出 0。即使在定时器里稍后才挂上处理函数,也算已观察。
|
|
33
34
|
|
|
34
35
|
## 定时器与事件循环
|
|
35
36
|
|