as-test 1.1.4 → 1.1.6-patch.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/assembly/__fuzz__/nested/array.fuzz.ts +13 -0
- package/bin/commands/build-core.js +27 -49
- package/bin/commands/fuzz-core.js +13 -53
- package/bin/commands/run-core.js +98 -99
- package/bin/commands/web-session.js +4 -0
- package/bin/crash-store.js +1 -1
- package/bin/index.js +37 -75
- package/bin/util.js +87 -6
- package/package.json +1 -1
- package/transform/lib/coverage.js +17 -16
- package/transform/lib/index.js +3 -1
- package/transform/lib/types.js +3 -0
- package/transform/lib/visitor.js +70 -67
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2026-05-19 - v1.1.6-patch.1
|
|
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`). Sidecar `.js` / `.d.ts` files sit next to the 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. Patterns without a static prefix (e.g. `**/*.spec.ts`) fall back to the file's cwd-relative path.
|
|
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, so the runner no longer reports `bindings artifact not found` after a single-file build.
|
|
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 (e.g. `tests/specs/**/*.spec.ts`) now nest correctly instead of silently falling back to basename-only paths.
|
|
15
|
+
- fix: 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.
|
|
16
|
+
- chore: delete the duplicate-basename plumbing introduced by the patch immediately preceding this one. Every code path that used to thread `Set<string>` of duplicate basenames now reads paths directly. No back-compat for the old flat-with-suffix scheme — `ast clean` removes any orphan artifacts.
|
|
17
|
+
|
|
18
|
+
## 2026-05-14 - v1.1.6
|
|
19
|
+
|
|
20
|
+
- fix: coverage `mode` and `dependencies` filtering now correctly handles AssemblyScript-normalized `~lib/<pkg>/...` paths, which are the actual runtime paths emitted for `node_modules` imports.
|
|
21
|
+
- fix: `ENTRY_FILE` injected by the transform now uses the full relative path instead of the basename, preventing snapshot key collisions between specs with the same filename in different directories; snapshot lookup normalizes the file prefix to maintain backward compatibility with existing `.snap` files.
|
|
22
|
+
- fix: transform visitor and coverage instrumentation now resolve `NodeKind` values at runtime instead of relying on compile-time const enum inlining, so they remain correct across AssemblyScript versions.
|
|
23
|
+
- fix: add a no-op `TupleType` case to the transform visitor so files using tuple types no longer throw during instrumentation.
|
|
24
|
+
- fix: coverage transform no longer wraps `return this` in constructors, preventing AS231 ("A class with a constructor explicitly returning something else than 'this' must be '@final'").
|
|
25
|
+
- fix: coverage transform preserves expression-body arrows instead of converting them to block bodies, preventing TS1140 ("Type argument expected") on typed arrow parameters such as `[1,2,3].map((x: i32) => x + 1)`.
|
|
26
|
+
|
|
3
27
|
## 2026-05-14 - v1.1.4
|
|
4
28
|
|
|
5
29
|
- feat: add `coverage.mode` (`project` or `all`) plus `coverage.dependencies` package allowlisting so dependency coverage can include normal or pnpm-installed packages without raw path globs.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, fuzz, FuzzSeed } from "as-test";
|
|
2
|
+
|
|
3
|
+
fuzz(
|
|
4
|
+
"nested array.fuzz: index windows stay ordered",
|
|
5
|
+
(start: i32, end: i32): bool => {
|
|
6
|
+
expect(start <= end).toBe(true);
|
|
7
|
+
return end - start <= 64;
|
|
8
|
+
},
|
|
9
|
+
).generate((seed: FuzzSeed, run: (start: i32, end: i32) => bool): void => {
|
|
10
|
+
const start = seed.i32({ min: 0, max: 64 });
|
|
11
|
+
const width = seed.i32({ min: 0, max: 64 });
|
|
12
|
+
run(start, start + width);
|
|
13
|
+
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { existsSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync } from "fs";
|
|
2
2
|
import { glob } from "glob";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
5
|
import * as path from "path";
|
|
6
6
|
import { createMemoryStream, main as ascMain, } from "assemblyscript/dist/asc.js";
|
|
7
|
-
import { applyMode, getPkgRunner, loadConfig, tokenizeCommand, resolveProjectModule, } from "../util.js";
|
|
7
|
+
import { applyMode, getPkgRunner, loadConfig, resolveArtifactPath, resolveSpecRelativePath, tokenizeCommand, resolveProjectModule, } from "../util.js";
|
|
8
8
|
import { persistCrashRecord } from "../crash-store.js";
|
|
9
9
|
import { BuildWorkerPool } from "../build-worker-pool.js";
|
|
10
10
|
const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
|
|
@@ -36,9 +36,10 @@ export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], mo
|
|
|
36
36
|
ensureDeps(config);
|
|
37
37
|
}
|
|
38
38
|
const pkgRunner = getPkgRunner();
|
|
39
|
-
const
|
|
39
|
+
const sourceInputPatterns = overrides.kind === "fuzz" ? config.fuzz.input : config.input;
|
|
40
|
+
const inputPatterns = resolveInputPatterns(sourceInputPatterns, selectors);
|
|
40
41
|
const inputFiles = (await glob(inputPatterns)).sort((a, b) => a.localeCompare(b));
|
|
41
|
-
|
|
42
|
+
await assertNoArtifactCollisions(sourceInputPatterns);
|
|
42
43
|
const coverageEnabled = resolveCoverageEnabled(config.coverage, featureToggles.coverage);
|
|
43
44
|
const buildEnv = {
|
|
44
45
|
...mode.env,
|
|
@@ -50,7 +51,7 @@ export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], mo
|
|
|
50
51
|
!hasCustomBuildCommand(config)) {
|
|
51
52
|
const pool = getSerialBuildWorkerPool();
|
|
52
53
|
for (const file of inputFiles) {
|
|
53
|
-
const outFile =
|
|
54
|
+
const outFile = path.join(config.outDir, resolveArtifactPath(file, sourceInputPatterns));
|
|
54
55
|
const invocation = getBuildCommand(config, pkgRunner, file, outFile, modeName, featureToggles);
|
|
55
56
|
await pool.buildFileMode({
|
|
56
57
|
configPath,
|
|
@@ -64,7 +65,8 @@ export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], mo
|
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
67
|
for (const file of inputFiles) {
|
|
67
|
-
const outFile =
|
|
68
|
+
const outFile = path.join(config.outDir, resolveArtifactPath(file, sourceInputPatterns));
|
|
69
|
+
mkdirSync(path.dirname(outFile), { recursive: true });
|
|
68
70
|
const invocation = getBuildCommand(config, pkgRunner, file, outFile, modeName, featureToggles);
|
|
69
71
|
try {
|
|
70
72
|
await buildFile(invocation, buildEnv);
|
|
@@ -79,6 +81,7 @@ export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], mo
|
|
|
79
81
|
kind,
|
|
80
82
|
stage: "build",
|
|
81
83
|
file,
|
|
84
|
+
entryKey: resolveSpecRelativePath(file, sourceInputPatterns).replace(/\.ts$/i, ""),
|
|
82
85
|
mode: modeLabel,
|
|
83
86
|
cwd: process.cwd(),
|
|
84
87
|
buildCommand,
|
|
@@ -127,8 +130,8 @@ export async function getBuildInvocationPreview(configPath = DEFAULT_CONFIG_PATH
|
|
|
127
130
|
if (overrides.args?.length) {
|
|
128
131
|
config.buildOptions.args = [...config.buildOptions.args, ...overrides.args];
|
|
129
132
|
}
|
|
130
|
-
const
|
|
131
|
-
const outFile =
|
|
133
|
+
const sourceInputPatterns = overrides.kind === "fuzz" ? config.fuzz.input : config.input;
|
|
134
|
+
const outFile = path.join(config.outDir, resolveArtifactPath(file, sourceInputPatterns));
|
|
132
135
|
return getBuildCommand(config, getPkgRunner(), file, outFile, modeName, featureToggles);
|
|
133
136
|
}
|
|
134
137
|
export async function getBuildReuseInfo(configPath = DEFAULT_CONFIG_PATH, file, modeName, featureToggles = {}, overrides = {}) {
|
|
@@ -145,8 +148,8 @@ export async function getBuildReuseInfo(configPath = DEFAULT_CONFIG_PATH, file,
|
|
|
145
148
|
if (hasCustomBuildCommand(config)) {
|
|
146
149
|
return null;
|
|
147
150
|
}
|
|
148
|
-
const
|
|
149
|
-
const outFile =
|
|
151
|
+
const sourceInputPatterns = overrides.kind === "fuzz" ? config.fuzz.input : config.input;
|
|
152
|
+
const outFile = path.join(config.outDir, resolveArtifactPath(file, sourceInputPatterns));
|
|
150
153
|
const invocation = getBuildCommand(config, getPkgRunner(), file, outFile, modeName, featureToggles);
|
|
151
154
|
const coverageEnabled = resolveCoverageEnabled(config.coverage, featureToggles.coverage);
|
|
152
155
|
const buildEnv = {
|
|
@@ -245,46 +248,21 @@ function expandBuildCommand(template, file, outFile, target, modeName) {
|
|
|
245
248
|
.replace(/<target>/g, target)
|
|
246
249
|
.replace(/<mode>/g, modeName ?? "");
|
|
247
250
|
}
|
|
248
|
-
function
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
.replace(/\.ts$/, "");
|
|
253
|
-
const legacy = !modeName
|
|
254
|
-
? `${path.basename(file).replace(".ts", ".wasm")}`
|
|
255
|
-
: `${base}.${modeName}.${target}.wasm`;
|
|
256
|
-
if (!duplicateSpecBasenames.has(path.basename(file))) {
|
|
257
|
-
return legacy;
|
|
258
|
-
}
|
|
259
|
-
const disambiguator = resolveDisambiguator(file);
|
|
260
|
-
if (!disambiguator.length) {
|
|
261
|
-
return legacy;
|
|
262
|
-
}
|
|
263
|
-
const ext = path.extname(legacy);
|
|
264
|
-
const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
|
|
265
|
-
return `${stem}.${disambiguator}${ext}`;
|
|
266
|
-
}
|
|
267
|
-
function resolveDuplicateBasenames(files) {
|
|
268
|
-
const counts = new Map();
|
|
251
|
+
async function assertNoArtifactCollisions(configured) {
|
|
252
|
+
const patterns = Array.isArray(configured) ? configured : [configured];
|
|
253
|
+
const files = await glob(patterns);
|
|
254
|
+
const seen = new Map();
|
|
269
255
|
for (const file of files) {
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
function resolveDisambiguator(file) {
|
|
281
|
-
const relDir = path.dirname(path.relative(process.cwd(), file));
|
|
282
|
-
if (!relDir.length || relDir == ".")
|
|
283
|
-
return "";
|
|
284
|
-
return relDir
|
|
285
|
-
.replace(/[\\/]+/g, "__")
|
|
286
|
-
.replace(/[^A-Za-z0-9._-]/g, "_")
|
|
287
|
-
.replace(/^_+|_+$/g, "");
|
|
256
|
+
const artifact = resolveArtifactPath(file, patterns);
|
|
257
|
+
const prev = seen.get(artifact);
|
|
258
|
+
if (prev != null && prev !== file) {
|
|
259
|
+
throw new Error(`Two input files resolve to the same artifact path "${artifact}":\n` +
|
|
260
|
+
` - ${prev}\n` +
|
|
261
|
+
` - ${file}\n` +
|
|
262
|
+
`Rename one of them or narrow the input patterns to disambiguate.`);
|
|
263
|
+
}
|
|
264
|
+
seen.set(artifact, file);
|
|
265
|
+
}
|
|
288
266
|
}
|
|
289
267
|
function resolveInputPatterns(configured, selectors) {
|
|
290
268
|
const configuredInputs = Array.isArray(configured)
|
|
@@ -3,7 +3,7 @@ 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 { applyMode, loadConfig } from "../util.js";
|
|
6
|
+
import { applyMode, loadConfig, resolveArtifactPath, resolveSpecRelativePath, } from "../util.js";
|
|
7
7
|
import { persistCrashRecord } from "../crash-store.js";
|
|
8
8
|
const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
|
|
9
9
|
const MAGIC = Buffer.from("WIPC");
|
|
@@ -19,14 +19,13 @@ export async function fuzz(configPath = DEFAULT_CONFIG_PATH, selectors = [], mod
|
|
|
19
19
|
if (!inputFiles.length) {
|
|
20
20
|
throw new Error(`No fuzz files matched: ${selectors.length ? selectors.join(", ") : "configured input patterns"}`);
|
|
21
21
|
}
|
|
22
|
-
const duplicateBasenames = resolveDuplicateBasenames(inputFiles);
|
|
23
22
|
const results = [];
|
|
24
23
|
for (const file of inputFiles) {
|
|
25
24
|
const buildStartedAt = Date.now();
|
|
26
25
|
await build(configPath, [file], modeName, { coverage: false }, { target: "bindings", args: ["--use", "AS_TEST_FUZZ=1"], kind: "fuzz" }, loadedConfig);
|
|
27
26
|
const buildFinishedAt = Date.now();
|
|
28
27
|
const buildTime = buildFinishedAt - buildStartedAt;
|
|
29
|
-
results.push(await runFuzzTarget(file, activeConfig.outDir,
|
|
28
|
+
results.push(await runFuzzTarget(file, activeConfig.outDir, config, fuzzerSelectors, buildStartedAt, buildFinishedAt, buildTime, modeName));
|
|
30
29
|
}
|
|
31
30
|
return results;
|
|
32
31
|
}
|
|
@@ -70,9 +69,9 @@ function encodeRunsOverrideKind(kind) {
|
|
|
70
69
|
return 4;
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
|
-
async function runFuzzTarget(file, outDir,
|
|
72
|
+
async function runFuzzTarget(file, outDir, config, fuzzerSelectors, buildStartedAt, buildFinishedAt, buildTime, modeName) {
|
|
74
73
|
const startedAt = Date.now();
|
|
75
|
-
const artifact =
|
|
74
|
+
const artifact = resolveArtifactPath(file, config.input);
|
|
76
75
|
const wasmPath = path.resolve(process.cwd(), outDir, artifact);
|
|
77
76
|
const jsPath = resolveBindingsHelperPath(wasmPath);
|
|
78
77
|
const helper = await import(pathToFileURL(jsPath).href + `?t=${Date.now()}`);
|
|
@@ -140,7 +139,7 @@ async function runFuzzTarget(file, outDir, duplicateBasenames, config, fuzzerSel
|
|
|
140
139
|
const crash = persistCrashRecord(config.crashDir, {
|
|
141
140
|
kind: "fuzz",
|
|
142
141
|
file,
|
|
143
|
-
entryKey: buildFuzzCrashEntryKey(file, modeName ?? "default"),
|
|
142
|
+
entryKey: buildFuzzCrashEntryKey(file, config.input, modeName ?? "default"),
|
|
144
143
|
mode: modeName ?? "default",
|
|
145
144
|
seed: config.seed,
|
|
146
145
|
error: crashMessage,
|
|
@@ -188,7 +187,7 @@ async function runFuzzTarget(file, outDir, duplicateBasenames, config, fuzzerSel
|
|
|
188
187
|
const crash = persistCrashRecord(config.crashDir, {
|
|
189
188
|
kind: "fuzz",
|
|
190
189
|
file,
|
|
191
|
-
entryKey: buildFuzzCrashEntryKey(file, modeName ?? "default"),
|
|
190
|
+
entryKey: buildFuzzCrashEntryKey(file, config.input, modeName ?? "default"),
|
|
192
191
|
mode: modeName ?? "default",
|
|
193
192
|
seed: config.seed,
|
|
194
193
|
error: `${reportParseError ? `invalid fuzz report payload: ${reportParseError}` : `missing fuzz report payload from ${path.basename(file)}`} (${diagnostics})`,
|
|
@@ -223,7 +222,7 @@ async function runFuzzTarget(file, outDir, duplicateBasenames, config, fuzzerSel
|
|
|
223
222
|
const crash = persistCrashRecord(config.crashDir, {
|
|
224
223
|
kind: "fuzz",
|
|
225
224
|
file,
|
|
226
|
-
entryKey: buildFuzzFailureEntryKey(file, fuzzer.name, modeName ?? "default"),
|
|
225
|
+
entryKey: buildFuzzFailureEntryKey(file, config.input, fuzzer.name, modeName ?? "default"),
|
|
227
226
|
mode: modeName ?? "default",
|
|
228
227
|
seed: firstFailureSeed,
|
|
229
228
|
reproCommand: buildFuzzReproCommand(file, firstFailureSeed, modeName ?? "default", fuzzer.selector, 1),
|
|
@@ -285,11 +284,13 @@ function buildFuzzReproCommand(file, seed, modeName, fuzzer, runs) {
|
|
|
285
284
|
const runsArg = typeof runs == "number" ? ` --runs ${runs}` : "";
|
|
286
285
|
return `ast fuzz ${file}${modeArg}${fuzzerArg} --seed ${seed}${runsArg}`;
|
|
287
286
|
}
|
|
288
|
-
function buildFuzzFailureEntryKey(file, name, modeName) {
|
|
289
|
-
|
|
287
|
+
function buildFuzzFailureEntryKey(file, inputPatterns, name, modeName) {
|
|
288
|
+
const stem = resolveSpecRelativePath(file, inputPatterns).replace(/\.ts$/i, "");
|
|
289
|
+
return `${stem}.${sanitizeEntryName(modeName)}.${sanitizeEntryName(name)}`;
|
|
290
290
|
}
|
|
291
|
-
function buildFuzzCrashEntryKey(file, modeName) {
|
|
292
|
-
|
|
291
|
+
function buildFuzzCrashEntryKey(file, inputPatterns, modeName) {
|
|
292
|
+
const stem = resolveSpecRelativePath(file, inputPatterns).replace(/\.ts$/i, "");
|
|
293
|
+
return `${stem}.${sanitizeEntryName(modeName)}`;
|
|
293
294
|
}
|
|
294
295
|
function sanitizeEntryName(name) {
|
|
295
296
|
return (name
|
|
@@ -398,47 +399,6 @@ function resolveFuzzInputPatterns(configured, selectors) {
|
|
|
398
399
|
}
|
|
399
400
|
return [...patterns];
|
|
400
401
|
}
|
|
401
|
-
function resolveArtifactFileName(file, duplicateBasenames, modeName) {
|
|
402
|
-
const base = path
|
|
403
|
-
.basename(file)
|
|
404
|
-
.replace(/\.spec\.ts$/, "")
|
|
405
|
-
.replace(/\.ts$/, "");
|
|
406
|
-
const legacy = !modeName
|
|
407
|
-
? `${path.basename(file).replace(".ts", ".wasm")}`
|
|
408
|
-
: `${base}.${modeName}.bindings.wasm`;
|
|
409
|
-
if (!duplicateBasenames.has(path.basename(file))) {
|
|
410
|
-
return legacy;
|
|
411
|
-
}
|
|
412
|
-
const disambiguator = resolveDisambiguator(file);
|
|
413
|
-
if (!disambiguator.length) {
|
|
414
|
-
return legacy;
|
|
415
|
-
}
|
|
416
|
-
const ext = path.extname(legacy);
|
|
417
|
-
const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
|
|
418
|
-
return `${stem}.${disambiguator}${ext}`;
|
|
419
|
-
}
|
|
420
|
-
function resolveDuplicateBasenames(files) {
|
|
421
|
-
const counts = new Map();
|
|
422
|
-
for (const file of files) {
|
|
423
|
-
const base = path.basename(file);
|
|
424
|
-
counts.set(base, (counts.get(base) ?? 0) + 1);
|
|
425
|
-
}
|
|
426
|
-
const duplicates = new Set();
|
|
427
|
-
for (const [base, count] of counts) {
|
|
428
|
-
if (count > 1)
|
|
429
|
-
duplicates.add(base);
|
|
430
|
-
}
|
|
431
|
-
return duplicates;
|
|
432
|
-
}
|
|
433
|
-
function resolveDisambiguator(file) {
|
|
434
|
-
const relDir = path.dirname(path.relative(process.cwd(), file));
|
|
435
|
-
if (!relDir.length || relDir == ".")
|
|
436
|
-
return "";
|
|
437
|
-
return relDir
|
|
438
|
-
.replace(/[\\/]+/g, "__")
|
|
439
|
-
.replace(/[^A-Za-z0-9._-]/g, "_")
|
|
440
|
-
.replace(/^_+|_+$/g, "");
|
|
441
|
-
}
|
|
442
402
|
function resolveBindingsHelperPath(wasmPath) {
|
|
443
403
|
const bindingsPath = wasmPath.replace(/\.wasm$/, ".bindings.js");
|
|
444
404
|
if (existsSync(bindingsPath))
|