as-test 0.5.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +74 -7
- package/as-test.config.schema.json +31 -0
- package/assembly/src/expectation.ts +32 -9
- package/bin/{build.js → commands/build-core.js} +113 -35
- package/bin/commands/build.js +16 -0
- package/bin/commands/doctor-core.js +335 -0
- package/bin/commands/doctor.js +5 -0
- package/bin/commands/init-core.js +991 -0
- package/bin/commands/init.js +6 -0
- package/bin/{run.js → commands/run-core.js} +95 -30
- package/bin/commands/run.js +20 -0
- package/bin/commands/test.js +23 -0
- package/bin/commands/types.js +1 -0
- package/bin/index.js +410 -52
- package/bin/util.js +573 -8
- package/package.json +8 -5
- package/transform/lib/index.js +2 -1
- package/bin/init.js +0 -497
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2026-03-11 - v1.0.0
|
|
4
|
+
|
|
5
|
+
### Docs
|
|
6
|
+
|
|
7
|
+
- docs: add README guidance for strict config validation behavior and example error output.
|
|
8
|
+
|
|
9
|
+
### CLI & Config Validation
|
|
10
|
+
|
|
11
|
+
- feat: validate config shape and field types before applying defaults for all CLI entry points.
|
|
12
|
+
- feat: reject unknown config keys with nearest-key suggestions.
|
|
13
|
+
- feat: show structured validation diagnostics with JSON paths and fix hints.
|
|
14
|
+
- feat: fail fast on invalid config JSON with parser error details.
|
|
15
|
+
-
|
|
16
|
+
### Release Readiness
|
|
17
|
+
|
|
18
|
+
- fix: resolve `@assemblyscript/wasi-shim` and `try-as` with package resolution instead of assuming a local `./node_modules` folder, so nested example projects and other valid installs run correctly.
|
|
19
|
+
- fix: pass the WASI shim config to `asc` as a cwd-relative path, which avoids nested-project WASI build failures with standalone examples.
|
|
20
|
+
- feat: add root `test:examples` coverage and include full standalone example validation in `release:check`.
|
|
21
|
+
- feat: validate all examples in both `wasi` and `bindings` modes as part of release readiness.
|
|
22
|
+
- docs: update README and examples docs for the standalone example layout and root-level validation flow.
|
|
23
|
+
- chore: promote package version to `1.0.0` and switch `publishConfig` to public npm publishing.
|
|
24
|
+
|
|
3
25
|
## 2026-02-25 - v0.5.3
|
|
4
26
|
|
|
5
27
|
### CLI, Modes & Matrix
|
package/README.md
CHANGED
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
- [Installation](#installation)
|
|
10
10
|
- [Examples](#examples)
|
|
11
11
|
- [Writing Tests](#writing-tests)
|
|
12
|
+
- [Setup Diagnostics](#setup-diagnostics)
|
|
12
13
|
- [Mocking](#mocking)
|
|
13
14
|
- [Snapshots](#snapshots)
|
|
14
15
|
- [Coverage](#coverage)
|
|
15
16
|
- [Custom Reporters](#custom-reporters)
|
|
16
17
|
- [Assertions](#assertions)
|
|
18
|
+
- [CLI Style Guide](#cli-style-guide)
|
|
17
19
|
- [License](#license)
|
|
18
20
|
- [Contact](#contact)
|
|
19
21
|
|
|
@@ -40,6 +42,11 @@ The installation script will set everything up for you:
|
|
|
40
42
|
npx as-test init --dir ./path-to-install
|
|
41
43
|
```
|
|
42
44
|
|
|
45
|
+
To scaffold and install dependencies in one step:
|
|
46
|
+
```bash
|
|
47
|
+
npx as-test init --dir ./path-to-install --install
|
|
48
|
+
```
|
|
49
|
+
|
|
43
50
|
Alternatively, you can install it manually:
|
|
44
51
|
```bash
|
|
45
52
|
npm install as-test --save-dev
|
|
@@ -49,13 +56,19 @@ npm install as-test --save-dev
|
|
|
49
56
|
|
|
50
57
|
Full runnable examples live in `examples/`, including:
|
|
51
58
|
|
|
59
|
+
- one standalone project per example (initialized with `ast init`)
|
|
52
60
|
- complete spec files for core features
|
|
53
61
|
- import mocking and import snapshot patterns
|
|
54
|
-
- mode-based runtime matrix config in `examples/as-test.config.json`
|
|
55
|
-
- a dedicated config you can run directly
|
|
56
62
|
|
|
57
63
|
See `examples/README.md` for the walkthrough.
|
|
58
64
|
|
|
65
|
+
Quick validation from this repo:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm test
|
|
69
|
+
npm run test:examples
|
|
70
|
+
```
|
|
71
|
+
|
|
59
72
|
## Writing Tests
|
|
60
73
|
|
|
61
74
|
Create `assembly/__tests__/math.spec.ts`:
|
|
@@ -130,6 +143,9 @@ No test files matched: ...
|
|
|
130
143
|
- `--disable <feature>`: disable as-test feature (`coverage`, `try-as`)
|
|
131
144
|
- `--verbose`: keep expanded suite/test lines and update running `....` statuses in place
|
|
132
145
|
- `--clean`: disable in-place TTY updates and print only final per-file verdict lines. Useful for CI/CD.
|
|
146
|
+
- `--list`: show resolved files, per-mode artifacts, and runtime command without executing
|
|
147
|
+
- `--list-modes`: show configured and selected modes without executing
|
|
148
|
+
- `--help` / `-h`: show command-specific help (`ast test --help`, `ast init --help`, etc.)
|
|
133
149
|
|
|
134
150
|
Example:
|
|
135
151
|
|
|
@@ -138,6 +154,39 @@ ast build --enable try-as
|
|
|
138
154
|
ast test --disable coverage
|
|
139
155
|
```
|
|
140
156
|
|
|
157
|
+
Preview execution plan:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
ast test --list
|
|
161
|
+
ast test --list-modes
|
|
162
|
+
ast run sleep --list --mode wasi
|
|
163
|
+
ast build --list --mode wasi,bindings
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Setup Diagnostics
|
|
167
|
+
|
|
168
|
+
Use `ast doctor` to validate local setup before running tests.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
ast doctor
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
You can also target specific modes and config files:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
ast doctor --config ./as-test.config.json --mode wasi,bindings
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`doctor` checks:
|
|
181
|
+
|
|
182
|
+
- config file loading and mode resolution
|
|
183
|
+
- required dependencies (for example `assemblyscript`, `@assemblyscript/wasi-shim` for WASI targets)
|
|
184
|
+
- runtime command parsing and executable availability
|
|
185
|
+
- runtime script path existence (for script-host runtimes)
|
|
186
|
+
- test spec file discovery from configured input patterns
|
|
187
|
+
|
|
188
|
+
If any `ERROR` checks are found, `ast doctor` exits non-zero.
|
|
189
|
+
|
|
141
190
|
## Mocking
|
|
142
191
|
|
|
143
192
|
Use these helpers when you need to replace behavior during tests:
|
|
@@ -248,10 +297,7 @@ Example:
|
|
|
248
297
|
{
|
|
249
298
|
"$schema": "./as-test.config.schema.json",
|
|
250
299
|
"input": ["./assembly/__tests__/*.spec.ts"],
|
|
251
|
-
"
|
|
252
|
-
"logs": "./.as-test/logs",
|
|
253
|
-
"coverageDir": "./.as-test/coverage",
|
|
254
|
-
"snapshotDir": "./.as-test/snapshots",
|
|
300
|
+
"output": "./.as-test/",
|
|
255
301
|
"config": "none",
|
|
256
302
|
"coverage": true,
|
|
257
303
|
"env": {},
|
|
@@ -273,10 +319,12 @@ Example:
|
|
|
273
319
|
Key fields:
|
|
274
320
|
|
|
275
321
|
- `input`: glob list of spec files
|
|
322
|
+
- `output`: output alias. Use a root string (`"./.as-test/"`) or object (`{ "build": "...", "logs": "...", "coverage": "...", "snapshots": "..." }`)
|
|
276
323
|
- `outDir`: compiled wasm output dir
|
|
277
324
|
- `logs`: log output dir or `"none"`
|
|
278
325
|
- `coverageDir`: coverage output dir or `"none"`
|
|
279
326
|
- `snapshotDir`: snapshot storage dir
|
|
327
|
+
- `outDir`, `logs`, `coverageDir`, and `snapshotDir` still work; when both are set, these explicit fields override `output`
|
|
280
328
|
- `env`: environment variables injected into build and runtime processes
|
|
281
329
|
- `buildOptions.cmd`: optional custom build command template; when set it replaces default build command and flags. Supports `<file>`, `<name>`, `<outFile>`, `<target>`, `<mode>`
|
|
282
330
|
- `buildOptions.target`: `wasi` or `bindings`
|
|
@@ -284,6 +332,25 @@ Key fields:
|
|
|
284
332
|
- `runOptions.runtime.cmd`: runtime command, supports `<file>` and `<name>`; if its script path is missing, as-test falls back to the default runner for the selected target
|
|
285
333
|
- `runOptions.reporter`: reporter selection as a string or object
|
|
286
334
|
|
|
335
|
+
Validation behavior:
|
|
336
|
+
|
|
337
|
+
- Config parsing is strict for `ast build`, `ast run`, `ast test`, and `ast doctor`.
|
|
338
|
+
- Invalid JSON fails early with parser details (`line`/`column` when provided by Node).
|
|
339
|
+
- Unknown properties are rejected and include a nearest-key suggestion when possible.
|
|
340
|
+
- Invalid property types are reported with their JSON path and a short fix hint.
|
|
341
|
+
- On validation failure, the command exits non-zero and prints `run "ast doctor" to check your setup.`
|
|
342
|
+
|
|
343
|
+
Example validation error:
|
|
344
|
+
|
|
345
|
+
```text
|
|
346
|
+
invalid config at ./as-test.config.json
|
|
347
|
+
1. $.inpoot: unknown property
|
|
348
|
+
fix: use "input" if that was intended, otherwise remove this property
|
|
349
|
+
2. $.runOptions.runtime.cmd: must be a string
|
|
350
|
+
fix: set to a runtime command including "<file>"
|
|
351
|
+
run "ast doctor" to check your setup.
|
|
352
|
+
```
|
|
353
|
+
|
|
287
354
|
Example multi-runtime matrix:
|
|
288
355
|
|
|
289
356
|
```json
|
|
@@ -501,7 +568,7 @@ Available matchers:
|
|
|
501
568
|
- `toStartWith(prefix)`
|
|
502
569
|
- `toEndWith(suffix)`
|
|
503
570
|
- `toHaveLength(length)`
|
|
504
|
-
- `toContain(
|
|
571
|
+
- `toContain(itemOrSubstring)` (`toContains` alias supported)
|
|
505
572
|
- `toThrow()` (with `try-as`)
|
|
506
573
|
- `toMatchSnapshot(name?)`
|
|
507
574
|
|
|
@@ -17,6 +17,37 @@
|
|
|
17
17
|
},
|
|
18
18
|
"default": ["./assembly/__tests__/*.spec.ts"]
|
|
19
19
|
},
|
|
20
|
+
"output": {
|
|
21
|
+
"description": "Output alias. Use a root string or a per-artifact object. Legacy fields (outDir/logs/coverageDir/snapshotDir) still work and override this alias when both are set.",
|
|
22
|
+
"oneOf": [
|
|
23
|
+
{
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Root output directory. Expands to build/logs/coverage/snapshots subdirectories."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "object",
|
|
29
|
+
"additionalProperties": false,
|
|
30
|
+
"properties": {
|
|
31
|
+
"build": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "Build artifact output directory (maps to outDir)."
|
|
34
|
+
},
|
|
35
|
+
"logs": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "Log artifact output directory (maps to logs). Use \"none\" to disable."
|
|
38
|
+
},
|
|
39
|
+
"coverage": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "Coverage artifact output directory (maps to coverageDir). Use \"none\" to disable."
|
|
42
|
+
},
|
|
43
|
+
"snapshots": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "Snapshot output directory (maps to snapshotDir)."
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
20
51
|
"outDir": {
|
|
21
52
|
"type": "string",
|
|
22
53
|
"description": "Directory where compiled artifacts are written.",
|
|
@@ -334,18 +334,41 @@ export class Expectation<T> extends Tests {
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
/**
|
|
337
|
-
* Tests if an array contains
|
|
337
|
+
* Tests if an array or string contains a value
|
|
338
338
|
*/
|
|
339
339
|
// @ts-ignore
|
|
340
340
|
toContain(value: valueof<T>): void {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
341
|
+
if (isString<T>()) {
|
|
342
|
+
// @ts-ignore
|
|
343
|
+
const left = this._left as string;
|
|
344
|
+
// @ts-ignore
|
|
345
|
+
const needle = value as string;
|
|
346
|
+
const passed = left.indexOf(needle) >= 0;
|
|
347
|
+
this._resolve(passed, "toContain", q(left), q(needle));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (isArray<T>()) {
|
|
352
|
+
// @ts-ignore
|
|
353
|
+
const passed = this._left.includes(value);
|
|
354
|
+
this._resolve(
|
|
355
|
+
passed,
|
|
356
|
+
"toContain",
|
|
357
|
+
stringifyValue<T>(this._left),
|
|
358
|
+
stringifyValue<valueof<T>>(value),
|
|
359
|
+
);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
ERROR("toContain() can only be used on string and array types!");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Alias for toContain().
|
|
368
|
+
*/
|
|
369
|
+
// @ts-ignore
|
|
370
|
+
toContains(value: valueof<T>): void {
|
|
371
|
+
this.toContain(value);
|
|
349
372
|
}
|
|
350
373
|
|
|
351
374
|
/**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { existsSync } from "fs";
|
|
2
2
|
import { glob } from "glob";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import {
|
|
4
|
+
import { spawnSync } from "child_process";
|
|
5
5
|
import * as path from "path";
|
|
6
|
-
import { applyMode, getPkgRunner, loadConfig } from "
|
|
6
|
+
import { applyMode, getPkgRunner, loadConfig, tokenizeCommand, resolveProjectModule, } from "../util.js";
|
|
7
7
|
const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
|
|
8
8
|
export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], modeName, featureToggles = {}) {
|
|
9
9
|
const loadedConfig = loadConfig(configPath, false);
|
|
@@ -15,20 +15,21 @@ export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], mo
|
|
|
15
15
|
const pkgRunner = getPkgRunner();
|
|
16
16
|
const inputPatterns = resolveInputPatterns(config.input, selectors);
|
|
17
17
|
const inputFiles = (await glob(inputPatterns)).sort((a, b) => a.localeCompare(b));
|
|
18
|
+
const duplicateSpecBasenames = await resolveDuplicateSpecBasenames(config.input);
|
|
18
19
|
const coverageEnabled = resolveCoverageEnabled(config.coverage, featureToggles.coverage);
|
|
19
20
|
const buildEnv = {
|
|
20
21
|
...mode.env,
|
|
21
22
|
AS_TEST_COVERAGE_ENABLED: coverageEnabled ? "1" : "0",
|
|
22
23
|
};
|
|
23
24
|
for (const file of inputFiles) {
|
|
24
|
-
const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName)}`;
|
|
25
|
-
const
|
|
25
|
+
const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
|
|
26
|
+
const invocation = getBuildCommand(config, pkgRunner, file, outFile, modeName, featureToggles);
|
|
26
27
|
try {
|
|
27
|
-
buildFile(
|
|
28
|
+
buildFile(invocation, buildEnv);
|
|
28
29
|
}
|
|
29
30
|
catch (error) {
|
|
30
31
|
const modeLabel = modeName ?? "default";
|
|
31
|
-
throw new Error(`Failed to build ${path.basename(file)} in mode ${modeLabel} with ${getBuildStderr(error)}\nBuild command: ${
|
|
32
|
+
throw new Error(`Failed to build ${path.basename(file)} in mode ${modeLabel} with ${getBuildStderr(error)}\nBuild command: ${formatInvocation(invocation)}`);
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
}
|
|
@@ -38,21 +39,27 @@ function hasCustomBuildCommand(config) {
|
|
|
38
39
|
function getBuildCommand(config, pkgRunner, file, outFile, modeName, featureToggles = {}) {
|
|
39
40
|
const userArgs = getUserBuildArgs(config);
|
|
40
41
|
if (hasCustomBuildCommand(config)) {
|
|
41
|
-
|
|
42
|
+
const tokens = tokenizeCommand(expandBuildCommand(config.buildOptions.cmd, file, outFile, config.buildOptions.target, modeName));
|
|
43
|
+
if (!tokens.length) {
|
|
44
|
+
throw new Error("custom build command is empty");
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
command: tokens[0],
|
|
48
|
+
args: [...tokens.slice(1), ...userArgs],
|
|
49
|
+
};
|
|
42
50
|
}
|
|
43
51
|
const defaultArgs = getDefaultBuildArgs(config, featureToggles);
|
|
44
|
-
|
|
45
|
-
if (config.outDir) {
|
|
46
|
-
|
|
52
|
+
const args = ["asc", file, ...userArgs, ...defaultArgs];
|
|
53
|
+
if (config.outDir.length) {
|
|
54
|
+
args.push("-o", outFile);
|
|
47
55
|
}
|
|
48
|
-
return
|
|
56
|
+
return {
|
|
57
|
+
command: pkgRunner,
|
|
58
|
+
args,
|
|
59
|
+
};
|
|
49
60
|
}
|
|
50
61
|
function getUserBuildArgs(config) {
|
|
51
|
-
|
|
52
|
-
if (args.length) {
|
|
53
|
-
return " " + args.join(" ");
|
|
54
|
-
}
|
|
55
|
-
return "";
|
|
62
|
+
return config.buildOptions.args.filter((value) => value.length > 0);
|
|
56
63
|
}
|
|
57
64
|
function expandBuildCommand(template, file, outFile, target, modeName) {
|
|
58
65
|
const name = path
|
|
@@ -66,15 +73,48 @@ function expandBuildCommand(template, file, outFile, target, modeName) {
|
|
|
66
73
|
.replace(/<target>/g, target)
|
|
67
74
|
.replace(/<mode>/g, modeName ?? "");
|
|
68
75
|
}
|
|
69
|
-
function resolveArtifactFileName(file, target, modeName) {
|
|
76
|
+
function resolveArtifactFileName(file, target, modeName, duplicateSpecBasenames = new Set()) {
|
|
70
77
|
const base = path
|
|
71
78
|
.basename(file)
|
|
72
79
|
.replace(/\.spec\.ts$/, "")
|
|
73
80
|
.replace(/\.ts$/, "");
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
const legacy = !modeName
|
|
82
|
+
? `${path.basename(file).replace(".ts", ".wasm")}`
|
|
83
|
+
: `${base}.${modeName}.${target}.wasm`;
|
|
84
|
+
if (!duplicateSpecBasenames.has(path.basename(file))) {
|
|
85
|
+
return legacy;
|
|
86
|
+
}
|
|
87
|
+
const disambiguator = resolveDisambiguator(file);
|
|
88
|
+
if (!disambiguator.length) {
|
|
89
|
+
return legacy;
|
|
76
90
|
}
|
|
77
|
-
|
|
91
|
+
const ext = path.extname(legacy);
|
|
92
|
+
const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
|
|
93
|
+
return `${stem}.${disambiguator}${ext}`;
|
|
94
|
+
}
|
|
95
|
+
async function resolveDuplicateSpecBasenames(configured) {
|
|
96
|
+
const patterns = Array.isArray(configured) ? configured : [configured];
|
|
97
|
+
const files = await glob(patterns);
|
|
98
|
+
const counts = new Map();
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
const base = path.basename(file);
|
|
101
|
+
counts.set(base, (counts.get(base) ?? 0) + 1);
|
|
102
|
+
}
|
|
103
|
+
const duplicates = new Set();
|
|
104
|
+
for (const [base, count] of counts) {
|
|
105
|
+
if (count > 1)
|
|
106
|
+
duplicates.add(base);
|
|
107
|
+
}
|
|
108
|
+
return duplicates;
|
|
109
|
+
}
|
|
110
|
+
function resolveDisambiguator(file) {
|
|
111
|
+
const relDir = path.dirname(path.relative(process.cwd(), file));
|
|
112
|
+
if (!relDir.length || relDir == ".")
|
|
113
|
+
return "";
|
|
114
|
+
return relDir
|
|
115
|
+
.replace(/[\\/]+/g, "__")
|
|
116
|
+
.replace(/[^A-Za-z0-9._-]/g, "_")
|
|
117
|
+
.replace(/^_+|_+$/g, "");
|
|
78
118
|
}
|
|
79
119
|
function resolveInputPatterns(configured, selectors) {
|
|
80
120
|
const configuredInputs = Array.isArray(configured)
|
|
@@ -131,18 +171,33 @@ function stripSuiteSuffix(selector) {
|
|
|
131
171
|
}
|
|
132
172
|
function ensureDeps(config) {
|
|
133
173
|
if (config.buildOptions.target == "wasi") {
|
|
134
|
-
if (!
|
|
174
|
+
if (!resolveWasiShim()) {
|
|
135
175
|
console.log(`${chalk.bgRed(" ERROR ")}${chalk.dim(":")} could not find @assemblyscript/wasi-shim! Add it to your dependencies to run with WASI!`);
|
|
136
176
|
process.exit(1);
|
|
137
177
|
}
|
|
138
178
|
}
|
|
139
179
|
}
|
|
140
|
-
function buildFile(
|
|
141
|
-
|
|
180
|
+
function buildFile(invocation, env) {
|
|
181
|
+
const result = spawnSync(invocation.command, invocation.args, {
|
|
142
182
|
stdio: ["ignore", "pipe", "pipe"],
|
|
143
183
|
encoding: "utf8",
|
|
144
184
|
env,
|
|
185
|
+
shell: false,
|
|
145
186
|
});
|
|
187
|
+
if (result.error)
|
|
188
|
+
throw result.error;
|
|
189
|
+
if (result.status !== 0) {
|
|
190
|
+
const error = new Error(result.stderr?.trim() ||
|
|
191
|
+
result.stdout?.trim() ||
|
|
192
|
+
`command exited with code ${result.status}`);
|
|
193
|
+
error.stderr = result.stderr ?? "";
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function formatInvocation(invocation) {
|
|
198
|
+
return [invocation.command, ...invocation.args]
|
|
199
|
+
.map((token) => (/\s/.test(token) ? JSON.stringify(token) : token))
|
|
200
|
+
.join(" ");
|
|
146
201
|
}
|
|
147
202
|
function getBuildStderr(error) {
|
|
148
203
|
const err = error;
|
|
@@ -161,27 +216,28 @@ function getBuildStderr(error) {
|
|
|
161
216
|
return message || "unknown error";
|
|
162
217
|
}
|
|
163
218
|
function getDefaultBuildArgs(config, featureToggles) {
|
|
164
|
-
|
|
219
|
+
const buildArgs = [];
|
|
165
220
|
const tryAsEnabled = resolveTryAsEnabled(featureToggles.tryAs);
|
|
166
|
-
buildArgs
|
|
221
|
+
buildArgs.push("--transform", "as-test/transform");
|
|
167
222
|
if (tryAsEnabled) {
|
|
168
|
-
buildArgs
|
|
223
|
+
buildArgs.push("--transform", "try-as/transform");
|
|
169
224
|
}
|
|
170
225
|
if (config.config && config.config !== "none") {
|
|
171
|
-
buildArgs
|
|
226
|
+
buildArgs.push("--config", config.config);
|
|
172
227
|
}
|
|
173
228
|
if (tryAsEnabled) {
|
|
174
|
-
buildArgs
|
|
229
|
+
buildArgs.push("--use", "AS_TEST_TRY_AS=1");
|
|
175
230
|
}
|
|
176
231
|
// Should also strip any bindings-enabling from asconfig
|
|
177
232
|
if (config.buildOptions.target == "bindings") {
|
|
178
|
-
buildArgs
|
|
179
|
-
buildArgs += " --bindings raw --exportRuntime --exportStart _start";
|
|
233
|
+
buildArgs.push("--use", "AS_TEST_BINDINGS=1", "--bindings", "raw", "--exportRuntime", "--exportStart", "_start");
|
|
180
234
|
}
|
|
181
235
|
else if (config.buildOptions.target == "wasi") {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
236
|
+
const wasiShim = resolveWasiShim();
|
|
237
|
+
if (!wasiShim) {
|
|
238
|
+
throw new Error('WASI target requires package "@assemblyscript/wasi-shim"');
|
|
239
|
+
}
|
|
240
|
+
buildArgs.push("--use", "AS_TEST_WASI=1", "--config", wasiShim.configPath);
|
|
185
241
|
}
|
|
186
242
|
else {
|
|
187
243
|
console.log(`${chalk.bgRed(" ERROR ")}${chalk.dim(":")} could not determine target in config! Set target to 'bindings' or 'wasi'`);
|
|
@@ -211,6 +267,28 @@ function resolveCoverageEnabled(rawCoverage, override) {
|
|
|
211
267
|
return true;
|
|
212
268
|
}
|
|
213
269
|
function hasTryAsRuntime() {
|
|
214
|
-
return (
|
|
215
|
-
|
|
270
|
+
return resolveProjectModule("try-as/package.json") != null;
|
|
271
|
+
}
|
|
272
|
+
function resolveWasiShim() {
|
|
273
|
+
const resolved = resolveProjectModule("@assemblyscript/wasi-shim/asconfig.json");
|
|
274
|
+
if (!resolved)
|
|
275
|
+
return null;
|
|
276
|
+
if (!existsSync(resolved))
|
|
277
|
+
return null;
|
|
278
|
+
const relative = path.relative(process.cwd(), resolved).replace(/\\/g, "/");
|
|
279
|
+
return {
|
|
280
|
+
configPath: normalizeCliPath(relative),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function quoteCliArg(value) {
|
|
284
|
+
if (!/[\s"]/g.test(value))
|
|
285
|
+
return value;
|
|
286
|
+
return `"${value.replace(/"/g, '\\"')}"`;
|
|
287
|
+
}
|
|
288
|
+
function normalizeCliPath(value) {
|
|
289
|
+
if (!value.length)
|
|
290
|
+
return ".";
|
|
291
|
+
if (value.startsWith(".") || value.startsWith("/"))
|
|
292
|
+
return value;
|
|
293
|
+
return "./" + value;
|
|
216
294
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { build } from "./build-core.js";
|
|
2
|
+
export async function executeBuildCommand(rawArgs, configPath, selectedModes, deps) {
|
|
3
|
+
const commandArgs = deps.resolveCommandArgs(rawArgs, "build");
|
|
4
|
+
const listFlags = deps.resolveListFlags(rawArgs, "build");
|
|
5
|
+
const featureToggles = deps.resolveFeatureToggles(rawArgs, "build");
|
|
6
|
+
const buildFeatureToggles = {
|
|
7
|
+
tryAs: featureToggles.tryAs,
|
|
8
|
+
coverage: featureToggles.coverage,
|
|
9
|
+
};
|
|
10
|
+
const modeTargets = deps.resolveExecutionModes(configPath, selectedModes);
|
|
11
|
+
if (listFlags.list || listFlags.listModes) {
|
|
12
|
+
await deps.listExecutionPlan("build", configPath, commandArgs, modeTargets, listFlags);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
await deps.runBuildModes(configPath, commandArgs, modeTargets, buildFeatureToggles);
|
|
16
|
+
}
|