@mpis/run 0.0.4 → 0.0.6
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/lib/autoindex.d.ts +6 -6
- package/lib/autoindex.d.ts.map +1 -1
- package/lib/autoindex.js +4 -4
- package/lib/autoindex.js.map +1 -1
- package/lib/bin.js +62 -30
- package/lib/bin.js.map +1 -1
- package/lib/commands/config.d.ts +3 -0
- package/lib/commands/config.d.ts.map +1 -0
- package/lib/commands/config.js +73 -0
- package/lib/commands/config.js.map +1 -0
- package/lib/common/args.d.ts +3 -1
- package/lib/common/args.d.ts.map +1 -1
- package/lib/common/args.js +26 -6
- package/lib/common/args.js.map +1 -1
- package/lib/common/config-file.d.ts +2 -1
- package/lib/common/config-file.d.ts.map +1 -1
- package/lib/common/config-file.js +45 -34
- package/lib/common/config-file.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -12
- package/src/autoindex.ts +10 -10
- package/src/bin.ts +64 -42
- package/src/commands/config.ts +78 -0
- package/src/common/args.ts +27 -6
- package/src/common/config-file.ts +51 -41
- package/src/common/paths.ts +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpis/run",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.6",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"bin": {
|
|
7
7
|
"run": "./loader/bin.js",
|
|
@@ -11,22 +11,23 @@
|
|
|
11
11
|
"./package.json": "./package.json"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
+
"split-cmd": "^1.1.0",
|
|
14
15
|
"execa": "^9.6.0",
|
|
15
16
|
"source-map-support": "^0.5.21",
|
|
16
|
-
"@build-script/rushstack-config-loader": "^0.0.
|
|
17
|
-
"@idlebox/args": "^0.0.
|
|
18
|
-
"@
|
|
19
|
-
"@idlebox/
|
|
20
|
-
"@idlebox/
|
|
21
|
-
"@mpis/
|
|
22
|
-
"@mpis/shared": "^0.0.
|
|
23
|
-
"@
|
|
17
|
+
"@build-script/rushstack-config-loader": "^0.0.24",
|
|
18
|
+
"@idlebox/args": "^0.0.13",
|
|
19
|
+
"@mpis/client": "^0.0.6",
|
|
20
|
+
"@idlebox/common": "^1.4.18",
|
|
21
|
+
"@idlebox/node": "^1.4.11",
|
|
22
|
+
"@mpis/server": "^0.0.5",
|
|
23
|
+
"@mpis/shared": "^0.0.6",
|
|
24
|
+
"@idlebox/logger": "^0.0.6"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@types/node": "^24.0.14",
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
28
|
+
"@idlebox/esbuild-executer": "^0.0.6",
|
|
29
|
+
"@internal/local-rig": "^1.0.1",
|
|
30
|
+
"@build-script/single-dog-asset": "^1.0.38"
|
|
30
31
|
},
|
|
31
32
|
"license": "MIT",
|
|
32
33
|
"author": "GongT <admin@gongt.me>",
|
package/src/autoindex.ts
CHANGED
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
/* eslint-disable */
|
|
4
4
|
|
|
5
5
|
/* common/args.ts */
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// Identifiers
|
|
7
|
+
export { printUsage } from './common/args.js';
|
|
8
8
|
/* common/paths.ts */
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// Identifiers
|
|
10
|
+
export { projectRoot } from './common/paths.js';
|
|
11
|
+
export { selfRoot } from './common/paths.js';
|
|
12
12
|
/* common/config-file.ts */
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
// Identifiers
|
|
14
|
+
export type { ICommand } from './common/config-file.js';
|
|
15
|
+
export type { IConfigFile } from './common/config-file.js';
|
|
16
|
+
export { loadConfigFile } from './common/config-file.js';
|
|
17
17
|
/* bin.ts */
|
|
18
|
-
|
|
18
|
+
// Identifiers
|
package/src/bin.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { humanDate, prettyFormatError, registerGlobalLifecycle, toDisposable } from '@idlebox/common';
|
|
2
2
|
import { logger } from '@idlebox/logger';
|
|
3
|
-
import { registerNodejsExitHandler } from '@idlebox/node';
|
|
3
|
+
import { registerNodejsExitHandler, shutdown } from '@idlebox/node';
|
|
4
4
|
import { channelClient } from '@mpis/client';
|
|
5
5
|
import { CompileError, ModeKind, ProcessIPCClient, WorkersManager } from '@mpis/server';
|
|
6
6
|
import { rmSync } from 'node:fs';
|
|
7
|
+
import { dumpConfig } from './commands/config.js';
|
|
7
8
|
import { context, parseCliArgs } from './common/args.js';
|
|
8
9
|
import { loadConfigFile } from './common/config-file.js';
|
|
9
10
|
import { projectRoot } from './common/paths.js';
|
|
@@ -31,7 +32,7 @@ process.title = `MpisRun`;
|
|
|
31
32
|
|
|
32
33
|
logger.info`Running command "${context.command}" in ${projectRoot}`;
|
|
33
34
|
|
|
34
|
-
const defaultNoClear = logger.debug.isEnabled;
|
|
35
|
+
const defaultNoClear = logger.debug.isEnabled || !process.stderr.isTTY;
|
|
35
36
|
let workersManager: WorkersManager;
|
|
36
37
|
|
|
37
38
|
const config = loadConfigFile(context.watchMode);
|
|
@@ -43,15 +44,29 @@ switch (context.command) {
|
|
|
43
44
|
executeClean();
|
|
44
45
|
break;
|
|
45
46
|
case 'build':
|
|
47
|
+
if (context.dumpConfig) {
|
|
48
|
+
dumpConfig(config);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
46
51
|
{
|
|
47
52
|
if (context.withCleanup) executeClean();
|
|
48
53
|
|
|
49
|
-
await executeBuild()
|
|
54
|
+
await executeBuild().catch((e: Error) => {
|
|
55
|
+
logger.error`failed ${context.command} project: ${e.message}`;
|
|
56
|
+
shutdown(1);
|
|
57
|
+
});
|
|
50
58
|
}
|
|
51
59
|
break;
|
|
52
60
|
case 'watch':
|
|
61
|
+
if (context.dumpConfig) {
|
|
62
|
+
dumpConfig(config);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
53
65
|
initializeStdin();
|
|
54
|
-
await executeBuild()
|
|
66
|
+
await executeBuild().catch((e: Error) => {
|
|
67
|
+
logger.error`failed ${context.command} project: ${e.message}`;
|
|
68
|
+
shutdown(1);
|
|
69
|
+
});
|
|
55
70
|
break;
|
|
56
71
|
}
|
|
57
72
|
|
|
@@ -61,14 +76,17 @@ async function executeBuild() {
|
|
|
61
76
|
workersManager = new WorkersManager(context.watchMode ? ModeKind.Watch : ModeKind.Build);
|
|
62
77
|
|
|
63
78
|
initializeWorkers();
|
|
79
|
+
const graph = workersManager.finalize();
|
|
80
|
+
|
|
81
|
+
workersManager.onTerminate((trans) => {
|
|
82
|
+
const w = trans.worker;
|
|
64
83
|
|
|
65
|
-
workersManager.onTerminate((w) => {
|
|
66
84
|
if (!ProcessIPCClient.is(w)) {
|
|
67
85
|
logger.fatal`worker "${w._id}" is not a ProcessIPCClient, this is a bug.`;
|
|
68
86
|
return;
|
|
69
87
|
}
|
|
70
88
|
|
|
71
|
-
const times = `(+${humanDate.delta(w.time.
|
|
89
|
+
const times = `(+${humanDate.delta(w.time.executeStart!, w.time.executeEnd!)})`;
|
|
72
90
|
|
|
73
91
|
if (context.watchMode) {
|
|
74
92
|
printFailedRunError(w, `unexpected exit in watch mode ${times}`);
|
|
@@ -79,9 +97,7 @@ async function executeBuild() {
|
|
|
79
97
|
}
|
|
80
98
|
});
|
|
81
99
|
|
|
82
|
-
workersManager.
|
|
83
|
-
|
|
84
|
-
if (workersManager.allWorkers.length === 0) {
|
|
100
|
+
if (workersManager.nodeNames.length === 0) {
|
|
85
101
|
logger.fatal`No workers to execute, check your config file.`;
|
|
86
102
|
return;
|
|
87
103
|
}
|
|
@@ -94,7 +110,9 @@ async function executeBuild() {
|
|
|
94
110
|
return;
|
|
95
111
|
}
|
|
96
112
|
logger.verbose`Workers initialized, starting execution...`;
|
|
97
|
-
await
|
|
113
|
+
await graph.startup();
|
|
114
|
+
|
|
115
|
+
logger.verbose`Startup returned.`;
|
|
98
116
|
|
|
99
117
|
reprintWatchModeError();
|
|
100
118
|
}
|
|
@@ -121,8 +139,7 @@ function initializeWorkers() {
|
|
|
121
139
|
worker.pathvar.add(path);
|
|
122
140
|
}
|
|
123
141
|
|
|
124
|
-
|
|
125
|
-
worker.displayTitle = `run:${cmd0}`;
|
|
142
|
+
worker.displayTitle = `run:${cmds.command[0]}`;
|
|
126
143
|
|
|
127
144
|
workersManager.addWorker(worker, last ? [last._id] : []);
|
|
128
145
|
|
|
@@ -147,42 +164,40 @@ function initializeWorkers() {
|
|
|
147
164
|
}
|
|
148
165
|
}
|
|
149
166
|
|
|
167
|
+
const cls = /\x1Bc/g;
|
|
168
|
+
|
|
150
169
|
function printFailedRunError(worker: ProcessIPCClient, message: string) {
|
|
151
|
-
if (context.watchMode) process.stderr.write('\x1Bc');
|
|
170
|
+
if (context.watchMode && process.stderr.isTTY) process.stderr.write('\x1Bc');
|
|
152
171
|
|
|
153
|
-
const text = worker.outputStream.toString().trimEnd();
|
|
172
|
+
const text = worker.outputStream.toString().trimEnd().replace(cls, '');
|
|
154
173
|
|
|
155
174
|
if (text) {
|
|
156
|
-
console.error(
|
|
157
|
-
'\n\x1B[48;5;1m%s\r \x1B[0;38;5;9;1m %s \x1B[0m',
|
|
158
|
-
' '.repeat(process.stderr.columns || 80),
|
|
159
|
-
`below is output of ${worker._id}`,
|
|
160
|
-
);
|
|
175
|
+
console.error('\n\x1B[48;5;1m%s\r \x1B[0;38;5;9;1m %s \x1B[0m', ' '.repeat(process.stderr.columns || 80), `[@mpis/run] below is output of ${worker._id}`);
|
|
161
176
|
console.error(text);
|
|
162
177
|
|
|
163
|
-
console.error(
|
|
164
|
-
'\x1B[48;5;1m%s\r \x1B[0;38;5;9;1m %s \x1B[0m\n',
|
|
165
|
-
' '.repeat(process.stderr.columns || 80),
|
|
166
|
-
`ending output of ${worker._id}`,
|
|
167
|
-
);
|
|
178
|
+
console.error('\x1B[48;5;1m%s\r \x1B[0;38;5;9;1m %s \x1B[0m\n', ' '.repeat(process.stderr.columns || 80), `[@mpis/run] ending output of ${worker._id}`);
|
|
168
179
|
} else {
|
|
169
|
-
console.error(
|
|
170
|
-
'\n\x1B[48;5;1m%s\r \x1B[0;38;5;9;1m %s \x1B[0m',
|
|
171
|
-
' '.repeat(process.stderr.columns || 80),
|
|
172
|
-
`no output from ${worker._id}`,
|
|
173
|
-
);
|
|
180
|
+
console.error('\n\x1B[48;5;1m%s\r \x1B[0;38;5;9;1m %s \x1B[0m', ' '.repeat(process.stderr.columns || 80), `[@mpis/run] no output from ${worker._id}`);
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
|
|
183
|
+
const graph = workersManager.finalize();
|
|
184
|
+
console.error('%s\n%s', graph.debugFormatGraph(), graph.debugFormatSummary());
|
|
177
185
|
logger.fatal`"${worker._id}" ${message}`;
|
|
178
186
|
}
|
|
179
187
|
|
|
188
|
+
let printTo: NodeJS.Timeout | undefined;
|
|
180
189
|
function reprintWatchModeError(noClear?: boolean) {
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
if (printTo) clearTimeout(printTo);
|
|
191
|
+
printTo = setTimeout(() => {
|
|
192
|
+
printTo = undefined;
|
|
193
|
+
|
|
194
|
+
if (context.watchMode) {
|
|
195
|
+
if (!noClear && !defaultNoClear) process.stderr.write('\x1Bc');
|
|
196
|
+
}
|
|
197
|
+
const graph = workersManager.finalize();
|
|
198
|
+
console.error('%s\n%s', graph.debugFormatList(), graph.debugFormatSummary());
|
|
199
|
+
printAllErrors();
|
|
200
|
+
}, 50);
|
|
186
201
|
}
|
|
187
202
|
|
|
188
203
|
function addDebugCommand() {
|
|
@@ -190,7 +205,7 @@ function addDebugCommand() {
|
|
|
190
205
|
name: ['continue', 'c'],
|
|
191
206
|
description: '开始执行',
|
|
192
207
|
callback: () => {
|
|
193
|
-
workersManager.startup();
|
|
208
|
+
workersManager.finalize().startup();
|
|
194
209
|
},
|
|
195
210
|
});
|
|
196
211
|
registerCommand({
|
|
@@ -198,7 +213,7 @@ function addDebugCommand() {
|
|
|
198
213
|
description: '切换调试模式(仅在启动前有效)',
|
|
199
214
|
callback: (text: string) => {
|
|
200
215
|
const [_, index, on_off] = text.split(/\s+/);
|
|
201
|
-
const list
|
|
216
|
+
const list = workersManager._allWorkers as ProcessIPCClient[];
|
|
202
217
|
const worker = list[Number(index)];
|
|
203
218
|
if (!worker) {
|
|
204
219
|
logger.error`worker index out of range: ${index}`;
|
|
@@ -228,10 +243,17 @@ function addDebugCommand() {
|
|
|
228
243
|
function sendStatus() {
|
|
229
244
|
const noError = errors.values().every((e) => !e);
|
|
230
245
|
if (noError) {
|
|
231
|
-
channelClient.success(`
|
|
246
|
+
channelClient.success(`all ${workersManager.size()} workers completed successfully.`);
|
|
232
247
|
} else {
|
|
233
|
-
|
|
234
|
-
|
|
248
|
+
let errorCnt = 0;
|
|
249
|
+
const arr: string[] = [];
|
|
250
|
+
for (const [client, err] of errors.entries()) {
|
|
251
|
+
if (err) {
|
|
252
|
+
errorCnt++;
|
|
253
|
+
arr.push(client._id);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
channelClient.failed(`mpis-run: ${arr.join(', ')} (${errorCnt} / ${workersManager.size()})`, formatAllErrors());
|
|
235
257
|
}
|
|
236
258
|
}
|
|
237
259
|
|
|
@@ -270,8 +292,8 @@ function printAllErrors() {
|
|
|
270
292
|
if (numFailed !== 0) {
|
|
271
293
|
console.error(formatAllErrors());
|
|
272
294
|
|
|
273
|
-
logger.error(`💥 ${numFailed} of ${workersManager.size} worker failed (${execTip})`);
|
|
295
|
+
logger.error(`💥 ${numFailed} of ${workersManager.size()} worker failed (${execTip})`);
|
|
274
296
|
} else {
|
|
275
|
-
logger.success(`✅ no error in ${workersManager.size} workers (${execTip})`);
|
|
297
|
+
logger.success(`✅ no error in ${workersManager.size()} workers (${execTip})`);
|
|
276
298
|
}
|
|
277
299
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ProjectConfig } from '@build-script/rushstack-config-loader';
|
|
2
|
+
import { CSI, logger } from '@idlebox/logger';
|
|
3
|
+
import { realpathSync } from 'node:fs';
|
|
4
|
+
import { projectRoot } from '../autoindex.js';
|
|
5
|
+
import type { ICommand, IConfigFile } from '../common/config-file.js';
|
|
6
|
+
|
|
7
|
+
export function dumpConfig(config: IConfigFile) {
|
|
8
|
+
const buildObject: Record<string, ICommand> = {};
|
|
9
|
+
for (const [key, value] of config.build.entries()) {
|
|
10
|
+
buildObject[key] = value;
|
|
11
|
+
}
|
|
12
|
+
const mapRaw = {
|
|
13
|
+
buildTitles: config.buildTitles,
|
|
14
|
+
build: buildObject,
|
|
15
|
+
clean: config.clean,
|
|
16
|
+
additionalPaths: config.additionalPaths,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (!process.stdout.isTTY) {
|
|
20
|
+
console.log(JSON.stringify(mapRaw, null, 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log(`🏗️ 构建命令 🏗️`);
|
|
26
|
+
|
|
27
|
+
for (const [key, value] of [...config.build.entries(), ...Object.entries(config.unusedBuild)]) {
|
|
28
|
+
if (config.build.has(key)) {
|
|
29
|
+
console.log(` ${CSI}38;5;10m✓ ${key}${CSI}0m`);
|
|
30
|
+
} else {
|
|
31
|
+
console.log(` ${CSI}38;5;9m✗ ${key}${CSI}0m`);
|
|
32
|
+
}
|
|
33
|
+
const cmdDump = value.command.map((e) => {
|
|
34
|
+
return `${CSI}3m${JSON.stringify(e)}${CSI}0m`;
|
|
35
|
+
});
|
|
36
|
+
console.log(` 命令: ${cmdDump.join(' ')}`);
|
|
37
|
+
console.log(` 工作目录: ${CSI}3m${JSON.stringify(value.cwd)}${CSI}0m`);
|
|
38
|
+
if (Object.keys(value.env).length > 0) {
|
|
39
|
+
console.log(` 额外环境变量:`);
|
|
40
|
+
for (const [key, val] of Object.entries(value.env)) {
|
|
41
|
+
console.log(` ${key}: ${CSI}2m${JSON.stringify(val)}${CSI}0m`);
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
console.log(` 额外环境变量: 无`);
|
|
45
|
+
}
|
|
46
|
+
console.log('');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`🧹 清理目录 🧹`);
|
|
50
|
+
for (const path of config.clean) {
|
|
51
|
+
console.log(` ${CSI}38;5;10m-${CSI}0m ${path}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(`🔍 附加路径 🔍`);
|
|
56
|
+
for (const path of config.additionalPaths) {
|
|
57
|
+
console.log(` ${CSI}38;5;10m-${CSI}0m ${path}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const cfgObj = new ProjectConfig(projectRoot, undefined, logger);
|
|
61
|
+
const cfgInfo = cfgObj.getJsonConfigInfo('commands');
|
|
62
|
+
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(`🔧 配置文件信息 🔧`);
|
|
65
|
+
if (cfgInfo.project.exists) {
|
|
66
|
+
console.log(` 项目: ${cfgInfo.project.path}`);
|
|
67
|
+
} else {
|
|
68
|
+
console.log(` 项目: (不存在) ${CSI}2m${cfgInfo.project.path}${CSI}0m`);
|
|
69
|
+
}
|
|
70
|
+
if (cfgInfo.rig.exists) {
|
|
71
|
+
console.log(` 脚手架: ${realpathSync(cfgInfo.rig.path)}`);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(` 脚手架: (不存在) ${CSI}2m${cfgInfo.rig.path}${CSI}0m`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log('');
|
|
78
|
+
}
|
package/src/common/args.ts
CHANGED
|
@@ -5,10 +5,10 @@ export function printUsage() {
|
|
|
5
5
|
console.log('Usage: my-cli <command>');
|
|
6
6
|
console.log();
|
|
7
7
|
console.log('Commands:');
|
|
8
|
-
console.log(' build run build');
|
|
9
|
-
console.log(' watch start watch mode');
|
|
8
|
+
console.log(' build run build [--dump]');
|
|
9
|
+
console.log(' watch start watch mode [--dump]');
|
|
10
10
|
console.log(' clean cleanup the project');
|
|
11
|
-
console.log('
|
|
11
|
+
// console.log(' init create config file if not');
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function parseCliArgs() {
|
|
@@ -26,7 +26,7 @@ export function parseCliArgs() {
|
|
|
26
26
|
|
|
27
27
|
createRootLogger('', level);
|
|
28
28
|
|
|
29
|
-
const command = argv.command(['build', 'watch', 'clean', '
|
|
29
|
+
const command = argv.command(['build', 'watch', 'clean', 'config']);
|
|
30
30
|
if (!command) {
|
|
31
31
|
printUsage();
|
|
32
32
|
throw logger.fatal`No command provided. Please specify a command to run.`;
|
|
@@ -35,32 +35,53 @@ export function parseCliArgs() {
|
|
|
35
35
|
const watchMode = command.value === 'watch';
|
|
36
36
|
const buildMode = command.value === 'build';
|
|
37
37
|
|
|
38
|
+
let dumpConfig = false;
|
|
39
|
+
|
|
38
40
|
let breakMode = false;
|
|
39
41
|
if (watchMode) {
|
|
42
|
+
dumpConfig = argv.flag('--dump') > 0;
|
|
40
43
|
breakMode = argv.flag('--break') > 0;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
let withCleanup = false;
|
|
44
47
|
if (buildMode) {
|
|
48
|
+
dumpConfig = argv.flag('--dump') > 0;
|
|
45
49
|
withCleanup = argv.flag('--clean') > 0;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
let configCommand;
|
|
53
|
+
if (command.value === 'config') {
|
|
54
|
+
const cfg = command.command(['dump']);
|
|
55
|
+
if (!cfg) {
|
|
56
|
+
printUsage();
|
|
57
|
+
throw logger.fatal`No sub command for "config"`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
configCommand = cfg.value;
|
|
61
|
+
// will do dump
|
|
62
|
+
} else {
|
|
63
|
+
configCommand = undefined;
|
|
50
64
|
}
|
|
51
65
|
|
|
52
66
|
const r = {
|
|
53
67
|
command: command.value,
|
|
68
|
+
configCommand,
|
|
54
69
|
debugMode,
|
|
55
70
|
verboseMode,
|
|
56
71
|
watchMode,
|
|
57
72
|
buildMode,
|
|
58
73
|
breakMode,
|
|
59
74
|
withCleanup,
|
|
75
|
+
dumpConfig,
|
|
60
76
|
};
|
|
61
77
|
|
|
62
78
|
context = r;
|
|
63
79
|
|
|
80
|
+
if (argv.unused().length) {
|
|
81
|
+
printUsage();
|
|
82
|
+
console.error('');
|
|
83
|
+
throw logger.fatal`Unknown arguments: ${argv.unused().join(' ')}`;
|
|
84
|
+
}
|
|
64
85
|
return r;
|
|
65
86
|
}
|
|
66
87
|
|
|
@@ -4,6 +4,7 @@ import { findUpUntilSync } from '@idlebox/node';
|
|
|
4
4
|
import { readFileSync } from 'node:fs';
|
|
5
5
|
import { resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { split as splitCmd } from 'split-cmd';
|
|
7
8
|
import { projectRoot, selfRoot } from './paths.js';
|
|
8
9
|
|
|
9
10
|
interface IPackageBinary {
|
|
@@ -27,38 +28,30 @@ interface IConfigFileInput {
|
|
|
27
28
|
|
|
28
29
|
export interface ICommand {
|
|
29
30
|
title: string;
|
|
30
|
-
command:
|
|
31
|
+
command: readonly string[];
|
|
31
32
|
cwd: string;
|
|
32
33
|
env: Record<string, string>;
|
|
33
34
|
}
|
|
34
35
|
export interface IConfigFile {
|
|
35
36
|
buildTitles: readonly string[];
|
|
36
37
|
build: ReadonlyMap<string, ICommand>;
|
|
38
|
+
unusedBuild: Record<string, ICommand>;
|
|
37
39
|
clean: readonly string[];
|
|
38
40
|
additionalPaths: readonly string[];
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
function watchModeCmd(
|
|
42
|
-
command
|
|
43
|
-
|
|
44
|
-
watchMode?: boolean,
|
|
45
|
-
): string | readonly string[] {
|
|
43
|
+
function watchModeCmd(command: string | readonly string[], watch?: string | readonly string[] | boolean, watchMode?: boolean): readonly string[] {
|
|
44
|
+
const cmdArr = typeof command === 'string' ? splitCmd(command) : command;
|
|
45
|
+
|
|
46
46
|
if (!watchMode) {
|
|
47
|
-
return
|
|
47
|
+
return cmdArr;
|
|
48
48
|
}
|
|
49
49
|
if (typeof watch === 'boolean') {
|
|
50
50
|
throw new Error(`Invalid watch value: ${watch}. Expected string or array.`);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return `${command} ${watch}`;
|
|
57
|
-
} else {
|
|
58
|
-
if (!watch) watch = ['-w'];
|
|
59
|
-
|
|
60
|
-
return [...command, ...watch];
|
|
61
|
-
}
|
|
53
|
+
if (!watch) watch = ['-w'];
|
|
54
|
+
return [...cmdArr, ...watch];
|
|
62
55
|
}
|
|
63
56
|
|
|
64
57
|
export function loadConfigFile(watchMode: boolean): IConfigFile {
|
|
@@ -72,6 +65,11 @@ export function loadConfigFile(watchMode: boolean): IConfigFile {
|
|
|
72
65
|
array(left, right, keyPath) {
|
|
73
66
|
switch (keyPath[0]) {
|
|
74
67
|
case 'build':
|
|
68
|
+
if (Array.isArray(right)) {
|
|
69
|
+
return right;
|
|
70
|
+
} else {
|
|
71
|
+
return left;
|
|
72
|
+
}
|
|
75
73
|
case 'clean': {
|
|
76
74
|
if (!Array.isArray(right)) {
|
|
77
75
|
return left;
|
|
@@ -87,13 +85,6 @@ export function loadConfigFile(watchMode: boolean): IConfigFile {
|
|
|
87
85
|
|
|
88
86
|
const buildMap = new Map<string, ICommand>();
|
|
89
87
|
const buildTitles: string[] = [];
|
|
90
|
-
function set(cmd: ICommand) {
|
|
91
|
-
if (buildMap.has(cmd.title)) {
|
|
92
|
-
throw new Error(`duplicate command "${cmd.title}", rename it before continue`);
|
|
93
|
-
}
|
|
94
|
-
buildMap.set(cmd.title, cmd);
|
|
95
|
-
buildTitles.push(cmd.title);
|
|
96
|
-
}
|
|
97
88
|
|
|
98
89
|
for (let item of input.build) {
|
|
99
90
|
if (typeof item === 'string') {
|
|
@@ -119,31 +110,30 @@ export function loadConfigFile(watchMode: boolean): IConfigFile {
|
|
|
119
110
|
continue;
|
|
120
111
|
}
|
|
121
112
|
|
|
122
|
-
const cmd = item
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
throw TypeError(`Invalid command type: ${typeof cmd}. Expected string or array or object.`);
|
|
113
|
+
const cmd = resolveCommand(config, item, watchMode);
|
|
114
|
+
|
|
115
|
+
if (buildMap.has(cmd.title)) {
|
|
116
|
+
throw new Error(`duplicate command "${cmd.title}", rename it before continue`);
|
|
117
|
+
}
|
|
118
|
+
buildMap.set(cmd.title, cmd);
|
|
119
|
+
buildTitles.push(cmd.title);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const unused: Record<string, ICommand> = {};
|
|
123
|
+
for (const [name, item] of Object.entries(input.commands)) {
|
|
124
|
+
const title = item.title ?? name;
|
|
125
|
+
if (buildMap.has(title)) {
|
|
126
|
+
continue;
|
|
137
127
|
}
|
|
128
|
+
|
|
129
|
+
unused[title] = resolveCommand(config, item, watchMode);
|
|
138
130
|
}
|
|
139
131
|
|
|
140
132
|
const additionalPaths: string[] = [];
|
|
141
133
|
if (config.rigConfig.rigFound) {
|
|
142
134
|
const nmPath = findUpUntilSync({ file: 'node_modules', from: config.rigConfig.getResolvedProfileFolder() });
|
|
143
135
|
if (!nmPath) {
|
|
144
|
-
throw new Error(
|
|
145
|
-
`Failed to find "node_modules" folder in rig profile "${config.rigConfig.getResolvedProfileFolder()}".`,
|
|
146
|
-
);
|
|
136
|
+
throw new Error(`Failed to find "node_modules" folder in rig profile "${config.rigConfig.getResolvedProfileFolder()}".`);
|
|
147
137
|
}
|
|
148
138
|
additionalPaths.push(resolve(nmPath, '.bin'));
|
|
149
139
|
}
|
|
@@ -160,11 +150,31 @@ export function loadConfigFile(watchMode: boolean): IConfigFile {
|
|
|
160
150
|
return {
|
|
161
151
|
buildTitles,
|
|
162
152
|
build: buildMap,
|
|
153
|
+
unusedBuild: unused,
|
|
163
154
|
clean,
|
|
164
155
|
additionalPaths: additionalPaths.toReversed(),
|
|
165
156
|
};
|
|
166
157
|
}
|
|
167
158
|
|
|
159
|
+
function resolveCommand(config: ProjectConfig, input: ICommandInput, watchMode: boolean): ICommand {
|
|
160
|
+
const cmd = input.command;
|
|
161
|
+
if (Array.isArray(cmd)) {
|
|
162
|
+
const copy = cmd.slice();
|
|
163
|
+
resolveCommandIsFile(config, copy);
|
|
164
|
+
return {
|
|
165
|
+
title: input.title ?? guessTitle(cmd),
|
|
166
|
+
command: watchModeCmd(copy, input.watch, watchMode),
|
|
167
|
+
cwd: resolve(projectRoot, input.cwd || '.'),
|
|
168
|
+
env: input.env ?? {},
|
|
169
|
+
};
|
|
170
|
+
} else if (typeof cmd === 'object' && 'package' in cmd) {
|
|
171
|
+
const obj = parsePackagedBinary(config, input, watchMode);
|
|
172
|
+
return obj;
|
|
173
|
+
} else {
|
|
174
|
+
throw TypeError(`Invalid command type: ${typeof cmd}. Expected string or array or object.`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
168
178
|
function guessTitle(command: string | readonly string[]): string {
|
|
169
179
|
if (typeof command === 'string') {
|
|
170
180
|
return command.split(' ')[0];
|
package/src/common/paths.ts
CHANGED