as-test 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +4 -9
  3. package/assembly/index.ts +10 -15
  4. package/assembly/src/expectation.ts +11 -11
  5. package/assembly/src/fuzz.ts +11 -7
  6. package/assembly/src/log.ts +2 -2
  7. package/assembly/src/suite.ts +5 -5
  8. package/assembly/src/tests.ts +8 -8
  9. package/assembly/util/wipc.ts +5 -1
  10. package/bin/build-worker-pool.js +146 -142
  11. package/bin/build-worker.js +37 -34
  12. package/bin/commands/build-core.js +577 -465
  13. package/bin/commands/build.js +49 -29
  14. package/bin/commands/clean-core.js +120 -113
  15. package/bin/commands/clean.js +14 -8
  16. package/bin/commands/doctor-core.js +288 -289
  17. package/bin/commands/doctor.js +1 -1
  18. package/bin/commands/fuzz-core.js +467 -414
  19. package/bin/commands/fuzz.js +27 -10
  20. package/bin/commands/init-core.js +905 -794
  21. package/bin/commands/init.js +2 -2
  22. package/bin/commands/run-core.js +2675 -2344
  23. package/bin/commands/run.js +43 -25
  24. package/bin/commands/test.js +56 -32
  25. package/bin/commands/web-runner-source.js +1 -1
  26. package/bin/commands/web-session.js +516 -525
  27. package/bin/coverage-points.js +363 -341
  28. package/bin/crash-store.js +56 -66
  29. package/bin/index.js +4092 -3150
  30. package/bin/reporters/default.js +1090 -890
  31. package/bin/reporters/tap.js +319 -325
  32. package/bin/types.js +67 -67
  33. package/bin/util.js +1290 -1239
  34. package/bin/wipc.js +70 -73
  35. package/lib/build/index.d.ts +3 -1
  36. package/lib/build/index.js +1039 -1034
  37. package/lib/build/web-runner/client.js +1 -1
  38. package/lib/build/web-runner/html.js +1 -1
  39. package/lib/build/web-runner/worker.js +1 -1
  40. package/package.json +6 -3
  41. package/transform/lib/coverage.js +4 -4
  42. package/transform/lib/log.js +9 -5
  43. package/assembly/util/json.ts +0 -112
@@ -3,548 +3,660 @@ 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
- import { createMemoryStream, main as ascMain, } from "assemblyscript/dist/asc.js";
7
- import { applyMode, getPkgRunner, loadConfig, tokenizeCommand, resolveProjectModule, } from "../util.js";
6
+ import {
7
+ createMemoryStream,
8
+ main as ascMain,
9
+ } from "assemblyscript/dist/asc.js";
10
+ import {
11
+ applyMode,
12
+ getPkgRunner,
13
+ loadConfig,
14
+ tokenizeCommand,
15
+ resolveProjectModule,
16
+ } from "../util.js";
8
17
  import { persistCrashRecord } from "../crash-store.js";
9
18
  import { BuildWorkerPool } from "../build-worker-pool.js";
10
19
  const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
