as-test 1.1.10 → 1.2.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/CHANGELOG.md +29 -0
- package/assembly/src/expectation.ts +42 -7
- package/bin/commands/build-core.js +39 -59
- package/bin/commands/fuzz-core.js +30 -56
- package/bin/commands/run-core.js +38 -119
- package/bin/commands/web-session.js +4 -0
- package/bin/crash-store.js +1 -1
- package/bin/index.js +34 -132
- package/bin/util.js +74 -0
- package/package.json +13 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2026-05-20 - v1.2.0
|
|
4
|
+
|
|
5
|
+
### Directory-preserving artifact layout
|
|
6
|
+
|
|
7
|
+
- feat: build artifacts, fuzz artifacts, snapshots, readable logs, coverage logs, and crash records now mirror the source tree under the configured input globs instead of being flattened into a single directory with a `____`-mangled disambiguator suffix. For `assembly/__tests__/nested/array.spec.ts` the artifact is `outDir/<mode>/nested/array.spec.wasm` (previously `outDir/<mode>/array.<mode>.<target>.assembly____tests____nested.wasm`).
|
|
8
|
+
- feat: filename simplified to `<stem>.wasm` — the `.<mode>.<target>` suffix has been dropped since the mode is already a directory level and the target is implied by the mode config.
|
|
9
|
+
- feat: add `resolveGlobBase`, `resolveSpecRelativePath`, and `resolveArtifactPath` in `cli/util.ts` as the shared path helpers used by every code path that writes or looks up a per-spec artifact. Glob bases are computed component-wise (so `assembly/__tests` is not a prefix of `assembly/__tests__/foo.spec.ts`) and the longest matching base wins when multiple configured input patterns overlap.
|
|
10
|
+
- feat: add an up-front collision check in `build()` that throws a clear error naming both source files when two configured inputs would resolve to the same artifact path.
|
|
11
|
+
- fix: `ast test <one-spec>`, `ast run <one-spec>`, and `ast fuzz <one-spec>` no longer drop the disambiguator when only one of two same-basename files is being built. The build side and the runner side now compute the same path from the same configured input set.
|
|
12
|
+
- fix: build sites now `mkdir -p` the artifact's parent directory before invoking `asc` — pinned `assemblyscript@0.28.17`'s `-o` flag does not create parents and would otherwise ENOENT for any new nested directory.
|
|
13
|
+
- fix: `persistCrashRecord` now `mkdir -p`'s the entry's parent directory, supporting `/` in entry keys so nested specs and fuzz failures get their own crash files instead of clobbering by basename.
|
|
14
|
+
- fix: replace the hardcoded `/__tests__/` and `/__fuzz__/` markers in snapshot and readable-log path resolution with proper glob-base computation, so projects with custom input layouts now nest correctly instead of falling back to basename-only paths.
|
|
15
|
+
|
|
16
|
+
### `.toThrow()` is a real matcher
|
|
17
|
+
|
|
18
|
+
- feat: `expect((): void => { throw new Error("boom"); }).toThrow()` now invokes the wrapped callback and asserts it threw, using try-as's `__ExceptionState.Failures` counter to detect the throw. Calling `.toThrow()` on a non-function value reports a clear "needs a function" failure.
|
|
19
|
+
- feat: requires `--enable try-as`. Without the feature flag, `.toThrow()` warns once and is a no-op (existing behavior preserved).
|
|
20
|
+
- chore: the bundled try-as integration spec lives at `assembly/__tests__/try-as.spec.ts` and is run by `npm test`, which now passes `--enable try-as`.
|
|
21
|
+
|
|
22
|
+
### Breaking
|
|
23
|
+
|
|
24
|
+
- chore: clean break on snapshot file layout — the legacy `${base}.snap.json` and `${base}.${disambiguator}.snap.json` fallbacks have been removed. After upgrading, run `--create-snapshots` or `--overwrite-snapshots` once so snapshots are written at their new relative-path locations.
|
|
25
|
+
- chore: artifact filenames no longer carry the `<mode>.<target>` suffix; tooling that grepped the old shape needs adjusting. `ast clean` removes any orphan artifacts.
|
|
26
|
+
- chore: `.toThrow()` no longer accepts a bare value — it now requires a `() => void` callback and the try-as feature flag.
|
|
27
|
+
|
|
28
|
+
### Tooling
|
|
29
|
+
|
|
30
|
+
- chore: husky pre-commit / commit-msg / pre-push hooks (build → format → typecheck → lint on commit; conventional-commits enforcement; full test gate only on push to `main` of `JairusSW/as-test`).
|
|
31
|
+
|
|
3
32
|
## 2026-05-19 - v1.1.10
|
|
4
33
|
|
|
5
34
|
- feat: when the user already declares `--transform json-as/...` in `buildOptions.args` or in their referenced `asconfig.json` (top-level `options.transform`, any `targets.*.transform`, or via a single level of `extends`), as-test no longer adds its own auto-include — letting users bring their own json-as version or load path. Detection matches bare specifiers (`json-as`, `json-as/transform`), absolute paths, and `./node_modules/...` paths.
|
|
@@ -457,15 +457,21 @@ export class Expectation<T> extends Tests {
|
|
|
457
457
|
}
|
|
458
458
|
|
|
459
459
|
/**
|
|
460
|
-
*
|
|
461
|
-
*
|
|
460
|
+
* Invokes the wrapped function inside a try/catch and asserts it threw.
|
|
461
|
+
* Requires the try-as feature (`--enable try-as`).
|
|
462
|
+
*
|
|
463
|
+
* expect((): void => { throw new Error("boom"); }).toThrow();
|
|
464
|
+
*
|
|
465
|
+
* The value passed to `expect()` must be a `() => void` callback — calling
|
|
466
|
+
* `.toThrow()` on a non-function value records a failure that explains the
|
|
467
|
+
* usage.
|
|
462
468
|
*/
|
|
463
469
|
toThrow(message: string = ""): void {
|
|
464
470
|
// @ts-ignore
|
|
465
471
|
if (!isDefined(AS_TEST_TRY_AS)) {
|
|
466
472
|
if (!warnedToThrowDisabled) {
|
|
467
473
|
sendWarning(
|
|
468
|
-
|
|
474
|
+
"toThrow() requires the try-as feature. Enable with --enable try-as.",
|
|
469
475
|
);
|
|
470
476
|
warnedToThrowDisabled = true;
|
|
471
477
|
}
|
|
@@ -473,13 +479,42 @@ export class Expectation<T> extends Tests {
|
|
|
473
479
|
return;
|
|
474
480
|
}
|
|
475
481
|
|
|
482
|
+
if (!isFunction<T>()) {
|
|
483
|
+
this._resolve(
|
|
484
|
+
false,
|
|
485
|
+
"toThrow",
|
|
486
|
+
q("non-function"),
|
|
487
|
+
q("() => void"),
|
|
488
|
+
message.length
|
|
489
|
+
? message
|
|
490
|
+
: "toThrow() requires a function: expect((): void => { ... }).toThrow()",
|
|
491
|
+
);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// try-as rewrites the throw inside the callback to bump
|
|
496
|
+
// __ExceptionState.Failures and return early from the arrow. We never
|
|
497
|
+
// wrap the call in try/catch here because try-as's source linker does not
|
|
498
|
+
// follow chained method calls (`expect(...).toThrow()`) and so it would
|
|
499
|
+
// not rewrite a `try` placed in this method body. Compare the failure
|
|
500
|
+
// counter before/after instead and consume any failure we observed.
|
|
501
|
+
// @ts-ignore: __ExceptionState is provided by the try-as transform
|
|
502
|
+
const beforeFailures = __ExceptionState.Failures;
|
|
503
|
+
// @ts-ignore: guarded by isFunction<T>() above
|
|
504
|
+
(this._left as () => void)();
|
|
476
505
|
// @ts-ignore
|
|
477
|
-
const
|
|
478
|
-
if (
|
|
506
|
+
const threw = __ExceptionState.Failures > beforeFailures;
|
|
507
|
+
if (threw) {
|
|
479
508
|
// @ts-ignore
|
|
480
|
-
__ExceptionState.Failures
|
|
509
|
+
__ExceptionState.Failures = beforeFailures;
|
|
481
510
|
}
|
|
482
|
-
this._resolve(
|
|
511
|
+
this._resolve(
|
|
512
|
+
threw,
|
|
513
|
+
"toThrow",
|
|
514
|
+
q(threw ? "threw" : "did not throw"),
|
|
515
|
+
q("throws"),
|
|
516
|
+
message,
|
|
517
|
+
);
|
|
483
518
|
}
|
|
484
519
|
|
|
485
520
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "fs";
|
|
2
2
|
import { glob } from "glob";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { spawn } from "child_process";
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
applyMode,
|
|
12
12
|
getPkgRunner,
|
|
13
13
|
loadConfig,
|
|
14
|
+
resolveArtifactPath,
|
|
15
|
+
resolveSpecRelativePath,
|
|
14
16
|
tokenizeCommand,
|
|
15
17
|
resolveProjectModule,
|
|
16
18
|
} from "../util.js";
|
|
@@ -64,11 +66,7 @@ export async function build(
|
|
|
64
66
|
const inputFiles = (await glob(inputPatterns)).sort((a, b) =>
|
|
65
67
|
a.localeCompare(b),
|
|
66
68
|
);
|
|
67
|
-
|
|
68
|
-
// selector-filtered subset, otherwise running `ast test <one-spec>` writes
|
|
69
|
-
// an artifact name the runner won't look up (it always globs full input).
|
|
70
|
-
const duplicateSpecBasenames =
|
|
71
|
-
await resolveAllConfiguredDuplicateBasenames(sourceInputPatterns);
|
|
69
|
+
await assertNoArtifactCollisions(sourceInputPatterns);
|
|
72
70
|
const coverageEnabled = resolveCoverageEnabled(
|
|
73
71
|
config.coverage,
|
|
74
72
|
featureToggles.coverage,
|
|
@@ -85,7 +83,10 @@ export async function build(
|
|
|
85
83
|
) {
|
|
86
84
|
const pool = getSerialBuildWorkerPool();
|
|
87
85
|
for (const file of inputFiles) {
|
|
88
|
-
const outFile =
|
|
86
|
+
const outFile = path.join(
|
|
87
|
+
config.outDir,
|
|
88
|
+
resolveArtifactPath(file, sourceInputPatterns),
|
|
89
|
+
);
|
|
89
90
|
const invocation = getBuildCommand(
|
|
90
91
|
config,
|
|
91
92
|
pkgRunner,
|
|
@@ -106,7 +107,11 @@ export async function build(
|
|
|
106
107
|
return;
|
|
107
108
|
}
|
|
108
109
|
for (const file of inputFiles) {
|
|
109
|
-
const outFile =
|
|
110
|
+
const outFile = path.join(
|
|
111
|
+
config.outDir,
|
|
112
|
+
resolveArtifactPath(file, sourceInputPatterns),
|
|
113
|
+
);
|
|
114
|
+
mkdirSync(path.dirname(outFile), { recursive: true });
|
|
110
115
|
const invocation = getBuildCommand(
|
|
111
116
|
config,
|
|
112
117
|
pkgRunner,
|
|
@@ -127,6 +132,10 @@ export async function build(
|
|
|
127
132
|
kind,
|
|
128
133
|
stage: "build",
|
|
129
134
|
file,
|
|
135
|
+
entryKey: resolveSpecRelativePath(file, sourceInputPatterns).replace(
|
|
136
|
+
/\.ts$/i,
|
|
137
|
+
"",
|
|
138
|
+
),
|
|
130
139
|
mode: modeLabel,
|
|
131
140
|
cwd: process.cwd(),
|
|
132
141
|
buildCommand,
|
|
@@ -189,9 +198,10 @@ export async function getBuildInvocationPreview(
|
|
|
189
198
|
}
|
|
190
199
|
const sourceInputPatterns =
|
|
191
200
|
overrides.kind === "fuzz" ? config.fuzz.input : config.input;
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
201
|
+
const outFile = path.join(
|
|
202
|
+
config.outDir,
|
|
203
|
+
resolveArtifactPath(file, sourceInputPatterns),
|
|
204
|
+
);
|
|
195
205
|
return getBuildCommand(
|
|
196
206
|
config,
|
|
197
207
|
getPkgRunner(),
|
|
@@ -229,9 +239,10 @@ export async function getBuildReuseInfo(
|
|
|
229
239
|
}
|
|
230
240
|
const sourceInputPatterns =
|
|
231
241
|
overrides.kind === "fuzz" ? config.fuzz.input : config.input;
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
242
|
+
const outFile = path.join(
|
|
243
|
+
config.outDir,
|
|
244
|
+
resolveArtifactPath(file, sourceInputPatterns),
|
|
245
|
+
);
|
|
235
246
|
const invocation = getBuildCommand(
|
|
236
247
|
config,
|
|
237
248
|
getPkgRunner(),
|
|
@@ -356,54 +367,23 @@ function expandBuildCommand(template, file, outFile, target, modeName) {
|
|
|
356
367
|
.replace(/<target>/g, target)
|
|
357
368
|
.replace(/<mode>/g, modeName ?? "");
|
|
358
369
|
}
|
|
359
|
-
function
|
|
360
|
-
file,
|
|
361
|
-
target,
|
|
362
|
-
modeName,
|
|
363
|
-
duplicateSpecBasenames = new Set(),
|
|
364
|
-
) {
|
|
365
|
-
const base = path
|
|
366
|
-
.basename(file)
|
|
367
|
-
.replace(/\.spec\.ts$/, "")
|
|
368
|
-
.replace(/\.ts$/, "");
|
|
369
|
-
const legacy = !modeName
|
|
370
|
-
? `${path.basename(file).replace(".ts", ".wasm")}`
|
|
371
|
-
: `${base}.${modeName}.${target}.wasm`;
|
|
372
|
-
if (!duplicateSpecBasenames.has(path.basename(file))) {
|
|
373
|
-
return legacy;
|
|
374
|
-
}
|
|
375
|
-
const disambiguator = resolveDisambiguator(file);
|
|
376
|
-
if (!disambiguator.length) {
|
|
377
|
-
return legacy;
|
|
378
|
-
}
|
|
379
|
-
const ext = path.extname(legacy);
|
|
380
|
-
const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
|
|
381
|
-
return `${stem}.${disambiguator}${ext}`;
|
|
382
|
-
}
|
|
383
|
-
async function resolveAllConfiguredDuplicateBasenames(configured) {
|
|
370
|
+
async function assertNoArtifactCollisions(configured) {
|
|
384
371
|
const patterns = Array.isArray(configured) ? configured : [configured];
|
|
385
372
|
const files = await glob(patterns);
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
function resolveDuplicateBasenames(files) {
|
|
389
|
-
const counts = new Map();
|
|
373
|
+
const seen = new Map();
|
|
390
374
|
for (const file of files) {
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
return relDir
|
|
404
|
-
.replace(/[\\/]+/g, "__")
|
|
405
|
-
.replace(/[^A-Za-z0-9._-]/g, "_")
|
|
406
|
-
.replace(/^_+|_+$/g, "");
|
|
375
|
+
const artifact = resolveArtifactPath(file, patterns);
|
|
376
|
+
const prev = seen.get(artifact);
|
|
377
|
+
if (prev != null && prev !== file) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
`Two input files resolve to the same artifact path "${artifact}":\n` +
|
|
380
|
+
` - ${prev}\n` +
|
|
381
|
+
` - ${file}\n` +
|
|
382
|
+
`Rename one of them or narrow the input patterns to disambiguate.`,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
seen.set(artifact, file);
|
|
386
|
+
}
|
|
407
387
|
}
|
|
408
388
|
function resolveInputPatterns(configured, selectors) {
|
|
409
389
|
const configuredInputs = Array.isArray(configured)
|
|
@@ -3,7 +3,12 @@ import * as path from "path";
|
|
|
3
3
|
import { pathToFileURL } from "url";
|
|
4
4
|
import { glob } from "glob";
|
|
5
5
|
import { build } from "./build-core.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
applyMode,
|
|
8
|
+
loadConfig,
|
|
9
|
+
resolveArtifactPath,
|
|
10
|
+
resolveSpecRelativePath,
|
|
11
|
+
} from "../util.js";
|
|
7
12
|
import { persistCrashRecord } from "../crash-store.js";
|
|
8
13
|
const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
|
|
9
14
|
const MAGIC = Buffer.from("WIPC");
|
|
@@ -29,13 +34,6 @@ export async function fuzz(
|
|
|
29
34
|
`No fuzz files matched: ${selectors.length ? selectors.join(", ") : "configured input patterns"}`,
|
|
30
35
|
);
|
|
31
36
|
}
|
|
32
|
-
// Disambiguation must consider the full configured fuzz input set, not the
|
|
33
|
-
// selector-filtered subset, so artifact names stay consistent across runs.
|
|
34
|
-
const fullPatterns = Array.isArray(config.input)
|
|
35
|
-
? config.input
|
|
36
|
-
: [config.input];
|
|
37
|
-
const allFuzzFiles = await glob(fullPatterns);
|
|
38
|
-
const duplicateBasenames = resolveDuplicateBasenames(allFuzzFiles);
|
|
39
37
|
const results = [];
|
|
40
38
|
for (const file of inputFiles) {
|
|
41
39
|
const buildStartedAt = Date.now();
|
|
@@ -53,7 +51,6 @@ export async function fuzz(
|
|
|
53
51
|
await runFuzzTarget(
|
|
54
52
|
file,
|
|
55
53
|
activeConfig.outDir,
|
|
56
|
-
duplicateBasenames,
|
|
57
54
|
config,
|
|
58
55
|
fuzzerSelectors,
|
|
59
56
|
buildStartedAt,
|
|
@@ -111,7 +108,6 @@ function encodeRunsOverrideKind(kind) {
|
|
|
111
108
|
async function runFuzzTarget(
|
|
112
109
|
file,
|
|
113
110
|
outDir,
|
|
114
|
-
duplicateBasenames,
|
|
115
111
|
config,
|
|
116
112
|
fuzzerSelectors,
|
|
117
113
|
buildStartedAt,
|
|
@@ -120,7 +116,7 @@ async function runFuzzTarget(
|
|
|
120
116
|
modeName,
|
|
121
117
|
) {
|
|
122
118
|
const startedAt = Date.now();
|
|
123
|
-
const artifact =
|
|
119
|
+
const artifact = resolveArtifactPath(file, config.input);
|
|
124
120
|
const wasmPath = path.resolve(process.cwd(), outDir, artifact);
|
|
125
121
|
const jsPath = resolveBindingsHelperPath(wasmPath);
|
|
126
122
|
const helper = await import(pathToFileURL(jsPath).href + `?t=${Date.now()}`);
|
|
@@ -185,7 +181,11 @@ async function runFuzzTarget(
|
|
|
185
181
|
const crash = persistCrashRecord(config.crashDir, {
|
|
186
182
|
kind: "fuzz",
|
|
187
183
|
file,
|
|
188
|
-
entryKey: buildFuzzCrashEntryKey(
|
|
184
|
+
entryKey: buildFuzzCrashEntryKey(
|
|
185
|
+
file,
|
|
186
|
+
config.input,
|
|
187
|
+
modeName ?? "default",
|
|
188
|
+
),
|
|
189
189
|
mode: modeName ?? "default",
|
|
190
190
|
seed: config.seed,
|
|
191
191
|
error: crashMessage,
|
|
@@ -232,7 +232,11 @@ async function runFuzzTarget(
|
|
|
232
232
|
const crash = persistCrashRecord(config.crashDir, {
|
|
233
233
|
kind: "fuzz",
|
|
234
234
|
file,
|
|
235
|
-
entryKey: buildFuzzCrashEntryKey(
|
|
235
|
+
entryKey: buildFuzzCrashEntryKey(
|
|
236
|
+
file,
|
|
237
|
+
config.input,
|
|
238
|
+
modeName ?? "default",
|
|
239
|
+
),
|
|
236
240
|
mode: modeName ?? "default",
|
|
237
241
|
seed: config.seed,
|
|
238
242
|
error: `${reportParseError ? `invalid fuzz report payload: ${reportParseError}` : `missing fuzz report payload from ${path.basename(file)}`} (${diagnostics})`,
|
|
@@ -269,6 +273,7 @@ async function runFuzzTarget(
|
|
|
269
273
|
file,
|
|
270
274
|
entryKey: buildFuzzFailureEntryKey(
|
|
271
275
|
file,
|
|
276
|
+
config.input,
|
|
272
277
|
fuzzer.name,
|
|
273
278
|
modeName ?? "default",
|
|
274
279
|
),
|
|
@@ -341,11 +346,19 @@ function buildFuzzReproCommand(file, seed, modeName, fuzzer, runs) {
|
|
|
341
346
|
const runsArg = typeof runs == "number" ? ` --runs ${runs}` : "";
|
|
342
347
|
return `ast fuzz ${file}${modeArg}${fuzzerArg} --seed ${seed}${runsArg}`;
|
|
343
348
|
}
|
|
344
|
-
function buildFuzzFailureEntryKey(file, name, modeName) {
|
|
345
|
-
|
|
349
|
+
function buildFuzzFailureEntryKey(file, inputPatterns, name, modeName) {
|
|
350
|
+
const stem = resolveSpecRelativePath(file, inputPatterns).replace(
|
|
351
|
+
/\.ts$/i,
|
|
352
|
+
"",
|
|
353
|
+
);
|
|
354
|
+
return `${stem}.${sanitizeEntryName(modeName)}.${sanitizeEntryName(name)}`;
|
|
346
355
|
}
|
|
347
|
-
function buildFuzzCrashEntryKey(file, modeName) {
|
|
348
|
-
|
|
356
|
+
function buildFuzzCrashEntryKey(file, inputPatterns, modeName) {
|
|
357
|
+
const stem = resolveSpecRelativePath(file, inputPatterns).replace(
|
|
358
|
+
/\.ts$/i,
|
|
359
|
+
"",
|
|
360
|
+
);
|
|
361
|
+
return `${stem}.${sanitizeEntryName(modeName)}`;
|
|
349
362
|
}
|
|
350
363
|
function sanitizeEntryName(name) {
|
|
351
364
|
return (
|
|
@@ -458,45 +471,6 @@ function resolveFuzzInputPatterns(configured, selectors) {
|
|
|
458
471
|
}
|
|
459
472
|
return [...patterns];
|
|
460
473
|
}
|
|
461
|
-
function resolveArtifactFileName(file, duplicateBasenames, modeName) {
|
|
462
|
-
const base = path
|
|
463
|
-
.basename(file)
|
|
464
|
-
.replace(/\.spec\.ts$/, "")
|
|
465
|
-
.replace(/\.ts$/, "");
|
|
466
|
-
const legacy = !modeName
|
|
467
|
-
? `${path.basename(file).replace(".ts", ".wasm")}`
|
|
468
|
-
: `${base}.${modeName}.bindings.wasm`;
|
|
469
|
-
if (!duplicateBasenames.has(path.basename(file))) {
|
|
470
|
-
return legacy;
|
|
471
|
-
}
|
|
472
|
-
const disambiguator = resolveDisambiguator(file);
|
|
473
|
-
if (!disambiguator.length) {
|
|
474
|
-
return legacy;
|
|
475
|
-
}
|
|
476
|
-
const ext = path.extname(legacy);
|
|
477
|
-
const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
|
|
478
|
-
return `${stem}.${disambiguator}${ext}`;
|
|
479
|
-
}
|
|
480
|
-
function resolveDuplicateBasenames(files) {
|
|
481
|
-
const counts = new Map();
|
|
482
|
-
for (const file of files) {
|
|
483
|
-
const base = path.basename(file);
|
|
484
|
-
counts.set(base, (counts.get(base) ?? 0) + 1);
|
|
485
|
-
}
|
|
486
|
-
const duplicates = new Set();
|
|
487
|
-
for (const [base, count] of counts) {
|
|
488
|
-
if (count > 1) duplicates.add(base);
|
|
489
|
-
}
|
|
490
|
-
return duplicates;
|
|
491
|
-
}
|
|
492
|
-
function resolveDisambiguator(file) {
|
|
493
|
-
const relDir = path.dirname(path.relative(process.cwd(), file));
|
|
494
|
-
if (!relDir.length || relDir == ".") return "";
|
|
495
|
-
return relDir
|
|
496
|
-
.replace(/[\\/]+/g, "__")
|
|
497
|
-
.replace(/[^A-Za-z0-9._-]/g, "_")
|
|
498
|
-
.replace(/^_+|_+$/g, "");
|
|
499
|
-
}
|
|
500
474
|
function resolveBindingsHelperPath(wasmPath) {
|
|
501
475
|
const bindingsPath = wasmPath.replace(/\.wasm$/, ".bindings.js");
|
|
502
476
|
if (existsSync(bindingsPath)) return bindingsPath;
|
package/bin/commands/run-core.js
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
formatTime,
|
|
9
9
|
getExec,
|
|
10
10
|
loadConfig,
|
|
11
|
+
resolveArtifactPath,
|
|
12
|
+
resolveSpecRelativePath,
|
|
11
13
|
tokenizeCommand,
|
|
12
14
|
} from "../util.js";
|
|
13
15
|
import * as path from "path";
|
|
@@ -23,7 +25,7 @@ import { persistCrashRecord } from "../crash-store.js";
|
|
|
23
25
|
import { describeCoveragePoint } from "../coverage-points.js";
|
|
24
26
|
const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
|
|
25
27
|
class SnapshotStore {
|
|
26
|
-
constructor(specFile, snapshotDir,
|
|
28
|
+
constructor(specFile, snapshotDir, inputPatterns) {
|
|
27
29
|
this.dirty = false;
|
|
28
30
|
this.created = 0;
|
|
29
31
|
this.updated = 0;
|
|
@@ -32,27 +34,19 @@ class SnapshotStore {
|
|
|
32
34
|
this.warnedMissing = new Set();
|
|
33
35
|
this.specBasename = path.basename(specFile);
|
|
34
36
|
const dir = path.join(process.cwd(), snapshotDir);
|
|
35
|
-
const relative =
|
|
36
|
-
/\.ts
|
|
37
|
+
const relative = resolveSpecRelativePath(specFile, inputPatterns).replace(
|
|
38
|
+
/\.ts$/i,
|
|
37
39
|
".snap",
|
|
38
40
|
);
|
|
39
41
|
this.filePath = path.join(dir, relative);
|
|
40
|
-
const sourcePath =
|
|
41
|
-
resolveSnapshotSourcePath(
|
|
42
|
-
specFile,
|
|
43
|
-
dir,
|
|
44
|
-
duplicateSpecBasenames,
|
|
45
|
-
this.filePath,
|
|
46
|
-
) ?? null;
|
|
42
|
+
const sourcePath = existsSync(this.filePath) ? this.filePath : null;
|
|
47
43
|
const loaded = sourcePath
|
|
48
44
|
? readSnapshotFile(sourcePath, specFile)
|
|
49
45
|
: { data: {}, normalized: false, preamble: "" };
|
|
50
46
|
this.data = loaded.data;
|
|
51
47
|
this.preamble = loaded.preamble;
|
|
52
|
-
this.existed = Boolean(sourcePath
|
|
53
|
-
this.dirty = Boolean(
|
|
54
|
-
(sourcePath && sourcePath != this.filePath) || loaded.normalized,
|
|
55
|
-
);
|
|
48
|
+
this.existed = Boolean(sourcePath);
|
|
49
|
+
this.dirty = Boolean(loaded.normalized);
|
|
56
50
|
}
|
|
57
51
|
assert(key, actual, allowSnapshot, createSnapshots, overwriteSnapshots) {
|
|
58
52
|
key = canonicalizeSnapshotKey(key);
|
|
@@ -103,26 +97,6 @@ class SnapshotStore {
|
|
|
103
97
|
);
|
|
104
98
|
}
|
|
105
99
|
}
|
|
106
|
-
function resolveSnapshotSourcePath(
|
|
107
|
-
specFile,
|
|
108
|
-
snapshotDir,
|
|
109
|
-
duplicateSpecBasenames,
|
|
110
|
-
preferredPath,
|
|
111
|
-
) {
|
|
112
|
-
if (existsSync(preferredPath)) return preferredPath;
|
|
113
|
-
const base = path.basename(specFile, ".ts");
|
|
114
|
-
const legacyFlat = path.join(snapshotDir, `${base}.snap.json`);
|
|
115
|
-
if (existsSync(legacyFlat)) return legacyFlat;
|
|
116
|
-
const disambiguator = resolveDisambiguator(specFile, duplicateSpecBasenames);
|
|
117
|
-
if (disambiguator.length) {
|
|
118
|
-
const legacyDisambiguated = path.join(
|
|
119
|
-
snapshotDir,
|
|
120
|
-
`${base}.${disambiguator}.snap.json`,
|
|
121
|
-
);
|
|
122
|
-
if (existsSync(legacyDisambiguated)) return legacyDisambiguated;
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
100
|
function readSnapshotFile(filePath, specFile) {
|
|
127
101
|
const raw = readFileSync(filePath, "utf8");
|
|
128
102
|
if (filePath.endsWith(".json")) {
|
|
@@ -267,17 +241,11 @@ function trimSnapshotPreamble(lines) {
|
|
|
267
241
|
while (end > 0 && !(lines[end - 1] ?? "").trim().length) end--;
|
|
268
242
|
return lines.slice(0, end).join("\n");
|
|
269
243
|
}
|
|
244
|
+
// Only the basename of the returned path matters — callers feed this into
|
|
245
|
+
// `path.basename(...)` to localize snapshot keys (strip the "${basename}::"
|
|
246
|
+
// prefix). The full path is therefore synthetic but stable.
|
|
270
247
|
function resolveSnapshotSpecFile(filePath) {
|
|
271
|
-
|
|
272
|
-
const marker = "/snapshots/";
|
|
273
|
-
const markerIndex = normalized.lastIndexOf(marker);
|
|
274
|
-
const suffix =
|
|
275
|
-
markerIndex >= 0
|
|
276
|
-
? normalized.slice(markerIndex + marker.length)
|
|
277
|
-
: path.basename(normalized);
|
|
278
|
-
const withoutMode = suffix.replace(/^default\//, "");
|
|
279
|
-
const relative = withoutMode.replace(/\.snap$/, ".ts");
|
|
280
|
-
return `assembly/__tests__/${relative}`;
|
|
248
|
+
return path.basename(filePath).replace(/\.snap$/, ".ts");
|
|
281
249
|
}
|
|
282
250
|
function localizeSnapshotKey(specFile, key) {
|
|
283
251
|
const prefix = `${path.basename(specFile)}::`;
|
|
@@ -315,24 +283,18 @@ function canonicalizeSnapshotLocalKey(localKey) {
|
|
|
315
283
|
}
|
|
316
284
|
return localKey;
|
|
317
285
|
}
|
|
318
|
-
function resolveArtifactRelativePath(sourceFile, segment) {
|
|
319
|
-
const normalized = sourceFile.replace(/\\/g, "/");
|
|
320
|
-
const marker = `/${segment}/`;
|
|
321
|
-
const index = normalized.lastIndexOf(marker);
|
|
322
|
-
if (index >= 0) return normalized.slice(index + marker.length);
|
|
323
|
-
return path.basename(normalized);
|
|
324
|
-
}
|
|
325
286
|
function writeReadableLog(
|
|
326
287
|
logRoot,
|
|
327
288
|
file,
|
|
289
|
+
inputPatterns,
|
|
328
290
|
suites,
|
|
329
291
|
modeName,
|
|
330
292
|
buildCommand,
|
|
331
293
|
runCommand,
|
|
332
294
|
snapshotSummary,
|
|
333
295
|
) {
|
|
334
|
-
const relative =
|
|
335
|
-
/\.ts
|
|
296
|
+
const relative = resolveSpecRelativePath(file, inputPatterns).replace(
|
|
297
|
+
/\.ts$/i,
|
|
336
298
|
".log",
|
|
337
299
|
);
|
|
338
300
|
const filePath = path.join(logRoot, relative);
|
|
@@ -605,9 +567,6 @@ export async function run(
|
|
|
605
567
|
const inputFiles = (await glob(inputPatterns)).sort((a, b) =>
|
|
606
568
|
a.localeCompare(b),
|
|
607
569
|
);
|
|
608
|
-
const duplicateSpecBasenames = await resolveDuplicateSpecBasenames(
|
|
609
|
-
config.input,
|
|
610
|
-
);
|
|
611
570
|
const snapshotEnabled = flags.snapshot !== false;
|
|
612
571
|
const createSnapshots = Boolean(flags.createSnapshots);
|
|
613
572
|
const overwriteSnapshots = Boolean(flags.overwriteSnapshots);
|
|
@@ -685,12 +644,7 @@ export async function run(
|
|
|
685
644
|
const file = inputFiles[i];
|
|
686
645
|
const outFile = path.join(
|
|
687
646
|
config.outDir,
|
|
688
|
-
|
|
689
|
-
file,
|
|
690
|
-
config.buildOptions.target,
|
|
691
|
-
options.modeName,
|
|
692
|
-
duplicateSpecBasenames,
|
|
693
|
-
),
|
|
647
|
+
resolveArtifactPath(file, config.input),
|
|
694
648
|
);
|
|
695
649
|
if (!existsSync(outFile)) {
|
|
696
650
|
const buildStartedAt = Date.now();
|
|
@@ -725,7 +679,7 @@ export async function run(
|
|
|
725
679
|
const snapshotStore = new SnapshotStore(
|
|
726
680
|
file,
|
|
727
681
|
config.snapshotDir,
|
|
728
|
-
|
|
682
|
+
config.input,
|
|
729
683
|
);
|
|
730
684
|
let report;
|
|
731
685
|
try {
|
|
@@ -739,11 +693,16 @@ export async function run(
|
|
|
739
693
|
? { BROWSER: config.runOptions.runtime.browser.trim() }
|
|
740
694
|
: {}),
|
|
741
695
|
};
|
|
696
|
+
const crashEntryKey = resolveSpecRelativePath(
|
|
697
|
+
file,
|
|
698
|
+
config.input,
|
|
699
|
+
).replace(/\.ts$/i, "");
|
|
742
700
|
report = webSession
|
|
743
701
|
? await runWebSessionProcess(
|
|
744
702
|
webSession,
|
|
745
703
|
file,
|
|
746
704
|
config.fuzz.crashDir,
|
|
705
|
+
crashEntryKey,
|
|
747
706
|
options.modeName,
|
|
748
707
|
snapshotStore,
|
|
749
708
|
snapshotEnabled,
|
|
@@ -757,6 +716,7 @@ export async function run(
|
|
|
757
716
|
invocation,
|
|
758
717
|
file,
|
|
759
718
|
config.fuzz.crashDir,
|
|
719
|
+
crashEntryKey,
|
|
760
720
|
options.modeName,
|
|
761
721
|
snapshotStore,
|
|
762
722
|
snapshotEnabled,
|
|
@@ -815,6 +775,7 @@ export async function run(
|
|
|
815
775
|
writeReadableLog(
|
|
816
776
|
logRoot,
|
|
817
777
|
report.file,
|
|
778
|
+
config.input,
|
|
818
779
|
report.suites,
|
|
819
780
|
options.modeName,
|
|
820
781
|
options.buildCommandsByFile?.[report.file] ??
|
|
@@ -842,16 +803,12 @@ export async function run(
|
|
|
842
803
|
coverageSummary.files.length > 0
|
|
843
804
|
) {
|
|
844
805
|
const resolvedCoverageDir = path.join(process.cwd(), coverageDir);
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
writeFileSync(
|
|
849
|
-
path.join(
|
|
850
|
-
resolvedCoverageDir,
|
|
851
|
-
options.coverageFileName ?? "coverage.log.json",
|
|
852
|
-
),
|
|
853
|
-
JSON.stringify(coverageSummary, null, 2),
|
|
806
|
+
const coverageFilePath = path.join(
|
|
807
|
+
resolvedCoverageDir,
|
|
808
|
+
options.coverageFileName ?? "coverage.log.json",
|
|
854
809
|
);
|
|
810
|
+
mkdirSync(path.dirname(coverageFilePath), { recursive: true });
|
|
811
|
+
writeFileSync(coverageFilePath, JSON.stringify(coverageSummary, null, 2));
|
|
855
812
|
}
|
|
856
813
|
if (options.emitRunComplete !== false) {
|
|
857
814
|
const totalModes = Math.max(options.modeSummaryTotal ?? 1, 1);
|
|
@@ -1091,53 +1048,6 @@ instantiate(imports)
|
|
|
1091
1048
|
}
|
|
1092
1049
|
return null;
|
|
1093
1050
|
}
|
|
1094
|
-
function resolveArtifactFileName(
|
|
1095
|
-
file,
|
|
1096
|
-
target,
|
|
1097
|
-
modeName,
|
|
1098
|
-
duplicateSpecBasenames = new Set(),
|
|
1099
|
-
) {
|
|
1100
|
-
const base = path
|
|
1101
|
-
.basename(file)
|
|
1102
|
-
.replace(/\.spec\.ts$/, "")
|
|
1103
|
-
.replace(/\.ts$/, "");
|
|
1104
|
-
const legacy = !modeName
|
|
1105
|
-
? `${path.basename(file).replace(".ts", ".wasm")}`
|
|
1106
|
-
: `${base}.${modeName}.${target}.wasm`;
|
|
1107
|
-
if (!duplicateSpecBasenames.has(path.basename(file))) {
|
|
1108
|
-
return legacy;
|
|
1109
|
-
}
|
|
1110
|
-
const disambiguator = resolveDisambiguator(file, duplicateSpecBasenames);
|
|
1111
|
-
if (!disambiguator.length) {
|
|
1112
|
-
return legacy;
|
|
1113
|
-
}
|
|
1114
|
-
const ext = path.extname(legacy);
|
|
1115
|
-
const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
|
|
1116
|
-
return `${stem}.${disambiguator}${ext}`;
|
|
1117
|
-
}
|
|
1118
|
-
async function resolveDuplicateSpecBasenames(configured) {
|
|
1119
|
-
const patterns = Array.isArray(configured) ? configured : [configured];
|
|
1120
|
-
const files = await glob(patterns);
|
|
1121
|
-
const counts = new Map();
|
|
1122
|
-
for (const file of files) {
|
|
1123
|
-
const base = path.basename(file);
|
|
1124
|
-
counts.set(base, (counts.get(base) ?? 0) + 1);
|
|
1125
|
-
}
|
|
1126
|
-
const duplicates = new Set();
|
|
1127
|
-
for (const [base, count] of counts) {
|
|
1128
|
-
if (count > 1) duplicates.add(base);
|
|
1129
|
-
}
|
|
1130
|
-
return duplicates;
|
|
1131
|
-
}
|
|
1132
|
-
function resolveDisambiguator(file, duplicateSpecBasenames) {
|
|
1133
|
-
if (!duplicateSpecBasenames.has(path.basename(file))) return "";
|
|
1134
|
-
const relDir = path.dirname(path.relative(process.cwd(), file));
|
|
1135
|
-
if (!relDir.length || relDir == ".") return "";
|
|
1136
|
-
return relDir
|
|
1137
|
-
.replace(/[\\/]+/g, "__")
|
|
1138
|
-
.replace(/[^A-Za-z0-9._-]/g, "_")
|
|
1139
|
-
.replace(/^_+|_+$/g, "");
|
|
1140
|
-
}
|
|
1141
1051
|
function resolveRuntimeTargetEnv(target, wasmPath) {
|
|
1142
1052
|
if (target == "bindings") {
|
|
1143
1053
|
return resolveBindingsRuntimeEnv(wasmPath);
|
|
@@ -1757,6 +1667,7 @@ async function runProcess(
|
|
|
1757
1667
|
invocation,
|
|
1758
1668
|
specFile,
|
|
1759
1669
|
crashDir,
|
|
1670
|
+
crashEntryKey,
|
|
1760
1671
|
modeName,
|
|
1761
1672
|
snapshots,
|
|
1762
1673
|
snapshotEnabled,
|
|
@@ -1975,6 +1886,7 @@ async function runProcess(
|
|
|
1975
1886
|
persistCrashRecord(crashDir, {
|
|
1976
1887
|
kind: "test",
|
|
1977
1888
|
file: specFile,
|
|
1889
|
+
entryKey: crashEntryKey,
|
|
1978
1890
|
mode: modeName ?? "default",
|
|
1979
1891
|
error: errorText,
|
|
1980
1892
|
stdout: stdoutBuffer,
|
|
@@ -2031,6 +1943,7 @@ async function runProcess(
|
|
|
2031
1943
|
persistCrashRecord(crashDir, {
|
|
2032
1944
|
kind: "test",
|
|
2033
1945
|
file: specFile,
|
|
1946
|
+
entryKey: crashEntryKey,
|
|
2034
1947
|
mode: modeName ?? "default",
|
|
2035
1948
|
error: fullError,
|
|
2036
1949
|
stdout: stdoutBuffer,
|
|
@@ -2075,6 +1988,7 @@ async function runProcess(
|
|
|
2075
1988
|
persistCrashRecord(crashDir, {
|
|
2076
1989
|
kind: "test",
|
|
2077
1990
|
file: specFile,
|
|
1991
|
+
entryKey: crashEntryKey,
|
|
2078
1992
|
mode: modeName ?? "default",
|
|
2079
1993
|
error: errorText || "runtime reported an unknown error",
|
|
2080
1994
|
stdout: stdoutBuffer,
|
|
@@ -2105,6 +2019,7 @@ async function runProcess(
|
|
|
2105
2019
|
persistCrashRecord(crashDir, {
|
|
2106
2020
|
kind: "test",
|
|
2107
2021
|
file: specFile,
|
|
2022
|
+
entryKey: crashEntryKey,
|
|
2108
2023
|
mode: modeName ?? "default",
|
|
2109
2024
|
error: fullError,
|
|
2110
2025
|
stdout: stdoutBuffer,
|
|
@@ -2132,6 +2047,7 @@ async function runProcess(
|
|
|
2132
2047
|
persistCrashRecord(crashDir, {
|
|
2133
2048
|
kind: "test",
|
|
2134
2049
|
file: specFile,
|
|
2050
|
+
entryKey: crashEntryKey,
|
|
2135
2051
|
mode: modeName ?? "default",
|
|
2136
2052
|
error: errorText || "runtime reported an unknown error",
|
|
2137
2053
|
stdout: stdoutBuffer,
|
|
@@ -2155,6 +2071,7 @@ async function runWebSessionProcess(
|
|
|
2155
2071
|
session,
|
|
2156
2072
|
specFile,
|
|
2157
2073
|
crashDir,
|
|
2074
|
+
crashEntryKey,
|
|
2158
2075
|
modeName,
|
|
2159
2076
|
snapshots,
|
|
2160
2077
|
snapshotEnabled,
|
|
@@ -2407,6 +2324,7 @@ async function runWebSessionProcess(
|
|
|
2407
2324
|
persistCrashRecord(crashDir, {
|
|
2408
2325
|
kind: "test",
|
|
2409
2326
|
file: specFile,
|
|
2327
|
+
entryKey: crashEntryKey,
|
|
2410
2328
|
mode: modeName ?? "default",
|
|
2411
2329
|
error: fullError,
|
|
2412
2330
|
stdout: stdoutBuffer,
|
|
@@ -2462,6 +2380,7 @@ async function runWebSessionProcess(
|
|
|
2462
2380
|
persistCrashRecord(crashDir, {
|
|
2463
2381
|
kind: "test",
|
|
2464
2382
|
file: specFile,
|
|
2383
|
+
entryKey: crashEntryKey,
|
|
2465
2384
|
mode: modeName ?? "default",
|
|
2466
2385
|
error: fullError,
|
|
2467
2386
|
stdout: stdoutBuffer,
|
|
@@ -56,6 +56,10 @@ export class PersistentWebSessionHost {
|
|
|
56
56
|
? env.AS_TEST_HELPER_PATH
|
|
57
57
|
: null;
|
|
58
58
|
const jobId = String(this.nextJobId++);
|
|
59
|
+
// URL paths use only the basename — that is safe because each job has
|
|
60
|
+
// exactly one wasm and one helper, scoped by the jobId path component.
|
|
61
|
+
// Nested directory artifacts (e.g. nested/array.spec.wasm) are read
|
|
62
|
+
// from disk via `wasmPath`/`helperPath`, not via these URLs.
|
|
59
63
|
const browserEnv = {
|
|
60
64
|
...env,
|
|
61
65
|
AS_TEST_WASM_PATH: `/job/${jobId}/${path.basename(wasmPath)}`,
|
package/bin/crash-store.js
CHANGED
|
@@ -5,9 +5,9 @@ export function persistCrashRecord(rootDir, record) {
|
|
|
5
5
|
? record.entryKey
|
|
6
6
|
: crashEntryKey(record.file);
|
|
7
7
|
const dir = path.resolve(process.cwd(), rootDir);
|
|
8
|
-
mkdirSync(dir, { recursive: true });
|
|
9
8
|
const jsonPath = path.join(dir, `${entry}.json`);
|
|
10
9
|
const logPath = path.join(dir, `${entry}.log`);
|
|
10
|
+
mkdirSync(path.dirname(jsonPath), { recursive: true });
|
|
11
11
|
const payload = {
|
|
12
12
|
timestamp: new Date().toISOString(),
|
|
13
13
|
...record,
|
package/bin/index.js
CHANGED
|
@@ -22,7 +22,9 @@ import {
|
|
|
22
22
|
getDefaultModeNames,
|
|
23
23
|
getCliVersion,
|
|
24
24
|
loadConfig,
|
|
25
|
+
resolveArtifactPath,
|
|
25
26
|
resolveModeNames,
|
|
27
|
+
resolveSpecRelativePath,
|
|
26
28
|
} from "./util.js";
|
|
27
29
|
import * as path from "path";
|
|
28
30
|
import { spawnSync } from "child_process";
|
|
@@ -1366,8 +1368,7 @@ async function runTestSequential(
|
|
|
1366
1368
|
const results = [];
|
|
1367
1369
|
let failed = false;
|
|
1368
1370
|
const buildIntervals = [];
|
|
1369
|
-
const
|
|
1370
|
-
await resolveAllConfiguredDuplicateSpecBasenames(configPath);
|
|
1371
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
1371
1372
|
for (const file of files) {
|
|
1372
1373
|
const buildStartedAt = Date.now();
|
|
1373
1374
|
let result;
|
|
@@ -1380,10 +1381,7 @@ async function runTestSequential(
|
|
|
1380
1381
|
modeName,
|
|
1381
1382
|
buildFeatureToggles,
|
|
1382
1383
|
);
|
|
1383
|
-
const artifactKey =
|
|
1384
|
-
file,
|
|
1385
|
-
duplicateSpecBasenames,
|
|
1386
|
-
);
|
|
1384
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
1387
1385
|
result = await run(runFlags, configPath, [file], false, {
|
|
1388
1386
|
reporter,
|
|
1389
1387
|
webSession,
|
|
@@ -1391,7 +1389,7 @@ async function runTestSequential(
|
|
|
1391
1389
|
emitRunStart: false,
|
|
1392
1390
|
emitRunComplete: false,
|
|
1393
1391
|
logFileName: `test.${artifactKey}.log.json`,
|
|
1394
|
-
coverageFileName:
|
|
1392
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
1395
1393
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
1396
1394
|
modeName,
|
|
1397
1395
|
});
|
|
@@ -1645,8 +1643,7 @@ async function runRuntimeMatrix(
|
|
|
1645
1643
|
failed: false,
|
|
1646
1644
|
passed: false,
|
|
1647
1645
|
}));
|
|
1648
|
-
const
|
|
1649
|
-
await resolveAllConfiguredDuplicateSpecBasenames(configPath);
|
|
1646
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
1650
1647
|
const buildIntervals = [];
|
|
1651
1648
|
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
|
|
1652
1649
|
const file = files[fileIndex];
|
|
@@ -1665,10 +1662,7 @@ async function runRuntimeMatrix(
|
|
|
1665
1662
|
modeName,
|
|
1666
1663
|
{},
|
|
1667
1664
|
);
|
|
1668
|
-
const artifactKey =
|
|
1669
|
-
file,
|
|
1670
|
-
duplicateSpecBasenames,
|
|
1671
|
-
);
|
|
1665
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
1672
1666
|
const result = await run(runFlags, configPath, [file], false, {
|
|
1673
1667
|
reporter: silentReporter,
|
|
1674
1668
|
reporterKind: "default",
|
|
@@ -1676,7 +1670,7 @@ async function runRuntimeMatrix(
|
|
|
1676
1670
|
emitRunStart: false,
|
|
1677
1671
|
emitRunComplete: false,
|
|
1678
1672
|
logFileName: `run.${artifactKey}.log.json`,
|
|
1679
|
-
coverageFileName:
|
|
1673
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
1680
1674
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
1681
1675
|
modeName,
|
|
1682
1676
|
});
|
|
@@ -2106,8 +2100,7 @@ async function runTestMatrix(
|
|
|
2106
2100
|
failed: false,
|
|
2107
2101
|
passed: false,
|
|
2108
2102
|
}));
|
|
2109
|
-
const
|
|
2110
|
-
await resolveAllConfiguredDuplicateSpecBasenames(configPath);
|
|
2103
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
2111
2104
|
const buildIntervals = [];
|
|
2112
2105
|
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
|
|
2113
2106
|
const file = files[fileIndex];
|
|
@@ -2135,17 +2128,14 @@ async function runTestMatrix(
|
|
|
2135
2128
|
modeName,
|
|
2136
2129
|
buildFeatureToggles,
|
|
2137
2130
|
);
|
|
2138
|
-
const artifactKey =
|
|
2139
|
-
file,
|
|
2140
|
-
duplicateSpecBasenames,
|
|
2141
|
-
);
|
|
2131
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
2142
2132
|
result = await run(runFlags, configPath, [file], false, {
|
|
2143
2133
|
reporter: silentReporter,
|
|
2144
2134
|
reporterKind: "default",
|
|
2145
2135
|
emitRunStart: false,
|
|
2146
2136
|
emitRunComplete: false,
|
|
2147
2137
|
logFileName: `test.${artifactKey}.log.json`,
|
|
2148
|
-
coverageFileName:
|
|
2138
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
2149
2139
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
2150
2140
|
modeName,
|
|
2151
2141
|
});
|
|
@@ -2413,8 +2403,7 @@ async function runRuntimeMatrixParallel(
|
|
|
2413
2403
|
const silentReporter = {};
|
|
2414
2404
|
const modeLabels = modes.map((modeName) => modeName ?? "default");
|
|
2415
2405
|
const showPerModeTimes = Boolean(runFlags.verbose);
|
|
2416
|
-
const
|
|
2417
|
-
await resolveAllConfiguredDuplicateSpecBasenames(configPath);
|
|
2406
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
2418
2407
|
const ordered = new Array(files.length);
|
|
2419
2408
|
const useQueueDisplay = reporterSession.reporterKind == "default";
|
|
2420
2409
|
const queueDisplay = new ParallelQueueDisplay(
|
|
@@ -2454,10 +2443,7 @@ async function runRuntimeMatrixParallel(
|
|
|
2454
2443
|
modeName,
|
|
2455
2444
|
{},
|
|
2456
2445
|
);
|
|
2457
|
-
const artifactKey =
|
|
2458
|
-
file,
|
|
2459
|
-
duplicateSpecBasenames,
|
|
2460
|
-
);
|
|
2446
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
2461
2447
|
result = await run(runFlags, configPath, [file], false, {
|
|
2462
2448
|
reporter: silentReporter,
|
|
2463
2449
|
reporterKind: "default",
|
|
@@ -2465,7 +2451,7 @@ async function runRuntimeMatrixParallel(
|
|
|
2465
2451
|
emitRunStart: false,
|
|
2466
2452
|
emitRunComplete: false,
|
|
2467
2453
|
logFileName: `run.${artifactKey}.log.json`,
|
|
2468
|
-
coverageFileName:
|
|
2454
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
2469
2455
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
2470
2456
|
modeName,
|
|
2471
2457
|
});
|
|
@@ -2563,8 +2549,7 @@ async function runTestSingleParallel(
|
|
|
2563
2549
|
snapshotEnabled,
|
|
2564
2550
|
createSnapshots: runFlags.createSnapshots,
|
|
2565
2551
|
});
|
|
2566
|
-
const
|
|
2567
|
-
await resolveAllConfiguredDuplicateSpecBasenames(configPath);
|
|
2552
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
2568
2553
|
const results = new Array(files.length);
|
|
2569
2554
|
const useQueueDisplay = reporterSession.reporterKind == "default";
|
|
2570
2555
|
const queueDisplay = new ParallelQueueDisplay(
|
|
@@ -2607,10 +2592,7 @@ async function runTestSingleParallel(
|
|
|
2607
2592
|
modeName,
|
|
2608
2593
|
buildFeatureToggles,
|
|
2609
2594
|
);
|
|
2610
|
-
const artifactKey =
|
|
2611
|
-
file,
|
|
2612
|
-
duplicateSpecBasenames,
|
|
2613
|
-
);
|
|
2595
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
2614
2596
|
result = await run(
|
|
2615
2597
|
{ ...runFlags, clean: true },
|
|
2616
2598
|
configPath,
|
|
@@ -2622,7 +2604,7 @@ async function runTestSingleParallel(
|
|
|
2622
2604
|
suiteSelectors,
|
|
2623
2605
|
emitRunComplete: false,
|
|
2624
2606
|
logFileName: `test.${artifactKey}.log.json`,
|
|
2625
|
-
coverageFileName:
|
|
2607
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
2626
2608
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
2627
2609
|
modeName,
|
|
2628
2610
|
},
|
|
@@ -2734,8 +2716,7 @@ async function runTestMatrixParallel(
|
|
|
2734
2716
|
const silentReporter = {};
|
|
2735
2717
|
const modeLabels = modes.map((modeName) => modeName ?? "default");
|
|
2736
2718
|
const showPerModeTimes = Boolean(runFlags.verbose);
|
|
2737
|
-
const
|
|
2738
|
-
await resolveAllConfiguredDuplicateSpecBasenames(configPath);
|
|
2719
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
2739
2720
|
const ordered = new Array(files.length);
|
|
2740
2721
|
const useQueueDisplay = reporterSession.reporterKind == "default";
|
|
2741
2722
|
const queueDisplay = new ParallelQueueDisplay(
|
|
@@ -2775,10 +2756,7 @@ async function runTestMatrixParallel(
|
|
|
2775
2756
|
modeName,
|
|
2776
2757
|
buildFeatureToggles,
|
|
2777
2758
|
);
|
|
2778
|
-
const artifactKey =
|
|
2779
|
-
file,
|
|
2780
|
-
duplicateSpecBasenames,
|
|
2781
|
-
);
|
|
2759
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
2782
2760
|
result = await run(runFlags, configPath, [file], false, {
|
|
2783
2761
|
reporter: silentReporter,
|
|
2784
2762
|
reporterKind: "default",
|
|
@@ -2786,7 +2764,7 @@ async function runTestMatrixParallel(
|
|
|
2786
2764
|
emitRunStart: false,
|
|
2787
2765
|
emitRunComplete: false,
|
|
2788
2766
|
logFileName: `test.${artifactKey}.log.json`,
|
|
2789
|
-
coverageFileName:
|
|
2767
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
2790
2768
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
2791
2769
|
modeName,
|
|
2792
2770
|
});
|
|
@@ -3601,80 +3579,21 @@ function isBareSuiteSelector(selector) {
|
|
|
3601
3579
|
function stripSuiteSuffix(selector) {
|
|
3602
3580
|
return selector.replace(/\.spec\.ts$/, "").replace(/\.ts$/, "");
|
|
3603
3581
|
}
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
}
|
|
3610
|
-
const duplicates = new Set();
|
|
3611
|
-
for (const [base, count] of counts) {
|
|
3612
|
-
if (count > 1) duplicates.add(base);
|
|
3613
|
-
}
|
|
3614
|
-
return duplicates;
|
|
3582
|
+
// Returns the spec relative path (under the configured input base) with the
|
|
3583
|
+
// trailing ".ts" stripped, suitable for use as a stable per-file key for
|
|
3584
|
+
// coverage and log filenames.
|
|
3585
|
+
function resolveArtifactStem(file, inputPatterns) {
|
|
3586
|
+
return resolveSpecRelativePath(file, inputPatterns).replace(/\.ts$/i, "");
|
|
3615
3587
|
}
|
|
3616
|
-
|
|
3617
|
-
// selector-filtered subset, otherwise running a single spec writes/looks up an
|
|
3618
|
-
// artifact name that the rest of the toolchain doesn't agree on.
|
|
3619
|
-
async function resolveAllConfiguredDuplicateSpecBasenames(configPath) {
|
|
3588
|
+
async function loadInputPatterns(configPath) {
|
|
3620
3589
|
const resolvedConfigPath =
|
|
3621
3590
|
configPath ?? path.join(process.cwd(), "./as-test.config.json");
|
|
3622
|
-
|
|
3623
|
-
return resolveDuplicateBasenamesForPatterns(config.input);
|
|
3591
|
+
return loadConfig(resolvedConfigPath, false).input;
|
|
3624
3592
|
}
|
|
3625
|
-
async function
|
|
3593
|
+
async function loadFuzzInputPatterns(configPath) {
|
|
3626
3594
|
const resolvedConfigPath =
|
|
3627
3595
|
configPath ?? path.join(process.cwd(), "./as-test.config.json");
|
|
3628
|
-
|
|
3629
|
-
return resolveDuplicateBasenamesForPatterns(config.fuzz.input);
|
|
3630
|
-
}
|
|
3631
|
-
async function resolveDuplicateBasenamesForPatterns(configured) {
|
|
3632
|
-
const patterns = Array.isArray(configured) ? configured : [configured];
|
|
3633
|
-
const files = await glob(patterns);
|
|
3634
|
-
return resolveDuplicateSpecBasenames(files);
|
|
3635
|
-
}
|
|
3636
|
-
function resolvePerFileArtifactKey(file, duplicateSpecBasenames) {
|
|
3637
|
-
const base = path.basename(file);
|
|
3638
|
-
let raw = base;
|
|
3639
|
-
if (duplicateSpecBasenames.has(base)) {
|
|
3640
|
-
const disambiguator = resolvePerFileDisambiguator(file);
|
|
3641
|
-
if (disambiguator.length) {
|
|
3642
|
-
raw = `${base}.${disambiguator}`;
|
|
3643
|
-
}
|
|
3644
|
-
}
|
|
3645
|
-
return raw.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3646
|
-
}
|
|
3647
|
-
function resolvePerFileDisambiguator(file) {
|
|
3648
|
-
const relDir = path.dirname(path.relative(process.cwd(), file));
|
|
3649
|
-
if (!relDir.length || relDir == ".") return "";
|
|
3650
|
-
return relDir
|
|
3651
|
-
.replace(/[\\/]+/g, "__")
|
|
3652
|
-
.replace(/[^A-Za-z0-9._-]/g, "_")
|
|
3653
|
-
.replace(/^_+|_+$/g, "");
|
|
3654
|
-
}
|
|
3655
|
-
function resolveArtifactFileNameForPreview(
|
|
3656
|
-
file,
|
|
3657
|
-
target,
|
|
3658
|
-
modeName,
|
|
3659
|
-
duplicateSpecBasenames,
|
|
3660
|
-
) {
|
|
3661
|
-
const base = path
|
|
3662
|
-
.basename(file)
|
|
3663
|
-
.replace(/\.spec\.ts$/, "")
|
|
3664
|
-
.replace(/\.ts$/, "");
|
|
3665
|
-
const legacy = !modeName
|
|
3666
|
-
? `${path.basename(file).replace(".ts", ".wasm")}`
|
|
3667
|
-
: `${base}.${modeName}.${target}.wasm`;
|
|
3668
|
-
if (!duplicateSpecBasenames.has(path.basename(file))) {
|
|
3669
|
-
return legacy;
|
|
3670
|
-
}
|
|
3671
|
-
const disambiguator = resolvePerFileDisambiguator(file);
|
|
3672
|
-
if (!disambiguator.length) {
|
|
3673
|
-
return legacy;
|
|
3674
|
-
}
|
|
3675
|
-
const ext = path.extname(legacy);
|
|
3676
|
-
const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
|
|
3677
|
-
return `${stem}.${disambiguator}${ext}`;
|
|
3596
|
+
return loadConfig(resolvedConfigPath, false).fuzz.input;
|
|
3678
3597
|
}
|
|
3679
3598
|
async function ensureWebBrowsersReady(configPath, modes, browserOverride) {
|
|
3680
3599
|
const resolvedConfigPath =
|
|
@@ -4201,10 +4120,8 @@ async function listExecutionPlan(
|
|
|
4201
4120
|
: `No test files matched: ${scope}`,
|
|
4202
4121
|
);
|
|
4203
4122
|
}
|
|
4204
|
-
const
|
|
4205
|
-
|
|
4206
|
-
const duplicateFuzzBasenames =
|
|
4207
|
-
await resolveAllConfiguredDuplicateFuzzBasenames(configPath);
|
|
4123
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
4124
|
+
const fuzzInputPatterns = await loadFuzzInputPatterns(configPath);
|
|
4208
4125
|
if (specFiles.length) {
|
|
4209
4126
|
process.stdout.write(chalk.bold("Resolved files:\n"));
|
|
4210
4127
|
for (const file of specFiles) {
|
|
@@ -4258,12 +4175,7 @@ async function listExecutionPlan(
|
|
|
4258
4175
|
if (specFiles.length) {
|
|
4259
4176
|
process.stdout.write(" artifacts:\n");
|
|
4260
4177
|
for (const file of specFiles) {
|
|
4261
|
-
const artifactName =
|
|
4262
|
-
file,
|
|
4263
|
-
active.buildOptions.target,
|
|
4264
|
-
modeName,
|
|
4265
|
-
duplicateSpecBasenames,
|
|
4266
|
-
);
|
|
4178
|
+
const artifactName = resolveArtifactPath(file, inputPatterns);
|
|
4267
4179
|
process.stdout.write(
|
|
4268
4180
|
` - ${path.join(active.outDir, artifactName)}\n`,
|
|
4269
4181
|
);
|
|
@@ -4272,12 +4184,7 @@ async function listExecutionPlan(
|
|
|
4272
4184
|
if (fuzzFiles.length && command == "test") {
|
|
4273
4185
|
process.stdout.write(" fuzz artifacts:\n");
|
|
4274
4186
|
for (const file of fuzzFiles) {
|
|
4275
|
-
const artifactName =
|
|
4276
|
-
file,
|
|
4277
|
-
"bindings",
|
|
4278
|
-
modeName,
|
|
4279
|
-
duplicateFuzzBasenames,
|
|
4280
|
-
);
|
|
4187
|
+
const artifactName = resolveArtifactPath(file, fuzzInputPatterns);
|
|
4281
4188
|
process.stdout.write(
|
|
4282
4189
|
` - ${path.join(active.outDir, artifactName)}\n`,
|
|
4283
4190
|
);
|
|
@@ -4285,12 +4192,7 @@ async function listExecutionPlan(
|
|
|
4285
4192
|
} else if (command == "fuzz") {
|
|
4286
4193
|
process.stdout.write(" artifacts:\n");
|
|
4287
4194
|
for (const file of fuzzFiles) {
|
|
4288
|
-
const artifactName =
|
|
4289
|
-
file,
|
|
4290
|
-
"bindings",
|
|
4291
|
-
modeName,
|
|
4292
|
-
duplicateFuzzBasenames,
|
|
4293
|
-
);
|
|
4195
|
+
const artifactName = resolveArtifactPath(file, fuzzInputPatterns);
|
|
4294
4196
|
process.stdout.write(
|
|
4295
4197
|
` - ${path.join(active.outDir, artifactName)}\n`,
|
|
4296
4198
|
);
|
package/bin/util.js
CHANGED
|
@@ -1449,3 +1449,77 @@ export function resolveProjectModule(specifier) {
|
|
|
1449
1449
|
}
|
|
1450
1450
|
return null;
|
|
1451
1451
|
}
|
|
1452
|
+
// picomatch-compatible glob metacharacters; first occurrence marks the start
|
|
1453
|
+
// of the dynamic part of a pattern and everything before it is the static base.
|
|
1454
|
+
const GLOB_META_RE = /[*?[\](){}!|+@]/;
|
|
1455
|
+
// Longest non-glob prefix of a single pattern, returned with native separators.
|
|
1456
|
+
// Examples:
|
|
1457
|
+
// "assembly/__tests__/**/*.spec.ts" -> "assembly/__tests__"
|
|
1458
|
+
// "**/*.spec.ts" -> ""
|
|
1459
|
+
// "assembly/foo.spec.ts" -> "assembly" (no glob → use dirname)
|
|
1460
|
+
// "/abs/path/**/*.ts" -> "/abs/path"
|
|
1461
|
+
export function resolveGlobBase(pattern) {
|
|
1462
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
1463
|
+
const metaIdx = normalized.search(GLOB_META_RE);
|
|
1464
|
+
let base;
|
|
1465
|
+
if (metaIdx < 0) {
|
|
1466
|
+
const dir = dirname(normalized);
|
|
1467
|
+
base = dir == "." ? "" : dir;
|
|
1468
|
+
} else {
|
|
1469
|
+
const slice = normalized.slice(0, metaIdx);
|
|
1470
|
+
const lastSlash = slice.lastIndexOf("/");
|
|
1471
|
+
base = lastSlash < 0 ? "" : slice.slice(0, lastSlash);
|
|
1472
|
+
}
|
|
1473
|
+
if (!base.length) return "";
|
|
1474
|
+
return base.split("/").join(sep);
|
|
1475
|
+
}
|
|
1476
|
+
// Strip the most-specific matching configured input base off `file`, returning
|
|
1477
|
+
// the path relative to that base (with native separators). If no base matches,
|
|
1478
|
+
// returns the basename of the file. Comparison is component-wise — so
|
|
1479
|
+
// "assembly/__tests" is not a prefix of "assembly/__tests__/foo.spec.ts".
|
|
1480
|
+
export function resolveSpecRelativePath(file, inputPatterns) {
|
|
1481
|
+
const patterns = Array.isArray(inputPatterns)
|
|
1482
|
+
? inputPatterns
|
|
1483
|
+
: [inputPatterns];
|
|
1484
|
+
const absFile = resolve(process.cwd(), file);
|
|
1485
|
+
const fileComponents = toComponents(absFile);
|
|
1486
|
+
let bestBaseAbs = null;
|
|
1487
|
+
let bestLength = -1;
|
|
1488
|
+
for (const pattern of patterns) {
|
|
1489
|
+
const base = resolveGlobBase(pattern);
|
|
1490
|
+
const absBase = base.length
|
|
1491
|
+
? resolve(process.cwd(), base)
|
|
1492
|
+
: resolve(process.cwd());
|
|
1493
|
+
const baseComponents = toComponents(absBase);
|
|
1494
|
+
if (!isComponentPrefix(baseComponents, fileComponents)) continue;
|
|
1495
|
+
if (baseComponents.length > bestLength) {
|
|
1496
|
+
bestBaseAbs = absBase;
|
|
1497
|
+
bestLength = baseComponents.length;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
if (bestBaseAbs == null) return basename(file);
|
|
1501
|
+
const rel = relative(bestBaseAbs, absFile);
|
|
1502
|
+
return rel.length ? rel : basename(file);
|
|
1503
|
+
}
|
|
1504
|
+
// Compute the artifact path (relative to outDir) for a given spec/fuzz source
|
|
1505
|
+
// file. Strips ".ts" only, keeping ".spec" / ".fuzz" suffixes so spec and
|
|
1506
|
+
// fuzz artifacts can coexist in the same outDir.
|
|
1507
|
+
// assembly/__tests__/array.spec.ts -> "array.spec.wasm"
|
|
1508
|
+
// assembly/__tests__/nested/array.spec.ts -> "nested/array.spec.wasm"
|
|
1509
|
+
// assembly/__fuzz__/nested/array.fuzz.ts -> "nested/array.fuzz.wasm"
|
|
1510
|
+
export function resolveArtifactPath(file, inputPatterns) {
|
|
1511
|
+
const rel = resolveSpecRelativePath(file, inputPatterns);
|
|
1512
|
+
return rel.replace(/\.ts$/i, ".wasm");
|
|
1513
|
+
}
|
|
1514
|
+
function toComponents(absPath) {
|
|
1515
|
+
return absPath
|
|
1516
|
+
.split(/[\\/]+/)
|
|
1517
|
+
.filter((segment, idx) => segment.length || idx == 0);
|
|
1518
|
+
}
|
|
1519
|
+
function isComponentPrefix(prefix, full) {
|
|
1520
|
+
if (prefix.length > full.length) return false;
|
|
1521
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
1522
|
+
if (prefix[i] !== full[i]) return false;
|
|
1523
|
+
}
|
|
1524
|
+
return true;
|
|
1525
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "as-test",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"author": "Jairus Tanaka",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,16 +18,17 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@assemblyscript/wasi-shim": "^0.1.0",
|
|
20
20
|
"@eslint/js": "^10.0.1",
|
|
21
|
-
"@types/node": "^25.
|
|
21
|
+
"@types/node": "^25.9.1",
|
|
22
22
|
"as-sleep": "^0.0.2",
|
|
23
23
|
"as-test": "./",
|
|
24
24
|
"assemblyscript": "^0.28.17",
|
|
25
25
|
"assemblyscript-prettier": "^3.0.4",
|
|
26
|
-
"
|
|
26
|
+
"husky": "^9.1.7",
|
|
27
|
+
"json-as": "^1.3.7",
|
|
27
28
|
"prettier": "3.8.3",
|
|
28
|
-
"try-as": "^1.0
|
|
29
|
+
"try-as": "^1.1.0",
|
|
29
30
|
"typescript": "^6.0.3",
|
|
30
|
-
"typescript-eslint": "^8.59.
|
|
31
|
+
"typescript-eslint": "^8.59.4",
|
|
31
32
|
"vitepress": "^1.6.4"
|
|
32
33
|
},
|
|
33
34
|
"bin": {
|
|
@@ -76,9 +77,9 @@
|
|
|
76
77
|
},
|
|
77
78
|
"scripts": {
|
|
78
79
|
"test": "npm run test:as && npm run test:integration",
|
|
79
|
-
"test:as": "node ./bin/index.js test --parallel",
|
|
80
|
+
"test:as": "node ./bin/index.js test --parallel --enable try-as",
|
|
80
81
|
"test:integration": "npm run build:cli && npm run build:lib && node --test tests/*.test.mjs",
|
|
81
|
-
"test:ci": "node ./bin/index.js test --parallel --tap --config ./as-test.ci.config.json",
|
|
82
|
+
"test:ci": "node ./bin/index.js test --parallel --tap --enable try-as --config ./as-test.ci.config.json",
|
|
82
83
|
"fuzz": "node ./bin/index.js fuzz",
|
|
83
84
|
"bench:seed": "node ./bin/index.js fuzz --config ./as-test.bench.config.json --clean",
|
|
84
85
|
"bench:seed:compare": "bash ./tools/bench-seed-compare.sh 7",
|
|
@@ -98,7 +99,11 @@
|
|
|
98
99
|
"docs:preview": "vitepress preview docs",
|
|
99
100
|
"format": "prettier -w .",
|
|
100
101
|
"release:check": "npm run build:cli && npm run build:lib && npm run build:transform && npm run test && npm run test:examples && npm pack --dry-run --cache /tmp/as-test-npm-cache",
|
|
101
|
-
"prepublishOnly": "npm run build:cli && npm run build:lib && npm run build:transform && npm run test && npm run format"
|
|
102
|
+
"prepublishOnly": "npm run build:cli && npm run build:lib && npm run build:transform && npm run test && npm run format",
|
|
103
|
+
"commitmsg:verify": "bash ./scripts/commit-msg.sh",
|
|
104
|
+
"precommit:verify": "bash ./scripts/pre-commit.sh",
|
|
105
|
+
"prepush:verify": "bash ./scripts/pre-push.sh",
|
|
106
|
+
"prepare": "husky"
|
|
102
107
|
},
|
|
103
108
|
"type": "module"
|
|
104
109
|
}
|