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.
- package/CHANGELOG.md +16 -1
- 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 +905 -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/coverage.js +4 -4
- package/transform/lib/log.js +9 -5
- package/assembly/util/json.ts +0 -112
|
@@ -5,331 +5,330 @@ import * as path from "path";
|
|
|
5
5
|
import { applyMode, getExec, loadConfig, tokenizeCommand } from "../util.js";
|
|
6
6
|
import { Config } from "../types.js";
|
|
7
7
|
const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
|
|
8
|
-
export async function doctor(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
export async function doctor(
|
|
9
|
+
configPath = DEFAULT_CONFIG_PATH,
|
|
10
|
+
selectedModes = [],
|
|
11
|
+
) {
|
|
12
|
+
const checks = [];
|
|
13
|
+
const resolvedConfigPath = configPath ?? DEFAULT_CONFIG_PATH;
|
|
14
|
+
const configExists = existsSync(resolvedConfigPath);
|
|
15
|
+
const loadedConfig = tryLoadConfig(resolvedConfigPath);
|
|
16
|
+
const config = loadedConfig.config;
|
|
17
|
+
if (!configExists) {
|
|
18
|
+
checks.push({
|
|
19
|
+
status: "warn",
|
|
20
|
+
scope: "config",
|
|
21
|
+
label: "Config file not found",
|
|
22
|
+
details: `No config at ${resolvedConfigPath}; default settings will be used.`,
|
|
23
|
+
fix: `Create a config with "ast init" or add ${path.basename(resolvedConfigPath)}.`,
|
|
24
|
+
});
|
|
25
|
+
} else if (!loadedConfig.loaded) {
|
|
26
|
+
checks.push({
|
|
27
|
+
status: "error",
|
|
28
|
+
scope: "config",
|
|
29
|
+
label: "Config parse failed",
|
|
30
|
+
details: `Could not parse ${resolvedConfigPath}.`,
|
|
31
|
+
fix: "Fix JSON syntax errors, then rerun `ast doctor`.",
|
|
32
|
+
});
|
|
33
|
+
} else {
|
|
34
|
+
checks.push({
|
|
35
|
+
status: "ok",
|
|
36
|
+
scope: "config",
|
|
37
|
+
label: "Config loaded",
|
|
38
|
+
details: resolvedConfigPath,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
checks.push(checkNodeVersion());
|
|
42
|
+
checks.push(checkDependency("assemblyscript", true));
|
|
43
|
+
const selected = selectedModes.length
|
|
44
|
+
? selectedModes
|
|
45
|
+
: Object.keys(config.modes).length
|
|
46
|
+
? Object.keys(config.modes)
|
|
47
|
+
: [undefined];
|
|
48
|
+
if (selectedModes.length) {
|
|
49
|
+
for (const modeName of selectedModes) {
|
|
50
|
+
if (!config.modes[modeName]) {
|
|
15
51
|
checks.push({
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
52
|
+
status: "error",
|
|
53
|
+
scope: "modes",
|
|
54
|
+
label: `Unknown mode "${modeName}"`,
|
|
55
|
+
details: `Available modes: ${Object.keys(config.modes).join(", ") || "(none)"}`,
|
|
56
|
+
fix: `Use "--mode <name>" with one of the configured modes, or remove "--mode ${modeName}".`,
|
|
21
57
|
});
|
|
58
|
+
}
|
|
22
59
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
status: "ok",
|
|
35
|
-
scope: "config",
|
|
36
|
-
label: "Config loaded",
|
|
37
|
-
details: resolvedConfigPath,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
checks.push(checkNodeVersion());
|
|
41
|
-
checks.push(checkDependency("assemblyscript", true));
|
|
42
|
-
const selected = selectedModes.length
|
|
43
|
-
? selectedModes
|
|
44
|
-
: Object.keys(config.modes).length
|
|
45
|
-
? Object.keys(config.modes)
|
|
46
|
-
: [undefined];
|
|
47
|
-
if (selectedModes.length) {
|
|
48
|
-
for (const modeName of selectedModes) {
|
|
49
|
-
if (!config.modes[modeName]) {
|
|
50
|
-
checks.push({
|
|
51
|
-
status: "error",
|
|
52
|
-
scope: "modes",
|
|
53
|
-
label: `Unknown mode "${modeName}"`,
|
|
54
|
-
details: `Available modes: ${Object.keys(config.modes).join(", ") || "(none)"}`,
|
|
55
|
-
fix: `Use "--mode <name>" with one of the configured modes, or remove "--mode ${modeName}".`,
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
for (const modeName of selected) {
|
|
61
|
-
const scope = modeName ?? "default";
|
|
62
|
-
const modeCheck = await checkMode(config, modeName, scope);
|
|
63
|
-
checks.push(...modeCheck);
|
|
64
|
-
}
|
|
65
|
-
renderChecks(checks, resolvedConfigPath, selected);
|
|
66
|
-
const hasErrors = checks.some((check) => check.status == "error");
|
|
67
|
-
if (hasErrors) {
|
|
68
|
-
throw new Error("doctor checks failed");
|
|
69
|
-
}
|
|
60
|
+
}
|
|
61
|
+
for (const modeName of selected) {
|
|
62
|
+
const scope = modeName ?? "default";
|
|
63
|
+
const modeCheck = await checkMode(config, modeName, scope);
|
|
64
|
+
checks.push(...modeCheck);
|
|
65
|
+
}
|
|
66
|
+
renderChecks(checks, resolvedConfigPath, selected);
|
|
67
|
+
const hasErrors = checks.some((check) => check.status == "error");
|
|
68
|
+
if (hasErrors) {
|
|
69
|
+
throw new Error("doctor checks failed");
|
|
70
|
+
}
|
|
70
71
|
}
|
|
71
72
|
function tryLoadConfig(configPath) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
73
|
+
try {
|
|
74
|
+
return { loaded: true, config: loadConfig(configPath, false) };
|
|
75
|
+
} catch {
|
|
76
|
+
return { loaded: false, config: new Config() };
|
|
77
|
+
}
|
|
78
78
|
}
|
|
79
79
|
async function checkMode(config, modeName, scope) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
93
|
-
return checks;
|
|
94
|
-
}
|
|
95
|
-
const active = applied.config;
|
|
96
|
-
const runtimeCommand = active.runOptions.runtime.cmd;
|
|
97
|
-
const target = active.buildOptions.target;
|
|
98
|
-
if (target == "wasi") {
|
|
99
|
-
checks.push(checkDependency("@assemblyscript/wasi-shim", true, scope));
|
|
100
|
-
}
|
|
101
|
-
checks.push(...checkRuntimeCommand(runtimeCommand, target, scope), ...(await checkInputPatterns(active.input, scope)));
|
|
80
|
+
const checks = [];
|
|
81
|
+
let applied;
|
|
82
|
+
try {
|
|
83
|
+
applied = applyMode(config, modeName);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
checks.push({
|
|
86
|
+
status: "error",
|
|
87
|
+
scope,
|
|
88
|
+
label: "Mode merge failed",
|
|
89
|
+
details: error instanceof Error ? error.message : String(error),
|
|
90
|
+
fix: `Fix mode "${scope}" in as-test.config.json.`,
|
|
91
|
+
});
|
|
102
92
|
return checks;
|
|
93
|
+
}
|
|
94
|
+
const active = applied.config;
|
|
95
|
+
const runtimeCommand = active.runOptions.runtime.cmd;
|
|
96
|
+
const target = active.buildOptions.target;
|
|
97
|
+
if (target == "wasi") {
|
|
98
|
+
checks.push(checkDependency("@assemblyscript/wasi-shim", true, scope));
|
|
99
|
+
}
|
|
100
|
+
checks.push(
|
|
101
|
+
...checkRuntimeCommand(runtimeCommand, target, scope),
|
|
102
|
+
...(await checkInputPatterns(active.input, scope)),
|
|
103
|
+
);
|
|
104
|
+
return checks;
|
|
103
105
|
}
|
|
104
106
|
function checkRuntimeCommand(runtimeCommand, target, scope) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
107
|
+
const checks = [];
|
|
108
|
+
if (!runtimeCommand.trim().length) {
|
|
109
|
+
checks.push({
|
|
110
|
+
status: "error",
|
|
111
|
+
scope,
|
|
112
|
+
label: "Runtime command missing",
|
|
113
|
+
details: "runOptions.runtime.cmd is empty.",
|
|
114
|
+
fix: 'Set "runOptions.runtime.cmd" in as-test.config.json (for example: node ./.as-test/runners/default.wasi.js <file>).',
|
|
115
|
+
});
|
|
116
|
+
return checks;
|
|
117
|
+
}
|
|
118
|
+
let tokens;
|
|
119
|
+
try {
|
|
120
|
+
tokens = tokenizeCommand(runtimeCommand);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
checks.push({
|
|
123
|
+
status: "error",
|
|
124
|
+
scope,
|
|
125
|
+
label: "Runtime command parsing failed",
|
|
126
|
+
details: error instanceof Error ? error.message : String(error),
|
|
127
|
+
fix: "Fix quotes/escaping in runOptions.runtime.cmd.",
|
|
128
|
+
});
|
|
129
|
+
return checks;
|
|
130
|
+
}
|
|
131
|
+
if (!tokens.length) {
|
|
132
|
+
checks.push({
|
|
133
|
+
status: "error",
|
|
134
|
+
scope,
|
|
135
|
+
label: "Runtime command empty",
|
|
136
|
+
details: "Command parsed to zero tokens.",
|
|
137
|
+
fix: "Provide a runtime command executable and args.",
|
|
138
|
+
});
|
|
139
|
+
return checks;
|
|
140
|
+
}
|
|
141
|
+
const execToken = tokens[0];
|
|
142
|
+
const execPath = getExec(execToken);
|
|
143
|
+
if (!execPath) {
|
|
144
|
+
checks.push({
|
|
145
|
+
status: "error",
|
|
146
|
+
scope,
|
|
147
|
+
label: `Runtime executable not found: ${execToken}`,
|
|
148
|
+
details: "Executable is not available in PATH.",
|
|
149
|
+
fix: `Install "${execToken}" or update runOptions.runtime.cmd.`,
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
checks.push({
|
|
153
|
+
status: "ok",
|
|
154
|
+
scope,
|
|
155
|
+
label: "Runtime executable",
|
|
156
|
+
details: `${execToken} -> ${execPath}`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (!tokens.some((token) => token.includes("<file>"))) {
|
|
160
|
+
checks.push({
|
|
161
|
+
status: "error",
|
|
162
|
+
scope,
|
|
163
|
+
label: "Runtime command missing <file> placeholder",
|
|
164
|
+
details: `Runtime command for target "${target}" cannot receive the wasm artifact path.`,
|
|
165
|
+
fix: 'Add "<file>" to runOptions.runtime.cmd.',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (isScriptHostRuntime(execToken)) {
|
|
169
|
+
const scriptPath = extractRuntimeScriptPath(tokens.slice(1));
|
|
170
|
+
if (scriptPath) {
|
|
171
|
+
const resolved = path.isAbsolute(scriptPath)
|
|
172
|
+
? scriptPath
|
|
173
|
+
: path.join(process.cwd(), scriptPath);
|
|
174
|
+
if (!existsSync(resolved)) {
|
|
152
175
|
checks.push({
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
176
|
+
status: "warn",
|
|
177
|
+
scope,
|
|
178
|
+
label: "Runtime script path not found",
|
|
179
|
+
details: `${scriptPath} does not exist.`,
|
|
180
|
+
fix: "Create the runner script, or use `ast init` to scaffold default runners.",
|
|
157
181
|
});
|
|
158
|
-
|
|
159
|
-
if (!tokens.some((token) => token.includes("<file>"))) {
|
|
182
|
+
} else {
|
|
160
183
|
checks.push({
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
fix: 'Add "<file>" to runOptions.runtime.cmd.',
|
|
184
|
+
status: "ok",
|
|
185
|
+
scope,
|
|
186
|
+
label: "Runtime script path",
|
|
187
|
+
details: scriptPath,
|
|
166
188
|
});
|
|
189
|
+
}
|
|
167
190
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (scriptPath) {
|
|
171
|
-
const resolved = path.isAbsolute(scriptPath)
|
|
172
|
-
? scriptPath
|
|
173
|
-
: path.join(process.cwd(), scriptPath);
|
|
174
|
-
if (!existsSync(resolved)) {
|
|
175
|
-
checks.push({
|
|
176
|
-
status: "warn",
|
|
177
|
-
scope,
|
|
178
|
-
label: "Runtime script path not found",
|
|
179
|
-
details: `${scriptPath} does not exist.`,
|
|
180
|
-
fix: "Create the runner script, or use `ast init` to scaffold default runners.",
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
checks.push({
|
|
185
|
-
status: "ok",
|
|
186
|
-
scope,
|
|
187
|
-
label: "Runtime script path",
|
|
188
|
-
details: scriptPath,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return checks;
|
|
191
|
+
}
|
|
192
|
+
return checks;
|
|
194
193
|
}
|
|
195
194
|
async function checkInputPatterns(input, scope) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return [
|
|
201
|
-
{
|
|
202
|
-
status: "warn",
|
|
203
|
-
scope,
|
|
204
|
-
label: "No spec files matched input patterns",
|
|
205
|
-
details: patterns.join(", "),
|
|
206
|
-
fix: 'Update "input" patterns or add `*.spec.ts` files.',
|
|
207
|
-
},
|
|
208
|
-
];
|
|
209
|
-
}
|
|
195
|
+
const patterns = Array.isArray(input) ? input : [input];
|
|
196
|
+
const files = await glob(patterns);
|
|
197
|
+
const specs = files.filter((file) => file.endsWith(".spec.ts"));
|
|
198
|
+
if (!specs.length) {
|
|
210
199
|
return [
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
200
|
+
{
|
|
201
|
+
status: "warn",
|
|
202
|
+
scope,
|
|
203
|
+
label: "No spec files matched input patterns",
|
|
204
|
+
details: patterns.join(", "),
|
|
205
|
+
fix: 'Update "input" patterns or add `*.spec.ts` files.',
|
|
206
|
+
},
|
|
217
207
|
];
|
|
208
|
+
}
|
|
209
|
+
return [
|
|
210
|
+
{
|
|
211
|
+
status: "ok",
|
|
212
|
+
scope,
|
|
213
|
+
label: "Spec file discovery",
|
|
214
|
+
details: `${specs.length} spec file(s) matched input patterns.`,
|
|
215
|
+
},
|
|
216
|
+
];
|
|
218
217
|
}
|
|
219
218
|
function checkNodeVersion() {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return {
|
|
224
|
-
status: "warn",
|
|
225
|
-
scope: "env",
|
|
226
|
-
label: "Node.js version is old",
|
|
227
|
-
details: `Detected v${version}.`,
|
|
228
|
-
fix: "Use Node.js 18+ for the best compatibility.",
|
|
229
|
-
};
|
|
230
|
-
}
|
|
219
|
+
const version = process.versions.node;
|
|
220
|
+
const major = Number(version.split(".")[0] ?? "0");
|
|
221
|
+
if (!Number.isFinite(major) || major < 18) {
|
|
231
222
|
return {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
223
|
+
status: "warn",
|
|
224
|
+
scope: "env",
|
|
225
|
+
label: "Node.js version is old",
|
|
226
|
+
details: `Detected v${version}.`,
|
|
227
|
+
fix: "Use Node.js 18+ for the best compatibility.",
|
|
236
228
|
};
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
status: "ok",
|
|
232
|
+
scope: "env",
|
|
233
|
+
label: "Node.js version",
|
|
234
|
+
details: `v${version}`,
|
|
235
|
+
};
|
|
237
236
|
}
|
|
238
237
|
function checkDependency(pkg, required, scope = "deps") {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
status: required ? "error" : "warn",
|
|
243
|
-
scope,
|
|
244
|
-
label: `Dependency missing: ${pkg}`,
|
|
245
|
-
details: `${pkg} is not installed in node_modules.`,
|
|
246
|
-
fix: `Install with: npm i -D ${pkg}`,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
238
|
+
const pkgJson = path.join(process.cwd(), "node_modules", pkg, "package.json");
|
|
239
|
+
if (!existsSync(pkgJson)) {
|
|
249
240
|
return {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
241
|
+
status: required ? "error" : "warn",
|
|
242
|
+
scope,
|
|
243
|
+
label: `Dependency missing: ${pkg}`,
|
|
244
|
+
details: `${pkg} is not installed in node_modules.`,
|
|
245
|
+
fix: `Install with: npm i -D ${pkg}`,
|
|
254
246
|
};
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
status: "ok",
|
|
250
|
+
scope,
|
|
251
|
+
label: `Dependency present: ${pkg}`,
|
|
252
|
+
details: pkgJson,
|
|
253
|
+
};
|
|
255
254
|
}
|
|
256
255
|
function isScriptHostRuntime(execToken) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
256
|
+
const token = path.basename(execToken).toLowerCase();
|
|
257
|
+
return (
|
|
258
|
+
token == "node" ||
|
|
259
|
+
token == "node.exe" ||
|
|
260
|
+
token == "node.cmd" ||
|
|
261
|
+
token == "bun" ||
|
|
262
|
+
token == "bun.exe" ||
|
|
263
|
+
token == "bun.cmd" ||
|
|
264
|
+
token == "deno" ||
|
|
265
|
+
token == "deno.exe" ||
|
|
266
|
+
token == "deno.cmd" ||
|
|
267
|
+
token == "tsx" ||
|
|
268
|
+
token == "tsx.cmd" ||
|
|
269
|
+
token == "ts-node" ||
|
|
270
|
+
token == "ts-node.cmd"
|
|
271
|
+
);
|
|
271
272
|
}
|
|
272
273
|
function extractRuntimeScriptPath(args) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return null;
|
|
280
|
-
}
|
|
281
|
-
if (token.startsWith("-"))
|
|
282
|
-
continue;
|
|
283
|
-
if (isLikelyScriptPath(token))
|
|
284
|
-
return token;
|
|
285
|
-
return null;
|
|
274
|
+
for (let i = 0; i < args.length; i++) {
|
|
275
|
+
const token = args[i];
|
|
276
|
+
if (token == "--") {
|
|
277
|
+
const next = args[i + 1];
|
|
278
|
+
if (next && isLikelyScriptPath(next)) return next;
|
|
279
|
+
return null;
|
|
286
280
|
}
|
|
281
|
+
if (token.startsWith("-")) continue;
|
|
282
|
+
if (isLikelyScriptPath(token)) return token;
|
|
287
283
|
return null;
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
288
286
|
}
|
|
289
287
|
function isLikelyScriptPath(token) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return true;
|
|
302
|
-
if (token.startsWith("/"))
|
|
303
|
-
return true;
|
|
304
|
-
if (token.startsWith(".\\"))
|
|
305
|
-
return true;
|
|
306
|
-
if (token.startsWith("..\\"))
|
|
307
|
-
return true;
|
|
308
|
-
if (/^[A-Za-z]:[\\/]/.test(token))
|
|
309
|
-
return true;
|
|
310
|
-
return /\.(mjs|cjs|js|ts)$/.test(token);
|
|
288
|
+
if (!token.length) return false;
|
|
289
|
+
if (token == "<file>" || token == "<name>") return false;
|
|
290
|
+
if (token.includes("://")) return false;
|
|
291
|
+
if (token.startsWith("-")) return false;
|
|
292
|
+
if (token.startsWith("./")) return true;
|
|
293
|
+
if (token.startsWith("../")) return true;
|
|
294
|
+
if (token.startsWith("/")) return true;
|
|
295
|
+
if (token.startsWith(".\\")) return true;
|
|
296
|
+
if (token.startsWith("..\\")) return true;
|
|
297
|
+
if (/^[A-Za-z]:[\\/]/.test(token)) return true;
|
|
298
|
+
return /\.(mjs|cjs|js|ts)$/.test(token);
|
|
311
299
|
}
|
|
312
300
|
function renderChecks(checks, configPath, selectedModes) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
301
|
+
const errors = checks.filter((check) => check.status == "error").length;
|
|
302
|
+
const warnings = checks.filter((check) => check.status == "warn").length;
|
|
303
|
+
const oks = checks.filter((check) => check.status == "ok").length;
|
|
304
|
+
process.stdout.write(chalk.bold.blueBright("as-test doctor") + "\n");
|
|
305
|
+
process.stdout.write(chalk.dim(`config: ${configPath}`) + "\n");
|
|
306
|
+
process.stdout.write(
|
|
307
|
+
chalk.dim(
|
|
308
|
+
`modes: ${
|
|
309
|
+
selectedModes.length
|
|
310
|
+
? selectedModes.map((mode) => mode ?? "default").join(", ")
|
|
311
|
+
: "default"
|
|
312
|
+
}`,
|
|
313
|
+
) + "\n\n",
|
|
314
|
+
);
|
|
315
|
+
for (const check of checks) {
|
|
316
|
+
const badge =
|
|
317
|
+
check.status == "ok"
|
|
318
|
+
? chalk.bgGreenBright.black(" OK ")
|
|
319
|
+
: check.status == "warn"
|
|
320
|
+
? chalk.bgYellow.black(" WARN ")
|
|
321
|
+
: chalk.bgRed.white(" ERROR ");
|
|
322
|
+
process.stdout.write(
|
|
323
|
+
`${badge} ${chalk.bold(`[${check.scope}]`)} ${check.label}\n`,
|
|
324
|
+
);
|
|
325
|
+
process.stdout.write(` ${check.details}\n`);
|
|
326
|
+
if (check.fix?.length) {
|
|
327
|
+
process.stdout.write(chalk.dim(` fix: ${check.fix}\n`));
|
|
332
328
|
}
|
|
333
|
-
|
|
334
|
-
|
|
329
|
+
}
|
|
330
|
+
process.stdout.write("\n");
|
|
331
|
+
process.stdout.write(
|
|
332
|
+
`${chalk.bold("Summary:")} ${chalk.greenBright(`${oks} ok`)}, ${warnings ? chalk.yellowBright(`${warnings} warn`) : chalk.gray("0 warn")}, ${errors ? chalk.redBright(`${errors} error`) : chalk.greenBright("0 error")}\n`,
|
|
333
|
+
);
|
|
335
334
|
}
|
package/bin/commands/doctor.js
CHANGED