11
20
  export class BuildFailureError extends Error {
12
- constructor(args) {
13
- super(args.message);
14
- this.name = "BuildFailureError";
15
- this.file = args.file;
16
- this.mode = args.mode;
17
- this.invocation = args.invocation;
18
- this.stdout = args.stdout;
19
- this.stderr = args.stderr;
20
- this.kind = args.kind;
21
- this.crashLogPath = args.crashLogPath;
22
- }
23
- }
24
- export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], modeName, featureToggles = {}, overrides = {}, resolvedConfig) {
25
- const loadedConfig = resolvedConfig ?? loadConfig(configPath, false);
26
- const mode = applyMode(loadedConfig, modeName);
27
- const config = Object.assign(Object.create(Object.getPrototypeOf(mode.config)), mode.config);
28
- config.buildOptions = Object.assign(Object.create(Object.getPrototypeOf(mode.config.buildOptions)), mode.config.buildOptions);
29
- if (overrides.target) {
30
- config.buildOptions.target = overrides.target;
31
- }
32
- if (overrides.args?.length) {
33
- config.buildOptions.args = [...config.buildOptions.args, ...overrides.args];
34
- }
35
- if (!hasCustomBuildCommand(config)) {
36
- ensureDeps(config);
37
- }
38
- const pkgRunner = getPkgRunner();
39
- const inputPatterns = resolveInputPatterns(config.input, selectors);
40
- const inputFiles = (await glob(inputPatterns)).sort((a, b) => a.localeCompare(b));
41
- const duplicateSpecBasenames = resolveDuplicateBasenames(inputFiles);
42
- const coverageEnabled = resolveCoverageEnabled(config.coverage, featureToggles.coverage);
43
- const buildEnv = {
44
- ...mode.env,
45
- ...config.buildOptions.env,
46
- AS_TEST_COVERAGE_ENABLED: coverageEnabled ? "1" : "0",
47
- };
48
- if (!resolvedConfig &&
49
- !process.env.AS_TEST_BUILD_API &&
50
- !hasCustomBuildCommand(config)) {
51
- const pool = getSerialBuildWorkerPool();
52
- for (const file of inputFiles) {
53
- const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
54
- const invocation = getBuildCommand(config, pkgRunner, file, outFile, modeName, featureToggles);
55
- await pool.buildFileMode({
56
- configPath,
57
- file,
58
- modeName,
59
- buildCommand: formatInvocation(invocation),
60
- featureToggles,
61
- overrides,
62
- });
63
- }
64
- return;
65
- }
21
+ constructor(args) {
22
+ super(args.message);
23
+ this.name = "BuildFailureError";
24
+ this.file = args.file;
25
+ this.mode = args.mode;
26
+ this.invocation = args.invocation;
27
+ this.stdout = args.stdout;
28
+ this.stderr = args.stderr;
29
+ this.kind = args.kind;
30
+ this.crashLogPath = args.crashLogPath;
31
+ }
32
+ }
33
+ export async function build(
34
+ configPath = DEFAULT_CONFIG_PATH,
35
+ selectors = [],
36
+ modeName,
37
+ featureToggles = {},
38
+ overrides = {},
39
+ resolvedConfig,
40
+ ) {
41
+ const loadedConfig = resolvedConfig ?? loadConfig(configPath, false);
42
+ const mode = applyMode(loadedConfig, modeName);
43
+ const config = Object.assign(
44
+ Object.create(Object.getPrototypeOf(mode.config)),
45
+ mode.config,
46
+ );
47
+ config.buildOptions = Object.assign(
48
+ Object.create(Object.getPrototypeOf(mode.config.buildOptions)),
49
+ mode.config.buildOptions,
50
+ );
51
+ if (overrides.target) {
52
+ config.buildOptions.target = overrides.target;
53
+ }
54
+ if (overrides.args?.length) {
55
+ config.buildOptions.args = [...config.buildOptions.args, ...overrides.args];
56
+ }
57
+ if (!hasCustomBuildCommand(config)) {
58
+ ensureDeps(config);
59
+ }
60
+ const pkgRunner = getPkgRunner();
61
+ const inputPatterns = resolveInputPatterns(config.input, selectors);
62
+ const inputFiles = (await glob(inputPatterns)).sort((a, b) =>
63
+ a.localeCompare(b),
64
+ );
65
+ const duplicateSpecBasenames = resolveDuplicateBasenames(inputFiles);
66
+ const coverageEnabled = resolveCoverageEnabled(
67
+ config.coverage,
68
+ featureToggles.coverage,
69
+ );
70
+ const buildEnv = {
71
+ ...mode.env,
72
+ ...config.buildOptions.env,
73
+ AS_TEST_COVERAGE_ENABLED: coverageEnabled ? "1" : "0",
74
+ };
75
+ if (
76
+ !resolvedConfig &&
77
+ !process.env.AS_TEST_BUILD_API &&
78
+ !hasCustomBuildCommand(config)
79
+ ) {
80
+ const pool = getSerialBuildWorkerPool();
66
81
  for (const file of inputFiles) {
67
- const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
68
- const invocation = getBuildCommand(config, pkgRunner, file, outFile, modeName, featureToggles);
69
- try {
70
- await buildFile(invocation, buildEnv);
71
- }
72
- catch (error) {
73
- const modeLabel = modeName ?? "default";
74
- const stdout = getBuildStdout(error);
75
- const stderr = getBuildStderr(error);
76
- const buildCommand = formatInvocation(invocation);
77
- const kind = overrides.kind ?? "test";
78
- const crash = persistCrashRecord(config.fuzz.crashDir, {
79
- kind,
80
- stage: "build",
81
- file,
82
- mode: modeLabel,
83
- cwd: process.cwd(),
84
- buildCommand,
85
- reproCommand: buildCommand,
86
- error: stderr || stdout || "unknown build error",
87
- stdout,
88
- stderr,
89
- });
90
- throw new BuildFailureError({
91
- file,
92
- mode: modeLabel,
93
- invocation,
94
- stdout,
95
- stderr,
96
- kind,
97
- crashLogPath: crash.logPath,
98
- message: `Failed to build ${path.basename(file)} in mode ${modeLabel} with ${stderr || stdout || "unknown build error"}\n` +
99
- `Build command: ${buildCommand}\n` +
100
- `Crash log: ${crash.logPath}`,
101
- });
102
- }
103
- }
82
+ const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
83
+ const invocation = getBuildCommand(
84
+ config,
85
+ pkgRunner,
86
+ file,
87
+ outFile,
88
+ modeName,
89
+ featureToggles,
90
+ );
91
+ await pool.buildFileMode({
92
+ configPath,
93
+ file,
94
+ modeName,
95
+ buildCommand: formatInvocation(invocation),
96
+ featureToggles,
97
+ overrides,
98
+ });
99
+ }
100
+ return;
101
+ }
102
+ for (const file of inputFiles) {
103
+ const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
104
+ const invocation = getBuildCommand(
105
+ config,
106
+ pkgRunner,
107
+ file,
108
+ outFile,
109
+ modeName,
110
+ featureToggles,
111
+ );
112
+ try {
113
+ await buildFile(invocation, buildEnv);
114
+ } catch (error) {
115
+ const modeLabel = modeName ?? "default";
116
+ const stdout = getBuildStdout(error);
117
+ const stderr = getBuildStderr(error);
118
+ const buildCommand = formatInvocation(invocation);
119
+ const kind = overrides.kind ?? "test";
120
+ const crash = persistCrashRecord(config.fuzz.crashDir, {
121
+ kind,
122
+ stage: "build",
123
+ file,
124
+ mode: modeLabel,
125
+ cwd: process.cwd(),
126
+ buildCommand,
127
+ reproCommand: buildCommand,
128
+ error: stderr || stdout || "unknown build error",
129
+ stdout,
130
+ stderr,
131
+ });
132
+ throw new BuildFailureError({
133
+ file,
134
+ mode: modeLabel,
135
+ invocation,
136
+ stdout,
137
+ stderr,
138
+ kind,
139
+ crashLogPath: crash.logPath,
140
+ message:
141
+ `Failed to build ${path.basename(file)} in mode ${modeLabel} with ${stderr || stdout || "unknown build error"}\n` +
142
+ `Build command: ${buildCommand}\n` +
143
+ `Crash log: ${crash.logPath}`,
144
+ });
145
+ }
146
+ }
104
147
  }
