xbintsc 0.3.46 → 0.3.61

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 (131) hide show
  1. package/AGENTS.md +95 -0
  2. package/README.md +25 -0
  3. package/README.zh-CN.md +23 -0
  4. package/dist/src/cli/hints.d.ts +54 -0
  5. package/dist/src/cli/hints.js +165 -0
  6. package/dist/src/cli/hints.js.map +1 -0
  7. package/dist/src/cli/main.js +73 -9
  8. package/dist/src/cli/main.js.map +1 -1
  9. package/dist/src/codegen/generator/tables.d.ts +26 -0
  10. package/dist/src/codegen/generator/tables.js +64 -12
  11. package/dist/src/codegen/generator/tables.js.map +1 -1
  12. package/dist/src/diagnostics/source-text.d.ts +22 -0
  13. package/dist/src/diagnostics/source-text.js +76 -0
  14. package/dist/src/diagnostics/source-text.js.map +1 -0
  15. package/dist/src/driver/bundler/graph.js +2 -1
  16. package/dist/src/driver/bundler/graph.js.map +1 -1
  17. package/dist/src/driver/compiler.js +28 -3
  18. package/dist/src/driver/compiler.js.map +1 -1
  19. package/dist/src/driver/config.d.ts +7 -0
  20. package/dist/src/driver/config.js.map +1 -1
  21. package/dist/src/driver/win-entry.d.ts +21 -0
  22. package/dist/src/driver/win-entry.js +52 -0
  23. package/dist/src/driver/win-entry.js.map +1 -0
  24. package/dist/src/lexer/scanner/strings.js +16 -3
  25. package/dist/src/lexer/scanner/strings.js.map +1 -1
  26. package/dist/tests/cli/hints.test.d.ts +9 -0
  27. package/dist/tests/cli/hints.test.js +143 -0
  28. package/dist/tests/cli/hints.test.js.map +1 -0
  29. package/dist/tests/cli/main.test.js +6 -4
  30. package/dist/tests/cli/main.test.js.map +1 -1
  31. package/dist/tests/codegen/llvm.test.js +17 -2
  32. package/dist/tests/codegen/llvm.test.js.map +1 -1
  33. package/dist/tests/e2e/gui-example-browser.test.d.ts +12 -0
  34. package/dist/tests/e2e/gui-example-browser.test.js +164 -0
  35. package/dist/tests/e2e/gui-example-browser.test.js.map +1 -0
  36. package/dist/tests/e2e/gui-example.test.d.ts +14 -0
  37. package/dist/tests/e2e/gui-example.test.js +76 -0
  38. package/dist/tests/e2e/gui-example.test.js.map +1 -0
  39. package/dist/tests/e2e/gui-helpers.d.ts +39 -4
  40. package/dist/tests/e2e/gui-helpers.js +38 -9
  41. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  42. package/dist/tests/e2e/gui-layout.test.js +155 -0
  43. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  44. package/dist/tests/e2e/gui-lifecycle.test.js +10 -1
  45. package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -1
  46. package/dist/tests/e2e/gui-paint.test.js +44 -4
  47. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  48. package/dist/tests/e2e/gui-script.test.js +8 -4
  49. package/dist/tests/e2e/gui-script.test.js.map +1 -1
  50. package/dist/tests/helpers.js +3 -2
  51. package/dist/tests/helpers.js.map +1 -1
  52. package/dist/tests/lexer/strings.test.js +14 -2
  53. package/dist/tests/lexer/strings.test.js.map +1 -1
  54. package/doc/DESIGN.md +117 -0
  55. package/doc/ai/README.md +63 -0
  56. package/doc/ai/build-recipe.md +137 -0
  57. package/doc/ai/cli.md +142 -0
  58. package/doc/ai/contributing.md +196 -0
  59. package/doc/ai/extensions.md +148 -0
  60. package/doc/ai/language-support.md +152 -0
  61. package/doc/ai/troubleshooting.md +163 -0
  62. package/doc/ai/zh-CN/README.md +56 -0
  63. package/doc/ai/zh-CN/build-recipe.md +132 -0
  64. package/doc/ai/zh-CN/cli.md +127 -0
  65. package/doc/ai/zh-CN/contributing.md +173 -0
  66. package/doc/ai/zh-CN/extensions.md +139 -0
  67. package/doc/ai/zh-CN/language-support.md +147 -0
  68. package/doc/ai/zh-CN/troubleshooting.md +150 -0
  69. package/doc/gui-scripts.md +350 -0
  70. package/doc/gui.md +752 -0
  71. package/doc/icon.md +265 -0
  72. package/doc/implemented.md +373 -0
  73. package/doc/node-implemented.md +588 -0
  74. package/doc/node-unimplemented.md +167 -0
  75. package/doc/post/announce.md +43 -0
  76. package/doc/requirements.md +145 -0
  77. package/doc/unimplemented.md +286 -0
  78. package/doc/xbintsc.config.schema.json +71 -0
  79. package/doc/zh-CN/DESIGN.md +104 -0
  80. package/doc/zh-CN/gui-scripts.md +329 -0
  81. package/doc/zh-CN/gui.md +656 -0
  82. package/doc/zh-CN/icon.md +241 -0
  83. package/doc/zh-CN/implemented.md +365 -0
  84. package/doc/zh-CN/node-implemented.md +533 -0
  85. package/doc/zh-CN/node-unimplemented.md +141 -0
  86. package/doc/zh-CN/plan-require-node-modules.md +284 -0
  87. package/doc/zh-CN/post/announce.md +47 -0
  88. package/doc/zh-CN/requirements.md +134 -0
  89. package/doc/zh-CN/unimplemented.md +247 -0
  90. package/llms.txt +45 -0
  91. package/package.json +4 -1
  92. package/runtime/ext_gui/dom_api.cpp +19 -0
  93. package/runtime/ext_gui/dom_api_node.cpp +4 -1
  94. package/runtime/ext_gui/dom_api_node_read.cpp +12 -0
  95. package/runtime/ext_gui/gui.cpp +45 -3
  96. package/runtime/ext_gui/gui_engine.h +15 -0
  97. package/runtime/ext_gui/gui_events.cpp +10 -3
  98. package/runtime/ext_gui/layout.cpp +6 -0
  99. package/runtime/ext_gui/layout.h +4 -0
  100. package/runtime/ext_gui/layout_flex.cpp +13 -5
  101. package/runtime/ext_gui/layout_flow.cpp +134 -4
  102. package/runtime/ext_gui/layout_flow_internal.h +41 -2
  103. package/runtime/ext_gui/layout_inline.cpp +11 -3
  104. package/runtime/ext_gui/paint.cpp +6 -0
  105. package/runtime/ext_gui/renderer.cpp +13 -11
  106. package/runtime/ext_gui/renderer_image.cpp +12 -8
  107. package/runtime/ext_gui/renderer_shaders.h +131 -4
  108. package/runtime/ext_gui/renderer_shaders_data.h +1809 -0
  109. package/runtime/ext_gui/renderer_text.cpp +12 -8
  110. package/runtime/ext_gui/script.cpp +12 -2
  111. package/runtime/ext_gui/shaders.hlsl +98 -0
  112. package/runtime/ext_gui/spirv/fill.frag +19 -0
  113. package/runtime/ext_gui/spirv/fill.vert +42 -0
  114. package/runtime/ext_gui/spirv/image.frag +16 -0
  115. package/runtime/ext_gui/spirv/quad.vert +30 -0
  116. package/runtime/ext_gui/spirv/text.frag +16 -0
  117. package/runtime/ext_gui/text.cpp +14 -0
  118. package/runtime/ext_gui/window.cpp +12 -0
  119. package/runtime/rt.h +10 -0
  120. package/scripts/build-gui-shaders.mjs +204 -0
  121. package/scripts/build-gui.ts +35 -0
  122. package/scripts/check-file-length.ts +5 -1
  123. package/src/cli/hints.ts +194 -0
  124. package/src/cli/main.ts +82 -9
  125. package/src/codegen/generator/tables.ts +60 -14
  126. package/src/diagnostics/source-text.ts +78 -0
  127. package/src/driver/bundler/graph.ts +2 -1
  128. package/src/driver/compiler.ts +29 -3
  129. package/src/driver/config.ts +7 -0
  130. package/src/driver/win-entry.ts +59 -0
  131. package/src/lexer/scanner/strings.ts +16 -3
