as-test 1.1.6 → 1.1.8
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 +17 -0
- package/README.md +4 -9
- package/assembly/index.ts +10 -15
- package/assembly/src/expectation.ts +11 -11
- package/assembly/src/fuzz.ts +11 -7
- package/assembly/src/log.ts +2 -2
- package/assembly/src/suite.ts +5 -5
- package/assembly/src/tests.ts +8 -8
- package/assembly/util/wipc.ts +5 -1
- package/bin/build-worker-pool.js +146 -142
- package/bin/build-worker.js +37 -34
- package/bin/commands/build-core.js +577 -465
- package/bin/commands/build.js +49 -29
- package/bin/commands/clean-core.js +120 -113
- package/bin/commands/clean.js +14 -8
- package/bin/commands/doctor-core.js +288 -289
- package/bin/commands/doctor.js +1 -1
- package/bin/commands/fuzz-core.js +467 -414
- package/bin/commands/fuzz.js +27 -10
- package/bin/commands/init-core.js +908 -794
- package/bin/commands/init.js +2 -2
- package/bin/commands/run-core.js +2675 -2344
- package/bin/commands/run.js +43 -25
- package/bin/commands/test.js +56 -32
- package/bin/commands/web-runner-source.js +1 -1
- package/bin/commands/web-session.js +516 -525
- package/bin/coverage-points.js +363 -341
- package/bin/crash-store.js +56 -66
- package/bin/index.js +4092 -3150
- package/bin/reporters/default.js +1090 -890
- package/bin/reporters/tap.js +319 -325
- package/bin/types.js +67 -67
- package/bin/util.js +1290 -1239
- package/bin/wipc.js +70 -73
- package/lib/build/index.d.ts +3 -1
- package/lib/build/index.js +1039 -1034
- package/lib/build/web-runner/client.js +1 -1
- package/lib/build/web-runner/html.js +1 -1
- package/lib/build/web-runner/worker.js +1 -1
- package/package.json +6 -3
- package/transform/lib/log.js +9 -5
- 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 {
|
|
7
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
export async function build(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
150
|
+
if (!serialBuildWorkerPool) {
|
|
151
|
+
serialBuildWorkerPool = new BuildWorkerPool(1);
|
|
152
|
+
}
|
|
153
|
+
return serialBuildWorkerPool;
|
|
111
154
|
}
|
|
112
155
|
export async function closeSerialBuildWorkerPool() {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
169
|
-
}
|
|
170
|
-
function getBuildCommand(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
324
|
+
out.push(arg);
|
|
325
|
+
}
|
|
326
|
+
return out;
|
|
230
327
|
}
|
|
231
328
|
function sortRecord(record) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
function resolveArtifactFileName(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
425
|
+
}
|
|
426
|
+
return expanded;
|
|
327
427
|
}
|
|
328
428
|
function shouldSplitSelector(selector) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
429
|
+
return (
|
|
430
|
+
selector.includes(",") &&
|
|
431
|
+
!selector.includes("/") &&
|
|
432
|
+
!selector.includes("\\") &&
|
|
433
|
+
!/[*?[\]{}]/.test(selector)
|
|
434
|
+
);
|
|
333
435
|
}
|
|
334
436
|
function isBareSuiteSelector(selector) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
437
|
+
return (
|
|
438
|
+
!selector.includes("/") &&
|
|
439
|
+
!selector.includes("\\") &&
|
|
440
|
+
!/[*?[\]{}]/.test(selector)
|
|
441
|
+
);
|
|
338
442
|
}
|
|
339
443
|
function stripSuiteSuffix(selector) {
|
|
340
|
-
|
|
444
|
+
return selector.replace(/\.spec\.ts$/, "").replace(/\.ts$/, "");
|
|
341
445
|
}
|
|
342
446
|
function ensureDeps(config) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
521
|
+
return { ...process.env };
|
|
411
522
|
}
|
|
412
523
|
function applyEnv(nextEnv) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (
|
|
519
|
-
|
|
520
|
-
|
|
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
|
-
|
|
641
|
+
return resolveProjectModule("try-as/package.json") != null;
|
|
527
642
|
}
|
|
528
643
|
function resolveWasiShim() {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
|
|
541
|
-
|
|
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
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
return value;
|
|
549
|
-
return "./" + value;
|
|
659
|
+
if (!value.length) return ".";
|
|
660
|
+
if (value.startsWith(".") || value.startsWith("/")) return value;
|
|
661
|
+
return "./" + value;
|
|
550
662
|
}
|