gofront 1.8.0 → 1.9.0

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 (177) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -4
  3. package/src/backend/js/expressions.js +849 -371
  4. package/src/backend/js/index.js +241 -147
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +512 -2547
  7. package/src/backend/js/statements.js +506 -103
  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 +177 -13
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +63 -171
  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 +643 -256
  16. package/src/backend/wasm/emit-exprs.js +124 -57
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-stmts.js +325 -75
  19. package/src/backend/wasm/emit.js +104 -13
  20. package/src/backend/wasm/globals.js +47 -8
  21. package/src/backend/wasm/glue.js +2 -4
  22. package/src/backend/wasm/index.js +36 -530
  23. package/src/backend/wasm/method-expr.js +12 -1
  24. package/src/backend/wasm/module-emitter.js +92 -717
  25. package/src/backend/wasm/monomorph.js +103 -6
  26. package/src/backend/wasm/natives.js +596 -0
  27. package/src/backend/wasm/prescan.js +5 -153
  28. package/src/backend/wasm/prune-funcs.js +89 -0
  29. package/src/backend/wasm/stdlib/index.js +2 -32
  30. package/src/backend/wasm/types.js +68 -20
  31. package/src/backend/wasm/variadic.js +3 -3
  32. package/src/cli/cli-core.js +5 -1
  33. package/src/cli/test-runner.js +115 -342
  34. package/src/compiler.js +108 -130
  35. package/src/lexer/lexer.js +105 -11
  36. package/src/lexer/templ-lexer.js +61 -18
  37. package/src/lexer/tokens.js +1 -0
  38. package/src/link/stdlib.js +354 -132
  39. package/src/link/type-names.js +148 -0
  40. package/src/link/wasm.js +30 -6
  41. package/src/lower/bounds.js +153 -0
  42. package/src/lower/captures.js +92 -33
  43. package/src/lower/embedding.js +32 -3
  44. package/src/lower/errors-as.js +196 -0
  45. package/src/lower/fmt-formatters.js +905 -0
  46. package/src/lower/functions.js +2 -0
  47. package/src/lower/gen-types.js +274 -0
  48. package/src/lower/index.js +7 -1
  49. package/src/lower/json-codecs.js +228 -247
  50. package/src/lower/ownership.js +25 -7
  51. package/src/lower/reachability.js +69 -51
  52. package/src/lower/walk.js +12 -0
  53. package/src/parser/declarations.js +28 -5
  54. package/src/parser/expressions.js +86 -16
  55. package/src/parser/statements.js +1 -0
  56. package/src/parser/templ.js +44 -5
  57. package/src/parser/types.js +1 -0
  58. package/src/resolver.js +1 -2
  59. package/src/typechecker/assignability.js +124 -11
  60. package/src/typechecker/const-fold.js +70 -27
  61. package/src/typechecker/constants.js +32 -6
  62. package/src/typechecker/expressions.js +326 -98
  63. package/src/typechecker/index.js +208 -33
  64. package/src/typechecker/init-order.js +136 -40
  65. package/src/typechecker/operand-text.js +40 -0
  66. package/src/typechecker/resolve.js +192 -36
  67. package/src/typechecker/statements.js +122 -11
  68. package/src/typechecker/stdlib/excluded.js +9 -5
  69. package/src/typechecker/stdlib/index.js +25 -9
  70. package/src/typechecker/termination.js +10 -1
  71. package/src/typechecker/types.js +203 -573
  72. package/stdlib/bytes/buffer.go +479 -0
  73. package/stdlib/bytes/bytes.go +1010 -0
  74. package/stdlib/bytes/reader.go +161 -0
  75. package/stdlib/context/context.go +115 -0
  76. package/stdlib/crypto/rand/rand.go +34 -0
  77. package/stdlib/crypto/sha256/sha256.go +249 -0
  78. package/stdlib/encoding/base64/base64.go +331 -0
  79. package/stdlib/encoding/binary/binary.go +332 -0
  80. package/stdlib/encoding/hex/hex.go +140 -0
  81. package/stdlib/encoding/json/json.go +62 -0
  82. package/stdlib/errors/errors.go +174 -0
  83. package/stdlib/fmt/errors.go +110 -0
  84. package/stdlib/fmt/format.go +732 -0
  85. package/stdlib/fmt/print.go +1232 -0
  86. package/stdlib/gom/gom.go +293 -0
  87. package/stdlib/html/html.go +251 -0
  88. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  89. package/stdlib/internal/jsonrt/dec.go +171 -38
  90. package/stdlib/internal/jsonrt/enc.go +186 -36
  91. package/stdlib/internal/jsonrt/scanner.go +17 -8
  92. package/stdlib/io/io.go +157 -1
  93. package/stdlib/maps/maps.go +131 -0
  94. package/stdlib/math/bits/bits.go +507 -0
  95. package/stdlib/math/extra.go +16 -14
  96. package/stdlib/math/math.go +439 -0
  97. package/stdlib/math/rand/rand.go +297 -0
  98. package/stdlib/os/os.go +93 -0
  99. package/stdlib/path/filepath/filepath.go +47 -0
  100. package/stdlib/path/path.go +394 -0
  101. package/stdlib/regexp/regexp.go +231 -0
  102. package/stdlib/slices/slices.go +526 -0
  103. package/stdlib/slices/sort.go +209 -0
  104. package/stdlib/slices/zsort_cmp.go +480 -0
  105. package/stdlib/slices/zsort_ordered.go +482 -0
  106. package/stdlib/sort/sort.go +391 -452
  107. package/stdlib/sort/zsort_func.go +480 -0
  108. package/stdlib/sort/zsort_iface.go +480 -0
  109. package/stdlib/strconv/float.go +1699 -0
  110. package/stdlib/strconv/isprint.go +752 -0
  111. package/stdlib/strconv/quote.go +235 -50
  112. package/stdlib/strconv/strconv.go +616 -0
  113. package/stdlib/strings/builder.go +95 -0
  114. package/stdlib/strings/reader.go +163 -0
  115. package/stdlib/strings/replacer.go +102 -0
  116. package/stdlib/strings/strings.go +807 -0
  117. package/stdlib/sync/sync.go +268 -0
  118. package/stdlib/testing/async_js.go +61 -0
  119. package/stdlib/testing/benchmark.go +297 -0
  120. package/stdlib/testing/testing.go +523 -0
  121. package/stdlib/time/duration.go +233 -0
  122. package/stdlib/time/format.go +745 -0
  123. package/stdlib/time/parse.go +790 -0
  124. package/stdlib/time/sleep_js.go +63 -0
  125. package/stdlib/time/time.go +834 -0
  126. package/stdlib/unicode/unicode.go +141 -3
  127. package/stdlib/unicode/utf8/utf8.go +464 -0
  128. package/src/backend/js/fmt-runtime.js +0 -573
  129. package/src/backend/js/stdlib/base64.js +0 -12
  130. package/src/backend/js/stdlib/binary.js +0 -102
  131. package/src/backend/js/stdlib/bits.js +0 -119
  132. package/src/backend/js/stdlib/builder.js +0 -98
  133. package/src/backend/js/stdlib/bytes.js +0 -368
  134. package/src/backend/js/stdlib/errors.js +0 -45
  135. package/src/backend/js/stdlib/fmt.js +0 -517
  136. package/src/backend/js/stdlib/gom.js +0 -155
  137. package/src/backend/js/stdlib/hex.js +0 -17
  138. package/src/backend/js/stdlib/html.js +0 -20
  139. package/src/backend/js/stdlib/io.js +0 -137
  140. package/src/backend/js/stdlib/json.js +0 -1167
  141. package/src/backend/js/stdlib/maps.js +0 -37
  142. package/src/backend/js/stdlib/math.js +0 -85
  143. package/src/backend/js/stdlib/os.js +0 -13
  144. package/src/backend/js/stdlib/path.js +0 -48
  145. package/src/backend/js/stdlib/rand.js +0 -32
  146. package/src/backend/js/stdlib/regexp.js +0 -54
  147. package/src/backend/js/stdlib/sha256.js +0 -12
  148. package/src/backend/js/stdlib/shared.js +0 -17
  149. package/src/backend/js/stdlib/simd.js +0 -34
  150. package/src/backend/js/stdlib/slices.js +0 -107
  151. package/src/backend/js/stdlib/sort.js +0 -30
  152. package/src/backend/js/stdlib/strconv.js +0 -79
  153. package/src/backend/js/stdlib/strings.js +0 -301
  154. package/src/backend/js/stdlib/sync.js +0 -13
  155. package/src/backend/js/stdlib/testing.js +0 -41
  156. package/src/backend/js/stdlib/time.js +0 -152
  157. package/src/backend/js/stdlib/unicode.js +0 -25
  158. package/src/backend/js/stdlib/utf8.js +0 -26
  159. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  160. package/src/backend/wasm/stdlib/core.js +0 -10278
  161. package/src/backend/wasm/stdlib/io.js +0 -3578
  162. package/src/backend/wasm/stdlib/packages.js +0 -2402
  163. package/src/backend/wasm/stdlib/pure.js +0 -720
  164. package/src/backend/wasm/stdlib/time.js +0 -2229
  165. package/src/typechecker/stdlib/core.js +0 -1079
  166. package/src/typechecker/stdlib/extended.js +0 -1210
  167. package/stdlib/bytes/iter.go +0 -192
  168. package/stdlib/bytes/search.go +0 -240
  169. package/stdlib/bytes/split.go +0 -218
  170. package/stdlib/errors/overlay.go +0 -12
  171. package/stdlib/maps/iter.go +0 -65
  172. package/stdlib/slices/extra.go +0 -73
  173. package/stdlib/slices/iter.go +0 -113
  174. package/stdlib/strings/extra.go +0 -35
  175. package/stdlib/strings/iter.go +0 -122
  176. package/stdlib/sync/extra.go +0 -105
  177. 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
  }