zero-query 0.9.0 → 0.9.5
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/README.md +11 -9
- package/cli/commands/bundle.js +15 -2
- package/cli/commands/dev/devtools/js/elements.js +5 -0
- package/cli/scaffold/app/app.js +1 -1
- package/cli/scaffold/global.css +1 -2
- package/cli/scaffold/index.html +2 -1
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +544 -71
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +51 -3
- package/index.js +32 -4
- package/package.json +1 -1
- package/src/component.js +60 -10
- package/src/core.js +65 -13
- package/src/diff.js +11 -5
- package/src/expression.js +104 -16
- package/src/http.js +23 -3
- package/src/reactive.js +8 -2
- package/src/router.js +43 -9
- package/src/ssr.js +1 -1
- package/src/store.js +5 -0
- package/src/utils.js +203 -11
- package/tests/audit.test.js +4030 -0
- package/tests/cli.test.js +456 -0
- package/tests/component.test.js +1387 -0
- package/tests/core.test.js +934 -1
- package/tests/diff.test.js +891 -0
- package/tests/errors.test.js +179 -0
- package/tests/expression.test.js +569 -0
- package/tests/http.test.js +160 -1
- package/tests/reactive.test.js +320 -0
- package/tests/router.test.js +1187 -0
- package/tests/ssr.test.js +261 -0
- package/tests/store.test.js +210 -0
- package/tests/utils.test.js +729 -1
- package/types/store.d.ts +3 -0
- package/types/utils.d.ts +103 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// CLI utils — stripComments
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
describe('CLI — stripComments', () => {
|
|
9
|
+
let stripComments;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
// Dynamic import to handle CommonJS module
|
|
13
|
+
const mod = await import('../cli/utils.js');
|
|
14
|
+
stripComments = mod.stripComments || mod.default?.stripComments;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('strips single-line comments', () => {
|
|
18
|
+
expect(stripComments('let x = 1; // comment\nlet y = 2;')).toBe('let x = 1; \nlet y = 2;');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('strips block comments', () => {
|
|
22
|
+
expect(stripComments('let x = /* inline comment */ 1;')).toBe('let x = 1;');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('strips multi-line block comments', () => {
|
|
26
|
+
const input = 'a;\n/* line1\n line2\n*/\nb;';
|
|
27
|
+
const result = stripComments(input);
|
|
28
|
+
expect(result).toBe('a;\n\nb;');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('preserves single-quoted strings containing //', () => {
|
|
32
|
+
expect(stripComments("let x = 'http://example.com';")).toBe("let x = 'http://example.com';");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('preserves double-quoted strings containing //', () => {
|
|
36
|
+
expect(stripComments('let x = "http://example.com";')).toBe('let x = "http://example.com";');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('preserves template literals containing //', () => {
|
|
40
|
+
expect(stripComments('let x = `http://example.com`;')).toBe('let x = `http://example.com`;');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('preserves template literals with ${} containing //', () => {
|
|
44
|
+
const input = 'let x = `${a}//not-a-comment`;';
|
|
45
|
+
const result = stripComments(input);
|
|
46
|
+
expect(result).toContain('//not-a-comment');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('strips comment after string', () => {
|
|
50
|
+
expect(stripComments('let x = "hello"; // comment')).toBe('let x = "hello"; ');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('handles escaped quotes in strings', () => {
|
|
54
|
+
expect(stripComments("let x = 'it\\'s a test'; // comment")).toBe("let x = 'it\\'s a test'; ");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('preserves regex containing //', () => {
|
|
58
|
+
const input = 'let re = /http:\\/\\//; // comment';
|
|
59
|
+
const result = stripComments(input);
|
|
60
|
+
expect(result).toContain('/http:\\/\\//');
|
|
61
|
+
expect(result).not.toContain('// comment');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('handles empty string', () => {
|
|
65
|
+
expect(stripComments('')).toBe('');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('handles string with no comments', () => {
|
|
69
|
+
expect(stripComments('let x = 1;')).toBe('let x = 1;');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('handles consecutive comments', () => {
|
|
73
|
+
const input = '// comment1\n// comment2\ncode;';
|
|
74
|
+
const result = stripComments(input);
|
|
75
|
+
expect(result).toBe('\n\ncode;');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('handles block comment at start of file', () => {
|
|
79
|
+
expect(stripComments('/* header */\ncode;')).toBe('\ncode;');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('handles nested template literal in ${} block', () => {
|
|
83
|
+
const input = 'let x = `outer ${`inner`} end`;';
|
|
84
|
+
const result = stripComments(input);
|
|
85
|
+
expect(result).toBe(input);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// CLI utils — minify
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
describe('CLI — minify', () => {
|
|
95
|
+
let minify;
|
|
96
|
+
|
|
97
|
+
beforeEach(async () => {
|
|
98
|
+
const mod = await import('../cli/utils.js');
|
|
99
|
+
minify = mod.minify || mod.default?.minify;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('collapses whitespace', () => {
|
|
103
|
+
const result = minify('let x = 1;', '/* banner */');
|
|
104
|
+
expect(result).toContain('let x=1;');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('preserves banner', () => {
|
|
108
|
+
const result = minify('let x = 1;', '/* v1.0 */');
|
|
109
|
+
expect(result.startsWith('/* v1.0 */')).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('strips comments during minification', () => {
|
|
113
|
+
const result = minify('let x = 1; // comment\nlet y = 2;', '');
|
|
114
|
+
expect(result).not.toContain('// comment');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('preserves string content', () => {
|
|
118
|
+
const result = minify('let x = "hello world";', '');
|
|
119
|
+
expect(result).toContain('"hello world"');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('preserves template literal content', () => {
|
|
123
|
+
const result = minify('let x = `hello world`;', '');
|
|
124
|
+
expect(result).toContain('`hello world`');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('does not merge ++ into + +', () => {
|
|
128
|
+
const result = minify('a + +b', '');
|
|
129
|
+
expect(result).toContain('+ +');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('does not merge -- into - -', () => {
|
|
133
|
+
const result = minify('a - -b', '');
|
|
134
|
+
expect(result).toContain('- -');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('preserves space between keywords and identifiers', () => {
|
|
138
|
+
const result = minify('return value;', '');
|
|
139
|
+
expect(result).toContain('return value');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('removes block comments', () => {
|
|
143
|
+
const result = minify('a /* comment */ = 1;', '');
|
|
144
|
+
expect(result).not.toContain('comment');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('handles empty input', () => {
|
|
148
|
+
const result = minify('', '');
|
|
149
|
+
expect(result).toBe('\n');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// CLI utils — sizeKB
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
describe('CLI — sizeKB', () => {
|
|
159
|
+
let sizeKB;
|
|
160
|
+
|
|
161
|
+
beforeEach(async () => {
|
|
162
|
+
const mod = await import('../cli/utils.js');
|
|
163
|
+
sizeKB = mod.sizeKB || mod.default?.sizeKB;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('formats 1024 bytes as 1.0', () => {
|
|
167
|
+
expect(sizeKB({ length: 1024 })).toBe('1.0');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('formats 512 bytes as 0.5', () => {
|
|
171
|
+
expect(sizeKB({ length: 512 })).toBe('0.5');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('formats 0 bytes as 0.0', () => {
|
|
175
|
+
expect(sizeKB({ length: 0 })).toBe('0.0');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('formats 2560 bytes as 2.5', () => {
|
|
179
|
+
expect(sizeKB({ length: 2560 })).toBe('2.5');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('formats large size correctly', () => {
|
|
183
|
+
expect(sizeKB({ length: 102400 })).toBe('100.0');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
// CLI args — flag and option
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
describe('CLI — args module', () => {
|
|
193
|
+
let originalArgv;
|
|
194
|
+
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
originalArgv = [...process.argv];
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
afterEach(() => {
|
|
200
|
+
process.argv = originalArgv;
|
|
201
|
+
// Clear the require cache so the module re-reads argv
|
|
202
|
+
vi.resetModules();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('flag returns true when --verbose is present', async () => {
|
|
206
|
+
process.argv = ['node', 'script', '--verbose'];
|
|
207
|
+
const mod = await import('../cli/args.js');
|
|
208
|
+
const { flag } = mod;
|
|
209
|
+
expect(flag('verbose')).toBe(true);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('flag returns false when flag is absent', async () => {
|
|
213
|
+
process.argv = ['node', 'script'];
|
|
214
|
+
const mod = await import('../cli/args.js');
|
|
215
|
+
const { flag } = mod;
|
|
216
|
+
expect(flag('verbose')).toBe(false);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('flag detects short flag -v', async () => {
|
|
220
|
+
process.argv = ['node', 'script', '-v'];
|
|
221
|
+
const mod = await import('../cli/args.js');
|
|
222
|
+
const { flag } = mod;
|
|
223
|
+
expect(flag('verbose', 'v')).toBe(true);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('option reads value after --port', async () => {
|
|
227
|
+
process.argv = ['node', 'script', '--port', '8080'];
|
|
228
|
+
const mod = await import('../cli/args.js');
|
|
229
|
+
const { option } = mod;
|
|
230
|
+
expect(option('port')).toBe('8080');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('option reads value after short -p', async () => {
|
|
234
|
+
process.argv = ['node', 'script', '-p', '3000'];
|
|
235
|
+
const mod = await import('../cli/args.js');
|
|
236
|
+
const { option } = mod;
|
|
237
|
+
expect(option('port', 'p')).toBe('3000');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('option returns fallback when missing', async () => {
|
|
241
|
+
process.argv = ['node', 'script'];
|
|
242
|
+
const mod = await import('../cli/args.js');
|
|
243
|
+
const { option } = mod;
|
|
244
|
+
expect(option('port', 'p', '3100')).toBe('3100');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('option returns fallback when flag has no value', async () => {
|
|
248
|
+
process.argv = ['node', 'script', '--port'];
|
|
249
|
+
const mod = await import('../cli/args.js');
|
|
250
|
+
const { option } = mod;
|
|
251
|
+
expect(option('port', 'p', '3100')).toBe('3100');
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
// ===========================================================================
|
|
257
|
+
// showHelp
|
|
258
|
+
// ===========================================================================
|
|
259
|
+
|
|
260
|
+
describe('CLI — showHelp', () => {
|
|
261
|
+
it('outputs help text to console', async () => {
|
|
262
|
+
const spy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
263
|
+
const showHelp = (await import('../cli/help.js')).default;
|
|
264
|
+
showHelp();
|
|
265
|
+
expect(spy).toHaveBeenCalled();
|
|
266
|
+
const text = spy.mock.calls.map(c => c.join(' ')).join('\n');
|
|
267
|
+
expect(text).toContain('create');
|
|
268
|
+
expect(text).toContain('dev');
|
|
269
|
+
expect(text).toContain('bundle');
|
|
270
|
+
expect(text).toContain('build');
|
|
271
|
+
spy.mockRestore();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('help text mentions port flag', async () => {
|
|
275
|
+
const spy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
276
|
+
const showHelp = (await import('../cli/help.js')).default;
|
|
277
|
+
showHelp();
|
|
278
|
+
const text = spy.mock.calls.map(c => c.join(' ')).join('\n');
|
|
279
|
+
expect(text).toContain('--port');
|
|
280
|
+
spy.mockRestore();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
// ===========================================================================
|
|
286
|
+
// stripComments — additional edge cases
|
|
287
|
+
// ===========================================================================
|
|
288
|
+
|
|
289
|
+
describe('CLI — stripComments extra', () => {
|
|
290
|
+
let stripComments;
|
|
291
|
+
beforeEach(async () => {
|
|
292
|
+
const mod = await import('../cli/utils.js');
|
|
293
|
+
stripComments = mod.stripComments;
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('handles consecutive single-line comments', () => {
|
|
297
|
+
const input = '// one\n// two\nconst x = 1;';
|
|
298
|
+
const result = stripComments(input);
|
|
299
|
+
expect(result).not.toContain('one');
|
|
300
|
+
expect(result).not.toContain('two');
|
|
301
|
+
expect(result).toContain('const x = 1');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('preserves code after block comment', () => {
|
|
305
|
+
const input = 'a /* comment */ + b';
|
|
306
|
+
const result = stripComments(input);
|
|
307
|
+
expect(result).toContain('a');
|
|
308
|
+
expect(result).toContain('+ b');
|
|
309
|
+
expect(result).not.toContain('comment');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('handles multiline block comment', () => {
|
|
313
|
+
const input = '/*\n line1\n line2\n*/\ncode();';
|
|
314
|
+
const result = stripComments(input);
|
|
315
|
+
expect(result).toContain('code()');
|
|
316
|
+
expect(result).not.toContain('line1');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('does not strip // inside template expression', () => {
|
|
320
|
+
const input = '`${a // b}`';
|
|
321
|
+
const result = stripComments(input);
|
|
322
|
+
// The template should be preserved as-is
|
|
323
|
+
expect(result).toContain('`');
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('handles code with no comments', () => {
|
|
327
|
+
const input = 'const x = 1;\nconst y = 2;';
|
|
328
|
+
expect(stripComments(input)).toBe(input);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
// ===========================================================================
|
|
334
|
+
// minify — additional edge cases
|
|
335
|
+
// ===========================================================================
|
|
336
|
+
|
|
337
|
+
describe('CLI — minify extra', () => {
|
|
338
|
+
let minify;
|
|
339
|
+
beforeEach(async () => {
|
|
340
|
+
const mod = await import('../cli/utils.js');
|
|
341
|
+
minify = mod.minify;
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('preserves string content with spaces', () => {
|
|
345
|
+
const result = minify('const msg = "hello world";', '');
|
|
346
|
+
expect(result).toContain('"hello world"');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('empty input returns banner only', () => {
|
|
350
|
+
const result = minify('', '/* banner */');
|
|
351
|
+
expect(result.trim()).toBe('/* banner */');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('collapses multiple newlines', () => {
|
|
355
|
+
const result = minify('a\n\n\n\nb', '');
|
|
356
|
+
// Should not have multiple consecutive newlines in output
|
|
357
|
+
expect(result).not.toMatch(/\n\n\n/);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('handles template literals with spaces', () => {
|
|
361
|
+
const result = minify('const x = `hello world`;', '');
|
|
362
|
+
expect(result).toContain('`hello world`');
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
// ===========================================================================
|
|
368
|
+
// sizeKB — edge cases
|
|
369
|
+
// ===========================================================================
|
|
370
|
+
|
|
371
|
+
describe('CLI — sizeKB extra', () => {
|
|
372
|
+
let sizeKB;
|
|
373
|
+
beforeEach(async () => {
|
|
374
|
+
const mod = await import('../cli/utils.js');
|
|
375
|
+
sizeKB = mod.sizeKB;
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('zero-length buffer', () => {
|
|
379
|
+
expect(sizeKB({ length: 0 })).toBe('0.0');
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('exactly 1KB', () => {
|
|
383
|
+
expect(sizeKB({ length: 1024 })).toBe('1.0');
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('small buffer', () => {
|
|
387
|
+
expect(sizeKB({ length: 100 })).toBe('0.1');
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
// ===========================================================================
|
|
393
|
+
// copyDirSync
|
|
394
|
+
// ===========================================================================
|
|
395
|
+
|
|
396
|
+
describe('CLI — copyDirSync', () => {
|
|
397
|
+
let copyDirSync;
|
|
398
|
+
const fs = require('fs');
|
|
399
|
+
const path = require('path');
|
|
400
|
+
const os = require('os');
|
|
401
|
+
|
|
402
|
+
beforeEach(async () => {
|
|
403
|
+
const mod = await import('../cli/utils.js');
|
|
404
|
+
copyDirSync = mod.copyDirSync;
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('copies directory recursively', () => {
|
|
408
|
+
const src = path.join(os.tmpdir(), 'zq-test-src-' + Date.now());
|
|
409
|
+
const dest = path.join(os.tmpdir(), 'zq-test-dest-' + Date.now());
|
|
410
|
+
|
|
411
|
+
// Create source structure
|
|
412
|
+
fs.mkdirSync(path.join(src, 'sub'), { recursive: true });
|
|
413
|
+
fs.writeFileSync(path.join(src, 'a.txt'), 'hello');
|
|
414
|
+
fs.writeFileSync(path.join(src, 'sub', 'b.txt'), 'world');
|
|
415
|
+
|
|
416
|
+
copyDirSync(src, dest);
|
|
417
|
+
|
|
418
|
+
expect(fs.existsSync(path.join(dest, 'a.txt'))).toBe(true);
|
|
419
|
+
expect(fs.readFileSync(path.join(dest, 'a.txt'), 'utf8')).toBe('hello');
|
|
420
|
+
expect(fs.existsSync(path.join(dest, 'sub', 'b.txt'))).toBe(true);
|
|
421
|
+
expect(fs.readFileSync(path.join(dest, 'sub', 'b.txt'), 'utf8')).toBe('world');
|
|
422
|
+
|
|
423
|
+
// Cleanup
|
|
424
|
+
fs.rmSync(src, { recursive: true, force: true });
|
|
425
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
// ===========================================================================
|
|
431
|
+
// flag/option — additional cases
|
|
432
|
+
// ===========================================================================
|
|
433
|
+
|
|
434
|
+
describe('CLI — flag/option extra', () => {
|
|
435
|
+
it('flag returns false when absent', async () => {
|
|
436
|
+
process.argv = ['node', 'script'];
|
|
437
|
+
vi.resetModules();
|
|
438
|
+
const { flag } = await import('../cli/args.js');
|
|
439
|
+
expect(flag('missing', 'm')).toBe(false);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('option with short form', async () => {
|
|
443
|
+
process.argv = ['node', 'script', '-o', '/dist'];
|
|
444
|
+
vi.resetModules();
|
|
445
|
+
const { option } = await import('../cli/args.js');
|
|
446
|
+
expect(option('output', 'o', 'default')).toBe('/dist');
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('multiple flags', async () => {
|
|
450
|
+
process.argv = ['node', 'script', '--verbose', '--watch'];
|
|
451
|
+
vi.resetModules();
|
|
452
|
+
const { flag } = await import('../cli/args.js');
|
|
453
|
+
expect(flag('verbose', 'v')).toBe(true);
|
|
454
|
+
expect(flag('watch', 'w')).toBe(true);
|
|
455
|
+
});
|
|
456
|
+
});
|