xbintsc 0.5.32 → 0.5.37
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 +10 -7
- package/README.zh-CN.md +10 -8
- package/dist/scripts/check-version.d.ts +27 -0
- package/dist/scripts/check-version.js +71 -0
- package/dist/scripts/check-version.js.map +1 -0
- package/dist/scripts/package-platform.d.ts +58 -0
- package/dist/scripts/package-platform.js +142 -0
- package/dist/scripts/package-platform.js.map +1 -0
- package/dist/scripts/verify-platform-package.d.ts +34 -0
- package/dist/scripts/verify-platform-package.js +161 -0
- package/dist/scripts/verify-platform-package.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/main.js +3 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/driver/runtime-lib.d.ts +29 -4
- package/dist/src/driver/runtime-lib.js +68 -10
- package/dist/src/driver/runtime-lib.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/index.js +18 -5
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/media/index.js +4 -3
- package/dist/src/extensions/media/index.js.map +1 -1
- package/dist/src/parser/context.d.ts +41 -0
- package/dist/src/parser/context.js +89 -4
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/literals.js +6 -12
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/tests/driver/platform-package.test.d.ts +1 -0
- package/dist/tests/driver/platform-package.test.js +49 -0
- package/dist/tests/driver/platform-package.test.js.map +1 -0
- package/dist/tests/driver/runtime-lib.test.js +51 -4
- package/dist/tests/driver/runtime-lib.test.js.map +1 -1
- package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
- package/dist/tests/e2e/gui-audio.test.js +287 -0
- package/dist/tests/e2e/gui-audio.test.js.map +1 -0
- package/dist/tests/e2e/gui-browser.test.js +65 -0
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
- package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
- package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-dom.test.js +215 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -1
- package/dist/tests/e2e/gui-events.test.js +112 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -1
- package/dist/tests/e2e/gui-helpers.d.ts +7 -0
- package/dist/tests/e2e/gui-helpers.js +1 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.js +78 -0
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/gui-style.test.js +55 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl.test.js +258 -0
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2.test.js +45 -1
- package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
- package/dist/tests/extensions/gui-index.test.js +1 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -1
- package/dist/tests/extensions/media.test.js +1 -0
- package/dist/tests/extensions/media.test.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +30 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/platform-tarball.test.d.ts +1 -0
- package/dist/tests/platform-tarball.test.js +111 -0
- package/dist/tests/platform-tarball.test.js.map +1 -0
- package/dist/tests/version.test.js +6 -0
- package/dist/tests/version.test.js.map +1 -1
- package/doc/ai/contributing.md +11 -0
- package/doc/ai/extensions.md +7 -4
- package/doc/ai/troubleshooting.md +7 -5
- package/doc/ai/zh-CN/contributing.md +9 -0
- package/doc/ai/zh-CN/extensions.md +4 -2
- package/doc/ai/zh-CN/troubleshooting.md +5 -3
- package/doc/gui/api.md +99 -22
- package/doc/gui/build.md +11 -4
- package/doc/gui/canvas.md +49 -18
- package/doc/gui/dom.md +57 -7
- package/doc/gui/media.md +4 -0
- package/doc/gui/webgl.md +105 -46
- package/doc/gui.md +7 -3
- package/doc/npm-package.md +55 -27
- package/doc/requirements.md +6 -2
- package/doc/zh-CN/gui/api.md +84 -14
- package/doc/zh-CN/gui/build.md +7 -2
- package/doc/zh-CN/gui/canvas.md +44 -19
- package/doc/zh-CN/gui/dom.md +48 -4
- package/doc/zh-CN/gui/media.md +3 -0
- package/doc/zh-CN/gui/webgl.md +22 -16
- package/doc/zh-CN/gui.md +3 -2
- package/doc/zh-CN/npm-package.md +44 -23
- package/doc/zh-CN/requirements.md +4 -1
- package/package.json +17 -1
- package/runtime/ext_gui/audio.cpp +365 -153
- package/runtime/ext_gui/audio_buffer.cpp +166 -0
- package/runtime/ext_gui/audio_buffer.h +37 -0
- package/runtime/ext_gui/audio_decode.cpp +219 -0
- package/runtime/ext_gui/audio_decode.h +39 -0
- package/runtime/ext_gui/audio_device.cpp +87 -0
- package/runtime/ext_gui/audio_effects.cpp +163 -0
- package/runtime/ext_gui/audio_effects.h +25 -0
- package/runtime/ext_gui/audio_internal.h +30 -0
- package/runtime/ext_gui/audio_mixer.cpp +494 -0
- package/runtime/ext_gui/audio_mixer.h +238 -0
- package/runtime/ext_gui/audio_param.cpp +104 -0
- package/runtime/ext_gui/audio_param_object.cpp +155 -0
- package/runtime/ext_gui/browser.cpp +45 -50
- package/runtime/ext_gui/browser_abort.cpp +249 -0
- package/runtime/ext_gui/browser_dialogs.cpp +76 -0
- package/runtime/ext_gui/browser_media.cpp +176 -0
- package/runtime/ext_gui/browser_navigation.cpp +130 -0
- package/runtime/ext_gui/canvas.cpp +88 -30
- package/runtime/ext_gui/canvas.h +74 -9
- package/runtime/ext_gui/canvas_image_data.cpp +143 -0
- package/runtime/ext_gui/canvas_internal.h +8 -5
- package/runtime/ext_gui/canvas_pattern.cpp +90 -0
- package/runtime/ext_gui/canvas_raster.cpp +31 -9
- package/runtime/ext_gui/canvas_style.cpp +4 -2
- package/runtime/ext_gui/css_selector.cpp +21 -3
- package/runtime/ext_gui/dom.h +4 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
- package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
- package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
- package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
- package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
- package/runtime/ext_gui/dom_api_document.cpp +58 -5
- package/runtime/ext_gui/dom_api_events.cpp +93 -17
- package/runtime/ext_gui/dom_api_internal.h +71 -2
- package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
- package/runtime/ext_gui/dom_api_node.cpp +40 -19
- package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
- package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
- package/runtime/ext_gui/dom_api_proto.cpp +35 -0
- package/runtime/ext_gui/dom_api_style.cpp +223 -13
- package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
- package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
- package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
- package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
- package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
- package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
- package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
- package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
- package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
- package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
- package/runtime/ext_gui/gui.cpp +103 -14
- package/runtime/ext_gui/gui_engine.h +56 -4
- package/runtime/ext_gui/gui_events.cpp +80 -9
- package/runtime/ext_gui/image_bitmap.cpp +28 -0
- package/runtime/ext_gui/image_bitmap.h +26 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
- package/runtime/ext_gui/listener_options.h +43 -0
- package/runtime/ext_gui/media.cpp +32 -0
- package/runtime/ext_gui/media.h +13 -0
- package/runtime/ext_gui/media_api.h +23 -0
- package/runtime/ext_gui/paint.cpp +28 -0
- package/runtime/ext_gui/paint.h +4 -0
- package/runtime/ext_gui/webgl.h +152 -18
- package/runtime/ext_gui/webgl_blit.cpp +95 -0
- package/runtime/ext_gui/webgl_buffer.cpp +178 -263
- package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
- package/runtime/ext_gui/webgl_clear.cpp +142 -0
- package/runtime/ext_gui/webgl_compressed.cpp +180 -0
- package/runtime/ext_gui/webgl_context.cpp +241 -156
- package/runtime/ext_gui/webgl_draw.cpp +68 -3
- package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
- package/runtime/ext_gui/webgl_glsl.h +40 -0
- package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
- package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
- package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
- package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
- package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
- package/runtime/ext_gui/webgl_program_internal.h +6 -0
- package/runtime/ext_gui/webgl_shader.cpp +13 -1
- package/runtime/ext_gui/webgl_state.cpp +228 -0
- package/runtime/ext_gui/webgl_texture.cpp +144 -17
- package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
- package/runtime/ext_gui/webgl_uniform_block.h +31 -0
- package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
- package/runtime/ext_gui/window.cpp +14 -65
- package/runtime/ext_gui/window_events.cpp +125 -0
- package/runtime/ext_media/decode_audio.cpp +308 -0
- package/runtime/ext_media/media_bridge.c +2 -0
- package/runtime/rt.h +43 -0
- package/runtime/rt_internal.h +4 -0
- package/runtime/xt_array_buffer.c +30 -0
- package/runtime/xt_builtins.c +0 -21
- package/runtime/xt_containers/access.inc +94 -2
- package/runtime/xt_containers/objects.inc +29 -31
- package/runtime/xt_typed_array/elements.inc +13 -0
- package/scripts/build-media.ts +14 -6
- package/scripts/build-runtime.ts +21 -16
- package/scripts/check-version.ts +39 -9
- package/scripts/package-platform.ts +183 -0
- package/scripts/set-platform-version.ts +62 -0
- package/scripts/verify-platform-package.ts +182 -0
- package/src/cli/main.ts +5 -1
- package/src/driver/runtime-lib.ts +73 -9
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/index.ts +18 -5
- package/src/extensions/media/index.ts +4 -3
- package/src/parser/context.ts +99 -4
- package/src/parser/literals.ts +6 -12
|
@@ -7,26 +7,90 @@
|
|
|
7
7
|
* runtime/lib/<os>-<arch>/core.a (the core `xt_*.c` runtime)
|
|
8
8
|
* runtime/lib/<os>-<arch>/ext_<name>.a (per-extension sources, e.g. ext_node.a)
|
|
9
9
|
*
|
|
10
|
+
* A source checkout or a release archive carries them in the package itself.
|
|
11
|
+
* An npm install instead receives them from the optional
|
|
12
|
+
* `@xbintsc/<os>-<arch>` package (installed only for the host platform), which
|
|
13
|
+
* uses the same `runtime/lib/<os>-<arch>/` layout. The driver reads that folder
|
|
14
|
+
* in place, so no install script has to copy anything into `node_modules`.
|
|
15
|
+
*
|
|
10
16
|
* Shipping these lets `build` link without compiling C, and shortens the first
|
|
11
17
|
* build. When an archive is missing the driver transparently falls back to
|
|
12
18
|
* compiling the `.c` sources, so the archives are an optimization, not a hard
|
|
13
19
|
* dependency.
|
|
14
20
|
*/
|
|
15
21
|
|
|
16
|
-
import { existsSync } from "node:fs";
|
|
17
|
-
import { join } from "node:path";
|
|
22
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
23
|
+
import { dirname, join } from "node:path";
|
|
18
24
|
import { findRuntimeDir, platformSlug } from "./paths.js";
|
|
19
25
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
26
|
+
/** Archive suffixes, in the order a name is probed. */
|
|
27
|
+
const ARCHIVE_SUFFIXES = [".a", ".lib"] as const;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Folder holding the archives for the current host inside the xbintsc package
|
|
31
|
+
* itself (a source checkout or an unpacked release archive).
|
|
32
|
+
*/
|
|
33
|
+
function packagedRuntimeLibDir(): string {
|
|
22
34
|
return join(findRuntimeDir(), "lib", platformSlug());
|
|
23
35
|
}
|
|
24
36
|
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Archive folders of an installed `@xbintsc/<os>-<arch>` package, given the
|
|
39
|
+
* root of the installed xbintsc package.
|
|
40
|
+
*
|
|
41
|
+
* Package managers place the optional dependency either inside xbintsc's own
|
|
42
|
+
* `node_modules` or in the nearest hoisted `node_modules`, so every ancestor is
|
|
43
|
+
* probed. Yarn PnP has no `node_modules`; there the driver simply falls back to
|
|
44
|
+
* compiling the runtime sources.
|
|
45
|
+
*/
|
|
46
|
+
export function platformRuntimeLibDirs(packageRoot: string, slug: string = platformSlug()): string[] {
|
|
47
|
+
const dirs = new Set<string>();
|
|
48
|
+
let dir = packageRoot;
|
|
49
|
+
for (;;) {
|
|
50
|
+
dirs.add(join(dir, "node_modules", "@xbintsc", slug, "runtime", "lib", slug));
|
|
51
|
+
const parent = dirname(dir);
|
|
52
|
+
if (parent === dir) break;
|
|
53
|
+
dir = parent;
|
|
54
|
+
}
|
|
55
|
+
return [...dirs];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Every folder searched for a prebuilt archive, most specific first. */
|
|
59
|
+
export function runtimeLibDirs(): readonly string[] {
|
|
60
|
+
return [packagedRuntimeLibDir(), ...platformRuntimeLibDirs(dirname(findRuntimeDir()))];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** True when a folder holds at least one runtime archive. */
|
|
64
|
+
function containsArchive(dir: string): boolean {
|
|
65
|
+
try {
|
|
66
|
+
return readdirSync(dir).some((entry) => ARCHIVE_SUFFIXES.some((suffix) => entry.endsWith(suffix)));
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Directory holding the prebuilt archives for the current host: the package's
|
|
74
|
+
* own `runtime/lib/<slug>`, or the optional platform package when the former
|
|
75
|
+
* has no archives (a normal npm install).
|
|
76
|
+
*/
|
|
77
|
+
export function runtimeLibDir(dirs: readonly string[] = runtimeLibDirs()): string {
|
|
78
|
+
return dirs.find((dir) => containsArchive(dir)) ?? dirs[0]!;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Absolute path to a prebuilt archive (`core`, `ext_node`, `gui`, `media`, …),
|
|
83
|
+
* if present. `dirs` is injectable for tests.
|
|
84
|
+
*/
|
|
85
|
+
export function findRuntimeLibrary(
|
|
86
|
+
name: string,
|
|
87
|
+
dirs: readonly string[] = runtimeLibDirs(),
|
|
88
|
+
): string | undefined {
|
|
89
|
+
for (const dir of dirs) {
|
|
90
|
+
for (const suffix of ARCHIVE_SUFFIXES) {
|
|
91
|
+
const candidate = join(dir, `${name}${suffix}`);
|
|
92
|
+
if (existsSync(candidate)) return candidate;
|
|
93
|
+
}
|
|
30
94
|
}
|
|
31
95
|
return undefined;
|
|
32
96
|
}
|
package/src/driver/version.ts
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
|
|
24
24
|
import { join } from "node:path";
|
|
25
25
|
import type { Extension, ExtensionModule } from "../registry.js";
|
|
26
|
-
import {
|
|
27
|
-
import { findRuntimeLibrary } from "../../driver/runtime-lib.js";
|
|
26
|
+
import { platformSlug } from "../../driver/paths.js";
|
|
27
|
+
import { findRuntimeLibrary, runtimeLibDir } from "../../driver/runtime-lib.js";
|
|
28
28
|
import { loadHtmlAsset } from "./html.js";
|
|
29
29
|
import { loadScriptAsset } from "./three.js";
|
|
30
30
|
|
|
@@ -61,6 +61,7 @@ const hostTypes = [
|
|
|
61
61
|
"ImageBitmap",
|
|
62
62
|
"ImageData",
|
|
63
63
|
"HTMLCanvasElement",
|
|
64
|
+
"Path2D",
|
|
64
65
|
"HTMLImageElement",
|
|
65
66
|
"HTMLVideoElement",
|
|
66
67
|
"HTMLAudioElement",
|
|
@@ -70,6 +71,16 @@ const hostTypes = [
|
|
|
70
71
|
"Event",
|
|
71
72
|
"CustomEvent",
|
|
72
73
|
"ProgressEvent",
|
|
74
|
+
"UIEvent",
|
|
75
|
+
"MouseEvent",
|
|
76
|
+
"PointerEvent",
|
|
77
|
+
"WheelEvent",
|
|
78
|
+
"KeyboardEvent",
|
|
79
|
+
"FocusEvent",
|
|
80
|
+
"InputEvent",
|
|
81
|
+
"SubmitEvent",
|
|
82
|
+
"AbortController",
|
|
83
|
+
"AbortSignal",
|
|
73
84
|
"EventTarget",
|
|
74
85
|
"DOMParser",
|
|
75
86
|
"Request",
|
|
@@ -102,7 +113,7 @@ const browserGlobals: Record<string, { symbol: string; argument?: string }> = {
|
|
|
102
113
|
navigator: { symbol: "xt_browser_navigator" },
|
|
103
114
|
localStorage: { symbol: "xt_browser_local_storage" },
|
|
104
115
|
__THREE_DEVTOOLS__: { symbol: "xt_browser_missing" },
|
|
105
|
-
createImageBitmap: { symbol: "
|
|
116
|
+
createImageBitmap: { symbol: "xt_browser_create_image_bitmap" },
|
|
106
117
|
};
|
|
107
118
|
for (const type of hostTypes) {
|
|
108
119
|
browserGlobals[type] = { symbol: "xt_browser_type", argument: type };
|
|
@@ -111,7 +122,7 @@ for (const type of hostTypes) {
|
|
|
111
122
|
/** Absolute path to the per-platform prebuilt GUI archive (`gui.a`, or
|
|
112
123
|
* `gui.lib` with the MSVC ABI on Windows). */
|
|
113
124
|
function guiArchive(): string {
|
|
114
|
-
return join(
|
|
125
|
+
return join(runtimeLibDir(), "gui.a");
|
|
115
126
|
}
|
|
116
127
|
|
|
117
128
|
export const guiExtension: Extension = {
|
|
@@ -122,7 +133,8 @@ export const guiExtension: Extension = {
|
|
|
122
133
|
if (archive === undefined) {
|
|
123
134
|
throw new Error(
|
|
124
135
|
`GUI native library not found at ${guiArchive()} (or gui.lib).\n` +
|
|
125
|
-
`
|
|
136
|
+
`The optional @xbintsc/${platformSlug()} package ships it: reinstall xbintsc ` +
|
|
137
|
+
`without --omit=optional, or build it from a source checkout (see doc/gui.md).`,
|
|
126
138
|
);
|
|
127
139
|
}
|
|
128
140
|
return [archive];
|
|
@@ -197,6 +209,7 @@ export const guiExtension: Extension = {
|
|
|
197
209
|
escape: { symbol: "xt_browser_escape" },
|
|
198
210
|
alert: { symbol: "xt_browser_alert" },
|
|
199
211
|
prompt: { symbol: "xt_browser_prompt" },
|
|
212
|
+
confirm: { symbol: "xt_browser_confirm_global" },
|
|
200
213
|
}),
|
|
201
214
|
/** Browser globals (`window`, `document`, the DOM type names). */
|
|
202
215
|
globalValues: () => browserGlobals,
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
import { join } from "node:path";
|
|
29
29
|
import type { Extension } from "../registry.js";
|
|
30
30
|
import { findRuntimeDir, platformSlug } from "../../driver/paths.js";
|
|
31
|
-
import { findRuntimeLibrary } from "../../driver/runtime-lib.js";
|
|
31
|
+
import { findRuntimeLibrary, runtimeLibDir } from "../../driver/runtime-lib.js";
|
|
32
32
|
|
|
33
33
|
/** Absolute path to the per-platform prebuilt media archive. */
|
|
34
34
|
function mediaArchive(): string {
|
|
35
|
-
return join(
|
|
35
|
+
return join(runtimeLibDir(), process.platform === "win32" ? "media.lib" : "media.a");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export const mediaExtension: Extension = {
|
|
@@ -50,7 +50,8 @@ export const mediaExtension: Extension = {
|
|
|
50
50
|
if (archive === undefined) {
|
|
51
51
|
throw new Error(
|
|
52
52
|
`media native library not found at ${mediaArchive()} (media.a or media.lib).\n` +
|
|
53
|
-
`
|
|
53
|
+
`The optional @xbintsc/${platformSlug()} package ships it: reinstall xbintsc ` +
|
|
54
|
+
`without --omit=optional, or build it with \`npm run media\` (see doc/gui/build.md).`,
|
|
54
55
|
);
|
|
55
56
|
}
|
|
56
57
|
return [archive];
|
package/src/parser/context.ts
CHANGED
|
@@ -15,11 +15,25 @@ import { SyntaxKind } from "../ast/kinds.js";
|
|
|
15
15
|
import type { Identifier, StringLiteral } from "../ast/nodes.js";
|
|
16
16
|
import { SpeculationError } from "./speculation.js";
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* The template-literal context of the scanner at one point in the token
|
|
20
|
+
* stream. `frames` holds the brace depth of every open `${ … }` substitution
|
|
21
|
+
* (innermost last); `pendingOffset` is the `}` that must be re-read as template
|
|
22
|
+
* text before the next token is scanned.
|
|
23
|
+
*/
|
|
24
|
+
interface TemplateState {
|
|
25
|
+
readonly frames: readonly number[];
|
|
26
|
+
readonly pendingOffset: number | undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const OUTSIDE_TEMPLATE: TemplateState = { frames: [], pendingOffset: undefined };
|
|
30
|
+
|
|
18
31
|
export interface ParserState {
|
|
19
32
|
index: number;
|
|
20
33
|
tokens: Token[];
|
|
21
34
|
pos: number;
|
|
22
35
|
consumed: number;
|
|
36
|
+
template: TemplateState;
|
|
23
37
|
}
|
|
24
38
|
|
|
25
39
|
export class ParserContext {
|
|
@@ -32,12 +46,22 @@ export class ParserContext {
|
|
|
32
46
|
speculating = 0;
|
|
33
47
|
/** Monotonic count of tokens consumed; used to detect lack of progress. */
|
|
34
48
|
consumed = 0;
|
|
49
|
+
/** Brace depth of each open `${ … }` substitution, innermost last. */
|
|
50
|
+
private templateFrames: number[] = [];
|
|
51
|
+
/** Offset of the `}` that must be re-read as template text on the next scan. */
|
|
52
|
+
private pendingTemplateOffset: number | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Template context after each buffered token. A parser method that rewinds
|
|
55
|
+
* the scanner (the regex fallback) restores the context recorded on the
|
|
56
|
+
* current token so a template that was only seen by lookahead stays coherent.
|
|
57
|
+
*/
|
|
58
|
+
private readonly templateStates = new WeakMap<Token, TemplateState>();
|
|
35
59
|
|
|
36
60
|
constructor(source: SourceFile, diagnostics: DiagnosticBag) {
|
|
37
61
|
this.source = source;
|
|
38
62
|
this.diagnostics = diagnostics;
|
|
39
63
|
this.scanner = new Scanner(source, diagnostics);
|
|
40
|
-
this.tokens = [this.
|
|
64
|
+
this.tokens = [this.scanToken()];
|
|
41
65
|
}
|
|
42
66
|
|
|
43
67
|
// -- token access ---------------------------------------------------------
|
|
@@ -61,7 +85,7 @@ export class ParserContext {
|
|
|
61
85
|
|
|
62
86
|
lookAhead(k: number): Token {
|
|
63
87
|
while (this.tokens.length <= this.index + k) {
|
|
64
|
-
this.tokens.push(this.
|
|
88
|
+
this.tokens.push(this.scanToken());
|
|
65
89
|
}
|
|
66
90
|
return this.tokens[this.index + k]!;
|
|
67
91
|
}
|
|
@@ -72,12 +96,76 @@ export class ParserContext {
|
|
|
72
96
|
if (this.index + 1 < this.tokens.length) {
|
|
73
97
|
this.index++;
|
|
74
98
|
} else {
|
|
75
|
-
this.tokens = [this.
|
|
99
|
+
this.tokens = [this.scanToken()];
|
|
76
100
|
this.index = 0;
|
|
77
101
|
}
|
|
78
102
|
return current;
|
|
79
103
|
}
|
|
80
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Scan the next token, keeping a template literal together. A `${ … }`
|
|
107
|
+
* substitution is ordinary token stream (so the parser can parse the
|
|
108
|
+
* expression), but the `}` that closes it must be followed by a token that
|
|
109
|
+
* re-reads the source as template text. Scanning the tail of a template as
|
|
110
|
+
* normal tokens would let its raw text (quotes, parens, `}`) leak into
|
|
111
|
+
* lookahead - and `findMatchingParen` would then count characters that are
|
|
112
|
+
* part of the literal.
|
|
113
|
+
*/
|
|
114
|
+
private scanToken(): Token {
|
|
115
|
+
let token: Token;
|
|
116
|
+
if (this.pendingTemplateOffset !== undefined) {
|
|
117
|
+
const offset = this.pendingTemplateOffset;
|
|
118
|
+
this.pendingTemplateOffset = undefined;
|
|
119
|
+
token = this.scanner.continueTemplate(offset);
|
|
120
|
+
if (token.kind === TokenKind.NoSubstitutionTemplateLiteral) {
|
|
121
|
+
this.templateFrames.pop();
|
|
122
|
+
} else if (token.kind === TokenKind.TemplateHead) {
|
|
123
|
+
/* Another `${` in the same template restarts its brace depth. */
|
|
124
|
+
const top = this.templateFrames.length - 1;
|
|
125
|
+
if (top >= 0) this.templateFrames[top] = 0;
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
token = this.scanner.nextToken();
|
|
129
|
+
if (token.kind === TokenKind.TemplateHead) {
|
|
130
|
+
this.templateFrames.push(0);
|
|
131
|
+
} else if (this.templateFrames.length > 0) {
|
|
132
|
+
const top = this.templateFrames.length - 1;
|
|
133
|
+
if (token.kind === TokenKind.OpenBrace) {
|
|
134
|
+
this.templateFrames[top] = this.templateFrames[top]! + 1;
|
|
135
|
+
} else if (token.kind === TokenKind.CloseBrace) {
|
|
136
|
+
if (this.templateFrames[top]! === 0) this.pendingTemplateOffset = token.start;
|
|
137
|
+
else this.templateFrames[top] = this.templateFrames[top]! - 1;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (this.templateFrames.length > 0 || this.pendingTemplateOffset !== undefined) {
|
|
142
|
+
this.templateStates.set(token, this.snapshotTemplateState());
|
|
143
|
+
}
|
|
144
|
+
return token;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Discard any lookahead and continue scanning at `offset`. Used when a
|
|
149
|
+
* literal's raw text has to be re-read (the regex fallback). The template
|
|
150
|
+
* context is restored to the state recorded for the current token, because
|
|
151
|
+
* the skipped source cannot have opened or closed a template.
|
|
152
|
+
*/
|
|
153
|
+
rescanFrom(offset: number): void {
|
|
154
|
+
this.restoreTemplateState(this.templateStates.get(this.token) ?? OUTSIDE_TEMPLATE);
|
|
155
|
+
this.scanner.seek(offset);
|
|
156
|
+
this.tokens = [this.scanToken()];
|
|
157
|
+
this.index = 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private snapshotTemplateState(): TemplateState {
|
|
161
|
+
return { frames: this.templateFrames.slice(), pendingOffset: this.pendingTemplateOffset };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private restoreTemplateState(state: TemplateState): void {
|
|
165
|
+
this.templateFrames = [...state.frames];
|
|
166
|
+
this.pendingTemplateOffset = state.pendingOffset;
|
|
167
|
+
}
|
|
168
|
+
|
|
81
169
|
parseExpected(kind: TokenKind, message?: string): Token {
|
|
82
170
|
if (this.token.kind === kind) return this.nextToken();
|
|
83
171
|
this.error(
|
|
@@ -173,7 +261,13 @@ export class ParserContext {
|
|
|
173
261
|
// -- state save / restore (for speculative parsing) -----------------------
|
|
174
262
|
|
|
175
263
|
saveState(): ParserState {
|
|
176
|
-
return {
|
|
264
|
+
return {
|
|
265
|
+
index: this.index,
|
|
266
|
+
tokens: this.tokens.slice(),
|
|
267
|
+
pos: this.scanner.position,
|
|
268
|
+
consumed: this.consumed,
|
|
269
|
+
template: this.snapshotTemplateState(),
|
|
270
|
+
};
|
|
177
271
|
}
|
|
178
272
|
|
|
179
273
|
restoreState(state: ParserState): void {
|
|
@@ -181,5 +275,6 @@ export class ParserContext {
|
|
|
181
275
|
this.tokens = state.tokens;
|
|
182
276
|
this.scanner.seek(state.pos);
|
|
183
277
|
this.consumed = state.consumed;
|
|
278
|
+
this.restoreTemplateState(state.template);
|
|
184
279
|
}
|
|
185
280
|
}
|
package/src/parser/literals.ts
CHANGED
|
@@ -206,10 +206,8 @@ export const literalMethods: LiteralMethods = {
|
|
|
206
206
|
} as unknown as TemplateLiteral;
|
|
207
207
|
}
|
|
208
208
|
if (startToken.kind === TokenKind.Backtick) {
|
|
209
|
-
// Re-scan from the backtick so the scanner produces a
|
|
210
|
-
this.
|
|
211
|
-
this.tokens = [this.scanner.nextToken()];
|
|
212
|
-
this.index = 0;
|
|
209
|
+
// Re-scan from the backtick so the scanner produces a template token.
|
|
210
|
+
this.rescanFrom(startToken.start);
|
|
213
211
|
return this.parseTemplateLiteral();
|
|
214
212
|
}
|
|
215
213
|
const head = String(startToken.value ?? "");
|
|
@@ -222,11 +220,9 @@ export const literalMethods: LiteralMethods = {
|
|
|
222
220
|
this.error(DiagnosticCode.UnterminatedTemplate, "Expected '}' to close template substitution");
|
|
223
221
|
return { kind: SyntaxKind.TemplateLiteral, head, raw: headRaw, spans, start: startToken.start, end: this.token.end };
|
|
224
222
|
}
|
|
225
|
-
// The `}` terminates a substitution;
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
this.tokens = [this.scanner.continueTemplate(closeStart)];
|
|
229
|
-
this.index = 0;
|
|
223
|
+
// The `}` terminates a substitution; the token after it is the template
|
|
224
|
+
// continuation (the token stream re-reads that source as literal text).
|
|
225
|
+
this.nextToken();
|
|
230
226
|
const literalToken = this.token;
|
|
231
227
|
if (literalToken.kind === TokenKind.NoSubstitutionTemplateLiteral) {
|
|
232
228
|
this.nextToken();
|
|
@@ -287,9 +283,7 @@ export const literalMethods: LiteralMethods = {
|
|
|
287
283
|
const flagsStart = i;
|
|
288
284
|
while (i < text.length && /[a-z]/i.test(text[i]!)) i++;
|
|
289
285
|
const flags = text.slice(flagsStart, i);
|
|
290
|
-
this.
|
|
291
|
-
this.tokens = [this.scanner.nextToken()];
|
|
292
|
-
this.index = 0;
|
|
286
|
+
this.rescanFrom(i);
|
|
293
287
|
return { kind: SyntaxKind.RegularExpressionLiteral, text: text.slice(token.start, i), pattern, flags, start: token.start, end: i };
|
|
294
288
|
},
|
|
295
289
|
};
|