105
148
  let serialBuildWorkerPool = null;
106
149
  function getSerialBuildWorkerPool() {
107
- if (!serialBuildWorkerPool) {
108
- serialBuildWorkerPool = new BuildWorkerPool(1);
109
- }
110
- return serialBuildWorkerPool;
150
+ if (!serialBuildWorkerPool) {
151
+ serialBuildWorkerPool = new BuildWorkerPool(1);
152
+ }
153
+ return serialBuildWorkerPool;
111
154
  }
112
155
  export async function closeSerialBuildWorkerPool() {
113
- if (!serialBuildWorkerPool)
114
- return;
115
- const pool = serialBuildWorkerPool;
116
- serialBuildWorkerPool = null;
117
- await pool.close();
118
- }
119
- export async function getBuildInvocationPreview(configPath = DEFAULT_CONFIG_PATH, file, modeName, featureToggles = {}, overrides = {}) {
120
- const loadedConfig = loadConfig(configPath, false);
121
- const mode = applyMode(loadedConfig, modeName);
122
- const config = Object.assign(Object.create(Object.getPrototypeOf(mode.config)), mode.config);
123
- config.buildOptions = Object.assign(Object.create(Object.getPrototypeOf(mode.config.buildOptions)), mode.config.buildOptions);
124
- if (overrides.target) {
125
- config.buildOptions.target = overrides.target;
126
- }
127
- if (overrides.args?.length) {
128
- config.buildOptions.args = [...config.buildOptions.args, ...overrides.args];
129
- }
130
- const duplicateSpecBasenames = resolveDuplicateBasenames([file]);
131
- const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
132
- return getBuildCommand(config, getPkgRunner(), file, outFile, modeName, featureToggles);
133
- }
134
- export async function getBuildReuseInfo(configPath = DEFAULT_CONFIG_PATH, file, modeName, featureToggles = {}, overrides = {}) {
135
- const loadedConfig = loadConfig(configPath, false);
136
- const mode = applyMode(loadedConfig, modeName);
137
- const config = Object.assign(Object.create(Object.getPrototypeOf(mode.config)), mode.config);
138
- config.buildOptions = Object.assign(Object.create(Object.getPrototypeOf(mode.config.buildOptions)), mode.config.buildOptions);
139
- if (overrides.target) {
140
- config.buildOptions.target = overrides.target;
141
- }
142
- if (overrides.args?.length) {
143
- config.buildOptions.args = [...config.buildOptions.args, ...overrides.args];
144
- }
145
- if (hasCustomBuildCommand(config)) {
146
- return null;
147
- }
148
- const duplicateSpecBasenames = resolveDuplicateBasenames([file]);
149
- const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
150
- const invocation = getBuildCommand(config, getPkgRunner(), file, outFile, modeName, featureToggles);
151
- const coverageEnabled = resolveCoverageEnabled(config.coverage, featureToggles.coverage);
152
- const buildEnv = {
153
- ...mode.env,
154
- ...config.buildOptions.env,
155
- AS_TEST_COVERAGE_ENABLED: coverageEnabled ? "1" : "0",
156
- };
157
- return {
158
- signature: JSON.stringify({
159
- command: invocation.command,
160
- args: stripOutputArgs(invocation.args),
161
- apiArgs: invocation.apiArgs ? stripOutputArgs(invocation.apiArgs) : [],
162
- env: sortRecord(buildEnv),
163
- }),
164
- outFile,
165
- };
156
+ if (!serialBuildWorkerPool) return;
157
+ const pool = serialBuildWorkerPool;
158
+ serialBuildWorkerPool = null;
159
+ await pool.close();
160
+ }
161
+ export async function getBuildInvocationPreview(
162
+ configPath = DEFAULT_CONFIG_PATH,
163
+ file,
164
+ modeName,
165
+ featureToggles = {},
166
+ overrides = {},
167
+ ) {
168
+ const loadedConfig = loadConfig(configPath, false);
169
+ const mode = applyMode(loadedConfig, modeName);
170
+ const config = Object.assign(
171
+ Object.create(Object.getPrototypeOf(mode.config)),
172
+ mode.config,
173
+ );
174
+ config.buildOptions = Object.assign(
175
+ Object.create(Object.getPrototypeOf(mode.config.buildOptions)),
176
+ mode.config.buildOptions,
177
+ );
178
+ if (overrides.target) {
179
+ config.buildOptions.target = overrides.target;
180
+ }
181
+ if (overrides.args?.length) {
182
+ config.buildOptions.args = [...config.buildOptions.args, ...overrides.args];
183
+ }
184
+ const duplicateSpecBasenames = resolveDuplicateBasenames([file]);
185
+ const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
186
+ return getBuildCommand(
187
+ config,
188
+ getPkgRunner(),
189
+ file,
190
+ outFile,
191
+ modeName,
192
+ featureToggles,
193
+ );
194
+ }
195
+ export async function getBuildReuseInfo(
196
+ configPath = DEFAULT_CONFIG_PATH,
197
+ file,
198
+ modeName,
199
+ featureToggles = {},
200
+ overrides = {},
201
+ ) {
202
+ const loadedConfig = loadConfig(configPath, false);
203
+ const mode = applyMode(loadedConfig, modeName);
204
+ const config = Object.assign(
205
+ Object.create(Object.getPrototypeOf(mode.config)),
206
+ mode.config,
207
+ );
208
+ config.buildOptions = Object.assign(
209
+ Object.create(Object.getPrototypeOf(mode.config.buildOptions)),
210
+ mode.config.buildOptions,
211
+ );
212
+ if (overrides.target) {
213
+ config.buildOptions.target = overrides.target;
214
+ }
215
+ if (overrides.args?.length) {
216
+ config.buildOptions.args = [...config.buildOptions.args, ...overrides.args];
217
+ }
218
+ if (hasCustomBuildCommand(config)) {
219
+ return null;
220
+ }
221
+ const duplicateSpecBasenames = resolveDuplicateBasenames([file]);
222
+ const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
223
+ const invocation = getBuildCommand(
224
+ config,
225
+ getPkgRunner(),
226
+ file,
227
+ outFile,
228
+ modeName,
229
+ featureToggles,
230
+ );
231
+ const coverageEnabled = resolveCoverageEnabled(
232
+ config.coverage,
233
+ featureToggles.coverage,
234
+ );
235
+ const buildEnv = {
236
+ ...mode.env,
237
+ ...config.buildOptions.env,
238
+ AS_TEST_COVERAGE_ENABLED: coverageEnabled ? "1" : "0",
239
+ };
240
+ return {
241
+ signature: JSON.stringify({
242
+ command: invocation.command,
243
+ args: stripOutputArgs(invocation.args),
244
+ apiArgs: invocation.apiArgs ? stripOutputArgs(invocation.apiArgs) : [],
245
+ env: sortRecord(buildEnv),
246
+ }),
247
+ outFile,
248
+ };
166
249
  }
