gofront 1.6.1 → 1.8.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.
- package/README.md +45 -49
- package/package.json +12 -8
- package/src/backend/js/expressions.js +1627 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +508 -102
- package/src/backend/js/runtime.js +1997 -106
- package/src/backend/js/statements.js +931 -87
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +7 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +19 -2
- package/src/backend/js/stdlib/json.js +1167 -0
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +34 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +446 -72
- package/src/backend/wasm/emit-builtins.js +320 -13
- package/src/backend/wasm/emit-exprs.js +1404 -199
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +518 -74
- package/src/backend/wasm/encode.js +128 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +3 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +499 -0
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +221 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +84 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +89 -15
- package/src/compiler.js +107 -269
- package/src/dts-parser.js +71 -14
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -6
- package/src/lower/captures.js +176 -32
- package/src/lower/embedding.js +2 -1
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/parser/struct-tag.js +144 -0
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +130 -13
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +215 -11
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +960 -99
- package/src/typechecker/index.js +491 -72
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +596 -52
- package/src/typechecker/statements.js +157 -17
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +359 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
- package/src/typechecker/stdlib/simd.js +123 -0
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +165 -26
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -6,13 +6,13 @@ 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 "
|
|
10
|
-
import { colorEnabled, createColors, formatDiagnostic } from "./colors.js";
|
|
9
|
+
import { HELPER_SPRINTF, HELPER_TESTING } from "../backend/js/runtime.js";
|
|
11
10
|
import {
|
|
12
11
|
compilePackageTests,
|
|
13
12
|
compilePackageTestsWasm,
|
|
14
13
|
gwFilesIn,
|
|
15
|
-
} from "
|
|
14
|
+
} from "../compiler.js";
|
|
15
|
+
import { colorEnabled, createColors, formatDiagnostic } from "./colors.js";
|
|
16
16
|
|
|
17
17
|
// WasmGC + exception handling (what the wasm backend emits) ship enabled by
|
|
18
18
|
// default from Node 22 (V8 11.9+). Older runtimes fail at instantiate time
|
|
@@ -30,11 +30,55 @@ export function checkWasmRuntime(version = process.version) {
|
|
|
30
30
|
export function isTestFunc(decl) {
|
|
31
31
|
if (decl.kind !== "FuncDecl") return false;
|
|
32
32
|
if (!decl.name.startsWith("Test")) return false;
|
|
33
|
+
if (decl.name === "TestMain") return false;
|
|
33
34
|
if (decl.params?.length !== 1) return false;
|
|
34
35
|
const pType = decl.params[0].type;
|
|
35
|
-
if (
|
|
36
|
+
if (
|
|
37
|
+
pType?.kind === "PointerType" &&
|
|
38
|
+
(pType.base?.name === "testing.T" || pType.base?.name === "T")
|
|
39
|
+
)
|
|
40
|
+
return true;
|
|
41
|
+
if (
|
|
42
|
+
pType?.kind === "TypeName" &&
|
|
43
|
+
(pType.name === "testing.T" || pType.name === "T")
|
|
44
|
+
)
|
|
45
|
+
return true;
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function isBenchmarkFunc(decl) {
|
|
50
|
+
if (decl.kind !== "FuncDecl") return false;
|
|
51
|
+
if (!decl.name.startsWith("Benchmark")) return false;
|
|
52
|
+
if (decl.params?.length !== 1) return false;
|
|
53
|
+
const pType = decl.params[0].type;
|
|
54
|
+
if (
|
|
55
|
+
pType?.kind === "PointerType" &&
|
|
56
|
+
(pType.base?.name === "testing.B" || pType.base?.name === "B")
|
|
57
|
+
)
|
|
58
|
+
return true;
|
|
59
|
+
if (
|
|
60
|
+
pType?.kind === "TypeName" &&
|
|
61
|
+
(pType.name === "testing.B" || pType.name === "B")
|
|
62
|
+
)
|
|
63
|
+
return true;
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function isTestMainFunc(decl) {
|
|
68
|
+
if (decl.kind !== "FuncDecl") return false;
|
|
69
|
+
if (decl.name !== "TestMain") return false;
|
|
70
|
+
if (decl.params?.length !== 1) return false;
|
|
71
|
+
const pType = decl.params[0].type;
|
|
72
|
+
if (
|
|
73
|
+
pType?.kind === "PointerType" &&
|
|
74
|
+
(pType.base?.name === "testing.M" || pType.base?.name === "M")
|
|
75
|
+
)
|
|
76
|
+
return true;
|
|
77
|
+
if (
|
|
78
|
+
pType?.kind === "TypeName" &&
|
|
79
|
+
(pType.name === "testing.M" || pType.name === "M")
|
|
80
|
+
)
|
|
36
81
|
return true;
|
|
37
|
-
if (pType?.kind === "TypeName" && pType.name === "testing.T") return true;
|
|
38
82
|
return false;
|
|
39
83
|
}
|
|
40
84
|
|
|
@@ -53,8 +97,38 @@ export function discoverTests(programs) {
|
|
|
53
97
|
return testNames;
|
|
54
98
|
}
|
|
55
99
|
|
|
100
|
+
export function discoverBenchmarks(programs) {
|
|
101
|
+
const benchNames = [];
|
|
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 (isBenchmarkFunc(d) && !benchNames.includes(d.name)) {
|
|
106
|
+
benchNames.push(d.name);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return benchNames;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function discoverTestMain(programs) {
|
|
114
|
+
for (const p of programs) {
|
|
115
|
+
if (p._filename && !p._filename.endsWith("_test.go")) continue;
|
|
116
|
+
for (const d of p.decls) {
|
|
117
|
+
if (isTestMainFunc(d)) return d.name;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
56
123
|
export function generateTestHarness(bundleJs, testNames, options = {}) {
|
|
57
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;
|
|
58
132
|
const verbose = Boolean(options.verbose);
|
|
59
133
|
const dom = Boolean(options.dom);
|
|
60
134
|
const color = Boolean(options.color);
|
|
@@ -65,6 +139,14 @@ export function generateTestHarness(bundleJs, testNames, options = {}) {
|
|
|
65
139
|
? pathToFileURL(options.jsdomPath).href
|
|
66
140
|
: "jsdom";
|
|
67
141
|
|
|
142
|
+
const allFuncNames = [
|
|
143
|
+
...new Set([
|
|
144
|
+
...testNames,
|
|
145
|
+
...benchNames,
|
|
146
|
+
...(testMainName ? [testMainName] : []),
|
|
147
|
+
]),
|
|
148
|
+
];
|
|
149
|
+
|
|
68
150
|
return `
|
|
69
151
|
${
|
|
70
152
|
dom
|
|
@@ -112,7 +194,10 @@ ${bundleJs}
|
|
|
112
194
|
|
|
113
195
|
async function __runGoFrontSuite() {
|
|
114
196
|
const allTests = ${JSON.stringify(testNames)};
|
|
197
|
+
const allBenchmarks = ${JSON.stringify(benchNames)};
|
|
198
|
+
const testMainName = ${JSON.stringify(testMainName)};
|
|
115
199
|
const runFilter = ${runRegex ? `new RegExp(${JSON.stringify(runRegex)})` : "null"};
|
|
200
|
+
const benchFilter = ${benchRegex ? `new RegExp(${JSON.stringify(benchRegex)})` : "null"};
|
|
116
201
|
const verbose = ${verbose};
|
|
117
202
|
const pkgName = ${JSON.stringify(pkgName)};
|
|
118
203
|
const paint = ${color}
|
|
@@ -126,19 +211,23 @@ async function __runGoFrontSuite() {
|
|
|
126
211
|
? allTests.filter((name) => runFilter.test(name))
|
|
127
212
|
: allTests;
|
|
128
213
|
|
|
129
|
-
|
|
214
|
+
const benchesToRun = benchFilter
|
|
215
|
+
? allBenchmarks.filter((name) => benchFilter.test(name))
|
|
216
|
+
: [];
|
|
217
|
+
|
|
218
|
+
if (testsToRun.length === 0 && benchesToRun.length === 0) {
|
|
130
219
|
if (allTests.length > 0 && runFilter) {
|
|
131
220
|
console.log(yellow("testing: warning: no tests to run matching " + ${JSON.stringify(runRegex)}));
|
|
132
221
|
}
|
|
222
|
+
if (allBenchmarks.length > 0 && benchFilter) {
|
|
223
|
+
console.log(yellow("testing: warning: no benchmarks to run matching " + ${JSON.stringify(benchRegex)}));
|
|
224
|
+
}
|
|
133
225
|
console.log(PASS);
|
|
134
226
|
okLine(pkgName, "0.000");
|
|
135
227
|
process.exit(0);
|
|
136
228
|
}
|
|
137
229
|
|
|
138
|
-
let totalFailures = 0;
|
|
139
|
-
let totalSkipped = 0;
|
|
140
230
|
const now = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
|
|
141
|
-
const suiteStart = now();
|
|
142
231
|
|
|
143
232
|
// Go-style streaming report. In non-verbose mode only failures and skips are
|
|
144
233
|
// shown, so the "=== RUN" header is emitted late for failures.
|
|
@@ -157,6 +246,28 @@ async function __runGoFrontSuite() {
|
|
|
157
246
|
}
|
|
158
247
|
};
|
|
159
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
|
+
|
|
160
271
|
// FailNow/SkipNow are control flow; anything else is a panic → failure.
|
|
161
272
|
const recordTestError = (t, e) => {
|
|
162
273
|
if (e instanceof __GoFront_FailNow || e instanceof __GoFront_SkipNow) return;
|
|
@@ -170,43 +281,221 @@ async function __runGoFrontSuite() {
|
|
|
170
281
|
globalThis.__onSubtestEnd = report;
|
|
171
282
|
|
|
172
283
|
const __testRegistry = {
|
|
173
|
-
${
|
|
284
|
+
${allFuncNames.map((n) => ` ${JSON.stringify(n)}: typeof ${n} !== "undefined" ? ${n} : null`).join(",\n")}
|
|
174
285
|
};
|
|
175
286
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
+
}
|
|
182
453
|
}
|
|
183
454
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
+
}
|
|
192
467
|
}
|
|
193
468
|
|
|
194
|
-
|
|
195
|
-
else if (t.failed) totalFailures++;
|
|
196
|
-
report(t);
|
|
469
|
+
return finish();
|
|
197
470
|
}
|
|
198
471
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
+
}
|
|
209
494
|
}
|
|
495
|
+
|
|
496
|
+
const res = runAll();
|
|
497
|
+
const exitCode = res && typeof res.then === "function" ? await res : res;
|
|
498
|
+
process.exit(exitCode);
|
|
210
499
|
}
|
|
211
500
|
|
|
212
501
|
__runGoFrontSuite();
|
|
@@ -374,8 +663,10 @@ export async function runTests(targetDir, options = {}) {
|
|
|
374
663
|
|
|
375
664
|
const { pkgName, js, programs } = compiled;
|
|
376
665
|
const testNames = discoverTests(programs);
|
|
666
|
+
const benchNames = discoverBenchmarks(programs);
|
|
667
|
+
const testMain = discoverTestMain(programs);
|
|
377
668
|
|
|
378
|
-
if (testNames.length === 0) {
|
|
669
|
+
if (testNames.length === 0 && benchNames.length === 0 && !testMain) {
|
|
379
670
|
return reportEmptyTests(pkgName, "no tests to run", options);
|
|
380
671
|
}
|
|
381
672
|
|
|
@@ -384,12 +675,14 @@ export async function runTests(targetDir, options = {}) {
|
|
|
384
675
|
resolvedDir,
|
|
385
676
|
generateTestHarness(bundleJs, testNames, {
|
|
386
677
|
...options,
|
|
678
|
+
benchNames,
|
|
679
|
+
testMain,
|
|
387
680
|
pkgName: label,
|
|
388
681
|
jsdomPath,
|
|
389
682
|
wasmBytes,
|
|
390
683
|
color: colorsFor(options, options.stdout ?? process.stdout).enabled,
|
|
391
684
|
}),
|
|
392
|
-
testNames.length,
|
|
685
|
+
testNames.length + benchNames.length,
|
|
393
686
|
options,
|
|
394
687
|
);
|
|
395
688
|
|
|
@@ -19,12 +19,17 @@ export function getExportNames(pkgName, globals = {}) {
|
|
|
19
19
|
const clean = base.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
20
20
|
const names = new Set([pkgName, base, clean]);
|
|
21
21
|
const extra = globals[pkgName];
|
|
22
|
-
for (const name of Array.isArray(extra)
|
|
23
|
-
|
|
22
|
+
for (const name of Array.isArray(extra)
|
|
23
|
+
? extra
|
|
24
|
+
: typeof extra === "string"
|
|
25
|
+
? [extra]
|
|
26
|
+
: []) {
|
|
27
|
+
if (typeof name === "string") names.add(name);
|
|
28
|
+
}
|
|
24
29
|
return [...names];
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
export function generateVendorEntry(packages, globals = {}) {
|
|
32
|
+
export function generateVendorEntry(packages, globals = {}, namedExports = {}) {
|
|
28
33
|
const imports = [];
|
|
29
34
|
const assignments = [];
|
|
30
35
|
const exports = [];
|
|
@@ -40,6 +45,46 @@ export function generateVendorEntry(packages, globals = {}) {
|
|
|
40
45
|
);
|
|
41
46
|
}
|
|
42
47
|
|
|
48
|
+
// Named export global assignments (e.g. window.h = preact.h)
|
|
49
|
+
const pkgNamed = Object.create(null);
|
|
50
|
+
const rawNamed = namedExports[pkg];
|
|
51
|
+
const isSafeKey = (k) =>
|
|
52
|
+
typeof k === "string" &&
|
|
53
|
+
k !== "__proto__" &&
|
|
54
|
+
k !== "prototype" &&
|
|
55
|
+
k !== "constructor";
|
|
56
|
+
|
|
57
|
+
if (Array.isArray(rawNamed)) {
|
|
58
|
+
for (const name of rawNamed) {
|
|
59
|
+
if (isSafeKey(name)) pkgNamed[name] = name;
|
|
60
|
+
}
|
|
61
|
+
} else if (rawNamed && typeof rawNamed === "object") {
|
|
62
|
+
for (const [k, v] of Object.entries(rawNamed)) {
|
|
63
|
+
if (isSafeKey(k) && isSafeKey(v)) pkgNamed[k] = v;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof globals[pkg] === "object" && !Array.isArray(globals[pkg])) {
|
|
68
|
+
for (const [k, v] of Object.entries(globals[pkg])) {
|
|
69
|
+
if (isSafeKey(k) && isSafeKey(v)) pkgNamed[k] = v;
|
|
70
|
+
}
|
|
71
|
+
} else if (Array.isArray(globals[pkg])) {
|
|
72
|
+
for (const item of globals[pkg]) {
|
|
73
|
+
if (typeof item === "object" && item !== null) {
|
|
74
|
+
for (const [k, v] of Object.entries(item)) {
|
|
75
|
+
if (isSafeKey(k) && isSafeKey(v)) pkgNamed[k] = v;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const [globalName, exportName] of Object.entries(pkgNamed)) {
|
|
82
|
+
if (!isSafeKey(globalName) || !isSafeKey(exportName)) continue;
|
|
83
|
+
assignments.push(
|
|
84
|
+
` window[${JSON.stringify(globalName)}] = ${id}[${JSON.stringify(exportName)}] !== undefined ? ${id}[${JSON.stringify(exportName)}] : (${id}.default ? ${id}.default[${JSON.stringify(exportName)}] : undefined);`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
43
88
|
const cleanId = pkg
|
|
44
89
|
.replace(/^@[^/]+\//, "")
|
|
45
90
|
.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
@@ -82,8 +127,26 @@ function findBundler(projectDir) {
|
|
|
82
127
|
return null;
|
|
83
128
|
}
|
|
84
129
|
|
|
85
|
-
|
|
86
|
-
|
|
130
|
+
export function normalizeNamedExports(raw) {
|
|
131
|
+
if (raw === undefined || raw === null) return {};
|
|
132
|
+
if (typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
133
|
+
const out = {};
|
|
134
|
+
for (const [pkg, value] of Object.entries(raw)) {
|
|
135
|
+
if (Array.isArray(value)) {
|
|
136
|
+
const map = {};
|
|
137
|
+
for (const name of value) {
|
|
138
|
+
if (typeof name === "string") map[name] = name;
|
|
139
|
+
}
|
|
140
|
+
out[pkg] = map;
|
|
141
|
+
} else if (typeof value === "object" && value !== null) {
|
|
142
|
+
out[pkg] = { ...value };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Normalises `vendor.globals` ({ pkg: string | string[] | object })
|
|
149
|
+
export function normalizeGlobals(raw) {
|
|
87
150
|
if (raw === undefined || raw === null) return {};
|
|
88
151
|
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
89
152
|
throw new Error(
|
|
@@ -92,13 +155,17 @@ function normalizeGlobals(raw) {
|
|
|
92
155
|
}
|
|
93
156
|
const out = {};
|
|
94
157
|
for (const [pkg, value] of Object.entries(raw)) {
|
|
95
|
-
|
|
96
|
-
|
|
158
|
+
if (typeof value === "string") {
|
|
159
|
+
out[pkg] = [value];
|
|
160
|
+
} else if (Array.isArray(value)) {
|
|
161
|
+
out[pkg] = value;
|
|
162
|
+
} else if (typeof value === "object" && value !== null) {
|
|
163
|
+
out[pkg] = value;
|
|
164
|
+
} else {
|
|
97
165
|
throw new Error(
|
|
98
|
-
`gofront: vendor.globals[${JSON.stringify(pkg)}] must be a string
|
|
166
|
+
`gofront: vendor.globals[${JSON.stringify(pkg)}] must be a string, array, or object`,
|
|
99
167
|
);
|
|
100
168
|
}
|
|
101
|
-
out[pkg] = list;
|
|
102
169
|
}
|
|
103
170
|
return out;
|
|
104
171
|
}
|
|
@@ -119,6 +186,7 @@ export function loadVendorConfig(projectDir) {
|
|
|
119
186
|
let packages = dependencies;
|
|
120
187
|
let minify = false;
|
|
121
188
|
let globals = {};
|
|
189
|
+
let namedExports = {};
|
|
122
190
|
|
|
123
191
|
if (typeof vendorConfig === "string") {
|
|
124
192
|
dest = vendorConfig;
|
|
@@ -127,13 +195,16 @@ export function loadVendorConfig(projectDir) {
|
|
|
127
195
|
if (Array.isArray(vendorConfig.packages)) packages = vendorConfig.packages;
|
|
128
196
|
if (typeof vendorConfig.minify === "boolean") minify = vendorConfig.minify;
|
|
129
197
|
globals = normalizeGlobals(vendorConfig.globals);
|
|
198
|
+
namedExports = normalizeNamedExports(
|
|
199
|
+
vendorConfig.namedExports ?? vendorConfig.exports,
|
|
200
|
+
);
|
|
130
201
|
}
|
|
131
202
|
|
|
132
203
|
if (!dest) {
|
|
133
204
|
dest = existsSync(join(projectDir, "app")) ? "app/vendor.js" : "vendor.js";
|
|
134
205
|
}
|
|
135
206
|
|
|
136
|
-
return { dest, packages, minify, globals };
|
|
207
|
+
return { dest, packages, minify, globals, namedExports };
|
|
137
208
|
}
|
|
138
209
|
|
|
139
210
|
function resolveDestinationPaths(projectRoot, dest) {
|
|
@@ -299,6 +370,10 @@ export async function bundleVendor(projectDir = ".", options = {}) {
|
|
|
299
370
|
const dest = options.dest ?? config.dest;
|
|
300
371
|
const minify = options.minify ?? config.minify ?? false;
|
|
301
372
|
const globals = { ...config.globals, ...normalizeGlobals(options.globals) };
|
|
373
|
+
const namedExports = {
|
|
374
|
+
...config.namedExports,
|
|
375
|
+
...normalizeNamedExports(options.namedExports ?? options.exports),
|
|
376
|
+
};
|
|
302
377
|
|
|
303
378
|
if (!packages || packages.length === 0) {
|
|
304
379
|
return {
|
|
@@ -366,11 +441,10 @@ export async function bundleVendor(projectDir = ".", options = {}) {
|
|
|
366
441
|
};
|
|
367
442
|
}
|
|
368
443
|
|
|
369
|
-
const entryCode = generateVendorEntry(packages, globals);
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
);
|
|
444
|
+
const entryCode = generateVendorEntry(packages, globals, namedExports);
|
|
445
|
+
// A fixed name: the bundler records the entry path in vendor.js, so a
|
|
446
|
+
// per-run name would make every prep produce different bytes.
|
|
447
|
+
const entryFile = join(projectRoot, ".gofront-vendor-entry.mjs");
|
|
374
448
|
writeFileSync(entryFile, entryCode);
|
|
375
449
|
|
|
376
450
|
try {
|