as-test 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -3
- package/assembly/__fuzz__/math.fuzz.ts +19 -0
- package/assembly/__fuzz__/string.fuzz.ts +31 -0
- package/assembly/index.ts +5 -5
- package/assembly/src/expectation.ts +93 -42
- package/assembly/util/format.ts +104 -0
- package/assembly/util/helpers.ts +7 -13
- package/assembly/util/json.ts +2 -2
- package/assembly/util/wipc.ts +4 -1
- package/bin/commands/clean-core.js +59 -16
- package/bin/commands/clean.js +48 -3
- package/bin/index.js +3 -2
- package/bin/wipc.js +7 -2
- package/lib/build/index.js +19 -1
- package/lib/src/index.ts +19 -1
- package/package.json +1 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## v1.1.1
|
|
4
|
+
|
|
5
|
+
- add `ast clean` command to remove build outputs, coverage outputs, crash reports, and logs.
|
|
6
|
+
- remove deps
|
|
7
|
+
|
|
8
|
+
## v1.1.0
|
|
4
9
|
|
|
5
10
|
### Upgrading to 1.1.0
|
|
6
11
|
|
|
@@ -40,9 +45,13 @@
|
|
|
40
45
|
### Modes & CLI
|
|
41
46
|
|
|
42
47
|
- feat: add per-mode `default: boolean` selection so modes can be included in implicit runs or kept manual-only.
|
|
43
|
-
- feat: add `ast clean` to remove configured build outputs, crash reports, and logs
|
|
48
|
+
- feat: add `ast clean` to remove configured build outputs, coverage outputs, crash reports, and logs.
|
|
49
|
+
- feat: make `ast clean` remove everything by default, prompt with `[Y/n]` before a full clean, and allow `-f` / `--force` to skip that confirmation.
|
|
44
50
|
- fix: restore unnamed root-config execution alongside named default modes when `--mode` is omitted.
|
|
45
|
-
- fix: make `ast clean
|
|
51
|
+
- fix: make `ast clean` ignore mode `default: false` flags and treat an omitted `--mode` as a full clean across every configured mode.
|
|
52
|
+
- fix: make `ast clean --mode ...` stay scoped to the selected mode(s) and skip shared output paths that are still owned by unselected modes instead of deleting them.
|
|
53
|
+
- fix: make full `ast clean` remove the configured output roots directly so stale legacy build, coverage, and log directories are removed too.
|
|
54
|
+
- fix: simplify `ast clean` console output so it only prints removed paths plus a final summary.
|
|
46
55
|
|
|
47
56
|
### Tests
|
|
48
57
|
|
|
@@ -7,3 +7,22 @@ fuzz("bounded integer addition", (left: i32, right: i32): bool => {
|
|
|
7
7
|
}).generate((seed: FuzzSeed, run: (left: i32, right: i32) => bool): void => {
|
|
8
8
|
run(seed.i32({ min: -1000, max: 1000 }), seed.i32({ min: -1000, max: 1000 }));
|
|
9
9
|
});
|
|
10
|
+
|
|
11
|
+
fuzz("numeric matchers stay consistent for bounded integers", (value: i32): bool => {
|
|
12
|
+
expect(value).toBeNumber();
|
|
13
|
+
expect(value).toBeInteger();
|
|
14
|
+
expect(value).toBeFinite();
|
|
15
|
+
expect(value).toBe(value);
|
|
16
|
+
expect(value).toBeGreaterOrEqualTo(-1000);
|
|
17
|
+
expect(value).toBeLessThanOrEqualTo(1000);
|
|
18
|
+
|
|
19
|
+
if (value != 0) {
|
|
20
|
+
expect(value).toBeTruthy();
|
|
21
|
+
} else {
|
|
22
|
+
expect(value).toBeFalsy();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return true;
|
|
26
|
+
}).generate((seed: FuzzSeed, run: (value: i32) => bool): void => {
|
|
27
|
+
run(seed.i32({ min: -1000, max: 1000 }));
|
|
28
|
+
}, 250);
|
|
@@ -19,3 +19,34 @@ fuzz(
|
|
|
19
19
|
}),
|
|
20
20
|
);
|
|
21
21
|
}, 250);
|
|
22
|
+
|
|
23
|
+
fuzz(
|
|
24
|
+
"string matchers stay consistent on derived slices",
|
|
25
|
+
(input: string): bool => {
|
|
26
|
+
const split = input.length >> 1;
|
|
27
|
+
const prefix = input.substr(0, split);
|
|
28
|
+
const suffix = input.substr(split);
|
|
29
|
+
|
|
30
|
+
expect(input).toBeString();
|
|
31
|
+
expect(input).toContain(prefix);
|
|
32
|
+
expect(input).toMatch(suffix);
|
|
33
|
+
|
|
34
|
+
if (input.length > 0) {
|
|
35
|
+
expect(input).toBeTruthy();
|
|
36
|
+
expect(input).toContain(input.charAt(input.length - 1));
|
|
37
|
+
} else {
|
|
38
|
+
expect(input).toBeFalsy();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return true;
|
|
42
|
+
},
|
|
43
|
+
).generate((seed: FuzzSeed, run: (input: string) => bool): void => {
|
|
44
|
+
run(
|
|
45
|
+
seed.string({
|
|
46
|
+
charset: "ascii",
|
|
47
|
+
min: 0,
|
|
48
|
+
max: 40,
|
|
49
|
+
exclude: [0x00, 0x0a, 0x0d],
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
}, 250);
|
package/assembly/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Suite } from "./src/suite";
|
|
2
2
|
import { Expectation } from "./src/expectation";
|
|
3
|
-
import { stringify } from "as-console/stringify";
|
|
4
3
|
import {
|
|
5
4
|
__COVER,
|
|
6
5
|
__POINTS,
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
sendReport,
|
|
17
16
|
} from "./util/wipc";
|
|
18
17
|
import { quote } from "./util/json";
|
|
18
|
+
import { bold, formatValue, green, red } from "./util/format";
|
|
19
19
|
import {
|
|
20
20
|
createFuzzer,
|
|
21
21
|
FuzzerBase,
|
|
@@ -245,7 +245,7 @@ export function log<T>(data: T): void {
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
export function __as_test_log_default<T>(data: T): string {
|
|
248
|
-
return
|
|
248
|
+
return formatValue(data);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
export function __as_test_log_is_enabled(): bool {
|
|
@@ -673,11 +673,11 @@ export class Result {
|
|
|
673
673
|
}
|
|
674
674
|
display(): string {
|
|
675
675
|
let out = "";
|
|
676
|
-
out += `${
|
|
676
|
+
out += `${bold(this.name)} `;
|
|
677
677
|
if (this.arg1) {
|
|
678
|
-
out += `${
|
|
678
|
+
out += `${bold(red(this.arg1.toString() + " failed"))}`;
|
|
679
679
|
} else {
|
|
680
|
-
out += `${
|
|
680
|
+
out += `${bold(green("0 failed"))}`;
|
|
681
681
|
}
|
|
682
682
|
out += ` ${this.arg1 + this.arg2} total\n`;
|
|
683
683
|
return out;
|
|
@@ -62,6 +62,7 @@ export class Expectation<T> extends Tests {
|
|
|
62
62
|
instr: string,
|
|
63
63
|
left: string,
|
|
64
64
|
right: string,
|
|
65
|
+
message: string = "",
|
|
65
66
|
): void {
|
|
66
67
|
if (this._skip) {
|
|
67
68
|
this.verdict = "skip";
|
|
@@ -77,7 +78,8 @@ export class Expectation<T> extends Tests {
|
|
|
77
78
|
this.instr = instr;
|
|
78
79
|
this.left = left;
|
|
79
80
|
this.right = right;
|
|
80
|
-
|
|
81
|
+
const resolvedMessage = message.length ? message : this._message;
|
|
82
|
+
this.message = isFail ? resolvedMessage : "";
|
|
81
83
|
if (isFail) {
|
|
82
84
|
sendAssertionFailure(this._snapshotKey, instr, left, right, this.message);
|
|
83
85
|
// @ts-ignore
|
|
@@ -103,7 +105,7 @@ export class Expectation<T> extends Tests {
|
|
|
103
105
|
/**
|
|
104
106
|
* Tests if a == null
|
|
105
107
|
*/
|
|
106
|
-
toBeNull(): void {
|
|
108
|
+
toBeNull(message: string = ""): void {
|
|
107
109
|
const passed =
|
|
108
110
|
(isNullable<T>() && changetype<usize>(this._left) == 0) ||
|
|
109
111
|
(isInteger<T>() && nameof<T>() == "usize" && this._left == 0);
|
|
@@ -117,13 +119,14 @@ export class Expectation<T> extends Tests {
|
|
|
117
119
|
visualize<T>(
|
|
118
120
|
load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
|
|
119
121
|
),
|
|
122
|
+
message,
|
|
120
123
|
);
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
/**
|
|
124
127
|
* Tests if a > b
|
|
125
128
|
*/
|
|
126
|
-
toBeGreaterThan(value: T): void {
|
|
129
|
+
toBeGreaterThan(value: T, message: string = ""): void {
|
|
127
130
|
if (!isInteger<T>() && !isFloat<T>())
|
|
128
131
|
ERROR("toBeGreaterThan() can only be used on number types!");
|
|
129
132
|
|
|
@@ -139,13 +142,14 @@ export class Expectation<T> extends Tests {
|
|
|
139
142
|
visualize<T>(
|
|
140
143
|
load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
|
|
141
144
|
),
|
|
145
|
+
message,
|
|
142
146
|
);
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
/**
|
|
146
150
|
* Tests if a >= b
|
|
147
151
|
*/
|
|
148
|
-
toBeGreaterOrEqualTo(value: T): void {
|
|
152
|
+
toBeGreaterOrEqualTo(value: T, message: string = ""): void {
|
|
149
153
|
if (!isInteger<T>() && !isFloat<T>())
|
|
150
154
|
ERROR("toBeGreaterOrEqualTo() can only be used on number types!");
|
|
151
155
|
|
|
@@ -161,13 +165,14 @@ export class Expectation<T> extends Tests {
|
|
|
161
165
|
visualize<T>(
|
|
162
166
|
load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
|
|
163
167
|
),
|
|
168
|
+
message,
|
|
164
169
|
);
|
|
165
170
|
}
|
|
166
171
|
|
|
167
172
|
/**
|
|
168
173
|
* Tests if a < b
|
|
169
174
|
*/
|
|
170
|
-
toBeLessThan(value: T): void {
|
|
175
|
+
toBeLessThan(value: T, message: string = ""): void {
|
|
171
176
|
if (!isInteger<T>() && !isFloat<T>())
|
|
172
177
|
ERROR("toBeLessThan() can only be used on number types!");
|
|
173
178
|
|
|
@@ -183,13 +188,14 @@ export class Expectation<T> extends Tests {
|
|
|
183
188
|
visualize<T>(
|
|
184
189
|
load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
|
|
185
190
|
),
|
|
191
|
+
message,
|
|
186
192
|
);
|
|
187
193
|
}
|
|
188
194
|
|
|
189
195
|
/**
|
|
190
196
|
* Tests if a <= b
|
|
191
197
|
*/
|
|
192
|
-
toBeLessThanOrEqualTo(value: T): void {
|
|
198
|
+
toBeLessThanOrEqualTo(value: T, message: string = ""): void {
|
|
193
199
|
if (!isInteger<T>() && !isFloat<T>())
|
|
194
200
|
ERROR("toBeLessThanOrEqualTo() can only be used on number types!");
|
|
195
201
|
|
|
@@ -205,93 +211,127 @@ export class Expectation<T> extends Tests {
|
|
|
205
211
|
visualize<T>(
|
|
206
212
|
load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
|
|
207
213
|
),
|
|
214
|
+
message,
|
|
208
215
|
);
|
|
209
216
|
}
|
|
210
217
|
|
|
211
218
|
/**
|
|
212
219
|
* Tests if a is string
|
|
213
220
|
*/
|
|
214
|
-
toBeString(): void {
|
|
215
|
-
this._resolve(
|
|
221
|
+
toBeString(message: string = ""): void {
|
|
222
|
+
this._resolve(
|
|
223
|
+
isString<T>(),
|
|
224
|
+
"toBeString",
|
|
225
|
+
q(nameof<T>()),
|
|
226
|
+
q("string"),
|
|
227
|
+
message,
|
|
228
|
+
);
|
|
216
229
|
}
|
|
217
230
|
|
|
218
231
|
/**
|
|
219
232
|
* Tests if a is boolean
|
|
220
233
|
*/
|
|
221
|
-
toBeBoolean(): void {
|
|
222
|
-
this._resolve(
|
|
234
|
+
toBeBoolean(message: string = ""): void {
|
|
235
|
+
this._resolve(
|
|
236
|
+
isBoolean<T>(),
|
|
237
|
+
"toBeBoolean",
|
|
238
|
+
q(nameof<T>()),
|
|
239
|
+
q("boolean"),
|
|
240
|
+
message,
|
|
241
|
+
);
|
|
223
242
|
}
|
|
224
243
|
|
|
225
244
|
/**
|
|
226
245
|
* Tests if a is array
|
|
227
246
|
*/
|
|
228
|
-
toBeArray(): void {
|
|
229
|
-
this._resolve(
|
|
247
|
+
toBeArray(message: string = ""): void {
|
|
248
|
+
this._resolve(
|
|
249
|
+
isArray<T>(),
|
|
250
|
+
"toBeArray",
|
|
251
|
+
q(nameof<T>()),
|
|
252
|
+
q("Array<any>"),
|
|
253
|
+
message,
|
|
254
|
+
);
|
|
230
255
|
}
|
|
231
256
|
|
|
232
257
|
/**
|
|
233
258
|
* Tests if a is number
|
|
234
259
|
*/
|
|
235
|
-
toBeNumber(): void {
|
|
260
|
+
toBeNumber(message: string = ""): void {
|
|
236
261
|
this._resolve(
|
|
237
262
|
isFloat<T>() || isInteger<T>(),
|
|
238
263
|
"toBeNumber",
|
|
239
264
|
q(nameof<T>()),
|
|
240
265
|
q("number"),
|
|
266
|
+
message,
|
|
241
267
|
);
|
|
242
268
|
}
|
|
243
269
|
|
|
244
270
|
/**
|
|
245
271
|
* Tests if a is integer
|
|
246
272
|
*/
|
|
247
|
-
toBeInteger(): void {
|
|
248
|
-
this._resolve(
|
|
273
|
+
toBeInteger(message: string = ""): void {
|
|
274
|
+
this._resolve(
|
|
275
|
+
isInteger<T>(),
|
|
276
|
+
"toBeInteger",
|
|
277
|
+
q(nameof<T>()),
|
|
278
|
+
q("integer"),
|
|
279
|
+
message,
|
|
280
|
+
);
|
|
249
281
|
}
|
|
250
282
|
|
|
251
283
|
/**
|
|
252
284
|
* Tests if a is float
|
|
253
285
|
*/
|
|
254
|
-
toBeFloat(): void {
|
|
255
|
-
this._resolve(
|
|
286
|
+
toBeFloat(message: string = ""): void {
|
|
287
|
+
this._resolve(
|
|
288
|
+
isFloat<T>(),
|
|
289
|
+
"toBeFloat",
|
|
290
|
+
q(nameof<T>()),
|
|
291
|
+
q("float"),
|
|
292
|
+
message,
|
|
293
|
+
);
|
|
256
294
|
}
|
|
257
295
|
|
|
258
296
|
/**
|
|
259
297
|
* Tests if a is finite
|
|
260
298
|
*/
|
|
261
|
-
toBeFinite(): void {
|
|
299
|
+
toBeFinite(message: string = ""): void {
|
|
262
300
|
// @ts-ignore
|
|
263
301
|
const passed = (isFloat<T>() || isInteger<T>()) && isFinite(this._left);
|
|
264
|
-
this._resolve(passed, "toBeFinite", q("Infinity"), q("Finite"));
|
|
302
|
+
this._resolve(passed, "toBeFinite", q("Infinity"), q("Finite"), message);
|
|
265
303
|
}
|
|
266
304
|
|
|
267
305
|
/**
|
|
268
306
|
* Tests if a value is truthy
|
|
269
307
|
*/
|
|
270
|
-
toBeTruthy(): void {
|
|
308
|
+
toBeTruthy(message: string = ""): void {
|
|
271
309
|
this._resolve(
|
|
272
310
|
isTruthy<T>(this._left),
|
|
273
311
|
"toBeTruthy",
|
|
274
312
|
q("falsy"),
|
|
275
313
|
q("truthy"),
|
|
314
|
+
message,
|
|
276
315
|
);
|
|
277
316
|
}
|
|
278
317
|
|
|
279
318
|
/**
|
|
280
319
|
* Tests if a value is falsy
|
|
281
320
|
*/
|
|
282
|
-
toBeFalsy(): void {
|
|
321
|
+
toBeFalsy(message: string = ""): void {
|
|
283
322
|
this._resolve(
|
|
284
323
|
!isTruthy<T>(this._left),
|
|
285
324
|
"toBeFalsy",
|
|
286
325
|
q("truthy"),
|
|
287
326
|
q("falsy"),
|
|
327
|
+
message,
|
|
288
328
|
);
|
|
289
329
|
}
|
|
290
330
|
|
|
291
331
|
/**
|
|
292
332
|
* Tests if a floating-point number is close to expected
|
|
293
333
|
*/
|
|
294
|
-
toBeCloseTo(expected: T, precision: i32 = 2): void {
|
|
334
|
+
toBeCloseTo(expected: T, precision: i32 = 2, message: string = ""): void {
|
|
295
335
|
if (!isFloat<T>() && !isInteger<T>())
|
|
296
336
|
ERROR("toBeCloseTo() can only be used on number types!");
|
|
297
337
|
const factor = Math.pow(10, precision as f64);
|
|
@@ -302,67 +342,74 @@ export class Expectation<T> extends Tests {
|
|
|
302
342
|
"toBeCloseTo",
|
|
303
343
|
visualize<T>(this._left),
|
|
304
344
|
visualize<T>(expected),
|
|
345
|
+
message,
|
|
305
346
|
);
|
|
306
347
|
}
|
|
307
348
|
|
|
308
349
|
/**
|
|
309
350
|
* Tests if a string contains substring
|
|
310
351
|
*/
|
|
311
|
-
toMatch(value: string): void {
|
|
352
|
+
toMatch(value: string, message: string = ""): void {
|
|
312
353
|
if (!isString<T>()) ERROR("toMatch() can only be used on string types!");
|
|
313
354
|
// @ts-ignore
|
|
314
355
|
const passed = this._left.indexOf(value) >= 0;
|
|
315
356
|
// @ts-ignore
|
|
316
|
-
this._resolve(passed, "toMatch", q(this._left as string), q(value));
|
|
357
|
+
this._resolve(passed, "toMatch", q(this._left as string), q(value), message);
|
|
317
358
|
}
|
|
318
359
|
|
|
319
360
|
/**
|
|
320
361
|
* Tests if a string starts with the provided prefix.
|
|
321
362
|
*/
|
|
322
|
-
toStartWith(value: string): void {
|
|
363
|
+
toStartWith(value: string, message: string = ""): void {
|
|
323
364
|
if (!isString<T>())
|
|
324
365
|
ERROR("toStartWith() can only be used on string types!");
|
|
325
366
|
// @ts-ignore
|
|
326
367
|
const left = this._left as string;
|
|
327
368
|
const passed = left.indexOf(value) == 0;
|
|
328
|
-
this._resolve(passed, "toStartWith", q(left), q(value));
|
|
369
|
+
this._resolve(passed, "toStartWith", q(left), q(value), message);
|
|
329
370
|
}
|
|
330
371
|
|
|
331
372
|
/**
|
|
332
373
|
* Tests if a string ends with the provided suffix.
|
|
333
374
|
*/
|
|
334
|
-
toEndWith(value: string): void {
|
|
375
|
+
toEndWith(value: string, message: string = ""): void {
|
|
335
376
|
if (!isString<T>()) ERROR("toEndWith() can only be used on string types!");
|
|
336
377
|
// @ts-ignore
|
|
337
378
|
const left = this._left as string;
|
|
338
379
|
const idx = left.lastIndexOf(value);
|
|
339
380
|
const passed = idx >= 0 && idx + value.length == left.length;
|
|
340
|
-
this._resolve(passed, "toEndWith", q(left), q(value));
|
|
381
|
+
this._resolve(passed, "toEndWith", q(left), q(value), message);
|
|
341
382
|
}
|
|
342
383
|
|
|
343
384
|
/**
|
|
344
385
|
* Tests if an array has length x
|
|
345
386
|
*/
|
|
346
|
-
toHaveLength(value: i32): void {
|
|
387
|
+
toHaveLength(value: i32, message: string = ""): void {
|
|
347
388
|
// @ts-ignore
|
|
348
389
|
const leftLen = this._left.length as i32;
|
|
349
390
|
// @ts-ignore
|
|
350
391
|
const passed = isArray<T>() && leftLen == value;
|
|
351
|
-
this._resolve(
|
|
392
|
+
this._resolve(
|
|
393
|
+
passed,
|
|
394
|
+
"toHaveLength",
|
|
395
|
+
leftLen.toString(),
|
|
396
|
+
value.toString(),
|
|
397
|
+
message,
|
|
398
|
+
);
|
|
352
399
|
}
|
|
353
400
|
|
|
354
401
|
/**
|
|
355
402
|
* Tests if an array or string contains a value
|
|
356
403
|
*/
|
|
357
404
|
// @ts-ignore
|
|
358
|
-
toContain(value: valueof<T
|
|
405
|
+
toContain(value: valueof<T>, message: string = ""): void {
|
|
359
406
|
if (isString<T>()) {
|
|
360
407
|
// @ts-ignore
|
|
361
408
|
const left = this._left as string;
|
|
362
409
|
// @ts-ignore
|
|
363
410
|
const needle = value as string;
|
|
364
411
|
const passed = left.indexOf(needle) >= 0;
|
|
365
|
-
this._resolve(passed, "toContain", q(left), q(needle));
|
|
412
|
+
this._resolve(passed, "toContain", q(left), q(needle), message);
|
|
366
413
|
return;
|
|
367
414
|
}
|
|
368
415
|
|
|
@@ -374,6 +421,7 @@ export class Expectation<T> extends Tests {
|
|
|
374
421
|
"toContain",
|
|
375
422
|
stringifyValue<T>(this._left),
|
|
376
423
|
stringifyValue<valueof<T>>(value),
|
|
424
|
+
message,
|
|
377
425
|
);
|
|
378
426
|
return;
|
|
379
427
|
}
|
|
@@ -385,28 +433,28 @@ export class Expectation<T> extends Tests {
|
|
|
385
433
|
* Alias for toContain().
|
|
386
434
|
*/
|
|
387
435
|
// @ts-ignore
|
|
388
|
-
toContains(value: valueof<T
|
|
389
|
-
this.toContain(value);
|
|
436
|
+
toContains(value: valueof<T>, message: string = ""): void {
|
|
437
|
+
this.toContain(value, message);
|
|
390
438
|
}
|
|
391
439
|
|
|
392
440
|
/**
|
|
393
441
|
* Tests if serialized value matches stored snapshot.
|
|
394
442
|
*/
|
|
395
|
-
toMatchSnapshot(name: string = ""): void {
|
|
443
|
+
toMatchSnapshot(name: string = "", message: string = ""): void {
|
|
396
444
|
let key = name.length
|
|
397
445
|
? namedSnapshotKey(this._snapshotKey, name)
|
|
398
446
|
: nextUnnamedSnapshotKey(this._snapshotKey);
|
|
399
447
|
|
|
400
448
|
const actual = stringifyValue<T>(this._left);
|
|
401
449
|
const res = snapshotAssert(key, actual);
|
|
402
|
-
this._resolve(res.ok, "toMatchSnapshot", actual, res.expected);
|
|
450
|
+
this._resolve(res.ok, "toMatchSnapshot", actual, res.expected, message);
|
|
403
451
|
}
|
|
404
452
|
|
|
405
453
|
/**
|
|
406
454
|
* Delegates throw assertions to try-as when available.
|
|
407
455
|
* If try-as is unavailable, this matcher is disabled and warns once.
|
|
408
456
|
*/
|
|
409
|
-
toThrow(): void {
|
|
457
|
+
toThrow(message: string = ""): void {
|
|
410
458
|
// @ts-ignore
|
|
411
459
|
if (!isDefined(AS_TEST_TRY_AS)) {
|
|
412
460
|
if (!warnedToThrowDisabled) {
|
|
@@ -415,7 +463,7 @@ export class Expectation<T> extends Tests {
|
|
|
415
463
|
);
|
|
416
464
|
warnedToThrowDisabled = true;
|
|
417
465
|
}
|
|
418
|
-
this._resolve(true, "toThrow", q("disabled"), q("disabled"));
|
|
466
|
+
this._resolve(true, "toThrow", q("disabled"), q("disabled"), message);
|
|
419
467
|
return;
|
|
420
468
|
}
|
|
421
469
|
|
|
@@ -425,13 +473,13 @@ export class Expectation<T> extends Tests {
|
|
|
425
473
|
// @ts-ignore
|
|
426
474
|
__ExceptionState.Failures--;
|
|
427
475
|
}
|
|
428
|
-
this._resolve(passed, "toThrow", q("throws"), q("throws"));
|
|
476
|
+
this._resolve(passed, "toThrow", q("throws"), q("throws"), message);
|
|
429
477
|
}
|
|
430
478
|
|
|
431
479
|
/**
|
|
432
480
|
* Tests for equality
|
|
433
481
|
*/
|
|
434
|
-
toBe(equals: T): void {
|
|
482
|
+
toBe(equals: T, message: string = ""): void {
|
|
435
483
|
const passed = this._left === equals;
|
|
436
484
|
|
|
437
485
|
this._resolve(
|
|
@@ -439,32 +487,35 @@ export class Expectation<T> extends Tests {
|
|
|
439
487
|
"toBe",
|
|
440
488
|
stringifyValue<T>(this._left),
|
|
441
489
|
stringifyValue<T>(equals),
|
|
490
|
+
message,
|
|
442
491
|
);
|
|
443
492
|
}
|
|
444
493
|
|
|
445
494
|
/**
|
|
446
495
|
* Tests for deep equality
|
|
447
496
|
*/
|
|
448
|
-
toEqual(equals: T): void {
|
|
497
|
+
toEqual(equals: T, message: string = ""): void {
|
|
449
498
|
const passed = valueEquals<T>(this._left, equals, false);
|
|
450
499
|
this._resolve(
|
|
451
500
|
passed,
|
|
452
501
|
"toEqual",
|
|
453
502
|
stringifyValue<T>(this._left),
|
|
454
503
|
stringifyValue<T>(equals),
|
|
504
|
+
message,
|
|
455
505
|
);
|
|
456
506
|
}
|
|
457
507
|
|
|
458
508
|
/**
|
|
459
509
|
* Tests for strict deep equality
|
|
460
510
|
*/
|
|
461
|
-
toStrictEqual(equals: T): void {
|
|
511
|
+
toStrictEqual(equals: T, message: string = ""): void {
|
|
462
512
|
const passed = valueEquals<T>(this._left, equals, true);
|
|
463
513
|
this._resolve(
|
|
464
514
|
passed,
|
|
465
515
|
"toStrictEqual",
|
|
466
516
|
stringifyValue<T>(this._left),
|
|
467
517
|
stringifyValue<T>(equals),
|
|
518
|
+
message,
|
|
468
519
|
);
|
|
469
520
|
}
|
|
470
521
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export function formatValue<T>(value: T, deep: boolean = false): string {
|
|
2
|
+
if (isNullable<T>() && changetype<usize>(value) == <usize>0) {
|
|
3
|
+
return "null";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (isString<T>()) {
|
|
7
|
+
const text = value as string;
|
|
8
|
+
return deep ? "'" + text + "'" : text;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (isBoolean<T>() || isInteger<T>() || isFloat<T>()) {
|
|
12
|
+
// @ts-expect-error: primitive formatting
|
|
13
|
+
return value.toString();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (isArray<T>()) {
|
|
17
|
+
// @ts-expect-error: array-like handling
|
|
18
|
+
const values = value as valueof<T>[];
|
|
19
|
+
if (!values.length) return "[]";
|
|
20
|
+
let out = "[";
|
|
21
|
+
for (let i = 0; i < values.length; i++) {
|
|
22
|
+
if (i) out += ", ";
|
|
23
|
+
out += formatValue<valueof<T>>(unchecked(values[i]), true);
|
|
24
|
+
}
|
|
25
|
+
out += "]";
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (value instanceof Map) {
|
|
30
|
+
// @ts-expect-error: generic runtime access
|
|
31
|
+
const keys = value.keys();
|
|
32
|
+
if (!keys.length) return "Map(0) {}";
|
|
33
|
+
// @ts-expect-error: generic runtime access
|
|
34
|
+
const values = value.values();
|
|
35
|
+
let out = "Map(" + keys.length.toString() + ") { ";
|
|
36
|
+
for (let i = 0; i < keys.length; i++) {
|
|
37
|
+
if (i) out += ", ";
|
|
38
|
+
out += formatValue(changetype<valueof<typeof keys>>(unchecked(keys[i])), true);
|
|
39
|
+
out += " => ";
|
|
40
|
+
out += formatValue(
|
|
41
|
+
changetype<valueof<typeof values>>(unchecked(values[i])),
|
|
42
|
+
true,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
out += " }";
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (value instanceof Set) {
|
|
50
|
+
// @ts-expect-error: generic runtime access
|
|
51
|
+
const values = value.values();
|
|
52
|
+
if (!values.length) return "Set(0) {}";
|
|
53
|
+
let out = "Set(" + values.length.toString() + ") { ";
|
|
54
|
+
for (let i = 0; i < values.length; i++) {
|
|
55
|
+
if (i) out += ", ";
|
|
56
|
+
out += formatValue(
|
|
57
|
+
changetype<valueof<typeof values>>(unchecked(values[i])),
|
|
58
|
+
true,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
out += " }";
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (isManaged<T>()) {
|
|
66
|
+
// @ts-expect-error: custom serializer when provided
|
|
67
|
+
if (isDefined(value.__as_test_json)) {
|
|
68
|
+
// @ts-expect-error: dynamic method dispatch
|
|
69
|
+
return value.__as_test_json();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return nameof<T>();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@inline
|
|
77
|
+
export function colorText(format: i32[], text: string): string {
|
|
78
|
+
return `\u001b[${format[0].toString()}m${text}\u001b[${format[1].toString()}m`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@inline
|
|
82
|
+
export function red(text: string): string {
|
|
83
|
+
return colorText([31, 39], text);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@inline
|
|
87
|
+
export function green(text: string): string {
|
|
88
|
+
return colorText([32, 39], text);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@inline
|
|
92
|
+
export function bgRed(text: string): string {
|
|
93
|
+
return colorText([41, 49], text);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@inline
|
|
97
|
+
export function bgGreen(text: string): string {
|
|
98
|
+
return colorText([42, 49], text);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@inline
|
|
102
|
+
export function bold(text: string): string {
|
|
103
|
+
return colorText([1, 22], text);
|
|
104
|
+
}
|
package/assembly/util/helpers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bgGreen, bgRed } from "./format";
|
|
2
2
|
|
|
3
3
|
export function visualize<T>(value: T): string {
|
|
4
4
|
if (isNullable<T>() && changetype<usize>(value) == <usize>0) {
|
|
@@ -61,16 +61,16 @@ export function diff(left: string, right: string, not: boolean = false): Diff {
|
|
|
61
61
|
const rChar = right.charAt(i);
|
|
62
62
|
if (not) {
|
|
63
63
|
if (lChar == rChar) {
|
|
64
|
-
lDiff +=
|
|
65
|
-
rDiff +=
|
|
64
|
+
lDiff += bgGreen(rChar);
|
|
65
|
+
rDiff += bgRed(lChar);
|
|
66
66
|
} else {
|
|
67
67
|
lDiff += rChar;
|
|
68
68
|
rDiff += lChar;
|
|
69
69
|
}
|
|
70
70
|
} else {
|
|
71
71
|
if (lChar != rChar) {
|
|
72
|
-
lDiff +=
|
|
73
|
-
rDiff +=
|
|
72
|
+
lDiff += bgGreen(rChar);
|
|
73
|
+
rDiff += bgRed(lChar);
|
|
74
74
|
} else {
|
|
75
75
|
lDiff += rChar;
|
|
76
76
|
rDiff += lChar;
|
|
@@ -80,9 +80,9 @@ export function diff(left: string, right: string, not: boolean = false): Diff {
|
|
|
80
80
|
|
|
81
81
|
if (!not) {
|
|
82
82
|
for (; i < left.length; i++) {
|
|
83
|
-
rDiff +=
|
|
83
|
+
rDiff += bgRed(left.charAt(i));
|
|
84
84
|
}
|
|
85
|
-
for (; i < right.length; i++) lDiff +=
|
|
85
|
+
for (; i < right.length; i++) lDiff += bgRed(right.charAt(i));
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
return {
|
|
@@ -90,9 +90,3 @@ export function diff(left: string, right: string, not: boolean = false): Diff {
|
|
|
90
90
|
right: rDiff,
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
// @ts-ignore
|
|
95
|
-
@inline
|
|
96
|
-
export function colorText(format: i32[], text: string): string {
|
|
97
|
-
return `\u001b[${format[0].toString()}m${text}\u001b[${format[1].toString()}m`;
|
|
98
|
-
}
|
package/assembly/util/json.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatValue } from "./format";
|
|
2
2
|
|
|
3
3
|
export function quote(value: string): string {
|
|
4
4
|
return '"' + escape(value) + '"';
|
|
@@ -36,7 +36,7 @@ export function stringifyValue<T>(value: T): string {
|
|
|
36
36
|
return value.__as_test_json();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const formatted =
|
|
39
|
+
const formatted = formatValue<T>(value);
|
|
40
40
|
if (formatted != "none") {
|
|
41
41
|
return quote(formatted);
|
|
42
42
|
}
|
package/assembly/util/wipc.ts
CHANGED
|
@@ -305,9 +305,12 @@ function wasiWriteAll(data: ArrayBuffer): void {
|
|
|
305
305
|
store<usize>(iovPtr, <usize>left, sizeof<usize>());
|
|
306
306
|
store<u32>(writtenPtr, 0, 0);
|
|
307
307
|
const errno = wasi_fd_write(1, iovPtr, 1, writtenPtr);
|
|
308
|
+
if (errno == WASI_ERRNO_AGAIN || errno == WASI_ERRNO_INTR) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
308
311
|
if (errno != 0) return;
|
|
309
312
|
const written = <i32>load<u32>(writtenPtr, 0);
|
|
310
|
-
if (written <= 0)
|
|
313
|
+
if (written <= 0) continue;
|
|
311
314
|
offset += written;
|
|
312
315
|
}
|
|
313
316
|
}
|
|
@@ -3,36 +3,50 @@ import { existsSync, rmSync } from "fs";
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import { applyMode, loadConfig } from "../util.js";
|
|
5
5
|
const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
|
|
6
|
-
export async function clean(configPath = DEFAULT_CONFIG_PATH, modes = [undefined]) {
|
|
6
|
+
export async function clean(configPath = DEFAULT_CONFIG_PATH, modes = [undefined], fullClean = false) {
|
|
7
7
|
const loadedConfig = loadConfig(configPath, true);
|
|
8
8
|
const targets = new Map();
|
|
9
9
|
const ownership = buildOwnershipMap(loadedConfig);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
if (fullClean) {
|
|
11
|
+
collectRootTarget(targets, loadedConfig.outDir, "build");
|
|
12
|
+
collectRootTarget(targets, loadedConfig.fuzz.crashDir, "crashes");
|
|
13
|
+
collectRootTarget(targets, loadedConfig.coverageDir, "coverage");
|
|
14
|
+
collectRootTarget(targets, loadedConfig.logs, "logs");
|
|
15
|
+
for (const modeName of modes) {
|
|
16
|
+
const active = applyMode(loadedConfig, modeName).config;
|
|
17
|
+
collectTarget(targets, active.outDir, modeName, "build");
|
|
18
|
+
collectTarget(targets, active.fuzz.crashDir, modeName, "crashes");
|
|
19
|
+
collectTarget(targets, active.coverageDir, modeName, "coverage");
|
|
20
|
+
collectTarget(targets, active.logs, modeName, "logs");
|
|
21
|
+
}
|
|
22
|
+
pruneNestedTargets(targets);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
for (const modeName of modes) {
|
|
26
|
+
const active = applyMode(loadedConfig, modeName).config;
|
|
27
|
+
collectTarget(targets, active.outDir, modeName, "build");
|
|
28
|
+
collectTarget(targets, active.fuzz.crashDir, modeName, "crashes");
|
|
29
|
+
collectTarget(targets, active.coverageDir, modeName, "coverage");
|
|
30
|
+
collectTarget(targets, active.logs, modeName, "logs");
|
|
31
|
+
}
|
|
15
32
|
}
|
|
16
33
|
let removed = 0;
|
|
17
|
-
let skipped = 0;
|
|
18
34
|
for (const [targetPath, owners] of [...targets.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
if (!fullClean) {
|
|
36
|
+
const allOwners = ownership.get(targetPath) ?? owners;
|
|
37
|
+
const unselectedOwners = allOwners.filter((owner) => !owners.includes(owner));
|
|
38
|
+
if (unselectedOwners.length) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
25
41
|
}
|
|
26
42
|
if (!existsSync(targetPath)) {
|
|
27
|
-
skipped++;
|
|
28
|
-
process.stdout.write(`${chalk.dim("skip")} ${toRelativePath(targetPath)} ${chalk.dim(`(${owners.join(", ")})`)}\n`);
|
|
29
43
|
continue;
|
|
30
44
|
}
|
|
31
45
|
rmSync(targetPath, { recursive: true, force: true });
|
|
32
46
|
removed++;
|
|
33
47
|
process.stdout.write(`${chalk.bgGreenBright.black(" CLEAN ")} ${toRelativePath(targetPath)} ${chalk.dim(`(${owners.join(", ")})`)}\n`);
|
|
34
48
|
}
|
|
35
|
-
process.stdout.write(`${chalk.bold("Summary:")} removed ${removed} path(s)
|
|
49
|
+
process.stdout.write(`${chalk.bold("Summary:")} removed ${removed} path(s)\n`);
|
|
36
50
|
}
|
|
37
51
|
function buildOwnershipMap(loadedConfig) {
|
|
38
52
|
const ownership = new Map();
|
|
@@ -44,10 +58,25 @@ function buildOwnershipMap(loadedConfig) {
|
|
|
44
58
|
const active = applyMode(loadedConfig, modeName).config;
|
|
45
59
|
collectOwnership(ownership, active.outDir, modeName, "build");
|
|
46
60
|
collectOwnership(ownership, active.fuzz.crashDir, modeName, "crashes");
|
|
61
|
+
collectOwnership(ownership, active.coverageDir, modeName, "coverage");
|
|
47
62
|
collectOwnership(ownership, active.logs, modeName, "logs");
|
|
48
63
|
}
|
|
49
64
|
return ownership;
|
|
50
65
|
}
|
|
66
|
+
function collectRootTarget(targets, rawPath, kind) {
|
|
67
|
+
if (!rawPath || rawPath == "none")
|
|
68
|
+
return;
|
|
69
|
+
const resolved = path.resolve(process.cwd(), rawPath);
|
|
70
|
+
ensureSafeCleanPath(resolved, rawPath, kind);
|
|
71
|
+
const owner = `all:${kind}`;
|
|
72
|
+
const existing = targets.get(resolved);
|
|
73
|
+
if (existing) {
|
|
74
|
+
if (!existing.includes(owner))
|
|
75
|
+
existing.push(owner);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
targets.set(resolved, [owner]);
|
|
79
|
+
}
|
|
51
80
|
function collectOwnership(ownership, rawPath, modeName, kind) {
|
|
52
81
|
if (!rawPath || rawPath == "none")
|
|
53
82
|
return;
|
|
@@ -76,6 +105,20 @@ function collectTarget(targets, rawPath, modeName, kind) {
|
|
|
76
105
|
}
|
|
77
106
|
targets.set(resolved, [owner]);
|
|
78
107
|
}
|
|
108
|
+
function pruneNestedTargets(targets) {
|
|
109
|
+
const paths = [...targets.keys()].sort((a, b) => a.length - b.length);
|
|
110
|
+
for (const targetPath of paths) {
|
|
111
|
+
for (const otherPath of paths) {
|
|
112
|
+
if (targetPath == otherPath)
|
|
113
|
+
continue;
|
|
114
|
+
const relative = path.relative(targetPath, otherPath);
|
|
115
|
+
if (!relative.length || relative == ".." || relative.startsWith(`..${path.sep}`)) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
targets.delete(otherPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
79
122
|
function ensureSafeCleanPath(resolvedPath, rawPath, kind) {
|
|
80
123
|
const cwd = path.resolve(process.cwd());
|
|
81
124
|
const relative = path.relative(cwd, resolvedPath);
|
package/bin/commands/clean.js
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { createInterface } from "readline";
|
|
1
3
|
import { clean } from "./clean-core.js";
|
|
4
|
+
import { loadConfig } from "../util.js";
|
|
2
5
|
export { clean } from "./clean-core.js";
|
|
3
|
-
export async function executeCleanCommand(configPath, selectedModes, resolveExecutionModes) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
+
export async function executeCleanCommand(rawArgs, configPath, selectedModes, resolveExecutionModes) {
|
|
7
|
+
const force = rawArgs.includes("-f") || rawArgs.includes("--force");
|
|
8
|
+
const modeTargets = selectedModes.length > 0
|
|
9
|
+
? resolveExecutionModes(configPath, selectedModes)
|
|
10
|
+
: resolveAllCleanModes(configPath);
|
|
11
|
+
if (!force && selectedModes.length == 0) {
|
|
12
|
+
await confirmFullClean(configPath);
|
|
13
|
+
}
|
|
14
|
+
await clean(configPath, modeTargets, selectedModes.length == 0);
|
|
15
|
+
}
|
|
16
|
+
function resolveAllCleanModes(configPath) {
|
|
17
|
+
const resolvedConfigPath = configPath ?? "./as-test.config.json";
|
|
18
|
+
const config = loadConfig(resolvedConfigPath, true);
|
|
19
|
+
return [undefined, ...Object.keys(config.modes)];
|
|
20
|
+
}
|
|
21
|
+
async function confirmFullClean(configPath) {
|
|
22
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
23
|
+
throw new Error('clean without --mode requires confirmation. Re-run with "-f" or "--force" to skip the prompt.');
|
|
24
|
+
}
|
|
25
|
+
const target = configPath ? ` in ${configPath}` : "";
|
|
26
|
+
process.stdout.write(chalk.bold.blue("◇ Confirm Clean") +
|
|
27
|
+
"\n" +
|
|
28
|
+
`│ This will remove configured build outputs, crash reports, and logs for every mode${target}.\n` +
|
|
29
|
+
"│\n");
|
|
30
|
+
const answer = await promptLine("Continue? [Y/n] ");
|
|
31
|
+
const normalized = answer.trim().toLowerCase();
|
|
32
|
+
if (normalized == "" || normalized == "y" || normalized == "yes")
|
|
33
|
+
return;
|
|
34
|
+
if (normalized == "n" || normalized == "no") {
|
|
35
|
+
process.stdout.write(chalk.dim("clean cancelled\n"));
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`invalid answer "${answer}". Expected yes or no.`);
|
|
39
|
+
}
|
|
40
|
+
function promptLine(question) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const rl = createInterface({
|
|
43
|
+
input: process.stdin,
|
|
44
|
+
output: process.stdout,
|
|
45
|
+
});
|
|
46
|
+
rl.question(question, (answer) => {
|
|
47
|
+
rl.close();
|
|
48
|
+
resolve(answer);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
6
51
|
}
|
package/bin/index.js
CHANGED
|
@@ -134,7 +134,7 @@ else if (COMMANDS.includes(args[0])) {
|
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
136
|
else if (command === "clean") {
|
|
137
|
-
executeCleanCommand(configPath, selectedModes, resolveExecutionModes).catch((error) => {
|
|
137
|
+
executeCleanCommand(_args, configPath, selectedModes, resolveExecutionModes).catch((error) => {
|
|
138
138
|
printCliError(error);
|
|
139
139
|
process.exit(1);
|
|
140
140
|
});
|
|
@@ -370,10 +370,11 @@ function printCommandHelp(command) {
|
|
|
370
370
|
}
|
|
371
371
|
if (command == "clean") {
|
|
372
372
|
process.stdout.write(chalk.bold("Usage: ast clean [flags]\n\n"));
|
|
373
|
-
process.stdout.write("Remove configured build outputs, crash reports, and logs
|
|
373
|
+
process.stdout.write("Remove configured build outputs, crash reports, and logs.\n\n");
|
|
374
374
|
process.stdout.write(chalk.bold("Flags:\n"));
|
|
375
375
|
process.stdout.write(" --config <path> Use a specific config file\n");
|
|
376
376
|
process.stdout.write(" --mode <name[,name...]> Clean one or multiple named modes\n");
|
|
377
|
+
process.stdout.write(" -f, --force Skip the full-clean confirmation prompt\n");
|
|
377
378
|
process.stdout.write(" --help, -h Show this help\n");
|
|
378
379
|
return;
|
|
379
380
|
}
|
package/bin/wipc.js
CHANGED
|
@@ -31,8 +31,12 @@ export class Channel {
|
|
|
31
31
|
return;
|
|
32
32
|
const idx = this.buffer.indexOf(Channel.MAGIC);
|
|
33
33
|
if (idx === -1) {
|
|
34
|
-
|
|
35
|
-
this.buffer
|
|
34
|
+
const keep = Math.min(this.buffer.length, Channel.MAGIC_PREFIX_MAX);
|
|
35
|
+
const flushLength = this.buffer.length - keep;
|
|
36
|
+
if (flushLength > 0) {
|
|
37
|
+
this.onPassthrough(this.buffer.subarray(0, flushLength));
|
|
38
|
+
this.buffer = this.buffer.subarray(flushLength);
|
|
39
|
+
}
|
|
36
40
|
return;
|
|
37
41
|
}
|
|
38
42
|
if (idx > 0) {
|
|
@@ -77,3 +81,4 @@ export class Channel {
|
|
|
77
81
|
}
|
|
78
82
|
Channel.MAGIC = Buffer.from("WIPC");
|
|
79
83
|
Channel.HEADER_SIZE = 9;
|
|
84
|
+
Channel.MAGIC_PREFIX_MAX = Channel.MAGIC.length - 1;
|
package/lib/build/index.js
CHANGED
|
@@ -96,7 +96,25 @@ function readExact(length) {
|
|
|
96
96
|
}
|
|
97
97
|
function writeRaw(data) {
|
|
98
98
|
const view = Buffer.from(data);
|
|
99
|
-
|
|
99
|
+
let offset = 0;
|
|
100
|
+
while (offset < view.byteLength) {
|
|
101
|
+
let written = 0;
|
|
102
|
+
try {
|
|
103
|
+
written = fs.writeSync(1, view, offset, view.byteLength - offset);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (error &&
|
|
107
|
+
typeof error == "object" &&
|
|
108
|
+
"code" in error &&
|
|
109
|
+
error.code == "EAGAIN") {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
if (!written)
|
|
115
|
+
continue;
|
|
116
|
+
offset += written;
|
|
117
|
+
}
|
|
100
118
|
}
|
|
101
119
|
function mergeImports(...groups) {
|
|
102
120
|
const out = {};
|
package/lib/src/index.ts
CHANGED
|
@@ -130,7 +130,25 @@ function readExact(length: number): ArrayBuffer {
|
|
|
130
130
|
|
|
131
131
|
function writeRaw(data: ArrayBuffer): void {
|
|
132
132
|
const view = Buffer.from(data);
|
|
133
|
-
|
|
133
|
+
let offset = 0;
|
|
134
|
+
while (offset < view.byteLength) {
|
|
135
|
+
let written = 0;
|
|
136
|
+
try {
|
|
137
|
+
written = fs.writeSync(1, view, offset, view.byteLength - offset);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (
|
|
140
|
+
error &&
|
|
141
|
+
typeof error == "object" &&
|
|
142
|
+
"code" in error &&
|
|
143
|
+
error.code == "EAGAIN"
|
|
144
|
+
) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
if (!written) continue;
|
|
150
|
+
offset += written;
|
|
151
|
+
}
|
|
134
152
|
}
|
|
135
153
|
|
|
136
154
|
function mergeImports(...groups: unknown[]): AnyImports {
|
package/package.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "as-test",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"author": "Jairus Tanaka",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/JairusSW/as-test.git"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"as-rainbow": "^0.1.0",
|
|
11
|
-
"as-console": "^7.0.0",
|
|
12
10
|
"chalk": "^5.6.2",
|
|
13
11
|
"glob": "^13.0.6",
|
|
14
12
|
"typer-diff": "^1.1.1",
|