@@ -5,27 +5,62 @@
5
5
  * the prebuilt `runtime/lib/<os>-<arch>/gui.a` and runs them. Programs close
6
6
  * themselves through the `XT_GUI_AUTOCLOSE_MS` hook, so the tests never block on
7
7
  * a real window. The suites are skipped when the GUI archive has not been built
8
- * (run `npm run gui` first) and on Windows, where the archive is not produced
9
- * yet.
8
+ * (run `npm run gui` first) and on Windows by default, where the archive is not
9
+ * validated in CI yet — `xbintsc_GUI_TESTS=1` opts in there.
10
+ *
11
+ * `XT_GUI_AUTOCLOSE_MS` is a *hang guard*, not a test budget: it is the wall
12
+ * clock the frame loop is allowed to run before it gives up and exits. A program
13
+ * that closes its own window ends the loop on its own, so a guard that is too
14
+ * tight only breaks tests that need several frames — raise it with
15
+ * `GuiRunOptions.autocloseMs` for those.
10
16
  *
11
17
  * `createGuiHarness()` owns one temp directory per suite plus the shared 4x4
12
18
  * test PNG; the suite calls `setup()` in `beforeAll` and `cleanup()` in
13
19
  * `afterAll`.
14
20
  */
15
- /** True when the GUI archive exists and we are not on Windows. */
21
+ /**
22
+ * True when the GUI archive exists and the suite may run.
23
+ *
24
+ * Windows is skipped by default: the archive (`gui.lib`) is produced by
25
+ * `scripts/build-gui.ts`, but CI has not validated the GUI there. Set
26
+ * `xbintsc_GUI_TESTS=1` to opt in on a Windows developer machine that has one —
27
+ * the suites open a real window, so they need a working GPU/display.
28
+ */
16
29
  export declare const guiAvailable: boolean;
17
30
  export interface GuiRunResult {
18
31
  stdout: string;
32
+ stderr: string;
19
33
  status: number | null;
20
34
  /** Read a `label=value` line out of `stdout`, asserting the label exists. */
21
35
  value(label: string): string;
22
36
  }