167
250
  function hasCustomBuildCommand(config) {
168
- return !!config.buildOptions.cmd.trim().length;
169
- }
170
- function getBuildCommand(config, pkgRunner, file, outFile, modeName, featureToggles = {}) {
171
- const userArgs = getUserBuildArgs(config);
172
- if (hasCustomBuildCommand(config)) {
173
- const tokens = tokenizeCommand(expandBuildCommand(config.buildOptions.cmd, file, outFile, config.buildOptions.target, modeName));
174
- if (!tokens.length) {
175
- throw new Error("custom build command is empty");
176
- }
177
- return {
178
- command: tokens[0],
179
- args: [...tokens.slice(1), ...userArgs],
180
- };
181
- }
182
- const defaultArgs = getDefaultBuildArgs(config, featureToggles);
183
- const ascInvocation = resolveAscInvocation(pkgRunner);
184
- const args = [...ascInvocation.args, file, ...userArgs, ...defaultArgs];
185
- if (config.outDir.length) {
186
- args.push("-o", outFile);
251
+ return !!config.buildOptions.cmd.trim().length;
252
+ }
253
+ function getBuildCommand(
254
+ config,
255
+ pkgRunner,
256
+ file,
257
+ outFile,
258
+ modeName,
259
+ featureToggles = {},
260
+ ) {
261
+ const userArgs = getUserBuildArgs(config);
262
+ if (hasCustomBuildCommand(config)) {
263
+ const tokens = tokenizeCommand(
264
+ expandBuildCommand(
265
+ config.buildOptions.cmd,
266
+ file,
267
+ outFile,
268
+ config.buildOptions.target,
269
+ modeName,
270
+ ),
271
+ );
272
+ if (!tokens.length) {
273
+ throw new Error("custom build command is empty");
187
274
  }
188
275
  return {
189
- command: ascInvocation.command,
190
- args,
191
- apiArgs: args.slice(1),
276
+ command: tokens[0],
277
+ args: [...tokens.slice(1), ...userArgs],
192
278
  };
279
+ }
280
+ const defaultArgs = getDefaultBuildArgs(config, featureToggles);
281
+ const ascInvocation = resolveAscInvocation(pkgRunner);
282
+ const args = [...ascInvocation.args, file, ...userArgs, ...defaultArgs];
283
+ if (config.outDir.length) {
284
+ args.push("-o", outFile);
285
+ }
286
+ return {
287
+ command: ascInvocation.command,
288
+ args,
289
+ apiArgs: args.slice(1),
290
+ };
193
291
  }
194
292
  function resolveAscInvocation(pkgRunner) {
195
- const assemblyscriptPkg = resolveProjectModule("assemblyscript/package.json");
196
- if (assemblyscriptPkg) {
197
- const ascPath = path.join(path.dirname(assemblyscriptPkg), "bin", "asc.js");
198
- if (existsSync(ascPath)) {
199
- return {
200
- command: process.execPath,
201
- args: [ascPath],
202
- };
203
- }
204
- }
205
- return {
206
- command: pkgRunner,
207
- args: ["asc"],
208
- };
293
+ const assemblyscriptPkg = resolveProjectModule("assemblyscript/package.json");
294
+ if (assemblyscriptPkg) {
295
+ const ascPath = path.join(path.dirname(assemblyscriptPkg), "bin", "asc.js");
296
+ if (existsSync(ascPath)) {
297
+ return {
298
+ command: process.execPath,
299
+ args: [ascPath],
300
+ };
301
+ }
302
+ }
303
+ return {
304
+ command: pkgRunner,
305
+ args: ["asc"],
306
+ };
209
307
  }
210
308
  function getUserBuildArgs(config) {
211
- const args = [];
212
- for (const value of config.buildOptions.args) {
213
- if (!value.length)
214
- continue;
215
- args.push(...tokenizeCommand(value));
216
- }
217
- return args;
309
+ const args = [];
310
+ for (const value of config.buildOptions.args) {
311
+ if (!value.length) continue;
312
+ args.push(...tokenizeCommand(value));
313
+ }
314
+ return args;
218
315
  }
219
316
  function stripOutputArgs(args) {
220
- const out = [];
221
- for (let i = 0; i < args.length; i++) {
222
- const arg = args[i];
223
- if (arg == "-o" || arg == "--outFile") {
224
- i++;
225
- continue;
226
- }
227
- out.push(arg);
317
+ const out = [];
318
+ for (let i = 0; i < args.length; i++) {
319
+ const arg = args[i];
320
+ if (arg == "-o" || arg == "--outFile") {
321
+ i++;
322
+ continue;
228
323
  }
229
- return out;
324
+ out.push(arg);
325
+ }
326
+ return out;
230
327
  }
231
328
  function sortRecord(record) {
232
- return Object.fromEntries(Object.entries(record)
233
- .filter((entry) => typeof entry[1] == "string")
234
- .sort((a, b) => a[0].localeCompare(b[0])));
329
+ return Object.fromEntries(
330
+ Object.entries(record)
331
+ .filter((entry) => typeof entry[1] == "string")
332
+ .sort((a, b) => a[0].localeCompare(b[0])),
333
+ );
235
334
  }
236
335
  function expandBuildCommand(template, file, outFile, target, modeName) {
237
- const name = path
238
- .basename(file)
239
- .replace(/\.spec\.ts$/, "")
240
- .replace(/\.ts$/, "");
241
- return template
242
- .replace(/<file>/g, file)
243
- .replace(/<name>/g, name)
244
- .replace(/<outFile>/g, outFile)
245
- .replace(/<target>/g, target)
246
- .replace(/<mode>/g, modeName ?? "");
247
- }
248
- function resolveArtifactFileName(file, target, modeName, duplicateSpecBasenames = new Set()) {
249
- const base = path
250
- .basename(file)
251
- .replace(/\.spec\.ts$/, "")
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}`;
336
+ const name = path
337
+ .basename(file)
338
+ .replace(/\.spec\.ts$/, "")
339
+ .replace(/\.ts$/, "");
340
+ return template
341
+ .replace(/<file>/g, file)
342
+ .replace(/<name>/g, name)
343
+ .replace(/<outFile>/g, outFile)
344
+ .replace(/<target>/g, target)
345
+ .replace(/<mode>/g, modeName ?? "");
346
+ }
347
+ function resolveArtifactFileName(
348
+ file,
349
+ target,
350
+ modeName,
351
+ duplicateSpecBasenames = new Set(),
352
+ ) {
353
+ const base = path
354
+ .basename(file)
355
+ .replace(/\.spec\.ts$/, "")
356
+ .replace(/\.ts$/, "");
357
+ const legacy = !modeName
358
+ ? `${path.basename(file).replace(".ts", ".wasm")}`
359
+ : `${base}.${modeName}.${target}.wasm`;
360
+ if (!duplicateSpecBasenames.has(path.basename(file))) {
361
+ return legacy;
362
+ }
363
+ const disambiguator = resolveDisambiguator(file);
364
+ if (!disambiguator.length) {
365
+ return legacy;
366
+ }
367
+ const ext = path.extname(legacy);
368
+ const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
369
+ return `${stem}.${disambiguator}${ext}`;
266
370
  }
267
371
  function resolveDuplicateBasenames(files) {
268
- const counts = new Map();
269
- for (const file of files) {
270
- const base = path.basename(file);
271
- counts.set(base, (counts.get(base) ?? 0) + 1);
272
- }
273
- const duplicates = new Set();
274
- for (const [base, count] of counts) {
275
- if (count > 1)
276
- duplicates.add(base);
277
- }
278
- return duplicates;
372
+ const counts = new Map();
373
+ for (const file of files) {
374
+ const base = path.basename(file);
375
+ counts.set(base, (counts.get(base) ?? 0) + 1);
376
+ }
377
+ const duplicates = new Set();
378
+ for (const [base, count] of counts) {
379
+ if (count > 1) duplicates.add(base);
380
+ }
381
+ return duplicates;
279
382
  }
280
383
  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, "");
384
+ const relDir = path.dirname(path.relative(process.cwd(), file));
385
+ if (!relDir.length || relDir == ".") return "";
386
+ return relDir
387
+ .replace(/[\\/]+/g, "__")
388
+ .replace(/[^A-Za-z0-9._-]/g, "_")
389
+ .replace(/^_+|_+$/g, "");
288
390
  }
289
391
  function resolveInputPatterns(configured, selectors) {
290
- const configuredInputs = Array.isArray(configured)
291
- ? configured
292
- : [configured];
293
- if (!selectors.length)
294
- return configuredInputs;
295
- const patterns = new Set();
296
- for (const selector of expandSelectors(selectors)) {
297
- if (!selector)
298
- continue;
299
- if (isBareSuiteSelector(selector)) {
300
- const base = stripSuiteSuffix(selector);
301
- for (const configuredInput of configuredInputs) {
302
- patterns.add(path.join(path.dirname(configuredInput), `${base}.spec.ts`));
303
- }
304
- continue;
305
- }
306
- patterns.add(selector);
307
- }
308
- return [...patterns];
392
+ const configuredInputs = Array.isArray(configured)
393
+ ? configured
394
+ : [configured];
395
+ if (!selectors.length) return configuredInputs;
396
+ const patterns = new Set();
397
+ for (const selector of expandSelectors(selectors)) {
398
+ if (!selector) continue;
399
+ if (isBareSuiteSelector(selector)) {
400
+ const base = stripSuiteSuffix(selector);
401
+ for (const configuredInput of configuredInputs) {
402
+ patterns.add(
403
+ path.join(path.dirname(configuredInput), `${base}.spec.ts`),
404
+ );
405
+ }
406
+ continue;
407
+ }
408
+ patterns.add(selector);
409
+ }
410
+ return [...patterns];
309
411
  }
310
412
  function expandSelectors(selectors) {
311
- const expanded = [];
312
- for (const selector of selectors) {
313
- if (!selector)
314
- continue;
315
- if (!shouldSplitSelector(selector)) {
316
- expanded.push(selector);
317
- continue;
318
- }
319
- for (const token of selector.split(",")) {
320
- const trimmed = token.trim();
321
- if (!trimmed.length)
322
- continue;
323
- expanded.push(trimmed);
324
- }
413
+ const expanded = [];
414
+ for (const selector of selectors) {
415
+ if (!selector) continue;
416
+ if (!shouldSplitSelector(selector)) {
417
+ expanded.push(selector);
418
+ continue;
419
+ }
420
+ for (const token of selector.split(",")) {
421
+ const trimmed = token.trim();
422
+ if (!trimmed.length) continue;
423
+ expanded.push(trimmed);
325
424
  }
326
- return expanded;
425
+ }
426
+ return expanded;
327
427
  }
328
428
  function shouldSplitSelector(selector) {
329
- return (selector.includes(",") &&
330
- !selector.includes("/") &&
331
- !selector.includes("\\") &&
332
- !/[*?[\]{}]/.test(selector));
429
+ return (
430
+ selector.includes(",") &&
431
+ !selector.includes("/") &&
432
+ !selector.includes("\\") &&
433
+ !/[*?[\]{}]/.test(selector)
434
+ );
333
435
  }
334
436
  function isBareSuiteSelector(selector) {
335
- return (!selector.includes("/") &&
336
- !selector.includes("\\") &&
337
- !/[*?[\]{}]/.test(selector));
437
+ return (
438
+ !selector.includes("/") &&
439
+ !selector.includes("\\") &&
440
+ !/[*?[\]{}]/.test(selector)
441
+ );
338
442
  }
339
443
  function stripSuiteSuffix(selector) {
340
- return selector.replace(/\.spec\.ts$/, "").replace(/\.ts$/, "");
444
+ return selector.replace(/\.spec\.ts$/, "").replace(/\.ts$/, "");
341
445
  }
342
446
  function ensureDeps(config) {
343
- if (config.buildOptions.target == "wasi") {
344
- if (!resolveWasiShim()) {
345
- console.log(`${chalk.bgRed(" ERROR ")}${chalk.dim(":")} could not find @assemblyscript/wasi-shim! Add it to your dependencies to run with WASI!`);
346
- process.exit(1);
347
- }
447
+ if (config.buildOptions.target == "wasi") {
448
+ if (!resolveWasiShim()) {
449
+ console.log(
450
+ `${chalk.bgRed(" ERROR ")}${chalk.dim(":")} could not find @assemblyscript/wasi-shim! Add it to your dependencies to run with WASI!`,
451
+ );
452
+ process.exit(1);
348
453
  }
454
+ }
349
455
  }
350
456
  async function buildFile(invocation, env) {
351
- if (process.env.AS_TEST_BUILD_API == "1" && invocation.apiArgs?.length) {
352
- await buildFileViaApi(invocation.apiArgs, env);
353
- return;
354
- }
355
- await buildFileViaSpawn(invocation, env);
457
+ if (process.env.AS_TEST_BUILD_API == "1" && invocation.apiArgs?.length) {
458
+ await buildFileViaApi(invocation.apiArgs, env);
459
+ return;
460
+ }
461
+ await buildFileViaSpawn(invocation, env);
356
462
  }
357
463
  async function buildFileViaApi(args, env) {
358
- const stdoutChunks = [];
359
- const stderrChunks = [];
360
- const stdout = createMemoryStream((chunk) => {
361
- stdoutChunks.push(typeof chunk == "string" ? chunk : Buffer.from(chunk).toString("utf8"));
362
- });
363
- const stderr = createMemoryStream((chunk) => {
364
- stderrChunks.push(typeof chunk == "string" ? chunk : Buffer.from(chunk).toString("utf8"));
365
- });
366
- const previousEnv = snapshotEnv();
367
- applyEnv(env);
368
- try {
369
- const result = await ascMain(args, { stdout, stderr });
370
- if (result.error) {
371
- const error = result.error;
372
- error.stderr = stderrChunks.join("").trim();
373
- error.stdout = stdoutChunks.join("").trim();
374
- throw error;
375
- }
376
- }
377
- finally {
378
- restoreEnv(previousEnv);
379
- }
464
+ const stdoutChunks = [];
465
+ const stderrChunks = [];
466
+ const stdout = createMemoryStream((chunk) => {
467
+ stdoutChunks.push(
468
+ typeof chunk == "string" ? chunk : Buffer.from(chunk).toString("utf8"),
469
+ );
470
+ });
471
+ const stderr = createMemoryStream((chunk) => {
472
+ stderrChunks.push(
473
+ typeof chunk == "string" ? chunk : Buffer.from(chunk).toString("utf8"),
474
+ );
475
+ });
476
+ const previousEnv = snapshotEnv();
477
+ applyEnv(env);
478
+ try {
479
+ const result = await ascMain(args, { stdout, stderr });
480
+ if (result.error) {
481
+ const error = result.error;
482
+ error.stderr = stderrChunks.join("").trim();
483
+ error.stdout = stdoutChunks.join("").trim();
484
+ throw error;
485
+ }
486
+ } finally {
487
+ restoreEnv(previousEnv);
488
+ }
380
489
  }
381
490
  async function buildFileViaSpawn(invocation, env) {
382
- await new Promise((resolve, reject) => {
383
- const child = spawn(invocation.command, invocation.args, {
384
- stdio: ["ignore", "pipe", "pipe"],
385
- env,
386
- shell: false,
387
- });
388
- let stdout = "";
389
- let stderr = "";
390
- child.stdout?.on("data", (chunk) => {
391
- stdout += chunk.toString();
392
- });
393
- child.stderr?.on("data", (chunk) => {
394
- stderr += chunk.toString();
395
- });
396
- child.on("error", reject);
397
- child.on("close", (code) => {
398
- if (code === 0) {
399
- resolve();
400
- return;
401
- }
402
- const error = new Error(stderr.trim() || stdout.trim() || `command exited with code ${code}`);
403
- error.stderr = stderr.trim();
404
- error.stdout = stdout.trim();
405
- reject(error);
406
- });
491
+ await new Promise((resolve, reject) => {
492
+ const child = spawn(invocation.command, invocation.args, {
493
+ stdio: ["ignore", "pipe", "pipe"],
494
+ env,
495
+ shell: false,
496
+ });
497
+ let stdout = "";
498
+ let stderr = "";
499
+ child.stdout?.on("data", (chunk) => {
500
+ stdout += chunk.toString();
501
+ });
502
+ child.stderr?.on("data", (chunk) => {
503
+ stderr += chunk.toString();
407
504
  });
505
+ child.on("error", reject);
506
+ child.on("close", (code) => {
507
+ if (code === 0) {
508
+ resolve();
509
+ return;
510
+ }
511
+ const error = new Error(
512
+ stderr.trim() || stdout.trim() || `command exited with code ${code}`,
513
+ );
514
+ error.stderr = stderr.trim();
515
+ error.stdout = stdout.trim();
516
+ reject(error);
517
+ });
518
+ });
408
519
  }
409
520
  function snapshotEnv() {
410
- return { ...process.env };
521
+ return { ...process.env };
411
522
  }
412
523
  function applyEnv(nextEnv) {
413
- const keys = new Set([
414
- ...Object.keys(process.env),
415
- ...Object.keys(nextEnv),
416
- ]);
417
- for (const key of keys) {
418
- const value = nextEnv[key];
419
- if (value == undefined) {
420
- delete process.env[key];
421
- }
422
- else {
423
- process.env[key] = value;
424
- }
524
+ const keys = new Set([...Object.keys(process.env), ...Object.keys(nextEnv)]);
525
+ for (const key of keys) {
526
+ const value = nextEnv[key];
527
+ if (value == undefined) {
528
+ delete process.env[key];
529
+ } else {
530
+ process.env[key] = value;
425
531
  }
532
+ }
426
533
  }
427
534
  function restoreEnv(previousEnv) {
428
- const keys = new Set([
429
- ...Object.keys(process.env),
430
- ...Object.keys(previousEnv),
431
- ]);
432
- for (const key of keys) {
433
- const value = previousEnv[key];
434
- if (value == undefined) {
435
- delete process.env[key];
436
- }
437
- else {
438
- process.env[key] = value;
439
- }
440
- }
535
+ const keys = new Set([
536
+ ...Object.keys(process.env),
537
+ ...Object.keys(previousEnv),
538
+ ]);
539
+ for (const key of keys) {
540
+ const value = previousEnv[key];
541
+ if (value == undefined) {
542
+ delete process.env[key];
543
+ } else {
544
+ process.env[key] = value;
545
+ }
546
+ }
441
547
  }
442
548
  function formatInvocation(invocation) {
443
- return [invocation.command, ...invocation.args]
444
- .map((token) => (/\s/.test(token) ? JSON.stringify(token) : token))
445
- .join(" ");
549
+ return [invocation.command, ...invocation.args]
550
+ .map((token) => (/\s/.test(token) ? JSON.stringify(token) : token))
551
+ .join(" ");
446
552
  }
447
553
  export { getBuildCommand, formatInvocation };
448
554
  function getBuildStderr(error) {
449
- const err = error;
450
- const stderr = err?.stderr;
451
- if (typeof stderr == "string") {
452
- const trimmed = stderr.trim();
453
- if (trimmed.length)
454
- return trimmed;
455
- }
456
- else if (stderr instanceof Buffer) {
457
- const trimmed = stderr.toString("utf8").trim();
458
- if (trimmed.length)
459
- return trimmed;
460
- }
461
- const message = typeof err?.message == "string" ? err.message.trim() : "";
462
- return message || "unknown error";
555
+ const err = error;
556
+ const stderr = err?.stderr;
557
+ if (typeof stderr == "string") {
558
+ const trimmed = stderr.trim();
559
+ if (trimmed.length) return trimmed;
560
+ } else if (stderr instanceof Buffer) {
561
+ const trimmed = stderr.toString("utf8").trim();
562
+ if (trimmed.length) return trimmed;
563
+ }
564
+ const message = typeof err?.message == "string" ? err.message.trim() : "";
565
+ return message || "unknown error";
463
566
  }
464
567
  function getBuildStdout(error) {
465
- const err = error;
466
- const stdout = err?.stdout;
467
- if (typeof stdout == "string")
468
- return stdout.trim();
469
- if (stdout instanceof Buffer)
470
- return stdout.toString("utf8").trim();
471
- return "";
568
+ const err = error;
569
+ const stdout = err?.stdout;
570
+ if (typeof stdout == "string") return stdout.trim();
571
+ if (stdout instanceof Buffer) return stdout.toString("utf8").trim();
572
+ return "";
472
573
  }
473
574
  function getDefaultBuildArgs(config, featureToggles) {
474
- const buildArgs = [];
475
- const tryAsEnabled = resolveTryAsEnabled(featureToggles.tryAs);
476
- buildArgs.push("--transform", "as-test/transform");
477
- if (tryAsEnabled) {
478
- buildArgs.push("--transform", "try-as/transform");
479
- }
480
- if (config.config && config.config !== "none") {
481
- buildArgs.push("--config", config.config);
482
- }
483
- if (tryAsEnabled) {
484
- buildArgs.push("--use", "AS_TEST_TRY_AS=1");
485
- }
486
- // Should also strip any bindings-enabling from asconfig
487
- if (config.buildOptions.target == "bindings" ||
488
- config.buildOptions.target == "web") {
489
- buildArgs.push("--use", "AS_TEST_BINDINGS=1", "--bindings", "raw", "--exportRuntime", "--exportStart", "_start");
490
- }
491
- else if (config.buildOptions.target == "wasi") {
492
- const wasiShim = resolveWasiShim();
493
- if (!wasiShim) {
494
- throw new Error('WASI target requires package "@assemblyscript/wasi-shim"');
495
- }
496
- buildArgs.push("--use", "AS_TEST_WASI=1", "--config", wasiShim.configPath);
497
- }
498
- else {
499
- console.log(`${chalk.bgRed(" ERROR ")}${chalk.dim(":")} could not determine target in config! Set target to 'bindings', 'web', or 'wasi'`);
500
- process.exit(1);
501
- }
502
- return buildArgs;
575
+ const buildArgs = [];
576
+ const tryAsEnabled = resolveTryAsEnabled(featureToggles.tryAs);
577
+ buildArgs.push("--transform", "as-test/transform");
578
+ const jsonAsTransform = resolveProjectModule("json-as/transform");
579
+ if (jsonAsTransform) {
580
+ buildArgs.push("--transform", jsonAsTransform);
581
+ }
582
+ if (tryAsEnabled) {
583
+ buildArgs.push("--transform", "try-as/transform");
584
+ }
585
+ if (config.config && config.config !== "none") {
586
+ buildArgs.push("--config", config.config);
587
+ }
588
+ if (tryAsEnabled) {
589
+ buildArgs.push("--use", "AS_TEST_TRY_AS=1");
590
+ }
591
+ // Should also strip any bindings-enabling from asconfig
592
+ if (
593
+ config.buildOptions.target == "bindings" ||
594
+ config.buildOptions.target == "web"
595
+ ) {
596
+ buildArgs.push(
597
+ "--use",
598
+ "AS_TEST_BINDINGS=1",
599
+ "--bindings",
600
+ "raw",
601
+ "--exportRuntime",
602
+ "--exportStart",
603
+ "_start",
604
+ );
605
+ } else if (config.buildOptions.target == "wasi") {
606
+ const wasiShim = resolveWasiShim();
607
+ if (!wasiShim) {
608
+ throw new Error(
609
+ 'WASI target requires package "@assemblyscript/wasi-shim"',
610
+ );
611
+ }
612
+ buildArgs.push("--use", "AS_TEST_WASI=1", "--config", wasiShim.configPath);
613
+ } else {
614
+ console.log(
615
+ `${chalk.bgRed(" ERROR ")}${chalk.dim(":")} could not determine target in config! Set target to 'bindings', 'web', or 'wasi'`,
616
+ );
617
+ process.exit(1);
618
+ }
619
+ return buildArgs;
503
620
  }
504
621
  function resolveTryAsEnabled(override) {
505
- const installed = hasTryAsRuntime();
506
- if (override === false)
507
- return false;
508
- if (override === true && !installed) {
509
- throw new Error('try-as feature was enabled, but package "try-as" is not installed');
510
- }
511
- return false;
622
+ const installed = hasTryAsRuntime();
623
+ if (override === false) return false;
624
+ if (override === true && !installed) {
625
+ throw new Error(
626
+ 'try-as feature was enabled, but package "try-as" is not installed',
627
+ );
628
+ }
629
+ return false;
512
630
  }
513
631
  function resolveCoverageEnabled(rawCoverage, override) {
514
- if (override != undefined)
515
- return override;
516
- if (typeof rawCoverage == "boolean")
517
- return rawCoverage;
518
- if (rawCoverage && typeof rawCoverage == "object") {
519
- const enabled = rawCoverage.enabled;
520
- if (typeof enabled == "boolean")
521
- return enabled;
522
- }
523
- return true;
632
+ if (override != undefined) return override;
633
+ if (typeof rawCoverage == "boolean") return rawCoverage;
634
+ if (rawCoverage && typeof rawCoverage == "object") {
635
+ const enabled = rawCoverage.enabled;
636
+ if (typeof enabled == "boolean") return enabled;
637
+ }
638
+ return true;
524
639
  }
525
640
  function hasTryAsRuntime() {
526
- return resolveProjectModule("try-as/package.json") != null;
641
+ return resolveProjectModule("try-as/package.json") != null;
527
642
  }
528
643
  function resolveWasiShim() {
529
- const resolved = resolveProjectModule("@assemblyscript/wasi-shim/asconfig.json");
530
- if (!resolved)
531
- return null;
532
- if (!existsSync(resolved))
533
- return null;
534
- const relative = path.relative(process.cwd(), resolved).replace(/\\/g, "/");
535
- return {
536
- configPath: normalizeCliPath(relative),
537
- };
644
+ const resolved = resolveProjectModule(
645
+ "@assemblyscript/wasi-shim/asconfig.json",
646
+ );
647
+ if (!resolved) return null;
648
+ if (!existsSync(resolved)) return null;
649
+ const relative = path.relative(process.cwd(), resolved).replace(/\\/g, "/");
650
+ return {
651
+ configPath: normalizeCliPath(relative),
652
+ };
538
653
  }
539
654
  function quoteCliArg(value) {
540
- if (!/[\s"]/g.test(value))
541
- return value;
542
- return `"${value.replace(/"/g, '\\"')}"`;
655
+ if (!/[\s"]/g.test(value)) return value;
656
+ return `"${value.replace(/"/g, '\\"')}"`;
543
657
  }
544
658
  function normalizeCliPath(value) {
545
- if (!value.length)
546
- return ".";
547
- if (value.startsWith(".") || value.startsWith("/"))
548
- return value;
549
- return "./" + value;
659
+ if (!value.length) return ".";
660
+ if (value.startsWith(".") || value.startsWith("/")) return value;
661
+ return "./" + value;
550
662
  }