gofront 1.8.0 → 1.9.1

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 (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -6,7 +6,6 @@ import { statSync } from "node:fs";
6
6
  import { createRequire } from "node:module";
7
7
  import { basename, dirname, join, resolve } from "node:path";
8
8
  import { pathToFileURL } from "node:url";
9
- import { HELPER_SPRINTF, HELPER_TESTING } from "../backend/js/runtime.js";
10
9
  import {
11
10
  compilePackageTests,
12
11
  compilePackageTestsWasm,
@@ -97,6 +96,19 @@ export function discoverTests(programs) {
97
96
  return testNames;
98
97
  }
99
98
 
99
+ // The `async func Test…` names (js packages only).
100
+ export function discoverAsyncTests(programs) {
101
+ const names = [];
102
+ for (const p of programs) {
103
+ if (p._filename && !p._filename.endsWith("_test.go")) continue;
104
+ for (const d of p.decls) {
105
+ if (isTestFunc(d) && (d.async || d.isAsync) && !names.includes(d.name))
106
+ names.push(d.name);
107
+ }
108
+ }
109
+ return names;
110
+ }
111
+
100
112
  export function discoverBenchmarks(programs) {
101
113
  const benchNames = [];
102
114
  for (const p of programs) {
@@ -120,15 +132,53 @@ export function discoverTestMain(programs) {
120
132
  return null;
121
133
  }
122
134
 
123
- export function generateTestHarness(bundleJs, testNames, options = {}) {
124
- const runRegex = options.run ? options.run : null;
125
- const benchRegex = options.bench
126
- ? options.bench === true
127
- ? "."
128
- : options.bench
129
- : null;
130
- const benchNames = options.benchNames ?? [];
131
- const testMainName = options.testMain ?? null;
135
+ // The Go source of the driver the runner compiles with a package's tests (see
136
+ // `testDriver` in compileFiles; it is never written to disk). `target` is the
137
+ // package's target: a `js` package is driven asynchronously so that async
138
+ // tests and subtests can be awaited; a TestMain gets the M and runs it
139
+ // itself.
140
+ export function generateTestDriver(
141
+ pkgName,
142
+ target,
143
+ { testNames, benchNames, testMain, asyncTests = [] },
144
+ ) {
145
+ if (testMain && asyncTests.length > 0) {
146
+ throw new Error(
147
+ `async test functions cannot be combined with TestMain (${asyncTests.join(", ")}): remove TestMain or make the tests synchronous`,
148
+ );
149
+ }
150
+ const entries = (names, _type) =>
151
+ names.map((n) => `\t\t{Name: ${JSON.stringify(n)}, F: ${n}},`).join("\n");
152
+ const table = (type, names) =>
153
+ names.length === 0
154
+ ? `[]testing.${type}{}`
155
+ : `[]testing.${type}{\n${entries(names)}\n\t}`;
156
+ const make = `\tm := testing.GofrontMain(${JSON.stringify(pkgName)}, run, bench, verbose, ${table("GofrontTest", testNames)}, ${table("GofrontBenchmark", benchNames)})`;
157
+ let body;
158
+ let head = "func";
159
+ if (testMain) {
160
+ body = `\tTestMain(m)\n\treturn m.ExitCode()`;
161
+ } else if (target === "js") {
162
+ head = "async func";
163
+ body = "\treturn await m.RunAsync()";
164
+ } else {
165
+ body = "\treturn m.Run()";
166
+ }
167
+ return `package ${pkgName}
168
+
169
+ import "testing"
170
+
171
+ ${head} GofrontRunTests(run, bench string, verbose bool) int {
172
+ ${make}
173
+ ${body}
174
+ }
175
+ `;
176
+ }
177
+
178
+ // The module the test child process runs: the compiled package (with its
179
+ // driver), then one call to GofrontRunTests. Everything else (running,
180
+ // filtering, reporting) happens inside the package.
181
+ export function generateTestHarness(bundleJs, options = {}) {
132
182
  const verbose = Boolean(options.verbose);
133
183
  const dom = Boolean(options.dom);
134
184
  const color = Boolean(options.color);
@@ -138,14 +188,12 @@ export function generateTestHarness(bundleJs, testNames, options = {}) {
138
188
  const jsdomSpec = options.jsdomPath
139
189
  ? pathToFileURL(options.jsdomPath).href
140
190
  : "jsdom";
141
-
142
- const allFuncNames = [
143
- ...new Set([
144
- ...testNames,
145
- ...benchNames,
146
- ...(testMainName ? [testMainName] : []),
147
- ]),
148
- ];
191
+ const run = options.run ?? "";
192
+ const bench = options.bench
193
+ ? options.bench === true
194
+ ? "."
195
+ : options.bench
196
+ : "";
149
197
 
150
198
  return `
151
199
  ${
@@ -177,14 +225,12 @@ globalThis.customElements = __dom.window.customElements;
177
225
  globalThis.document = globalThis.document ?? null;
178
226
  `
179
227
  }
180
- globalThis.__gofront_verbose = ${verbose};
228
+ // The package line and colours the testing package prints come from here.
229
+ process.env.GOFRONT_TEST_LABEL = ${JSON.stringify(pkgName)};
230
+ process.env.GOFRONT_TEST_COLOR = ${color ? '"1"' : '"0"'};
181
231
  ${
182
232
  options.wasmBytes
183
- ? `
184
- // ── WASM module (tests run inside app.wasm; *testing.T stays in JS) ──
185
- ${HELPER_SPRINTF}
186
- ${HELPER_TESTING}
187
- globalThis.__GOFRONT_WASM_BYTES = Uint8Array.from(atob(${JSON.stringify(Buffer.from(options.wasmBytes).toString("base64"))}), (c) => c.charCodeAt(0));
233
+ ? `globalThis.__GOFRONT_WASM_BYTES = Uint8Array.from(atob(${JSON.stringify(Buffer.from(options.wasmBytes).toString("base64"))}), (c) => c.charCodeAt(0));
188
234
  `
189
235
  : ""
190
236
  }
@@ -192,313 +238,13 @@ globalThis.__GOFRONT_WASM_BYTES = Uint8Array.from(atob(${JSON.stringify(Buffer.f
192
238
  ${bundleJs}
193
239
  // ─────────────────────────────
194
240
 
195
- async function __runGoFrontSuite() {
196
- const allTests = ${JSON.stringify(testNames)};
197
- const allBenchmarks = ${JSON.stringify(benchNames)};
198
- const testMainName = ${JSON.stringify(testMainName)};
199
- const runFilter = ${runRegex ? `new RegExp(${JSON.stringify(runRegex)})` : "null"};
200
- const benchFilter = ${benchRegex ? `new RegExp(${JSON.stringify(benchRegex)})` : "null"};
201
- const verbose = ${verbose};
202
- const pkgName = ${JSON.stringify(pkgName)};
203
- const paint = ${color}
204
- ? (code) => (s) => "\\x1b[" + code + "m" + s + "\\x1b[0m"
205
- : () => (s) => s;
206
- const red = paint(31), green = paint(32), yellow = paint(33), dim = paint(2), bold = paint(1);
207
- const PASS = green("PASS"), FAIL = bold(red("FAIL")), SKIP = yellow("SKIP");
208
- const okLine = (name, t) => console.log(green("ok ") + "\\t" + name + "\\t" + dim(t + "s"));
209
-
210
- const testsToRun = runFilter
211
- ? allTests.filter((name) => runFilter.test(name))
212
- : allTests;
213
-
214
- const benchesToRun = benchFilter
215
- ? allBenchmarks.filter((name) => benchFilter.test(name))
216
- : [];
217
-
218
- if (testsToRun.length === 0 && benchesToRun.length === 0) {
219
- if (allTests.length > 0 && runFilter) {
220
- console.log(yellow("testing: warning: no tests to run matching " + ${JSON.stringify(runRegex)}));
221
- }
222
- if (allBenchmarks.length > 0 && benchFilter) {
223
- console.log(yellow("testing: warning: no benchmarks to run matching " + ${JSON.stringify(benchRegex)}));
224
- }
225
- console.log(PASS);
226
- okLine(pkgName, "0.000");
227
- process.exit(0);
228
- }
229
-
230
- const now = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
231
-
232
- // Go-style streaming report. In non-verbose mode only failures and skips are
233
- // shown, so the "=== RUN" header is emitted late for failures.
234
- const report = (t) => {
235
- const elapsed = dim("(" + ((now() - t._start) / 1000).toFixed(2) + "s)");
236
- if (t.skipped) {
237
- console.log("--- " + SKIP + ": " + t._name + " " + elapsed);
238
- for (const log of t._logs) console.log(" " + log);
239
- } else if (t.failed) {
240
- if (!verbose) console.log(dim("=== RUN ") + t._name);
241
- for (const log of t._logs) console.log(" " + red(log));
242
- console.log("--- " + FAIL + ": " + t._name + " " + elapsed);
243
- } else if (verbose) {
244
- for (const log of t._logs) console.log(" " + log);
245
- console.log("--- " + PASS + ": " + t._name + " " + elapsed);
246
- }
247
- };
248
-
249
- const reportBenchmark = (b) => {
250
- if (b.skipped) {
251
- console.log("--- " + SKIP + ": " + b._name);
252
- for (const log of b._logs) console.log(" " + log);
253
- return;
254
- }
255
- if (b.failed) {
256
- console.log("--- " + FAIL + ": " + b._name);
257
- for (const log of b._logs) console.log(" " + red(log));
258
- return;
259
- }
260
- const elapsedNs = Number(b.Elapsed());
261
- const nsPerOp = b.N > 0 ? (elapsedNs / b.N).toFixed(0) : "0";
262
- let line = \`\${b._name.padEnd(20)}\\t\${String(b.N).padStart(8)}\\t\${nsPerOp.padStart(10)} ns/op\`;
263
- if (b._bytes > 0n && elapsedNs > 0) {
264
- const mbPerSec = (Number(b._bytes * BigInt(b.N)) / (elapsedNs / 1e9) / (1024 * 1024)).toFixed(2);
265
- line += \`\\t\${mbPerSec.padStart(8)} MB/s\`;
266
- }
267
- console.log(line);
268
- for (const log of b._logs) console.log(" " + log);
269
- };
270
-
271
- // FailNow/SkipNow are control flow; anything else is a panic → failure.
272
- const recordTestError = (t, e) => {
273
- if (e instanceof __GoFront_FailNow || e instanceof __GoFront_SkipNow) return;
274
- t.Fail();
275
- t.Log("panic: " + (e?.message ?? String(e)));
276
- };
277
-
278
- globalThis.__onSubtestStart = (name) => {
279
- if (verbose) console.log(dim("=== RUN ") + name);
280
- };
281
- globalThis.__onSubtestEnd = report;
282
-
283
- const __testRegistry = {
284
- ${allFuncNames.map((n) => ` ${JSON.stringify(n)}: typeof ${n} !== "undefined" ? ${n} : null`).join(",\n")}
285
- };
286
-
287
- function runAll() {
288
- let totalFailures = 0;
289
- let totalSkipped = 0;
290
- const suiteStart = now();
291
-
292
- const finish = () => {
293
- const suiteElapsed = ((now() - suiteStart) / 1000).toFixed(3);
294
- if (totalFailures > 0) {
295
- console.log(FAIL);
296
- console.log(FAIL + "\\t" + pkgName + "\\t" + dim(suiteElapsed + "s"));
297
- return 1;
298
- } else {
299
- console.log(PASS);
300
- okLine(pkgName, suiteElapsed);
301
- return 0;
302
- }
303
- };
304
-
305
- const runOneTest = (name) => {
306
- const fn = __testRegistry[name] ?? (typeof globalThis[name] === "function" ? globalThis[name] : null);
307
- if (typeof fn !== "function") {
308
- console.error(red("testing: cannot find test function " + name));
309
- totalFailures++;
310
- return null;
311
- }
312
-
313
- const t = new __GoFront_T(name);
314
- if (verbose) console.log(dim("=== RUN ") + name);
315
-
316
- let res;
317
- try {
318
- res = fn(t);
319
- } catch (e) {
320
- recordTestError(t, e);
321
- t._runCleanups();
322
- if (t.skipped) totalSkipped++;
323
- else if (t.failed) totalFailures++;
324
- report(t);
325
- return null;
326
- }
327
-
328
- if (res && typeof res.then === "function") {
329
- return res
330
- .catch((e) => { recordTestError(t, e); })
331
- .finally(() => {
332
- t._runCleanups();
333
- if (t.skipped) totalSkipped++;
334
- else if (t.failed) totalFailures++;
335
- report(t);
336
- });
337
- }
338
-
339
- t._runCleanups();
340
- if (t.skipped) totalSkipped++;
341
- else if (t.failed) totalFailures++;
342
- report(t);
343
- return null;
344
- };
345
-
346
- const runOneBench = (name) => {
347
- const fn = __testRegistry[name] ?? (typeof globalThis[name] === "function" ? globalThis[name] : null);
348
- if (typeof fn !== "function") {
349
- console.error(red("testing: cannot find benchmark function " + name));
350
- totalFailures++;
351
- return null;
352
- }
353
- if (verbose) console.log(dim("=== RUN ") + name);
354
- const b = new __GoFront_B(name);
355
- b._benchDurationNs = 1e8; // 100ms
356
- const runIters = () => {
357
- b.N = 1;
358
- b.ResetTimer();
359
- const res = fn(b);
360
- if (res && typeof res.then === "function") {
361
- return res.then(() => loopBench(b, fn));
362
- }
363
- return loopBench(b, fn);
364
- };
365
- const loopBench = (b, fn) => {
366
- if (!b._loopStarted && !b.failed && !b.skipped) {
367
- b.StopTimer();
368
- b._runCleanups();
369
- let n = 1;
370
- while (!b.failed && !b.skipped) {
371
- const elapsedNs = Number(b.Elapsed());
372
- if (elapsedNs >= b._benchDurationNs || n >= 1e9) break;
373
- let nextN;
374
- if (elapsedNs === 0) nextN = n * 100;
375
- else nextN = Math.max(n + 1, Math.min(1e9, Math.floor(n * (b._benchDurationNs / elapsedNs) * 1.2)));
376
- if (nextN <= n) break;
377
- n = nextN;
378
- const nextB = new __GoFront_B(name);
379
- nextB._benchDurationNs = b._benchDurationNs;
380
- nextB.N = n;
381
- nextB.ResetTimer();
382
- const res = fn(nextB);
383
- if (res && typeof res.then === "function") {
384
- return res.then(() => {
385
- nextB.StopTimer();
386
- nextB._runCleanups();
387
- b.N = nextB.N;
388
- b._elapsedMs = nextB._elapsedMs;
389
- b._timerRunning = false;
390
- b.failed = nextB.failed;
391
- b.skipped = nextB.skipped;
392
- b._logs = nextB._logs;
393
- });
394
- }
395
- nextB.StopTimer();
396
- nextB._runCleanups();
397
- b.N = nextB.N;
398
- b._elapsedMs = nextB._elapsedMs;
399
- b._timerRunning = false;
400
- b.failed = nextB.failed;
401
- b.skipped = nextB.skipped;
402
- b._logs = nextB._logs;
403
- }
404
- }
405
- };
406
-
407
- let res;
408
- try {
409
- res = runIters();
410
- } catch (e) {
411
- recordTestError(b, e);
412
- b._runCleanups();
413
- if (b.failed) totalFailures++;
414
- else if (b.skipped) totalSkipped++;
415
- reportBenchmark(b);
416
- return null;
417
- }
418
-
419
- if (res && typeof res.then === "function") {
420
- return res
421
- .catch((e) => { recordTestError(b, e); })
422
- .finally(() => {
423
- b._runCleanups();
424
- if (b.failed) totalFailures++;
425
- else if (b.skipped) totalSkipped++;
426
- reportBenchmark(b);
427
- });
428
- }
429
-
430
- b._runCleanups();
431
- if (b.failed) totalFailures++;
432
- else if (b.skipped) totalSkipped++;
433
- reportBenchmark(b);
434
- return null;
435
- };
436
-
437
- let ti = 0;
438
- while (ti < testsToRun.length) {
439
- const p = runOneTest(testsToRun[ti++]);
440
- if (p) {
441
- return p.then(async () => {
442
- while (ti < testsToRun.length) {
443
- const nextP = runOneTest(testsToRun[ti++]);
444
- if (nextP) await nextP;
445
- }
446
- for (const bName of benchesToRun) {
447
- const nextP = runOneBench(bName);
448
- if (nextP) await nextP;
449
- }
450
- return finish();
451
- });
452
- }
453
- }
454
-
455
- let bi = 0;
456
- while (bi < benchesToRun.length) {
457
- const p = runOneBench(benchesToRun[bi++]);
458
- if (p) {
459
- return p.then(async () => {
460
- while (bi < benchesToRun.length) {
461
- const nextP = runOneBench(benchesToRun[bi++]);
462
- if (nextP) await nextP;
463
- }
464
- return finish();
465
- });
466
- }
467
- }
468
-
469
- return finish();
470
- }
471
-
472
- if (testMainName) {
473
- const mainFn = __testRegistry[testMainName] ?? (typeof globalThis[testMainName] === "function" ? globalThis[testMainName] : null);
474
- if (typeof mainFn === "function") {
475
- let exitCode = 0;
476
- const m = new __GoFront_M(() => {
477
- const res = runAll();
478
- if (res && typeof res.then === "function") {
479
- return res.then((code) => { exitCode = code; return code; });
480
- }
481
- exitCode = res;
482
- return exitCode;
483
- });
484
- try {
485
- const res = mainFn(m);
486
- if (res && typeof res.then === "function") await res;
487
- } catch (e) {
488
- console.error(red("panic in TestMain: " + (e?.message ?? String(e))));
489
- process.exit(1);
490
- }
491
- if (m._promise) await m._promise;
492
- process.exit(exitCode);
493
- }
494
- }
495
-
496
- const res = runAll();
497
- const exitCode = res && typeof res.then === "function" ? await res : res;
498
- process.exit(exitCode);
241
+ let __exitCode = 1;
242
+ try {
243
+ __exitCode = Number(await GofrontRunTests(${JSON.stringify(run)}, ${JSON.stringify(bench)}, ${verbose}));
244
+ } catch (e) {
245
+ console.error("panic: " + (e?.message ?? String(e)));
499
246
  }
500
-
501
- __runGoFrontSuite();
247
+ process.exit(__exitCode);
502
248
  `;
503
249
  }
504
250
 
@@ -613,10 +359,16 @@ function reportBuildFailure(err, resolvedDir, options) {
613
359
 
614
360
  // `both` packages run twice (JS and wasm) so the two backends are kept in
615
361
  // agreement; output of both runs is concatenated.
616
- async function runDualTarget(compiled, resolvedDir, runHarness, options) {
362
+ async function runDualTarget(
363
+ compiled,
364
+ resolvedDir,
365
+ runHarness,
366
+ compileOptions,
367
+ options,
368
+ ) {
617
369
  let wasmCompiled;
618
370
  try {
619
- wasmCompiled = compilePackageTestsWasm(resolvedDir, options);
371
+ wasmCompiled = compilePackageTestsWasm(resolvedDir, compileOptions);
620
372
  } catch (err) {
621
373
  return reportBuildFailure(err, resolvedDir, options);
622
374
  }
@@ -654,18 +406,35 @@ export async function runTests(targetDir, options = {}) {
654
406
  return reportEmptyTests(basename(resolvedDir), "no test files", options);
655
407
  }
656
408
 
409
+ // The driver is generated while the package compiles, once its files are
410
+ // parsed and its target is known.
411
+ let testNames = [];
412
+ let benchNames = [];
413
+ let testMain = null;
414
+ const compileOptions = {
415
+ ...options,
416
+ testDriver: (programs, target) => {
417
+ testNames = discoverTests(programs);
418
+ benchNames = discoverBenchmarks(programs);
419
+ testMain = discoverTestMain(programs);
420
+ if (testNames.length === 0 && benchNames.length === 0 && !testMain)
421
+ return null;
422
+ return generateTestDriver(programs[0].pkg.name, target, {
423
+ testNames,
424
+ benchNames,
425
+ testMain,
426
+ asyncTests: discoverAsyncTests(programs),
427
+ });
428
+ },
429
+ };
657
430
  let compiled;
658
431
  try {
659
- compiled = compilePackageTests(resolvedDir, options);
432
+ compiled = compilePackageTests(resolvedDir, compileOptions);
660
433
  } catch (err) {
661
434
  return reportBuildFailure(err, resolvedDir, options);
662
435
  }
663
436
 
664
- const { pkgName, js, programs } = compiled;
665
- const testNames = discoverTests(programs);
666
- const benchNames = discoverBenchmarks(programs);
667
- const testMain = discoverTestMain(programs);
668
-
437
+ const { pkgName, js } = compiled;
669
438
  if (testNames.length === 0 && benchNames.length === 0 && !testMain) {
670
439
  return reportEmptyTests(pkgName, "no tests to run", options);
671
440
  }
@@ -673,10 +442,8 @@ export async function runTests(targetDir, options = {}) {
673
442
  const runHarness = (bundleJs, label, wasmBytes) =>
674
443
  spawnTestRunner(
675
444
  resolvedDir,
676
- generateTestHarness(bundleJs, testNames, {
445
+ generateTestHarness(bundleJs, {
677
446
  ...options,
678
- benchNames,
679
- testMain,
680
447
  pkgName: label,
681
448
  jsdomPath,
682
449
  wasmBytes,
@@ -696,7 +463,13 @@ export async function runTests(targetDir, options = {}) {
696
463
  }
697
464
  }
698
465
  if (compiled.target === "both") {
699
- return runDualTarget(compiled, resolvedDir, runHarness, options);
466
+ return runDualTarget(
467
+ compiled,
468
+ resolvedDir,
469
+ runHarness,
470
+ compileOptions,
471
+ options,
472
+ );
700
473
  }
701
474
  return runHarness(js, pkgName, compiled.wasm ?? null);
702
475
  }