37
+ export interface GuiRunOptions {
38
+ /** Extra files written into the work directory before the build. */
39
+ files?: Record<string, string>;
40
+ /**
41
+ * Milliseconds the frame loop may run before `XT_GUI_AUTOCLOSE_MS` closes
42
+ * every window. A program whose handlers close the window does not need this
43
+ * at all — it is only the ceiling for a program that would otherwise hang, so
44
+ * raising it costs nothing when the test passes. Programs that need more than
45
+ * one rendered frame must raise it: the first frame does the one-time GPU work
46
+ * (shader and pipeline creation, the font atlas), which a software Vulkan
47
+ * driver on a loaded CI runner can stretch far past the default.
48
+ */
49
+ autocloseMs?: number;
50
+ }
23
51
  export interface GuiHarness {
24
52
  /** Absolute path of the 4x4 test PNG written during `setup()`. */
25
53
  readonly logoPath: string;
54
+ /**
55
+ * The same path, safe to embed in HTML/CSS. On Windows an absolute path
56
+ * contains backslashes, which an HTML attribute (or a CSS `url()`) would eat,
57
+ * so it is written with `/` separators and percent-encoded on the way in —
58
+ * the engine decodes it again (`xt_image_load`).
59
+ */
60
+ imageSrc(absolutePath: string): string;
26
61
  setup(): void;
27
62
  cleanup(): void;
28
63
  /** Compile a program and run it with the auto-close hook, returning stdout. */
29
- compileAndRun(name: string, source: string, extraFiles?: Record<string, string>): GuiRunResult;
64
+ compileAndRun(name: string, source: string, options?: GuiRunOptions): GuiRunResult;
30
65
  }
31
66
  export declare function createGuiHarness(): GuiHarness;
@@ -5,8 +5,14 @@
5
5
  * the prebuilt `runtime/lib/<os>-<arch>/gui.a` and runs them. Programs close
6
6
  * themselves through the `XT_GUI_AUTOCLOSE_MS` hook, so the tests never block on
7
7
  * a real window. The suites are skipped when the GUI archive has not been built
8
- * (run `npm run gui` first) and on Windows, where the archive is not produced
9
- * yet.
8
+ * (run `npm run gui` first) and on Windows by default, where the archive is not
9
+ * validated in CI yet — `xbintsc_GUI_TESTS=1` opts in there.
10
+ *
11
+ * `XT_GUI_AUTOCLOSE_MS` is a *hang guard*, not a test budget: it is the wall
12
+ * clock the frame loop is allowed to run before it gives up and exits. A program
13
+ * that closes its own window ends the loop on its own, so a guard that is too
14
+ * tight only breaks tests that need several frames — raise it with
15
+ * `GuiRunOptions.autocloseMs` for those.
10
16
  *
11
17
  * `createGuiHarness()` owns one temp directory per suite plus the shared 4x4
12
18
  * test PNG; the suite calls `setup()` in `beforeAll` and `cleanup()` in
@@ -22,15 +28,33 @@ import { createDefaultRegistry } from "../../src/extensions/registry.js";
22
28
  import { guiExtension } from "../../src/extensions/gui/index.js";
23
29
  import { findRuntimeDir, platformSlug } from "../../src/driver/paths.js";
24
30
  const archivePath = join(findRuntimeDir(), "lib", platformSlug(), "gui.a");
25
- /** True when the GUI archive exists and we are not on Windows. */
26
- export const guiAvailable = existsSync(archivePath) && process.platform !== "win32";
31
+ /**
32
+ * True when the GUI archive exists and the suite may run.
33
+ *
34
+ * Windows is skipped by default: the archive (`gui.lib`) is produced by
35
+ * `scripts/build-gui.ts`, but CI has not validated the GUI there. Set
36
+ * `xbintsc_GUI_TESTS=1` to opt in on a Windows developer machine that has one —
37
+ * the suites open a real window, so they need a working GPU/display.
38
+ */
39
+ export const guiAvailable = (existsSync(archivePath) || existsSync(join(findRuntimeDir(), "lib", platformSlug(), "gui.lib"))) &&
40
+ (process.platform !== "win32" || process.env.xbintsc_GUI_TESTS === "1");
41
+ /** Enough for programs whose whole body runs in the first frame's `ready` handler. */
42
+ const DEFAULT_AUTOCLOSE_MS = 400;
27
43
  /** A 4x4 PNG: left half red, right half blue (base64-encoded inline). */
28
44
  const LOGO_PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAFElEQVR42mP4z8DwH4Sh1H8G0gUALFAf4eNWqTEAAAAASUVORK5CYII=";
