@naturalcycles/dev-lib 20.25.0 → 20.26.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/dist/bin/dev-lib.js +14 -11
- package/dist/check.util.d.ts +40 -8
- package/dist/check.util.js +148 -31
- package/dist/testing/testOffline.js +1 -1
- package/dist/vendor/mitm.d.ts +19 -0
- package/dist/vendor/mitm.js +98 -0
- package/package.json +5 -3
package/dist/bin/dev-lib.js
CHANGED
|
@@ -3,7 +3,7 @@ import { _by } from '@naturalcycles/js-lib/array/array.util.js';
|
|
|
3
3
|
import { _assert } from '@naturalcycles/js-lib/error/assert.js';
|
|
4
4
|
import { fs2 } from '@naturalcycles/nodejs-lib/fs2';
|
|
5
5
|
import { runScript } from '@naturalcycles/nodejs-lib/runScript';
|
|
6
|
-
import { buildCopy, buildProd, eslintAll, lintStagedCommand, requireOxlintConfig, runBiome, runCheck, runOxlint, runPrettier, runTest, stylelintAll, typecheckWithTSC, } from '../check.util.js';
|
|
6
|
+
import { buildCopy, buildProd, eslintAll, lintStagedCommand, requireOxlintConfig, runBiome, runCheck, runOxlint, runPrettier, runTest, stylelintAll, typecheckWithTS, typecheckWithTSC, typecheckWithTSGO, } from '../check.util.js';
|
|
7
7
|
import { runCommitlint } from '../commitlint.js';
|
|
8
8
|
const commands = [
|
|
9
9
|
{ name: 'check', fn: runCheck, desc: '"Run all possible checks": lint, typecheck, then test.' },
|
|
@@ -12,17 +12,21 @@ const commands = [
|
|
|
12
12
|
fn: quickCheck,
|
|
13
13
|
desc: 'Like check, but without slow parts, to perform preliminary checks',
|
|
14
14
|
},
|
|
15
|
-
{ name: 'bt', fn: bt, desc: 'Build & Test: run "typecheck"
|
|
15
|
+
{ name: 'bt', fn: bt, desc: 'Build & Test: run "typecheck" and then "test".' },
|
|
16
16
|
{
|
|
17
17
|
name: 'typecheck',
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
desc: 'Run typecheck via oxlint --type-aware',
|
|
18
|
+
fn: typecheckWithTS,
|
|
19
|
+
desc: 'Run typecheck via tsgo (if available) or tsc',
|
|
21
20
|
},
|
|
22
21
|
{
|
|
23
22
|
name: 'typecheck-with-tsc',
|
|
24
23
|
fn: typecheckWithTSC,
|
|
25
|
-
desc: 'Run typecheck (tsc) in folders (src, scripts, e2e) if there is tsconfig.json present.
|
|
24
|
+
desc: 'Run typecheck (tsc) in folders (src, scripts, e2e) if there is tsconfig.json present.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'typecheck-with-tsgo',
|
|
28
|
+
fn: typecheckWithTSGO,
|
|
29
|
+
desc: 'Run typecheck (tsgo) in folders (src, scripts, e2e) if there is tsconfig.json present.',
|
|
26
30
|
},
|
|
27
31
|
{
|
|
28
32
|
name: 'build',
|
|
@@ -63,7 +67,7 @@ const commands = [
|
|
|
63
67
|
{
|
|
64
68
|
name: 'lint',
|
|
65
69
|
fn: () => runCheck({
|
|
66
|
-
|
|
70
|
+
typecheck: false,
|
|
67
71
|
test: false,
|
|
68
72
|
}),
|
|
69
73
|
desc: 'Run all linters: eslint, prettier, stylelint, ktlint, actionlint.',
|
|
@@ -162,14 +166,13 @@ async function quickCheck() {
|
|
|
162
166
|
eslint: false,
|
|
163
167
|
prettier: false,
|
|
164
168
|
stylelint: false,
|
|
165
|
-
|
|
169
|
+
typecheck: false,
|
|
166
170
|
});
|
|
167
171
|
}
|
|
168
172
|
async function bt() {
|
|
169
|
-
// Still using
|
|
170
|
-
// todo: attempt to use tsgo!
|
|
173
|
+
// Still using ts, as oxlint is found to fail in certain cases
|
|
171
174
|
// await typecheckWithOxlint()
|
|
172
|
-
await
|
|
175
|
+
await typecheckWithTS();
|
|
173
176
|
runTest();
|
|
174
177
|
}
|
|
175
178
|
async function _typecheckWithOxlint() {
|
package/dist/check.util.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SemVerString } from '@naturalcycles/js-lib/types';
|
|
2
2
|
/**
|
|
3
3
|
* Every boolean defaults to true, so, by default - everything is being run.
|
|
4
4
|
* Pass false to skip it.
|
|
@@ -9,7 +9,13 @@ export interface CheckOptions {
|
|
|
9
9
|
stylelint?: boolean;
|
|
10
10
|
prettier?: boolean;
|
|
11
11
|
ktlint?: boolean;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* true - run tsgo, otherwise tsc
|
|
14
|
+
* tsgo - run tsgo
|
|
15
|
+
* tsc - run tsc
|
|
16
|
+
* false - skip
|
|
17
|
+
*/
|
|
18
|
+
typecheck?: boolean | 'tsc' | 'tsgo';
|
|
13
19
|
test?: boolean;
|
|
14
20
|
}
|
|
15
21
|
/**
|
|
@@ -24,27 +30,50 @@ interface EslintAllOptions {
|
|
|
24
30
|
}
|
|
25
31
|
/**
|
|
26
32
|
* Runs `eslint` command for all predefined paths (e.g /src, /scripts, etc).
|
|
33
|
+
*
|
|
34
|
+
* Returns true if it ran
|
|
35
|
+
*/
|
|
36
|
+
export declare function eslintAll(opt?: EslintAllOptions): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Returns true if it ran.
|
|
27
39
|
*/
|
|
28
|
-
export declare function
|
|
29
|
-
export declare function runOxlint(fix?: boolean): void;
|
|
40
|
+
export declare function runOxlint(fix?: boolean): boolean;
|
|
30
41
|
export declare function requireOxlintConfig(): void;
|
|
31
42
|
export declare function hasOxlintConfig(): boolean;
|
|
32
43
|
interface RunPrettierOptions {
|
|
33
44
|
experimentalCli?: boolean;
|
|
34
45
|
fix?: boolean;
|
|
35
46
|
}
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Returns true if it ran.
|
|
49
|
+
*/
|
|
50
|
+
export declare function runPrettier(opt?: RunPrettierOptions): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Returns true if it ran.
|
|
53
|
+
*/
|
|
54
|
+
export declare function stylelintAll(fix?: boolean): boolean;
|
|
38
55
|
export declare function lintStagedCommand(): Promise<void>;
|
|
39
56
|
export declare function requireActionlintVersion(): void;
|
|
40
57
|
export declare function getActionLintVersion(): SemVerString | undefined;
|
|
41
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Returns true if it ran.
|
|
60
|
+
*/
|
|
61
|
+
export declare function runBiome(fix?: boolean): boolean;
|
|
42
62
|
export declare function buildProd(): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Uses tsgo if it's installed, otherwise tsc
|
|
65
|
+
*/
|
|
66
|
+
export declare function typecheckWithTS(): Promise<void>;
|
|
43
67
|
export declare function typecheckWithTSC(): Promise<void>;
|
|
68
|
+
export declare function typecheckWithTSGO(): Promise<void>;
|
|
44
69
|
/**
|
|
45
70
|
* Use 'src' to indicate root.
|
|
46
71
|
*/
|
|
47
72
|
export declare function runTSCInFolders(dirs: string[], args?: string[], parallel?: boolean): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Use 'src' to indicate root.
|
|
75
|
+
*/
|
|
76
|
+
export declare function runTSGOInFolders(dirs: string[], args?: string[]): Promise<void>;
|
|
48
77
|
export declare function runTSCProd(args?: string[]): Promise<void>;
|
|
49
78
|
export declare function buildCopy(): void;
|
|
50
79
|
interface RunTestOptions {
|
|
@@ -52,6 +81,9 @@ interface RunTestOptions {
|
|
|
52
81
|
manual?: boolean;
|
|
53
82
|
leaks?: boolean;
|
|
54
83
|
}
|
|
55
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Returns true if it ran.
|
|
86
|
+
*/
|
|
87
|
+
export declare function runTest(opt?: RunTestOptions): boolean;
|
|
56
88
|
export declare function findPackageBinPath(pkg: string, cmd: string): string;
|
|
57
89
|
export {};
|
package/dist/check.util.js
CHANGED
|
@@ -8,6 +8,7 @@ import { _since } from '@naturalcycles/js-lib/datetime/time.util.js';
|
|
|
8
8
|
import { _assert } from '@naturalcycles/js-lib/error/assert.js';
|
|
9
9
|
import { _filterFalsyValues } from '@naturalcycles/js-lib/object/object.util.js';
|
|
10
10
|
import { semver2 } from '@naturalcycles/js-lib/semver';
|
|
11
|
+
import { _stringMapEntries, } from '@naturalcycles/js-lib/types';
|
|
11
12
|
import { dimGrey, white } from '@naturalcycles/nodejs-lib/colors';
|
|
12
13
|
import { exec2 } from '@naturalcycles/nodejs-lib/exec2';
|
|
13
14
|
import { fs2 } from '@naturalcycles/nodejs-lib/fs2';
|
|
@@ -22,8 +23,10 @@ const { CI, ESLINT_CONCURRENCY } = process.env;
|
|
|
22
23
|
* If full=false - the "slow" linters are skipped.
|
|
23
24
|
*/
|
|
24
25
|
export async function runCheck(opt = {}) {
|
|
25
|
-
const { fastLinters = true, eslint = true, stylelint = true, prettier = true, ktlint = true,
|
|
26
|
+
const { fastLinters = true, eslint = true, stylelint = true, prettier = true, ktlint = true, typecheck = true, test = true, } = opt;
|
|
26
27
|
const started = Date.now();
|
|
28
|
+
let s;
|
|
29
|
+
const timings = {};
|
|
27
30
|
// const { commitOnChanges, failOnChanges } = _yargs().options({
|
|
28
31
|
// commitOnChanges: {
|
|
29
32
|
// type: 'boolean',
|
|
@@ -47,34 +50,68 @@ export async function runCheck(opt = {}) {
|
|
|
47
50
|
if (fastLinters) {
|
|
48
51
|
// Fast linters (that run in <1 second) go first
|
|
49
52
|
runActionLint();
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
s = Date.now();
|
|
54
|
+
if (runBiome(fix)) {
|
|
55
|
+
timings['biome'] = Date.now() - s;
|
|
56
|
+
}
|
|
57
|
+
s = Date.now();
|
|
58
|
+
if (runOxlint(fix)) {
|
|
59
|
+
timings['oxlint'] = Date.now() - s;
|
|
60
|
+
}
|
|
52
61
|
}
|
|
53
62
|
// From this point we start the "slow" linters, with ESLint leading the way
|
|
54
63
|
if (eslint) {
|
|
55
64
|
// We run eslint BEFORE Prettier, because eslint can delete e.g unused imports.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
s = Date.now();
|
|
66
|
+
if (eslintAll({ fix })) {
|
|
67
|
+
timings['eslint'] = Date.now() - s;
|
|
68
|
+
}
|
|
59
69
|
}
|
|
60
70
|
if (stylelint &&
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
hasDependencyInNodeModules('stylelint') &&
|
|
72
|
+
hasDependencyInNodeModules('stylelint-config-standard-scss')) {
|
|
73
|
+
s = Date.now();
|
|
74
|
+
if (stylelintAll(fix)) {
|
|
75
|
+
timings['stylelint'] = Date.now() - s;
|
|
76
|
+
}
|
|
64
77
|
}
|
|
65
78
|
if (prettier) {
|
|
66
|
-
|
|
79
|
+
s = Date.now();
|
|
80
|
+
if (runPrettier({ fix })) {
|
|
81
|
+
timings['prettier'] = Date.now() - s;
|
|
82
|
+
}
|
|
67
83
|
}
|
|
68
84
|
if (ktlint) {
|
|
69
|
-
|
|
85
|
+
s = Date.now();
|
|
86
|
+
if (await runKTLint(fix)) {
|
|
87
|
+
timings['ktlint'] = Date.now() - s;
|
|
88
|
+
}
|
|
70
89
|
}
|
|
71
|
-
if (
|
|
72
|
-
|
|
90
|
+
if (typecheck) {
|
|
91
|
+
s = Date.now();
|
|
92
|
+
if (typecheck === 'tsgo') {
|
|
93
|
+
await typecheckWithTSGO();
|
|
94
|
+
timings['tsgo'] = Date.now() - s;
|
|
95
|
+
}
|
|
96
|
+
else if (typecheck === 'tsc') {
|
|
97
|
+
await typecheckWithTSC();
|
|
98
|
+
timings['tsc'] = Date.now() - s;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
await typecheckWithTS();
|
|
102
|
+
timings['typecheck'] = Date.now() - s;
|
|
103
|
+
}
|
|
73
104
|
}
|
|
74
105
|
if (test) {
|
|
75
|
-
|
|
106
|
+
s = Date.now();
|
|
107
|
+
if (runTest()) {
|
|
108
|
+
timings['test'] = Date.now() - s;
|
|
109
|
+
}
|
|
76
110
|
}
|
|
77
111
|
console.log(`${check(true)}${white(`check`)} ${dimGrey(`took ` + _since(started))}`);
|
|
112
|
+
for (const [job, ms] of _stringMapEntries(timings)) {
|
|
113
|
+
console.log(`${job.padStart(12, ' ')}: ${String(ms).padStart(5, ' ')} ms`);
|
|
114
|
+
}
|
|
78
115
|
// if (needToTrackChanges) {
|
|
79
116
|
// const gitStatusAfter = gitStatus()
|
|
80
117
|
// const hasChanges = gitStatusAfter !== gitStatusAtStart
|
|
@@ -97,6 +134,8 @@ export async function runCheck(opt = {}) {
|
|
|
97
134
|
}
|
|
98
135
|
/**
|
|
99
136
|
* Runs `eslint` command for all predefined paths (e.g /src, /scripts, etc).
|
|
137
|
+
*
|
|
138
|
+
* Returns true if it ran
|
|
100
139
|
*/
|
|
101
140
|
export function eslintAll(opt) {
|
|
102
141
|
const { argv } = _yargs().options({
|
|
@@ -114,13 +153,16 @@ export function eslintAll(opt) {
|
|
|
114
153
|
...opt,
|
|
115
154
|
};
|
|
116
155
|
const extensions = ext.split(',');
|
|
117
|
-
runESLint(extensions, fix);
|
|
156
|
+
return runESLint(extensions, fix);
|
|
118
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Returns true if it ran.
|
|
160
|
+
*/
|
|
119
161
|
function runESLint(extensions = eslintExtensions.split(','), fix = true) {
|
|
120
162
|
const eslintConfigPath = `eslint.config.js`;
|
|
121
163
|
if (!existsSync(eslintConfigPath)) {
|
|
122
164
|
// faster to bail-out like this
|
|
123
|
-
return;
|
|
165
|
+
return false;
|
|
124
166
|
}
|
|
125
167
|
// const tsconfigRootDir = [cwd, configDir !== '.' && configDir].filter(Boolean).join('/')
|
|
126
168
|
const eslintPath = findPackageBinPath('eslint', 'eslint');
|
|
@@ -149,11 +191,15 @@ function runESLint(extensions = eslintExtensions.split(','), fix = true) {
|
|
|
149
191
|
TIMING: CI ? 'true' : '',
|
|
150
192
|
}),
|
|
151
193
|
});
|
|
194
|
+
return true;
|
|
152
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Returns true if it ran.
|
|
198
|
+
*/
|
|
153
199
|
export function runOxlint(fix = true) {
|
|
154
200
|
if (!hasOxlintConfig()) {
|
|
155
201
|
console.log('.oxlintrc.json is not found, skipping to run oxlint');
|
|
156
|
-
return;
|
|
202
|
+
return false;
|
|
157
203
|
}
|
|
158
204
|
const oxlintPath = findPackageBinPath('oxlint', 'oxlint');
|
|
159
205
|
exec2.spawn(oxlintPath, {
|
|
@@ -169,6 +215,7 @@ export function runOxlint(fix = true) {
|
|
|
169
215
|
].filter(_isTruthy),
|
|
170
216
|
shell: false,
|
|
171
217
|
});
|
|
218
|
+
return true;
|
|
172
219
|
}
|
|
173
220
|
export function requireOxlintConfig() {
|
|
174
221
|
_assert(hasOxlintConfig(), '.oxlintrc.json config is not found');
|
|
@@ -185,11 +232,15 @@ const prettierPaths = [
|
|
|
185
232
|
// Exclude
|
|
186
233
|
...lintExclude.map((s) => `!${s}`),
|
|
187
234
|
];
|
|
235
|
+
/**
|
|
236
|
+
* Returns true if it ran.
|
|
237
|
+
*/
|
|
188
238
|
export function runPrettier(opt = {}) {
|
|
189
239
|
let { experimentalCli = true, fix = true } = opt;
|
|
190
240
|
const prettierConfigPath = [`./prettier.config.js`].find(f => existsSync(f));
|
|
191
|
-
if (!prettierConfigPath)
|
|
192
|
-
return;
|
|
241
|
+
if (!prettierConfigPath) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
193
244
|
const prettierPath = findPackageBinPath('prettier', 'prettier');
|
|
194
245
|
const cacheLocation = 'node_modules/.cache/prettier';
|
|
195
246
|
const cacheFound = existsSync(cacheLocation);
|
|
@@ -214,6 +265,7 @@ export function runPrettier(opt = {}) {
|
|
|
214
265
|
].filter(_isTruthy),
|
|
215
266
|
shell: false,
|
|
216
267
|
});
|
|
268
|
+
return true;
|
|
217
269
|
}
|
|
218
270
|
const stylelintPaths = [
|
|
219
271
|
// Everything inside these folders
|
|
@@ -221,6 +273,9 @@ const stylelintPaths = [
|
|
|
221
273
|
// Exclude
|
|
222
274
|
...lintExclude.map((s) => `!${s}`),
|
|
223
275
|
];
|
|
276
|
+
/**
|
|
277
|
+
* Returns true if it ran.
|
|
278
|
+
*/
|
|
224
279
|
export function stylelintAll(fix) {
|
|
225
280
|
const argv = _yargs().options({
|
|
226
281
|
fix: {
|
|
@@ -230,14 +285,16 @@ export function stylelintAll(fix) {
|
|
|
230
285
|
}).argv;
|
|
231
286
|
fix ??= argv.fix;
|
|
232
287
|
const config = [`./stylelint.config.js`].find(f => existsSync(f));
|
|
233
|
-
if (!config)
|
|
234
|
-
return;
|
|
288
|
+
if (!config) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
235
291
|
// stylelint is never hoisted from dev-lib, so, no need to search for its path
|
|
236
292
|
exec2.spawn('stylelint', {
|
|
237
293
|
name: fix ? 'stylelint' : 'stylelint --no-fix',
|
|
238
294
|
args: [fix ? `--fix` : '', `--allow-empty-input`, `--config`, config, ...stylelintPaths].filter(Boolean),
|
|
239
295
|
shell: false,
|
|
240
296
|
});
|
|
297
|
+
return true;
|
|
241
298
|
}
|
|
242
299
|
export async function lintStagedCommand() {
|
|
243
300
|
const localConfig = `./lint-staged.config.js`;
|
|
@@ -250,24 +307,33 @@ export async function lintStagedCommand() {
|
|
|
250
307
|
if (!success)
|
|
251
308
|
process.exit(3);
|
|
252
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Returns true if it ran.
|
|
312
|
+
*/
|
|
253
313
|
async function runKTLint(fix = true) {
|
|
254
|
-
if (!existsSync(`node_modules/@naturalcycles/ktlint`))
|
|
255
|
-
return;
|
|
314
|
+
if (!existsSync(`node_modules/@naturalcycles/ktlint`)) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
256
317
|
// @ts-expect-error ktlint is not installed (due to size in node_modules), but it's ok
|
|
257
318
|
const { ktlintAll } = await import('@naturalcycles/ktlint');
|
|
258
319
|
await ktlintAll(fix ? ['-F'] : []);
|
|
320
|
+
return true;
|
|
259
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Returns true if it ran.
|
|
324
|
+
*/
|
|
260
325
|
function runActionLint() {
|
|
261
326
|
// Only run if there is a folder of `.github/workflows`, otherwise actionlint will fail
|
|
262
|
-
if (!existsSync('.github/workflows'))
|
|
263
|
-
return;
|
|
327
|
+
if (!existsSync('.github/workflows')) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
264
330
|
if (canRunBinary('actionlint')) {
|
|
265
331
|
requireActionlintVersion();
|
|
266
332
|
exec2.spawn(`actionlint`);
|
|
333
|
+
return true;
|
|
267
334
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
335
|
+
console.log(`actionlint is not installed and won't be run.\nThis is how to install it: https://github.com/rhysd/actionlint/blob/main/docs/install.md`);
|
|
336
|
+
return false;
|
|
271
337
|
}
|
|
272
338
|
export function requireActionlintVersion() {
|
|
273
339
|
const version = getActionLintVersion();
|
|
@@ -281,11 +347,14 @@ export function getActionLintVersion() {
|
|
|
281
347
|
return;
|
|
282
348
|
return exec2.exec('actionlint --version').split('\n')[0];
|
|
283
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Returns true if it ran.
|
|
352
|
+
*/
|
|
284
353
|
export function runBiome(fix = true) {
|
|
285
354
|
const configPath = `biome.jsonc`;
|
|
286
355
|
if (!existsSync(configPath)) {
|
|
287
356
|
console.log(`biome is skipped, because ./biome.jsonc is not present`);
|
|
288
|
-
return;
|
|
357
|
+
return false;
|
|
289
358
|
}
|
|
290
359
|
const biomePath = findPackageBinPath('@biomejs/biome', 'biome');
|
|
291
360
|
const dirs = [`src`, `scripts`, `e2e`].filter(d => existsSync(d));
|
|
@@ -294,16 +363,29 @@ export function runBiome(fix = true) {
|
|
|
294
363
|
args: [`lint`, fix && '--write', fix && '--unsafe', '--no-errors-on-unmatched', ...dirs].filter(_isTruthy),
|
|
295
364
|
shell: false,
|
|
296
365
|
});
|
|
366
|
+
return true;
|
|
297
367
|
}
|
|
298
368
|
export async function buildProd() {
|
|
299
369
|
// fs2.emptyDir('./dist') // it doesn't delete the dir itself, to prevent IDE jumping
|
|
300
370
|
buildCopy();
|
|
301
371
|
await runTSCProd();
|
|
302
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Uses tsgo if it's installed, otherwise tsc
|
|
375
|
+
*/
|
|
376
|
+
export async function typecheckWithTS() {
|
|
377
|
+
if (hasDependencyInNodeModules('@typescript/native-preview')) {
|
|
378
|
+
await typecheckWithTSGO();
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
await typecheckWithTSC();
|
|
382
|
+
}
|
|
303
383
|
export async function typecheckWithTSC() {
|
|
304
|
-
// todo: try tsgo
|
|
305
384
|
await runTSCInFolders(['src', 'scripts', 'e2e'], ['--noEmit']);
|
|
306
385
|
}
|
|
386
|
+
export async function typecheckWithTSGO() {
|
|
387
|
+
await runTSGOInFolders(['src', 'scripts', 'e2e'], ['--noEmit', '--incremental', 'false']);
|
|
388
|
+
}
|
|
307
389
|
/**
|
|
308
390
|
* Use 'src' to indicate root.
|
|
309
391
|
*/
|
|
@@ -339,6 +421,34 @@ async function runTSCInFolder(dir, args = []) {
|
|
|
339
421
|
shell: false,
|
|
340
422
|
});
|
|
341
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Use 'src' to indicate root.
|
|
426
|
+
*/
|
|
427
|
+
export async function runTSGOInFolders(dirs, args = []) {
|
|
428
|
+
// Run sequential, since tsgo (unlike tsc) uses all cpu cores already
|
|
429
|
+
for (const dir of dirs) {
|
|
430
|
+
await runTSGOInFolder(dir, args);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Pass 'src' to run in root.
|
|
435
|
+
*/
|
|
436
|
+
async function runTSGOInFolder(dir, args = []) {
|
|
437
|
+
let configDir = dir;
|
|
438
|
+
if (dir === 'src') {
|
|
439
|
+
configDir = '';
|
|
440
|
+
}
|
|
441
|
+
const tsconfigPath = [configDir, 'tsconfig.json'].filter(Boolean).join('/');
|
|
442
|
+
if (!fs2.pathExists(tsconfigPath) || !fs2.pathExists(dir)) {
|
|
443
|
+
// console.log(`Skipping to run tsgo for ${tsconfigPath}, as it doesn't exist`)
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
const tsgoPath = findPackageBinPath('@typescript/native-preview', 'tsgo');
|
|
447
|
+
await exec2.spawnAsync(tsgoPath, {
|
|
448
|
+
args: ['-P', tsconfigPath, ...args],
|
|
449
|
+
shell: false,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
342
452
|
export async function runTSCProd(args = []) {
|
|
343
453
|
const tsconfigPath = [`./tsconfig.prod.json`].find(p => fs2.pathExists(p)) || 'tsconfig.json';
|
|
344
454
|
const tscPath = findPackageBinPath('typescript', 'tsc');
|
|
@@ -365,13 +475,17 @@ export function buildCopy() {
|
|
|
365
475
|
dotfiles: true,
|
|
366
476
|
});
|
|
367
477
|
}
|
|
478
|
+
/**
|
|
479
|
+
* Returns true if it ran.
|
|
480
|
+
*/
|
|
368
481
|
export function runTest(opt = {}) {
|
|
369
482
|
// if (nodeModuleExists('vitest')) {
|
|
370
483
|
if (fs2.pathExists('vitest.config.ts')) {
|
|
371
484
|
runVitest(opt);
|
|
372
|
-
return;
|
|
485
|
+
return true;
|
|
373
486
|
}
|
|
374
487
|
console.log(dimGrey(`vitest.config.ts not found, skipping tests`));
|
|
488
|
+
return false;
|
|
375
489
|
}
|
|
376
490
|
function runVitest(opt) {
|
|
377
491
|
const { integration, manual } = opt;
|
|
@@ -411,6 +525,9 @@ function canRunBinary(name) {
|
|
|
411
525
|
return false;
|
|
412
526
|
}
|
|
413
527
|
}
|
|
528
|
+
function hasDependencyInNodeModules(name) {
|
|
529
|
+
return existsSync(`node_modules/${name}`);
|
|
530
|
+
}
|
|
414
531
|
// function gitStatus(): string | undefined {
|
|
415
532
|
// try {
|
|
416
533
|
// return execSync('git status -s', {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AppError } from '@naturalcycles/js-lib/error/error.util.js';
|
|
2
2
|
import { red } from '@naturalcycles/nodejs-lib/colors';
|
|
3
|
-
import createMitm from 'mitm';
|
|
3
|
+
import { createMitm } from '../vendor/mitm.js';
|
|
4
4
|
const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1']);
|
|
5
5
|
const detectLeaks = process.argv.some(a => a.includes('detectLeaks'));
|
|
6
6
|
let mitm;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type * as NetType from 'node:net';
|
|
2
|
+
export interface SocketOptions {
|
|
3
|
+
port: number;
|
|
4
|
+
host?: string;
|
|
5
|
+
localAddress?: string;
|
|
6
|
+
localPort?: string;
|
|
7
|
+
family?: number;
|
|
8
|
+
allowHalfOpen?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface BypassableSocket extends NetType.Socket {
|
|
11
|
+
bypass: () => void;
|
|
12
|
+
bypassed?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export type ConnectCallback = (socket: BypassableSocket, opts: SocketOptions) => void;
|
|
15
|
+
export interface Mitm {
|
|
16
|
+
on: (event: 'connect', callback: ConnectCallback) => void;
|
|
17
|
+
disable: () => void;
|
|
18
|
+
}
|
|
19
|
+
export declare function createMitm(): Mitm;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
// Use require() to get mutable module objects (ESM namespace objects are frozen)
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
/* eslint-disable @typescript-eslint/naming-convention -- vendored code uses PascalCase for modules */
|
|
5
|
+
const Net = require('node:net');
|
|
6
|
+
const Tls = require('node:tls');
|
|
7
|
+
const Http = require('node:http');
|
|
8
|
+
const Https = require('node:https');
|
|
9
|
+
export function createMitm() {
|
|
10
|
+
const stubs = [];
|
|
11
|
+
const listeners = [];
|
|
12
|
+
function stub(obj, prop, value) {
|
|
13
|
+
stubs.push([obj, prop, obj[prop]]);
|
|
14
|
+
obj[prop] = value;
|
|
15
|
+
}
|
|
16
|
+
function restore() {
|
|
17
|
+
let s;
|
|
18
|
+
while ((s = stubs.pop())) {
|
|
19
|
+
s[0][s[1]] = s[2];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function connect(orig, opts, done) {
|
|
23
|
+
// Create a bypassable socket that we'll pass to listeners
|
|
24
|
+
const socket = new Net.Socket();
|
|
25
|
+
socket.bypassed = false;
|
|
26
|
+
socket.bypass = function () {
|
|
27
|
+
this.bypassed = true;
|
|
28
|
+
};
|
|
29
|
+
// Emit connect event to all listeners
|
|
30
|
+
for (const listener of listeners) {
|
|
31
|
+
listener(socket, opts);
|
|
32
|
+
}
|
|
33
|
+
// If bypassed, call the original connect function
|
|
34
|
+
if (socket.bypassed) {
|
|
35
|
+
return orig.call(Net, opts, done);
|
|
36
|
+
}
|
|
37
|
+
// Not bypassed - return the socket that will never actually connect.
|
|
38
|
+
// testOffline throws an error before we get here, so this is just a fallback.
|
|
39
|
+
return socket;
|
|
40
|
+
}
|
|
41
|
+
function normalizeArgs(args) {
|
|
42
|
+
// Handle the various signatures of Net.connect:
|
|
43
|
+
// connect(port, host?, callback?)
|
|
44
|
+
// connect(options, callback?)
|
|
45
|
+
// connect(path, callback?) - Unix socket
|
|
46
|
+
if (typeof args[0] === 'number') {
|
|
47
|
+
const opts = { port: args[0] };
|
|
48
|
+
if (typeof args[1] === 'string') {
|
|
49
|
+
opts.host = args[1];
|
|
50
|
+
return [opts, args[2]];
|
|
51
|
+
}
|
|
52
|
+
return [opts, args[1]];
|
|
53
|
+
}
|
|
54
|
+
if (typeof args[0] === 'object' && args[0] !== null) {
|
|
55
|
+
return [args[0], args[1]];
|
|
56
|
+
}
|
|
57
|
+
// Fallback for other cases (e.g., Unix socket path)
|
|
58
|
+
return [{ port: 0 }, args[1]];
|
|
59
|
+
}
|
|
60
|
+
function createNetConnect(orig) {
|
|
61
|
+
return function (...args) {
|
|
62
|
+
const [opts, done] = normalizeArgs(args);
|
|
63
|
+
return connect(orig, opts, done);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function createTlsConnect(orig) {
|
|
67
|
+
return function (...args) {
|
|
68
|
+
const [opts, done] = normalizeArgs(args);
|
|
69
|
+
return connect(orig, opts, done);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// Stub network functions
|
|
73
|
+
const netConnect = createNetConnect(Net.connect);
|
|
74
|
+
const tlsConnect = createTlsConnect(Tls.connect);
|
|
75
|
+
stub(Net, 'connect', netConnect);
|
|
76
|
+
stub(Net, 'createConnection', netConnect);
|
|
77
|
+
stub(Http.Agent.prototype, 'createConnection', netConnect);
|
|
78
|
+
stub(Tls, 'connect', tlsConnect);
|
|
79
|
+
// Disable keep-alive on global agents to force new connections
|
|
80
|
+
const httpAgent = Http.globalAgent;
|
|
81
|
+
const httpsAgent = Https.globalAgent;
|
|
82
|
+
if (httpAgent['keepAlive']) {
|
|
83
|
+
stub(httpAgent, 'keepAlive', false);
|
|
84
|
+
}
|
|
85
|
+
if (httpsAgent['keepAlive']) {
|
|
86
|
+
stub(httpsAgent, 'keepAlive', false);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
on(event, callback) {
|
|
90
|
+
if (event === 'connect') {
|
|
91
|
+
listeners.push(callback);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
disable() {
|
|
95
|
+
restore();
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/dev-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "20.
|
|
4
|
+
"version": "20.26.1",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@biomejs/biome": "^2",
|
|
7
7
|
"@eslint/js": "^9",
|
|
@@ -16,19 +16,22 @@
|
|
|
16
16
|
"globals": "^17",
|
|
17
17
|
"lint-staged": "^16",
|
|
18
18
|
"micromatch": "^4",
|
|
19
|
-
"mitm": "^1",
|
|
20
19
|
"oxlint": "^1",
|
|
21
20
|
"oxlint-tsgolint": "*",
|
|
22
21
|
"prettier": "^3",
|
|
23
22
|
"typescript-eslint": "^8"
|
|
24
23
|
},
|
|
25
24
|
"peerDependencies": {
|
|
25
|
+
"@typescript/native-preview": "^7.0.0-0",
|
|
26
26
|
"husky": "^9",
|
|
27
27
|
"stylelint": "^16",
|
|
28
28
|
"stylelint-config-standard-scss": "^16",
|
|
29
29
|
"typescript": "^5"
|
|
30
30
|
},
|
|
31
31
|
"peerDependenciesMeta": {
|
|
32
|
+
"@typescript/native-preview": {
|
|
33
|
+
"optional": true
|
|
34
|
+
},
|
|
32
35
|
"stylelint": {
|
|
33
36
|
"optional": true
|
|
34
37
|
},
|
|
@@ -37,7 +40,6 @@
|
|
|
37
40
|
}
|
|
38
41
|
},
|
|
39
42
|
"devDependencies": {
|
|
40
|
-
"@types/mitm": "^1",
|
|
41
43
|
"@types/node": "^25",
|
|
42
44
|
"@types/prompts": "^2"
|
|
43
45
|
},
|