45
+ /**
46
+ * Make an absolute path embeddable in an HTML attribute or CSS `url()`: use `/`
47
+ * separators (valid on Windows too) and percent-encode the rest, which is how
48
+ * the engine's image loader expects a local path to arrive.
49
+ */
50
+ function toImageSrc(absolutePath) {
51
+ return encodeURI(absolutePath.split("\\").join("/"));
52
+ }
29
53
  export function createGuiHarness() {
30
54
  let workdir = "";
31
55
  let logoPath = "";
32
- const compileAndRun = (name, source, extraFiles = {}) => {
33
- for (const [fileName, contents] of Object.entries(extraFiles)) {
56
+ const compileAndRun = (name, source, options = {}) => {
57
+ for (const [fileName, contents] of Object.entries(options.files ?? {})) {
34
58
  writeFileSync(join(workdir, fileName), contents);
35
59
  }
36
60
  const entry = join(workdir, `${name}.ts`);
@@ -45,21 +69,26 @@ export function createGuiHarness() {
45
69
  expect(result.diagnostics.filter((diagnostic) => diagnostic.category === "error")).toEqual([]);
46
70
  const executed = spawnSync(result.outputPath, [], {
47
71
  encoding: "utf8",
48
- env: { ...process.env, XT_GUI_AUTOCLOSE_MS: "400" },
72
+ env: {
73
+ ...process.env,
74
+ XT_GUI_AUTOCLOSE_MS: String(options.autocloseMs ?? DEFAULT_AUTOCLOSE_MS),
75
+ },
49
76
  });
50
77
  expect(executed.status, executed.stderr).toBe(0);
51
78
  const stdout = executed.stdout;
79
+ const stderr = executed.stderr;
52
80
  const value = (label) => {
53
81
  const line = stdout.split("\n").find((entry) => entry.startsWith(`${label}=`));
54
- expect(line, `missing ${label} in:\n${stdout}`).toBeDefined();
82
+ expect(line, `missing ${label} in stdout:\n${stdout}\nstderr:\n${stderr}`).toBeDefined();
55
83
  return line.slice(label.length + 1).trim();
56
84
  };
57
- return { stdout, status: executed.status, value };
85
+ return { stdout, stderr, status: executed.status, value };
58
86
  };
59
87
  return {
60
88
  get logoPath() {
61
89
  return logoPath;
62
90
  },
91
+ imageSrc: toImageSrc,
63
92
  setup() {
64
93
  workdir = mkdtempSync(join(tmpdir(), "xbintsc-gui-"));
65
94
  logoPath = join(workdir, "logo.png");
@@ -1 +1 @@
1
- {"version":3,"file":"gui-helpers.js","sourceRoot":"","sources":["../../../tests/e2e/gui-helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gui-helpers.js","sourceRoot":"","sources":["../../../tests/e2e/gui-helpers.ts"],"names":[],"mappings":"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"}
@@ -53,6 +53,161 @@ describe.skipIf(!guiAvailable)("gui extension — layout", () => {
53
53
  expect(wrap.slice(0, 3)).toEqual([0, 154, 60]);
54
54
  expect(wrap[3]).toBeCloseTo(2 * Number(value("lh")), 1);
55
55
  });
56
+ it("positions boxes with `position` and its offsets", () => {
57
+ const { value } = harness.compileAndRun("position", `
58
+ import { createWindow, run } from "gui";
59
+
60
+ const HTML = \`
61
+ <html><head><style>
62
+ body { margin: 0; }
63
+ .rel { position: relative; top: -20px; left: 15px; height: 30px; }
64
+ .flow { height: 50px; }
65
+ #stage { position: absolute; top: 40px; left: 0px; right: 0px; height: 300px; }
66
+ #stage .inner { position: absolute; top: 30px; left: 40px; width: 50px; height: 20px; }
67
+ #stage .pinned { position: absolute; right: 10px; bottom: 20px; width: 60px; height: 30px; }
68
+ #stage .filler { height: 25px; }
69
+ #stage .stretch { position: absolute; left: 0px; right: 0px; top: 200px; height: 10px; }
70
+ #stage .auto { position: absolute; left: 10px; bottom: 200px; }
71
+ </style></head><body>
72
+ <div class="flow" id="a"></div>
73
+ <div class="rel" id="b"></div>
74
+ <div class="flow" id="c"></div>
75
+ <div id="stage">
76
+ <div class="inner" id="inner"></div>
77
+ <div class="pinned" id="pinned"></div>
78
+ <div class="filler" id="filler"></div>
79
+ <div class="stretch" id="stretch"></div>
80
+ <div class="auto" id="auto"><span style="font-size: 10px">abcd</span></div>
81
+ </div>
82
+ </body></html>
83
+ \`;
84
+
85
+ const win = createWindow({ title: "position", width: 800, height: 600 });
86
+ win.on("ready", () => {
87
+ const rect = (selector) => {
88
+ const r = win.getBoundingClientRect(selector);
89
+ return r.x + "," + r.y + "," + r.width + "," + r.height;
90
+ };
91
+ for (const id of ["a", "b", "c", "stage", "inner", "pinned", "filler", "stretch", "auto"]) {
92
+ console.log(id + "=" + rect("#" + id));
93
+ }
94
+ win.close();
95
+ });
96
+ win.loadHTML(HTML);
97
+ run();
98
+ `);
99
+ // `relative` moves the box without changing the space it reserved, so the
100
+ // following sibling advances by the *unshifted* height: `a` and `b` occupy
101
+ // 50 + 50, and `c` starts at 100 - 20 = 80.
102
+ expect(value("a")).toBe("0,0,800,50");
103
+ expect(value("b")).toBe("15,30,800,30");
104
+ expect(value("c")).toBe("0,80,800,50");
105
+ // The stage fills `left:0/right:0` and starts at `top:40px`.
106
+ expect(value("stage")).toBe("0,40,800,300");
107
+ // Absolute offsets resolve against the stage's padding box (its origin).
108
+ expect(value("inner")).toBe("40,70,50,20");
109
+ // `right`/`bottom` are measured from the stage's far edges: x = 800 - 10 -
110
+ // 60, y = 40 + 300 - 20 - 30.
111
+ expect(value("pinned")).toBe("730,290,60,30");
112
+ // Normal flow inside the stage is not disturbed by the absolute boxes.
113
+ expect(value("filler")).toBe("0,40,800,25");
114
+ // `left` + `right` with `auto` width fills the containing block.
115
+ expect(value("stretch")).toBe("0,240,800,10");
116
+ // `auto` width with only one inset is shrink-to-fit, not full width; the
117
+ // `bottom: 200px` still pins its bottom edge to the containing block.
118
+ const auto = value("auto").split(",").map(Number);
119
+ const autoY = Number(auto[1]);
120
+ const autoW = Number(auto[2]);
121
+ const autoH = Number(auto[3]);
122
+ expect(autoY + autoH).toBe(140);
123
+ expect(autoH).toBeGreaterThan(8);
124
+ expect(autoW).toBeGreaterThan(10);
125
+ expect(autoW).toBeLessThan(60);
126
+ });
127
+ it("keeps margin in flex layout", () => {
128
+ const { value } = harness.compileAndRun("flexmargin", `
129
+ import { createWindow, run } from "gui";
130
+
131
+ const HTML = \`
132
+ <html><head><style>
133
+ body { margin: 0; }
134
+ #col { display: flex; flex-direction: column; height: 200px; }
135
+ #col .one { height: 50px; }
136
+ #col .two { height: 50px; margin-top: -20px; }
137
+ #col .three { height: 50px; margin-left: 30px; }
138
+ #row { display: flex; width: 300px; }
139
+ #row .a { width: 40px; height: 20px; margin-right: 10px; }
140
+ #row .b { width: 40px; height: 20px; margin-top: 15px; }
141
+ </style></head><body>
142
+ <div id="col"><div class="one" id="one"></div><div class="two" id="two"></div><div class="three" id="three"></div></div>
143
+ <div id="row"><div class="a" id="ra"></div><div class="b" id="rb"></div></div>
144
+ </body></html>
145
+ \`;
146
+
147
+ const win = createWindow({ title: "flexmargin", width: 800, height: 600 });
148
+ win.on("ready", () => {
149
+ const rect = (selector) => {
150
+ const r = win.getBoundingClientRect(selector);
151
+ return r.x + "," + r.y + "," + r.width + "," + r.height;
152
+ };
153
+ for (const id of ["one", "two", "three", "ra", "rb"]) console.log(id + "=" + rect("#" + id));
154
+ win.close();
155
+ });
156
+ win.loadHTML(HTML);
157
+ run();
158
+ `);
159
+ // A negative `margin-top` in a flex column pulls the item up (50 -> 30).
160
+ expect(value("one")).toBe("0,0,800,50");
161
+ expect(value("two")).toBe("0,30,800,50");
162
+ // `margin-left` shifts the item; `align-items` defaults to stretch, so the
163
+ // cross-size still fills the column.
164
+ expect(value("three")).toBe("30,80,770,50");
165
+ // In a flex row the margin is part of the item's outer size.
166
+ expect(value("ra")).toBe("0,200,40,20");
167
+ expect(value("rb")).toBe("50,215,40,20");
168
+ });
169
+ it("collapses source whitespace between block boxes", () => {
170
+ const { value } = harness.compileAndRun("whitespace", `
171
+ import { createWindow, run } from "gui";
172
+
173
+ /* Written the way a real document is: the indentation is whitespace-only
174
+ text between block boxes, which HTML collapses away. */
175
+ const HTML = "<html>\\n" +
176
+ " <head><style>body { margin: 0; } .box { height: 20px; }</style></head>\\n" +
177
+ " <body>\\n" +
178
+ " <div class=\\"box\\" id=\\"one\\"></div>\\n" +
179
+ " <p id=\\"para\\">hello <span>world</span></p>\\n" +
180
+ " <div class=\\"box\\" id=\\"two\\"></div>\\n" +
181
+ " </body>\\n" +
182
+ "</html>";
183
+
184
+ const win = createWindow({ title: "whitespace", width: 400, height: 300 });
185
+ win.on("ready", () => {
186
+ const rect = (selector) => {
187
+ const r = win.getBoundingClientRect(selector);
188
+ return r.x + "," + r.y + "," + r.width + "," + r.height;
189
+ };
190
+ for (const id of ["one", "para", "two"]) console.log(id + "=" + rect("#" + id));
191
+ win.close();
192
+ });
193
+ win.loadHTML(HTML);
194
+ run();
195
+ `);
196
+ // The first block starts at the very top: the newline/indentation before it
197
+ // (and between the boxes) must not become text or an empty line.
198
+ expect(value("one")).toBe("0,0,400,20");
199
+ // A paragraph keeps its own margins, and the whitespace *inside* it is not
200
+ // touched (only whitespace between block boxes collapses).
201
+ const para = value("para").split(",").map(Number);
202
+ const paraTop = Number(para[1]);
203
+ const paraHeight = Number(para[3]);
204
+ expect(para[2]).toBe(400);
205
+ expect(paraTop).toBeGreaterThanOrEqual(20);
206
+ expect(paraHeight).toBeGreaterThan(0);
207
+ // `two` follows the paragraph rather than sitting next to `one`.
208
+ const twoTop = Number(value("two").split(",")[1]);
209
+ expect(twoTop).toBeGreaterThan(paraTop + paraHeight);
210
+ });
56
211
  it("shapes and measures text with the font stack", () => {
57
212
  const { value } = harness.compileAndRun("text", `
58
213
  import { createWindow, run } from "gui";
@@ -1 +1 @@
1
- {"version":3,"file":"gui-layout.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-layout.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,wBAAwB,EAAE,GAAG,EAAE;IAC5D,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,QAAQ,EACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCC,CACF,CAAC;QAEF,yEAAyE;QACzE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzC,mFAAmF;QACnF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACzC,yEAAyE;QACzE,yEAAyE;QACzE,8DAA8D;QAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,MAAM,EACN;;;;;;;;;;;;;;;;;OAiBC,CACF,CAAC;QAEF,0EAA0E;QAC1E,sDAAsD;QACtD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACjC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"gui-layout.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-layout.test.ts"],"names":[],"mappings":"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"}
@@ -32,6 +32,9 @@ describe.skipIf(!guiAvailable)("gui extension — lifecycle", () => {
32
32
  let frames = 0;
33
33
  const tick = (t: number) => {
34
34
  frames = frames + 1;
35
+ // Per-frame progress, so a run cut short reports how many frames ran
36
+ // instead of only the ready-handler output.
37
+ console.log("tick=" + frames);
35
38
  win.document.getElementById("label").textContent = String(frames);
36
39
  if (frames < 3) win.requestAnimationFrame(tick);
37
40
  else {
@@ -48,7 +51,13 @@ describe.skipIf(!guiAvailable)("gui extension — lifecycle", () => {
48
51
  });
49
52
  win.loadHTML("<div id='label'>0</div>");
50
53
  run();
51
- `);
54
+ `,
55
+ // The program closes its own window after three frames, so this is only a
56
+ // hang guard. It must be loose: the first frame builds the shaders,
57
+ // pipelines and font atlas, which the software Vulkan driver the CI uses
58
+ // (lavapipe under Xvfb) can spend most of a second on — a tight budget
59
+ // expires before the second frame and the run ends with no `frames=` line.
60
+ { autocloseMs: 10_000 });
52
61
  expect(value("id")).toBe("true");
53
62
  expect(value("frames")).toBe("3:true"); // callback receives a timestamp
54
63
  expect(value("label")).toBe("3"); // the DOM write was visible on the next read
@@ -1 +1 @@
1
- {"version":3,"file":"gui-lifecycle.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-lifecycle.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,2BAA2B,EAAE,GAAG,EAAE;IAC/D,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CACtC,QAAQ,EACR;;;;;;;;;OASC,CACF,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CAC7C,KAAK,EACL;;;;;;;;;;;;;;;;;;;;;;;OAuBC,CACF,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,gCAAgC;QACxE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,6CAA6C;QAC/E,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"gui-lifecycle.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-lifecycle.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,2BAA2B,EAAE,GAAG,EAAE;IAC/D,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CACtC,QAAQ,EACR;;;;;;;;;OASC,CACF,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CAC7C,KAAK,EACL;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BC;QACD,0EAA0E;QAC1E,oEAAoE;QACpE,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,EAAE,WAAW,EAAE,MAAM,EAAE,CACxB,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,gCAAgC;QACxE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,6CAA6C;QAC/E,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -9,6 +9,7 @@ describe.skipIf(!guiAvailable)("gui extension — paint", () => {
9
9
  beforeAll(() => harness.setup());
10
10
  afterAll(() => harness.cleanup());
11
11
  it("sizes and paints images (intrinsic and explicit)", () => {
12
+ const src = harness.imageSrc(harness.logoPath);
12
13
  const { stdout } = harness.compileAndRun("image", `
13
14
  import { createWindow, run } from "gui";
14
15
 
@@ -17,8 +18,8 @@ describe.skipIf(!guiAvailable)("gui extension — paint", () => {
17
18
  body { margin: 0; }
18
19
  #logo { width: 40px; height: 40px; }
19
20
  </style></head><body>
20
- <img id="logo" src="${harness.logoPath}">
21
- <img id="natural" src="${harness.logoPath}">
21
+ <img id="logo" src="${src}">
22
+ <img id="natural" src="${src}">
22
23
  </body></html>
23
24
  \`;
24
25
 
@@ -37,8 +38,8 @@ describe.skipIf(!guiAvailable)("gui extension — paint", () => {
37
38
  // Explicit CSS size wins; without one the intrinsic 4x4 size is used.
38
39
  expect(stdout).toContain("logo=40x40");
39
40
  expect(stdout).toContain("natural=4x4");
40
- expect(stdout).toContain(`image x=0.0 y=0.0 w=40.0 h=40.0 src=${harness.logoPath}`);
41
- expect(stdout).toContain(`image x=0.0 y=40.0 w=4.0 h=4.0 src=${harness.logoPath}`);
41
+ expect(stdout).toContain(`image x=0.0 y=0.0 w=40.0 h=40.0 src=${src}`);
42
+ expect(stdout).toContain(`image x=0.0 y=40.0 w=4.0 h=4.0 src=${src}`);
42
43
  });
43
44
  it("builds a display list of backgrounds and borders", () => {
44
45
  const { value, stdout } = harness.compileAndRun("paint", `
@@ -107,5 +108,44 @@ describe.skipIf(!guiAvailable)("gui extension — paint", () => {
107
108
  expect(list).toContain('size=20.0');
108
109
  expect(list).toContain('color=#0000ffff "Hi"');
109
110
  });
111
+ it("does not paint an `opacity: 0` box or its subtree", () => {
112
+ const { value, stdout } = harness.compileAndRun("opacity", `
113
+ import { createWindow, run } from "gui";
114
+
115
+ const HTML = \`
116
+ <html><head><style>
117
+ body { margin: 0; background-color: #123456; }
118
+ #box { width: 30px; height: 10px; background: #ff0000; }
119
+ #panel { width: 40px; height: 20px; background: #00ff00; opacity: 0; }
120
+ #label { color: #ffff00; }
121
+ </style></head><body>
122
+ <div id="box"></div>
123
+ <div id="panel"><div id="label">hidden</div></div>
124
+ </body></html>
125
+ \`;
126
+
127
+ const win = createWindow({ title: "opacity", width: 400, height: 300 });
128
+ win.on("ready", () => {
129
+ // A hidden box is still laid out — it just is not painted.
130
+ const panel = win.getBoundingClientRect("#panel");
131
+ console.log("panel-rect=" + panel.x + "," + panel.y + "," + panel.width + "," + panel.height);
132
+ console.log("paint-count=" + win.paintCount());
133
+ console.log("paint-list-start");
134
+ console.log(win.paintList());
135
+ console.log("paint-list-end");
136
+ });
137
+ win.loadHTML(HTML);
138
+ run();
139
+ `);
140
+ const list = stdout.slice(stdout.indexOf("paint-list-start"), stdout.indexOf("paint-list-end"));
141
+ // Layout is unaffected: the 40x20 box still occupies its place.
142
+ expect(value("panel-rect")).toBe("0,10,40,20");
143
+ // body background + #box background only: #panel and its text paint nothing.
144
+ expect(value("paint-count")).toBe("2");
145
+ expect(list).not.toContain("#00ff00ff");
146
+ expect(list).not.toContain("hidden");
147
+ // Anything above 0 is painted in full (there is no partial compositing yet).
148
+ expect(list).toContain("#ff0000ff");
149
+ });
110
150
  });
111
151
  //# sourceMappingURL=gui-paint.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gui-paint.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-paint.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gui-paint.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-paint.test.ts"],"names":[],"mappings":"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"}
@@ -25,7 +25,8 @@ describe.skipIf(!guiAvailable)("gui extension — scripts", () => {
25
25
  win.loadHTML(page);
26
26
  run();
27
27
  `, {
28
- "page.html": `<html><body>
28
+ files: {
29
+ "page.html": `<html><body>
29
30
  <div id="count">0</div>
30
31
  <button id="b">0</button>
31
32
  <script lang="ts">
@@ -39,6 +40,7 @@ describe.skipIf(!guiAvailable)("gui extension — scripts", () => {
39
40
  });
40
41
  </script>
41
42
  </body></html>`,
43
+ },
42
44
  });
43
45
  expect(value("after")).toBe("2");
44
46
  expect(value("button")).toBe("2");
@@ -59,8 +61,9 @@ describe.skipIf(!guiAvailable)("gui extension — scripts", () => {
59
61
  win.loadHTML(page);
60
62
  run();
61
63
  `, {
62
- "page-src.html": `<html><body><button id="b">0</button><script src="./counter.ts"></script></body></html>`,
63
- "counter.ts": `
64
+ files: {
65
+ "page-src.html": `<html><body><button id="b">0</button><script src="./counter.ts"></script></body></html>`,
66
+ "counter.ts": `
64
67
  import { double } from "./helper";
65
68
  const button = document.getElementById("b");
66
69
  let n = 0;
@@ -69,7 +72,8 @@ button.addEventListener("click", () => {
69
72
  button.textContent = String(n);
70
73
  });
71
74
  `,
72
- "helper.ts": "export function double(n: number) { return n * 2; }",
75
+ "helper.ts": "export function double(n: number) { return n * 2; }",
76
+ },
73
77
  });
74
78
  /* 0 -> 1 -> 3 proves the hoisted import (double) is linked. */
75
79
  expect(value("srcAfter")).toBe("3");
@@ -1 +1 @@
1
- {"version":3,"file":"gui-script.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-script.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,yBAAyB,EAAE,GAAG,EAAE;IAC7D,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,QAAQ,EACR;;;;;;;;;;;;;;;OAeC,EACD;YACE,WAAW,EAAE;;;;;;;;;;;;;eAaN;SACR,CACF,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,WAAW,EACX;;;;;;;;;;;;;;OAcC,EACD;YACE,eAAe,EAAE,yFAAyF;YAC1G,YAAY,EAAE;;;;;;;;CAQrB;YACO,WAAW,EAAE,qDAAqD;SACnE,CACF,CAAC;QACF,+DAA+D;QAC/D,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"gui-script.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-script.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,yBAAyB,EAAE,GAAG,EAAE;IAC7D,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,QAAQ,EACR;;;;;;;;;;;;;;;OAeC,EACD;YACE,KAAK,EAAE;gBACL,WAAW,EAAE;;;;;;;;;;;;;eAaR;aACN;SACF,CACF,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,WAAW,EACX;;;;;;;;;;;;;;OAcC,EACD;YACE,KAAK,EAAE;gBACL,eAAe,EAAE,yFAAyF;gBAC1G,YAAY,EAAE;;;;;;;;CAQvB;gBACS,WAAW,EAAE,qDAAqD;aACnE;SACF,CACF,CAAC;QACF,+DAA+D;QAC/D,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,6 +1,7 @@
1
1
  /** Shared helpers for the per-module test suites. */
2
2
  import { DiagnosticBag } from "../src/diagnostics/diagnostic.js";
3
3
  import { SourceFile } from "../src/diagnostics/source.js";
4
+ import { sourceTextBytes } from "../src/diagnostics/source-text.js";
4
5
  import { Scanner } from "../src/lexer/scanner.js";
5
6
  import { Parser } from "../src/parser/parser.js";
6
7
  import { bind } from "../src/binder/binder.js";
@@ -8,13 +9,13 @@ import { generate } from "../src/codegen/llvm.js";
8
9
  import { createDefaultRegistry } from "../src/extensions/registry.js";
9
10
  import { realRunner } from "../src/driver/toolchain.js";
10
11
  export function lex(text) {
11
- const source = new SourceFile("test.ts", text);
12
+ const source = new SourceFile("test.ts", sourceTextBytes(text));
12
13
  const diagnostics = new DiagnosticBag();
13
14
  const scanner = new Scanner(source, diagnostics);
14
15
  return { tokens: scanner.tokenize(), diagnostics: diagnostics.diagnostics };
15
16
  }
16
17
  export function parse(text) {
17
- const source = new SourceFile("test.ts", text);
18
+ const source = new SourceFile("test.ts", sourceTextBytes(text));
18
19
  const diagnostics = new DiagnosticBag();
19
20
  const parser = new Parser(source, diagnostics);
20
21
  const file = parser.parseSourceFile();
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../tests/helpers.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,OAAO,EAAE,aAAa,EAAmB,MAAM,kCAAkC,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAmB,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAA0B,MAAM,+BAA+B,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGxD,MAAM,UAAU,GAAG,CAAC,IAAY;IAC9B,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,IAAI,aAAa,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACjD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,IAAI,aAAa,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;IACtC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,aAAa,EAAE,CAAC;IAChC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACxB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,UAA8B,EAC9B,MAA2C;IAE3C,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,IAAI,aAAa,EAAE,CAAC;IACxC,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,UAAU,IAAI,qBAAqB,EAAE,CAAC;IACvD,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE;QAClD,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE;QAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE;QAC3B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9B,CAAC,CAAC;IACH,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAC1F,CAAC"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../tests/helpers.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,14 @@ function decoded(source) {
8
8
  expect(diagnostics).toHaveLength(0);
9
9
  return tokens[0].value;
10
10
  }
11
+ /**
12
+ * The text a code point's escape decodes to: its UTF-8 bytes, one code unit
13
+ * each (`escapeText` in the scanner spells them the same way, so both hosts
14
+ * store a literal the same).
15
+ */
16
+ function escaped(code) {
17
+ return Buffer.from(String.fromCodePoint(code), "utf8").toString("latin1");
18
+ }
11
19
  describe("scanner string literals", () => {
12
20
  it("decodes the simple control escapes", () => {
13
21
  expect(decoded('"a\\nb"')).toBe("a\nb");
@@ -17,9 +25,13 @@ describe("scanner string literals", () => {
17
25
  expect(decoded('"a\\fb"')).toBe("a\fb");
18
26
  });
19
27
  it("decodes braced, fixed-width and hex unicode escapes", () => {
20
- expect(decoded('"\\u{1F600}"')).toBe("\u{1F600}");
28
+ // An escape names a code point; the literal holds that code point's UTF-8
29
+ // bytes, which codegen emits unchanged.
30
+ expect(decoded('"\\u{1F600}"')).toBe(escaped(0x1f600));
21
31
  expect(decoded('"\\u0041"')).toBe("A");
32
+ expect(decoded('"\\u00e9"')).toBe(escaped(0xe9));
22
33
  expect(decoded('"\\x41"')).toBe("A");
34
+ expect(decoded('"\\xe9"')).toBe(escaped(0xe9));
23
35
  });
24
36
  it("falls back to NUL for malformed escapes", () => {
25
37
  expect(decoded('"\\u{zz}"')).toBe("\0");
@@ -34,7 +46,7 @@ describe("scanner string literals", () => {
34
46
  expect(decoded('"\\q"')).toBe("q");
35
47
  });
36
48
  it("keeps surrogate pairs and non-ASCII characters intact", () => {
37
- expect(decoded('"héllo→"')).toBe("héllo→");
49
+ expect(decoded('"héllo→"')).toBe(Buffer.from("héllo→", "utf8").toString("latin1"));
38
50
  });
39
51
  it("reports an unterminated string when a line break is reached", () => {
40
52
  const { diagnostics } = lex('"line\nnext"');
@@ -1 +1 @@
1
- {"version":3,"file":"strings.test.js","sourceRoot":"","sources":["../../../tests/lexer/strings.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strings.test.js","sourceRoot":"","sources":["../../../tests/lexer/strings.test.ts"],"names":[],"mappings":"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"}