project-runner 0.1.2 → 0.3.0
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 +25 -0
- package/dist/index.js +511 -122
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,10 +104,35 @@ pr info
|
|
|
104
104
|
|------|------|
|
|
105
105
|
| `-v, --verbose` | 显示详细检测过程 |
|
|
106
106
|
| `-d, --dir <path>` | 指定项目目录(默认:当前目录)|
|
|
107
|
+
| `-i, --install` | 强制执行依赖安装 |
|
|
107
108
|
| `--no-install` | 跳过依赖安装步骤 |
|
|
109
|
+
| `-e, --entry <name>` | 指定 MPA 入口(也可用 `PR_ENTRY`) |
|
|
108
110
|
| `-h, --help` | 显示帮助信息 |
|
|
109
111
|
| `-V, --version` | 显示版本号 |
|
|
110
112
|
|
|
113
|
+
## MPA 项目支持
|
|
114
|
+
|
|
115
|
+
`pr run` 会自动检测是否存在 `dev:<entry>` 形式的多入口脚本。
|
|
116
|
+
|
|
117
|
+
- 在交互终端中:自动弹出入口列表并让你选择。
|
|
118
|
+
- 在 CI / 非交互环境中:按优先级选择入口
|
|
119
|
+
1. `--entry <name>`
|
|
120
|
+
2. `PR_ENTRY=<name>`
|
|
121
|
+
3. `.pr.local.json` 中的 `defaultEntry`
|
|
122
|
+
4. 若仍无法确定则报错并提示可选入口
|
|
123
|
+
|
|
124
|
+
本地配置文件(推荐):
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"entries": ["main", "formengine", "design", "approve"],
|
|
129
|
+
"defaultEntry": "main"
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- 文件名固定:`.pr.local.json`
|
|
134
|
+
- 当该文件存在时,`pr` 会自动确保 `.gitignore` 包含 `.pr.local.json`
|
|
135
|
+
|
|
111
136
|
## 示例输出
|
|
112
137
|
|
|
113
138
|
### `pr info`
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,13 @@ var colors = {
|
|
|
17
17
|
white: "\x1B[37m",
|
|
18
18
|
gray: "\x1B[90m"
|
|
19
19
|
};
|
|
20
|
+
var CliError = class extends Error {
|
|
21
|
+
constructor(message, exitCode = 1) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.exitCode = exitCode;
|
|
24
|
+
this.name = "CliError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
20
27
|
var isVerbose = false;
|
|
21
28
|
function setVerbose(verbose) {
|
|
22
29
|
isVerbose = verbose;
|
|
@@ -74,6 +81,34 @@ async function execute(cmd, options = {}) {
|
|
|
74
81
|
});
|
|
75
82
|
});
|
|
76
83
|
}
|
|
84
|
+
async function executeCapture(cmd, options = {}) {
|
|
85
|
+
const { cwd = process.cwd(), env } = options;
|
|
86
|
+
return new Promise((resolve2, reject) => {
|
|
87
|
+
const isWindows = process.platform === "win32";
|
|
88
|
+
const command = cmd[0] || "";
|
|
89
|
+
const args = cmd.slice(1);
|
|
90
|
+
const proc = spawn(command, args, {
|
|
91
|
+
cwd,
|
|
92
|
+
env: { ...process.env, ...env },
|
|
93
|
+
shell: isWindows,
|
|
94
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
95
|
+
});
|
|
96
|
+
let stdout = "";
|
|
97
|
+
let stderr = "";
|
|
98
|
+
proc.stdout.on("data", (data) => {
|
|
99
|
+
stdout += data.toString();
|
|
100
|
+
});
|
|
101
|
+
proc.stderr.on("data", (data) => {
|
|
102
|
+
stderr += data.toString();
|
|
103
|
+
});
|
|
104
|
+
proc.on("close", (code) => {
|
|
105
|
+
resolve2({ stdout, stderr, exitCode: code ?? 0 });
|
|
106
|
+
});
|
|
107
|
+
proc.on("error", (err) => {
|
|
108
|
+
reject(err);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
77
112
|
function setupSignalHandlers() {
|
|
78
113
|
process.on("SIGINT", () => {
|
|
79
114
|
if (currentProcess) {
|
|
@@ -89,13 +124,36 @@ function setupSignalHandlers() {
|
|
|
89
124
|
});
|
|
90
125
|
}
|
|
91
126
|
|
|
127
|
+
// src/cli/run.ts
|
|
128
|
+
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
129
|
+
|
|
92
130
|
// src/analyzer/index.ts
|
|
93
|
-
import { readFile as
|
|
131
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
94
132
|
import { join as join4 } from "path";
|
|
95
133
|
|
|
96
134
|
// src/analyzer/package-manager.ts
|
|
97
|
-
import { readFile, stat } from "fs/promises";
|
|
98
135
|
import { join } from "path";
|
|
136
|
+
|
|
137
|
+
// src/utils/fs.ts
|
|
138
|
+
import { stat } from "fs/promises";
|
|
139
|
+
async function fileExists(path) {
|
|
140
|
+
try {
|
|
141
|
+
const stats = await stat(path);
|
|
142
|
+
return stats.isFile();
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function directoryExists(path) {
|
|
148
|
+
try {
|
|
149
|
+
const stats = await stat(path);
|
|
150
|
+
return stats.isDirectory();
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/analyzer/package-manager.ts
|
|
99
157
|
var LOCKFILE_MAP = {
|
|
100
158
|
"bun.lockb": "bun",
|
|
101
159
|
"bun.lock": "bun",
|
|
@@ -103,9 +161,7 @@ var LOCKFILE_MAP = {
|
|
|
103
161
|
"yarn.lock": "yarn",
|
|
104
162
|
"package-lock.json": "npm"
|
|
105
163
|
};
|
|
106
|
-
async function detectPackageManager(projectDir) {
|
|
107
|
-
const packageJsonPath = join(projectDir, "package.json");
|
|
108
|
-
const packageJson = await readPackageJson(packageJsonPath);
|
|
164
|
+
async function detectPackageManager(projectDir, packageJson) {
|
|
109
165
|
if (!packageJson) {
|
|
110
166
|
return { name: "npm", source: "default" };
|
|
111
167
|
}
|
|
@@ -164,46 +220,18 @@ function getInstallCommand(pm) {
|
|
|
164
220
|
return ["npm", "install"];
|
|
165
221
|
}
|
|
166
222
|
}
|
|
167
|
-
async function fileExists(path) {
|
|
168
|
-
try {
|
|
169
|
-
const stats = await stat(path);
|
|
170
|
-
return stats.isFile();
|
|
171
|
-
} catch {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
async function readPackageJson(path) {
|
|
176
|
-
try {
|
|
177
|
-
const content = await readFile(path, "utf-8");
|
|
178
|
-
return JSON.parse(content);
|
|
179
|
-
} catch {
|
|
180
|
-
}
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
223
|
|
|
184
224
|
// src/analyzer/scripts.ts
|
|
185
|
-
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
186
|
-
import { join as join2 } from "path";
|
|
187
225
|
var DEV_PATTERNS = ["dev", "serve", "start:dev", "develop", "watch"];
|
|
188
226
|
var TEST_PATTERNS = ["test", "test:unit", "test:all", "spec"];
|
|
189
227
|
var BUILD_PATTERNS = ["build", "compile", "bundle", "dist"];
|
|
190
|
-
var START_PATTERNS = ["start", "
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return
|
|
195
|
-
} catch {
|
|
196
|
-
return false;
|
|
228
|
+
var START_PATTERNS = ["start", "preview", "production"];
|
|
229
|
+
var STANDARD_TYPES = ["dev", "test", "build", "start"];
|
|
230
|
+
function analyzeScripts(packageJson, localConfig) {
|
|
231
|
+
if (!packageJson) {
|
|
232
|
+
return null;
|
|
197
233
|
}
|
|
198
|
-
}
|
|
199
|
-
async function analyzeScripts(projectDir) {
|
|
200
|
-
const packageJsonPath = join2(projectDir, "package.json");
|
|
201
234
|
try {
|
|
202
|
-
if (!await fileExists2(packageJsonPath)) {
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
const content = await readFile2(packageJsonPath, "utf-8");
|
|
206
|
-
const packageJson = JSON.parse(content);
|
|
207
235
|
const scripts = packageJson.scripts || {};
|
|
208
236
|
const detected = {
|
|
209
237
|
dev: findMatchingScript(scripts, DEV_PATTERNS),
|
|
@@ -211,11 +239,81 @@ async function analyzeScripts(projectDir) {
|
|
|
211
239
|
build: findMatchingScript(scripts, BUILD_PATTERNS),
|
|
212
240
|
start: findMatchingScript(scripts, START_PATTERNS)
|
|
213
241
|
};
|
|
214
|
-
|
|
242
|
+
const mpa = analyzeMpa(packageJson, scripts, localConfig);
|
|
243
|
+
return { scripts, detected, mpa };
|
|
215
244
|
} catch {
|
|
216
245
|
return null;
|
|
217
246
|
}
|
|
218
247
|
}
|
|
248
|
+
function analyzeMpa(packageJson, scripts, localConfig) {
|
|
249
|
+
const configuredEntriesFromLocal = normalizeEntryList(localConfig?.entries);
|
|
250
|
+
const configuredEntriesFromPackage = normalizeEntryList(packageJson?.pr?.entries);
|
|
251
|
+
const configuredEntries = configuredEntriesFromLocal.length > 0 ? configuredEntriesFromLocal : configuredEntriesFromPackage;
|
|
252
|
+
const scriptEntries = collectEntriesByPrefix(scripts, "dev");
|
|
253
|
+
let entries = configuredEntries.length > 0 ? configuredEntries : scriptEntries;
|
|
254
|
+
let source = "scripts";
|
|
255
|
+
if (configuredEntriesFromLocal.length > 0) {
|
|
256
|
+
source = "local-config";
|
|
257
|
+
} else if (configuredEntriesFromPackage.length > 0) {
|
|
258
|
+
source = "package-json";
|
|
259
|
+
}
|
|
260
|
+
const defaultEntry = normalizeEntryName(localConfig?.defaultEntry ?? packageJson?.pr?.defaultEntry);
|
|
261
|
+
if (defaultEntry && !entries.includes(defaultEntry) && scripts[`dev:${defaultEntry}`]) {
|
|
262
|
+
entries = [...entries, defaultEntry];
|
|
263
|
+
}
|
|
264
|
+
entries = dedupe(entries);
|
|
265
|
+
if (entries.length === 0) {
|
|
266
|
+
source = "none";
|
|
267
|
+
}
|
|
268
|
+
const scriptsByType = {};
|
|
269
|
+
for (const type of STANDARD_TYPES) {
|
|
270
|
+
const mapping = {};
|
|
271
|
+
for (const entry of entries) {
|
|
272
|
+
const scriptName = `${type}:${entry}`;
|
|
273
|
+
if (scripts[scriptName]) {
|
|
274
|
+
mapping[entry] = scriptName;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (Object.keys(mapping).length > 0) {
|
|
278
|
+
scriptsByType[type] = mapping;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
isMpa: entries.length > 0,
|
|
283
|
+
entries,
|
|
284
|
+
defaultEntry: defaultEntry && entries.includes(defaultEntry) ? defaultEntry : void 0,
|
|
285
|
+
source,
|
|
286
|
+
scriptsByType
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function normalizeEntryList(value) {
|
|
290
|
+
if (!Array.isArray(value)) {
|
|
291
|
+
return [];
|
|
292
|
+
}
|
|
293
|
+
return dedupe(value.map(normalizeEntryName).filter((item) => Boolean(item)));
|
|
294
|
+
}
|
|
295
|
+
function normalizeEntryName(value) {
|
|
296
|
+
if (typeof value !== "string") {
|
|
297
|
+
return void 0;
|
|
298
|
+
}
|
|
299
|
+
const trimmed = value.trim();
|
|
300
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
301
|
+
}
|
|
302
|
+
function collectEntriesByPrefix(scripts, prefix) {
|
|
303
|
+
const prefixWithColon = `${prefix}:`;
|
|
304
|
+
const entries = [];
|
|
305
|
+
for (const name of Object.keys(scripts)) {
|
|
306
|
+
if (!name.startsWith(prefixWithColon)) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const entry = name.slice(prefixWithColon.length).trim();
|
|
310
|
+
if (!entry) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
entries.push(entry);
|
|
314
|
+
}
|
|
315
|
+
return dedupe(entries);
|
|
316
|
+
}
|
|
219
317
|
function findMatchingScript(scripts, patterns) {
|
|
220
318
|
const scriptNames = Object.keys(scripts);
|
|
221
319
|
for (const pattern of patterns) {
|
|
@@ -228,8 +326,12 @@ function findMatchingScript(scripts, patterns) {
|
|
|
228
326
|
if (!name.toLowerCase().includes(pattern.toLowerCase())) {
|
|
229
327
|
return false;
|
|
230
328
|
}
|
|
231
|
-
const scriptContent = scripts[name]
|
|
232
|
-
if (scriptContent
|
|
329
|
+
const scriptContent = scripts[name];
|
|
330
|
+
if (!scriptContent) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
const content = scriptContent.toLowerCase();
|
|
334
|
+
if (content.includes("npm i") || content.includes("npm install") || content.includes("yarn install") || content.includes("pnpm install") || content.includes("bun install")) {
|
|
233
335
|
return false;
|
|
234
336
|
}
|
|
235
337
|
return true;
|
|
@@ -240,10 +342,13 @@ function findMatchingScript(scripts, patterns) {
|
|
|
240
342
|
}
|
|
241
343
|
return void 0;
|
|
242
344
|
}
|
|
345
|
+
function dedupe(items) {
|
|
346
|
+
return [...new Set(items)];
|
|
347
|
+
}
|
|
243
348
|
|
|
244
349
|
// src/analyzer/dependencies.ts
|
|
245
|
-
import { stat as
|
|
246
|
-
import { join as
|
|
350
|
+
import { stat as stat2 } from "fs/promises";
|
|
351
|
+
import { join as join2 } from "path";
|
|
247
352
|
var LOCKFILES = [
|
|
248
353
|
"bun.lockb",
|
|
249
354
|
"bun.lock",
|
|
@@ -252,7 +357,7 @@ var LOCKFILES = [
|
|
|
252
357
|
"package-lock.json"
|
|
253
358
|
];
|
|
254
359
|
async function checkDependencyStatus(projectDir) {
|
|
255
|
-
const nodeModulesPath =
|
|
360
|
+
const nodeModulesPath = join2(projectDir, "node_modules");
|
|
256
361
|
const nodeModulesExists = await directoryExists(nodeModulesPath);
|
|
257
362
|
if (!nodeModulesExists) {
|
|
258
363
|
return {
|
|
@@ -261,11 +366,11 @@ async function checkDependencyStatus(projectDir) {
|
|
|
261
366
|
reason: "node_modules \u4E0D\u5B58\u5728"
|
|
262
367
|
};
|
|
263
368
|
}
|
|
369
|
+
const nodeModulesMtime = await getModifiedTime(nodeModulesPath);
|
|
264
370
|
const lockfilePath = await findLockfile(projectDir);
|
|
265
371
|
if (lockfilePath) {
|
|
266
372
|
const lockfileMtime = await getModifiedTime(lockfilePath);
|
|
267
|
-
|
|
268
|
-
if (lockfileMtime && nodeModulesMtime2 && lockfileMtime > nodeModulesMtime2) {
|
|
373
|
+
if (lockfileMtime && nodeModulesMtime && lockfileMtime > nodeModulesMtime) {
|
|
269
374
|
return {
|
|
270
375
|
hasNodeModules: true,
|
|
271
376
|
needsInstall: true,
|
|
@@ -273,9 +378,8 @@ async function checkDependencyStatus(projectDir) {
|
|
|
273
378
|
};
|
|
274
379
|
}
|
|
275
380
|
}
|
|
276
|
-
const packageJsonPath =
|
|
381
|
+
const packageJsonPath = join2(projectDir, "package.json");
|
|
277
382
|
const packageJsonMtime = await getModifiedTime(packageJsonPath);
|
|
278
|
-
const nodeModulesMtime = await getModifiedTime(nodeModulesPath);
|
|
279
383
|
if (packageJsonMtime && nodeModulesMtime && packageJsonMtime > nodeModulesMtime) {
|
|
280
384
|
return {
|
|
281
385
|
hasNodeModules: true,
|
|
@@ -290,8 +394,8 @@ async function checkDependencyStatus(projectDir) {
|
|
|
290
394
|
}
|
|
291
395
|
async function findLockfile(projectDir) {
|
|
292
396
|
for (const lockfile of LOCKFILES) {
|
|
293
|
-
const lockfilePath =
|
|
294
|
-
if (await
|
|
397
|
+
const lockfilePath = join2(projectDir, lockfile);
|
|
398
|
+
if (await fileExists(lockfilePath)) {
|
|
295
399
|
return lockfilePath;
|
|
296
400
|
}
|
|
297
401
|
}
|
|
@@ -299,93 +403,210 @@ async function findLockfile(projectDir) {
|
|
|
299
403
|
}
|
|
300
404
|
async function getModifiedTime(path) {
|
|
301
405
|
try {
|
|
302
|
-
const stats = await
|
|
406
|
+
const stats = await stat2(path);
|
|
303
407
|
return stats.mtimeMs;
|
|
304
408
|
} catch {
|
|
305
409
|
return null;
|
|
306
410
|
}
|
|
307
411
|
}
|
|
308
|
-
|
|
412
|
+
|
|
413
|
+
// src/analyzer/local-config.ts
|
|
414
|
+
import { appendFile, readFile, writeFile } from "fs/promises";
|
|
415
|
+
import { join as join3 } from "path";
|
|
416
|
+
var LOCAL_CONFIG_FILENAME = ".pr.local.json";
|
|
417
|
+
async function loadPrLocalConfig(projectDir) {
|
|
418
|
+
const configPath = join3(projectDir, LOCAL_CONFIG_FILENAME);
|
|
419
|
+
if (!await fileExists(configPath)) {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
let parsed;
|
|
309
423
|
try {
|
|
310
|
-
const
|
|
311
|
-
|
|
424
|
+
const content = await readFile(configPath, "utf-8");
|
|
425
|
+
parsed = JSON.parse(content.replace(/^\uFEFF/, ""));
|
|
312
426
|
} catch {
|
|
313
|
-
return
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
await ensureLocalConfigGitignored(projectDir);
|
|
430
|
+
if (!parsed || typeof parsed !== "object") {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
const config = {};
|
|
434
|
+
if (Array.isArray(parsed.entries)) {
|
|
435
|
+
config.entries = parsed.entries.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
314
436
|
}
|
|
437
|
+
if (typeof parsed.defaultEntry === "string" && parsed.defaultEntry.trim()) {
|
|
438
|
+
config.defaultEntry = parsed.defaultEntry.trim();
|
|
439
|
+
}
|
|
440
|
+
return config;
|
|
315
441
|
}
|
|
316
|
-
async function
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
442
|
+
async function ensureLocalConfigGitignored(projectDir) {
|
|
443
|
+
const gitignorePath = join3(projectDir, ".gitignore");
|
|
444
|
+
const ignoreEntry = LOCAL_CONFIG_FILENAME;
|
|
445
|
+
if (!await fileExists(gitignorePath)) {
|
|
446
|
+
await writeFile(gitignorePath, `${ignoreEntry}
|
|
447
|
+
`, "utf-8");
|
|
448
|
+
return;
|
|
322
449
|
}
|
|
450
|
+
const content = await readFile(gitignorePath, "utf-8");
|
|
451
|
+
const lines = content.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
452
|
+
if (lines.includes(ignoreEntry) || lines.includes(`/${ignoreEntry}`)) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const suffix = content.endsWith("\n") || content.length === 0 ? "" : "\n";
|
|
456
|
+
await appendFile(gitignorePath, `${suffix}${ignoreEntry}
|
|
457
|
+
`, "utf-8");
|
|
323
458
|
}
|
|
324
459
|
|
|
325
460
|
// src/analyzer/index.ts
|
|
326
|
-
async function fileExists4(path) {
|
|
327
|
-
try {
|
|
328
|
-
const stats = await stat4(path);
|
|
329
|
-
return stats.isFile();
|
|
330
|
-
} catch {
|
|
331
|
-
return false;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
461
|
async function analyzeProject(projectDir) {
|
|
335
462
|
const packageJsonPath = join4(projectDir, "package.json");
|
|
336
|
-
const hasPackageJson = await
|
|
463
|
+
const hasPackageJson = await fileExists(packageJsonPath);
|
|
337
464
|
if (!hasPackageJson) {
|
|
338
465
|
return {
|
|
339
466
|
type: "unknown",
|
|
340
467
|
packageManager: { name: "npm", source: "default" },
|
|
341
468
|
scripts: null,
|
|
342
|
-
dependencies: { hasNodeModules: false, needsInstall: false }
|
|
469
|
+
dependencies: { hasNodeModules: false, needsInstall: false },
|
|
470
|
+
localConfig: null
|
|
343
471
|
};
|
|
344
472
|
}
|
|
345
473
|
let packageJson = {};
|
|
346
474
|
try {
|
|
347
|
-
const content = await
|
|
348
|
-
packageJson = JSON.parse(content);
|
|
475
|
+
const content = await readFile2(packageJsonPath, "utf-8");
|
|
476
|
+
packageJson = JSON.parse(content.replace(/^\uFEFF/, ""));
|
|
349
477
|
} catch {
|
|
350
478
|
}
|
|
351
|
-
const [packageManager,
|
|
352
|
-
detectPackageManager(projectDir),
|
|
353
|
-
|
|
354
|
-
|
|
479
|
+
const [packageManager, dependencies, localConfig] = await Promise.all([
|
|
480
|
+
detectPackageManager(projectDir, packageJson),
|
|
481
|
+
checkDependencyStatus(projectDir),
|
|
482
|
+
loadPrLocalConfig(projectDir)
|
|
355
483
|
]);
|
|
484
|
+
const scripts = analyzeScripts(packageJson, localConfig);
|
|
356
485
|
return {
|
|
357
486
|
type: "nodejs",
|
|
358
487
|
packageManager,
|
|
359
488
|
scripts,
|
|
360
489
|
dependencies,
|
|
490
|
+
localConfig,
|
|
361
491
|
name: packageJson.name,
|
|
362
492
|
version: packageJson.version,
|
|
363
493
|
description: packageJson.description
|
|
364
494
|
};
|
|
365
495
|
}
|
|
366
496
|
|
|
497
|
+
// src/utils/pm-availability.ts
|
|
498
|
+
import { createInterface } from "node:readline/promises";
|
|
499
|
+
var availabilityCache = /* @__PURE__ */ new Map();
|
|
500
|
+
async function isPmAvailable(pm) {
|
|
501
|
+
if (availabilityCache.has(pm)) {
|
|
502
|
+
return availabilityCache.get(pm);
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
const result = await executeCapture([pm, "--version"]);
|
|
506
|
+
const available = result.exitCode === 0 && result.stdout.trim().length > 0;
|
|
507
|
+
availabilityCache.set(pm, available);
|
|
508
|
+
return available;
|
|
509
|
+
} catch {
|
|
510
|
+
availabilityCache.set(pm, false);
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
async function ensurePmAvailable(pm) {
|
|
515
|
+
if (pm === "npm") {
|
|
516
|
+
return pm;
|
|
517
|
+
}
|
|
518
|
+
const available = await isPmAvailable(pm);
|
|
519
|
+
if (available) {
|
|
520
|
+
return pm;
|
|
521
|
+
}
|
|
522
|
+
warn(`\u5305\u7BA1\u7406\u5668 ${pm} \u672A\u5B89\u88C5`);
|
|
523
|
+
if (!process.stdin.isTTY) {
|
|
524
|
+
warn("\u975E\u4EA4\u4E92\u73AF\u5883\uFF0C\u81EA\u52A8\u56DE\u9000\u5230 npm");
|
|
525
|
+
return "npm";
|
|
526
|
+
}
|
|
527
|
+
return await promptPmResolution(pm);
|
|
528
|
+
}
|
|
529
|
+
async function promptPmResolution(pm) {
|
|
530
|
+
console.log();
|
|
531
|
+
console.log(` \u68C0\u6D4B\u5230\u9879\u76EE\u4F7F\u7528 ${colors.cyan}${pm}${colors.reset}\uFF0C\u4F46\u7CFB\u7EDF\u672A\u5B89\u88C5\u3002`);
|
|
532
|
+
console.log();
|
|
533
|
+
console.log(` ${colors.bold}\u8BF7\u9009\u62E9\u64CD\u4F5C:${colors.reset}`);
|
|
534
|
+
console.log(` ${colors.cyan}1${colors.reset}) \u81EA\u52A8\u5B89\u88C5 ${pm} ${colors.dim}(npm install -g ${pm})${colors.reset}`);
|
|
535
|
+
console.log(` ${colors.cyan}2${colors.reset}) \u4F7F\u7528 npm \u4EE3\u66FF`);
|
|
536
|
+
console.log(` ${colors.cyan}3${colors.reset}) \u9000\u51FA\uFF0C\u624B\u52A8\u5904\u7406`);
|
|
537
|
+
console.log();
|
|
538
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
539
|
+
try {
|
|
540
|
+
const answer = await rl.question(` \u8BF7\u8F93\u5165\u9009\u9879 ${colors.dim}[1/2/3]${colors.reset}: `);
|
|
541
|
+
const choice = answer.trim();
|
|
542
|
+
switch (choice) {
|
|
543
|
+
case "1":
|
|
544
|
+
return await autoInstallPm(pm);
|
|
545
|
+
case "2":
|
|
546
|
+
warn(`\u4F7F\u7528 npm \u4EE3\u66FF ${pm}`);
|
|
547
|
+
return "npm";
|
|
548
|
+
case "3":
|
|
549
|
+
default:
|
|
550
|
+
throw new CliError(`\u8BF7\u5148\u5B89\u88C5 ${pm}: npm install -g ${pm}`);
|
|
551
|
+
}
|
|
552
|
+
} finally {
|
|
553
|
+
rl.close();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function autoInstallPm(pm) {
|
|
557
|
+
info(`\u6B63\u5728\u5B89\u88C5 ${pm}...`);
|
|
558
|
+
console.log();
|
|
559
|
+
try {
|
|
560
|
+
const result = await executeCapture(["npm", "install", "-g", pm]);
|
|
561
|
+
if (result.exitCode !== 0) {
|
|
562
|
+
error(`\u5B89\u88C5 ${pm} \u5931\u8D25`);
|
|
563
|
+
if (result.stderr.trim()) {
|
|
564
|
+
console.error(result.stderr.trim());
|
|
565
|
+
}
|
|
566
|
+
throw new CliError(`\u65E0\u6CD5\u81EA\u52A8\u5B89\u88C5 ${pm}\uFF0C\u8BF7\u624B\u52A8\u8FD0\u884C: npm install -g ${pm}`);
|
|
567
|
+
}
|
|
568
|
+
} catch (err) {
|
|
569
|
+
if (err instanceof CliError)
|
|
570
|
+
throw err;
|
|
571
|
+
throw new CliError(`\u5B89\u88C5 ${pm} \u65F6\u51FA\u9519\uFF0C\u8BF7\u624B\u52A8\u8FD0\u884C: npm install -g ${pm}`);
|
|
572
|
+
}
|
|
573
|
+
availabilityCache.delete(pm);
|
|
574
|
+
const nowAvailable = await isPmAvailable(pm);
|
|
575
|
+
if (!nowAvailable) {
|
|
576
|
+
throw new CliError(`${pm} \u5DF2\u5B89\u88C5\u4F46\u65E0\u6CD5\u6267\u884C\u3002\u8BF7\u68C0\u67E5 PATH \u6216\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u540E\u91CD\u8BD5\u3002`);
|
|
577
|
+
}
|
|
578
|
+
success(`${pm} \u5B89\u88C5\u5B8C\u6210`);
|
|
579
|
+
console.log();
|
|
580
|
+
return pm;
|
|
581
|
+
}
|
|
582
|
+
|
|
367
583
|
// src/cli/run.ts
|
|
368
584
|
async function runCommand(projectDir, options = {}) {
|
|
369
|
-
const { noInstall = false, forceInstall = false, scriptType = "dev" } = options;
|
|
585
|
+
const { noInstall = false, forceInstall = false, scriptType = "dev", entry } = options;
|
|
370
586
|
log("\u6B63\u5728\u5206\u6790\u9879\u76EE...");
|
|
371
587
|
const project = await analyzeProject(projectDir);
|
|
372
588
|
if (project.type === "unknown") {
|
|
373
|
-
|
|
374
|
-
process.exit(1);
|
|
589
|
+
throw new CliError("\u672A\u68C0\u6D4B\u5230\u9879\u76EE\u7C7B\u578B\u3002\u8BF7\u786E\u4FDD\u5F53\u524D\u76EE\u5F55\u5305\u542B package.json");
|
|
375
590
|
}
|
|
376
591
|
log(`\u9879\u76EE\u7C7B\u578B: ${project.type}`);
|
|
377
592
|
log(`\u5305\u7BA1\u7406\u5668: ${project.packageManager.name} (from ${project.packageManager.source})`);
|
|
378
593
|
if (!project.scripts) {
|
|
379
|
-
|
|
380
|
-
process.exit(1);
|
|
594
|
+
throw new CliError("\u65E0\u6CD5\u8BFB\u53D6 package.json \u7684 scripts");
|
|
381
595
|
}
|
|
382
|
-
const
|
|
596
|
+
const resolvedEntry = await resolveEntry(project, scriptType, entry);
|
|
597
|
+
const scriptName = findScript(project, scriptType, resolvedEntry);
|
|
383
598
|
if (!scriptName) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
599
|
+
showAvailableScripts(project, scriptType, resolvedEntry);
|
|
600
|
+
throw new CliError(`\u672A\u627E\u5230 ${scriptType}${resolvedEntry ? `:${resolvedEntry}` : ""} \u76F8\u5173\u7684\u811A\u672C`);
|
|
601
|
+
}
|
|
602
|
+
if (resolvedEntry) {
|
|
603
|
+
log(`\u5165\u53E3: ${resolvedEntry}`);
|
|
387
604
|
}
|
|
388
605
|
log(`\u5C06\u6267\u884C\u811A\u672C: ${scriptName}`);
|
|
606
|
+
const resolvedPm = await ensurePmAvailable(project.packageManager.name);
|
|
607
|
+
if (resolvedPm !== project.packageManager.name) {
|
|
608
|
+
log(`\u4F7F\u7528 ${resolvedPm} \u4EE3\u66FF ${project.packageManager.name}`);
|
|
609
|
+
}
|
|
389
610
|
const shouldInstall = !noInstall && (forceInstall || project.dependencies.needsInstall);
|
|
390
611
|
if (shouldInstall) {
|
|
391
612
|
if (forceInstall) {
|
|
@@ -394,27 +615,32 @@ async function runCommand(projectDir, options = {}) {
|
|
|
394
615
|
log(`\u4F9D\u8D56\u72B6\u6001: ${project.dependencies.reason || "\u9700\u8981\u5B89\u88C5"}`);
|
|
395
616
|
}
|
|
396
617
|
newline();
|
|
397
|
-
const installCmd = getInstallCommand(
|
|
618
|
+
const installCmd = getInstallCommand(resolvedPm);
|
|
398
619
|
const installExitCode = await execute(installCmd, { cwd: projectDir });
|
|
399
620
|
if (installExitCode !== 0) {
|
|
400
|
-
|
|
401
|
-
process.exit(installExitCode);
|
|
621
|
+
throw new CliError("\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25", installExitCode);
|
|
402
622
|
}
|
|
403
623
|
success("\u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210");
|
|
404
624
|
newline();
|
|
405
625
|
} else if (!noInstall) {
|
|
406
626
|
log("\u4F9D\u8D56\u72B6\u6001: \u5DF2\u662F\u6700\u65B0");
|
|
407
627
|
}
|
|
408
|
-
const runCmd = getRunCommand(
|
|
628
|
+
const runCmd = getRunCommand(resolvedPm, scriptName);
|
|
409
629
|
const exitCode = await execute(runCmd, { cwd: projectDir });
|
|
410
630
|
if (exitCode !== 0) {
|
|
411
|
-
|
|
631
|
+
throw new CliError("\u811A\u672C\u6267\u884C\u5931\u8D25", exitCode);
|
|
412
632
|
}
|
|
413
633
|
}
|
|
414
|
-
function findScript(project, scriptType) {
|
|
634
|
+
function findScript(project, scriptType, entry) {
|
|
415
635
|
const scripts = project.scripts;
|
|
416
636
|
if (!scripts)
|
|
417
637
|
return void 0;
|
|
638
|
+
if (entry) {
|
|
639
|
+
const entryScript = `${scriptType}:${entry}`;
|
|
640
|
+
if (scripts.scripts[entryScript]) {
|
|
641
|
+
return entryScript;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
418
644
|
const detected = scripts.detected[scriptType];
|
|
419
645
|
if (detected) {
|
|
420
646
|
return detected;
|
|
@@ -424,9 +650,115 @@ function findScript(project, scriptType) {
|
|
|
424
650
|
}
|
|
425
651
|
return void 0;
|
|
426
652
|
}
|
|
427
|
-
function
|
|
653
|
+
async function resolveEntry(project, scriptType, entry) {
|
|
654
|
+
if (!project.scripts) {
|
|
655
|
+
return entry;
|
|
656
|
+
}
|
|
657
|
+
if (entry) {
|
|
658
|
+
validateEntry(project, scriptType, entry);
|
|
659
|
+
return entry;
|
|
660
|
+
}
|
|
661
|
+
if (scriptType !== "dev") {
|
|
662
|
+
return void 0;
|
|
663
|
+
}
|
|
664
|
+
const { mpa } = project.scripts;
|
|
665
|
+
if (!mpa.isMpa || mpa.entries.length === 0) {
|
|
666
|
+
return void 0;
|
|
667
|
+
}
|
|
668
|
+
if (mpa.entries.length === 1) {
|
|
669
|
+
const onlyEntry = mpa.entries[0];
|
|
670
|
+
if (onlyEntry) {
|
|
671
|
+
return onlyEntry;
|
|
672
|
+
}
|
|
673
|
+
return void 0;
|
|
674
|
+
}
|
|
675
|
+
const envEntry = process.env.PR_ENTRY?.trim();
|
|
676
|
+
if (envEntry) {
|
|
677
|
+
validateEntry(project, scriptType, envEntry);
|
|
678
|
+
return envEntry;
|
|
679
|
+
}
|
|
680
|
+
const defaultEntry = mpa.defaultEntry && hasEntryScript(project, scriptType, mpa.defaultEntry) ? mpa.defaultEntry : void 0;
|
|
681
|
+
if (!process.stdin.isTTY) {
|
|
682
|
+
if (defaultEntry) {
|
|
683
|
+
return defaultEntry;
|
|
684
|
+
}
|
|
685
|
+
throw new CliError(
|
|
686
|
+
`\u68C0\u6D4B\u5230 MPA \u5165\u53E3: ${mpa.entries.join(", ")}\u3002\u975E\u4EA4\u4E92\u73AF\u5883\u8BF7\u4F7F\u7528 --entry <name> \u6216\u8BBE\u7F6E PR_ENTRY\u3002`
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
return promptSelectEntry(project, scriptType, defaultEntry);
|
|
690
|
+
}
|
|
691
|
+
function validateEntry(project, scriptType, entry) {
|
|
692
|
+
if (!hasEntryScript(project, scriptType, entry)) {
|
|
693
|
+
const available = getAvailableEntriesByType(project, scriptType);
|
|
694
|
+
if (available.length > 0) {
|
|
695
|
+
throw new CliError(`\u5165\u53E3 "${entry}" \u4E0D\u5B58\u5728\u3002\u53EF\u9009\u5165\u53E3: ${available.join(", ")}`);
|
|
696
|
+
}
|
|
697
|
+
throw new CliError(`\u5165\u53E3 "${entry}" \u4E0D\u5B58\u5728\uFF0C\u4E14\u672A\u627E\u5230 ${scriptType}:<entry> \u811A\u672C`);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
function hasEntryScript(project, scriptType, entry) {
|
|
701
|
+
const scripts = project.scripts;
|
|
702
|
+
if (!scripts)
|
|
703
|
+
return false;
|
|
704
|
+
return Boolean(scripts.scripts[`${scriptType}:${entry}`]);
|
|
705
|
+
}
|
|
706
|
+
function getAvailableEntriesByType(project, scriptType) {
|
|
707
|
+
const scripts = project.scripts;
|
|
708
|
+
if (!scripts)
|
|
709
|
+
return [];
|
|
710
|
+
const mapping = scripts.mpa.scriptsByType[scriptType];
|
|
711
|
+
if (!mapping)
|
|
712
|
+
return [];
|
|
713
|
+
return Object.keys(mapping);
|
|
714
|
+
}
|
|
715
|
+
async function promptSelectEntry(project, scriptType, defaultEntry) {
|
|
716
|
+
const entries = getAvailableEntriesByType(project, scriptType);
|
|
717
|
+
if (entries.length === 0) {
|
|
718
|
+
throw new CliError(`\u672A\u627E\u5230 ${scriptType}:<entry> \u811A\u672C\uFF0C\u65E0\u6CD5\u9009\u62E9 MPA \u5165\u53E3`);
|
|
719
|
+
}
|
|
720
|
+
const defaultResolved = defaultEntry && entries.includes(defaultEntry) ? defaultEntry : entries[0];
|
|
721
|
+
if (!defaultResolved) {
|
|
722
|
+
throw new CliError("\u672A\u627E\u5230\u53EF\u7528\u5165\u53E3\uFF0C\u65E0\u6CD5\u7EE7\u7EED\u6267\u884C");
|
|
723
|
+
}
|
|
724
|
+
info("\u68C0\u6D4B\u5230 MPA \u9879\u76EE\uFF0C\u8BF7\u9009\u62E9\u542F\u52A8\u5165\u53E3\uFF1A");
|
|
725
|
+
entries.forEach((item, index) => {
|
|
726
|
+
const marker = item === defaultResolved ? " (\u9ED8\u8BA4)" : "";
|
|
727
|
+
console.log(` ${index + 1}) ${item}${marker}`);
|
|
728
|
+
});
|
|
729
|
+
console.log();
|
|
730
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
731
|
+
try {
|
|
732
|
+
const answer = await rl.question(`\u8BF7\u8F93\u5165\u5E8F\u53F7 [1-${entries.length}]\uFF0C\u56DE\u8F66\u9ED8\u8BA4 ${defaultResolved}: `);
|
|
733
|
+
const trimmed = answer.trim();
|
|
734
|
+
if (!trimmed) {
|
|
735
|
+
return defaultResolved;
|
|
736
|
+
}
|
|
737
|
+
const number = Number(trimmed);
|
|
738
|
+
if (Number.isInteger(number) && number >= 1 && number <= entries.length) {
|
|
739
|
+
const selectedByNumber = entries[number - 1];
|
|
740
|
+
if (selectedByNumber) {
|
|
741
|
+
return selectedByNumber;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (entries.includes(trimmed)) {
|
|
745
|
+
return trimmed;
|
|
746
|
+
}
|
|
747
|
+
} finally {
|
|
748
|
+
rl.close();
|
|
749
|
+
}
|
|
750
|
+
throw new CliError("\u65E0\u6548\u7684\u5165\u53E3\u9009\u62E9\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C\u5E76\u8F93\u5165\u6B63\u786E\u5E8F\u53F7");
|
|
751
|
+
}
|
|
752
|
+
function showAvailableScripts(project, scriptType, entry) {
|
|
428
753
|
if (!project.scripts)
|
|
429
754
|
return;
|
|
755
|
+
if (entry) {
|
|
756
|
+
const availableEntries = getAvailableEntriesByType(project, scriptType);
|
|
757
|
+
if (availableEntries.length > 0) {
|
|
758
|
+
info(`\u53EF\u9009 ${scriptType} \u5165\u53E3: ${availableEntries.join(", ")}`);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
430
762
|
const scriptNames = Object.keys(project.scripts.scripts);
|
|
431
763
|
if (scriptNames.length === 0) {
|
|
432
764
|
warn("package.json \u4E2D\u6CA1\u6709\u5B9A\u4E49\u4EFB\u4F55 scripts");
|
|
@@ -437,15 +769,15 @@ function showAvailableScripts(project) {
|
|
|
437
769
|
console.log(` - ${name}`);
|
|
438
770
|
}
|
|
439
771
|
console.log();
|
|
440
|
-
info("\u4F7F\u7528
|
|
772
|
+
info("\u4F7F\u7528 pr <script> \u8FD0\u884C\u4EFB\u610F\u811A\u672C");
|
|
441
773
|
}
|
|
442
774
|
|
|
443
775
|
// src/cli/info.ts
|
|
444
776
|
async function infoCommand(projectDir) {
|
|
445
777
|
const project = await analyzeProject(projectDir);
|
|
446
778
|
if (project.type === "unknown") {
|
|
447
|
-
console.log(`${colors.red}\
|
|
448
|
-
console.log(" \u8BF7\u786E\
|
|
779
|
+
console.log(`${colors.red}\xD7${colors.reset} \u672A\u68C0\u6D4B\u5230\u9879\u76EE\u7C7B\u578B`);
|
|
780
|
+
console.log(" \u8BF7\u786E\u8BA4\u5F53\u524D\u76EE\u5F55\u5305\u542B package.json");
|
|
449
781
|
return;
|
|
450
782
|
}
|
|
451
783
|
console.log();
|
|
@@ -467,10 +799,28 @@ async function infoCommand(projectDir) {
|
|
|
467
799
|
pmInfo += `@${pm.version}`;
|
|
468
800
|
}
|
|
469
801
|
pmInfo += ` ${colors.dim}(${pm.source})${colors.reset}`;
|
|
470
|
-
|
|
802
|
+
const pmAvailable = await isPmAvailable(pm.name);
|
|
803
|
+
const pmStatus = pmAvailable ? `${colors.green}\u5DF2\u5B89\u88C5${colors.reset}` : `${colors.red}\u672A\u5B89\u88C5${colors.reset}`;
|
|
804
|
+
console.log(`${colors.bold}\u5305\u7BA1\u7406\u5668:${colors.reset} ${pmInfo} [${pmStatus}]`);
|
|
471
805
|
const deps = project.dependencies;
|
|
472
806
|
const depsStatus = deps.needsInstall ? `${colors.yellow}\u9700\u8981\u5B89\u88C5${colors.reset} (${deps.reason})` : `${colors.green}\u5DF2\u5C31\u7EEA${colors.reset}`;
|
|
473
807
|
console.log(`${colors.bold}\u4F9D\u8D56\u72B6\u6001:${colors.reset} ${depsStatus}`);
|
|
808
|
+
if (project.scripts) {
|
|
809
|
+
const { mpa } = project.scripts;
|
|
810
|
+
if (mpa.isMpa) {
|
|
811
|
+
const sourceLabel = mpa.source === "local-config" ? ".pr.local.json" : mpa.source === "package-json" ? "package.json#pr" : "scripts";
|
|
812
|
+
console.log(`${colors.bold}MPA \u6A21\u5F0F:${colors.reset} \u662F ${colors.dim}(${sourceLabel})${colors.reset}`);
|
|
813
|
+
console.log(`${colors.bold}\u53EF\u9009\u5165\u53E3:${colors.reset} ${mpa.entries.join(", ")}`);
|
|
814
|
+
if (mpa.defaultEntry) {
|
|
815
|
+
console.log(`${colors.bold}\u9ED8\u8BA4\u5165\u53E3:${colors.reset} ${mpa.defaultEntry}`);
|
|
816
|
+
}
|
|
817
|
+
if (project.localConfig) {
|
|
818
|
+
console.log(`${colors.bold}\u672C\u5730\u914D\u7F6E:${colors.reset} .pr.local.json ${colors.dim}(\u81EA\u52A8\u52A0\u5165 .gitignore)${colors.reset}`);
|
|
819
|
+
}
|
|
820
|
+
} else {
|
|
821
|
+
console.log(`${colors.bold}MPA \u6A21\u5F0F:${colors.reset} \u5426`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
474
824
|
console.log();
|
|
475
825
|
if (project.scripts) {
|
|
476
826
|
const { scripts, detected } = project.scripts;
|
|
@@ -493,7 +843,10 @@ async function infoCommand(projectDir) {
|
|
|
493
843
|
console.log(`${colors.bold}\u6240\u6709\u811A\u672C:${colors.reset}`);
|
|
494
844
|
for (const name of allScripts) {
|
|
495
845
|
const cmd = scripts[name];
|
|
496
|
-
|
|
846
|
+
if (!cmd) {
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
const displayCmd = cmd.length > 60 ? cmd.slice(0, 60) + "..." : cmd;
|
|
497
850
|
console.log(` ${colors.cyan}${name}${colors.reset} ${colors.dim}\u2192 ${displayCmd}${colors.reset}`);
|
|
498
851
|
}
|
|
499
852
|
}
|
|
@@ -505,26 +858,23 @@ async function infoCommand(projectDir) {
|
|
|
505
858
|
async function scriptCommand(projectDir, scriptName) {
|
|
506
859
|
const project = await analyzeProject(projectDir);
|
|
507
860
|
if (project.type === "unknown") {
|
|
508
|
-
|
|
509
|
-
process.exit(1);
|
|
861
|
+
throw new CliError("\u672A\u68C0\u6D4B\u5230\u9879\u76EE\u7C7B\u578B\u3002\u8BF7\u786E\u4FDD\u5F53\u524D\u76EE\u5F55\u5305\u542B package.json");
|
|
510
862
|
}
|
|
511
863
|
if (!project.scripts) {
|
|
512
|
-
|
|
513
|
-
process.exit(1);
|
|
864
|
+
throw new CliError("\u65E0\u6CD5\u8BFB\u53D6 package.json \u7684 scripts");
|
|
514
865
|
}
|
|
515
866
|
const scripts = project.scripts.scripts;
|
|
516
867
|
if (!(scriptName in scripts)) {
|
|
517
|
-
error(`\u811A\u672C "${scriptName}" \u4E0D\u5B58\u5728`);
|
|
518
|
-
console.log();
|
|
519
868
|
showAvailableScripts2(scripts);
|
|
520
|
-
|
|
869
|
+
throw new CliError(`\u811A\u672C "${scriptName}" \u4E0D\u5B58\u5728`);
|
|
521
870
|
}
|
|
522
871
|
log(`\u5305\u7BA1\u7406\u5668: ${project.packageManager.name}`);
|
|
523
872
|
log(`\u6267\u884C\u811A\u672C: ${scriptName}`);
|
|
524
|
-
const
|
|
873
|
+
const resolvedPm = await ensurePmAvailable(project.packageManager.name);
|
|
874
|
+
const runCmd = getRunCommand(resolvedPm, scriptName);
|
|
525
875
|
const exitCode = await execute(runCmd, { cwd: projectDir });
|
|
526
876
|
if (exitCode !== 0) {
|
|
527
|
-
|
|
877
|
+
throw new CliError("\u811A\u672C\u6267\u884C\u5931\u8D25", exitCode);
|
|
528
878
|
}
|
|
529
879
|
}
|
|
530
880
|
function showAvailableScripts2(scripts) {
|
|
@@ -540,7 +890,7 @@ function showAvailableScripts2(scripts) {
|
|
|
540
890
|
}
|
|
541
891
|
|
|
542
892
|
// src/index.ts
|
|
543
|
-
var VERSION = "0.
|
|
893
|
+
var VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
544
894
|
function parseArgs(args) {
|
|
545
895
|
const options = {
|
|
546
896
|
verbose: false,
|
|
@@ -553,14 +903,30 @@ function parseArgs(args) {
|
|
|
553
903
|
let i = 0;
|
|
554
904
|
while (i < args.length) {
|
|
555
905
|
const arg = args[i];
|
|
906
|
+
if (!arg) {
|
|
907
|
+
i++;
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
556
910
|
if (arg === "-v" || arg === "--verbose") {
|
|
557
911
|
options.verbose = true;
|
|
558
912
|
} else if (arg === "-d" || arg === "--dir") {
|
|
559
|
-
|
|
913
|
+
const dirArg = args[i + 1];
|
|
914
|
+
if (!dirArg || dirArg.startsWith("-")) {
|
|
915
|
+
throw new CliError("--dir \u9700\u8981\u4E00\u4E2A\u76EE\u5F55\u53C2\u6570");
|
|
916
|
+
}
|
|
917
|
+
options.dir = resolve(dirArg);
|
|
918
|
+
i++;
|
|
560
919
|
} else if (arg === "--no-install") {
|
|
561
920
|
options.noInstall = true;
|
|
562
921
|
} else if (arg === "-i" || arg === "--install") {
|
|
563
922
|
options.install = true;
|
|
923
|
+
} else if (arg === "-e" || arg === "--entry") {
|
|
924
|
+
const entryArg = args[i + 1];
|
|
925
|
+
if (!entryArg || entryArg.startsWith("-")) {
|
|
926
|
+
throw new CliError("--entry \u9700\u8981\u4E00\u4E2A\u5165\u53E3\u540D\u53C2\u6570");
|
|
927
|
+
}
|
|
928
|
+
options.entry = entryArg.trim();
|
|
929
|
+
i++;
|
|
564
930
|
} else if (arg === "-h" || arg === "--help") {
|
|
565
931
|
command = "help";
|
|
566
932
|
} else if (arg === "-V" || arg === "--version") {
|
|
@@ -571,6 +937,8 @@ function parseArgs(args) {
|
|
|
571
937
|
} else {
|
|
572
938
|
remainingArgs.push(arg);
|
|
573
939
|
}
|
|
940
|
+
} else {
|
|
941
|
+
warn(`\u672A\u77E5\u9009\u9879: ${arg}`);
|
|
574
942
|
}
|
|
575
943
|
i++;
|
|
576
944
|
}
|
|
@@ -583,7 +951,7 @@ ${"\x1B[36m"}pr${"\x1B[0m"} v${VERSION} - \u96F6\u914D\u7F6E\u667A\u80FD\u9879\u
|
|
|
583
951
|
${"\x1B[1m"}\u7528\u6CD5:${"\x1B[0m"} pr <command> [options]
|
|
584
952
|
|
|
585
953
|
${"\x1B[1m"}\u547D\u4EE4:${"\x1B[0m"}
|
|
586
|
-
run \u5B8C\u6574\u6D41\u7A0B\uFF1A\u68C0\u6D4B
|
|
954
|
+
run \u5B8C\u6574\u6D41\u7A0B\uFF1A\u68C0\u6D4B -> install -> \u542F\u52A8\u5F00\u53D1\u811A\u672C
|
|
587
955
|
test \u8FD0\u884C\u6D4B\u8BD5
|
|
588
956
|
build \u6784\u5EFA\u9879\u76EE
|
|
589
957
|
start \u751F\u4EA7\u6A21\u5F0F\u542F\u52A8
|
|
@@ -595,16 +963,16 @@ ${"\x1B[1m"}\u9009\u9879:${"\x1B[0m"}
|
|
|
595
963
|
-d, --dir <path> \u6307\u5B9A\u9879\u76EE\u76EE\u5F55 (\u9ED8\u8BA4: \u5F53\u524D\u76EE\u5F55)
|
|
596
964
|
-i, --install \u5F3A\u5236\u6267\u884C\u4F9D\u8D56\u5B89\u88C5
|
|
597
965
|
--no-install \u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5\u6B65\u9AA4
|
|
966
|
+
-e, --entry \u6307\u5B9A MPA \u5165\u53E3 (\u4E5F\u53EF\u7528\u73AF\u5883\u53D8\u91CF PR_ENTRY)
|
|
598
967
|
-h, --help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
|
|
599
968
|
-V, --version \u663E\u793A\u7248\u672C\u53F7
|
|
600
969
|
|
|
601
970
|
${"\x1B[1m"}\u793A\u4F8B:${"\x1B[0m"}
|
|
602
|
-
pr run
|
|
603
|
-
pr run
|
|
604
|
-
pr run
|
|
605
|
-
pr
|
|
606
|
-
pr
|
|
607
|
-
pr info \u67E5\u770B\u9879\u76EE\u4FE1\u606F
|
|
971
|
+
pr run
|
|
972
|
+
pr run --entry main
|
|
973
|
+
PR_ENTRY=formengine pr run
|
|
974
|
+
pr build --entry approve
|
|
975
|
+
pr info
|
|
608
976
|
`);
|
|
609
977
|
}
|
|
610
978
|
function showVersion() {
|
|
@@ -612,7 +980,7 @@ function showVersion() {
|
|
|
612
980
|
}
|
|
613
981
|
async function main() {
|
|
614
982
|
setupSignalHandlers();
|
|
615
|
-
const { command, options
|
|
983
|
+
const { command, options } = parseArgs(process.argv.slice(2));
|
|
616
984
|
setVerbose(options.verbose);
|
|
617
985
|
switch (command) {
|
|
618
986
|
case "":
|
|
@@ -623,16 +991,33 @@ async function main() {
|
|
|
623
991
|
showVersion();
|
|
624
992
|
break;
|
|
625
993
|
case "run":
|
|
626
|
-
await runCommand(options.dir, {
|
|
994
|
+
await runCommand(options.dir, {
|
|
995
|
+
noInstall: options.noInstall,
|
|
996
|
+
forceInstall: options.install,
|
|
997
|
+
scriptType: "dev",
|
|
998
|
+
entry: options.entry
|
|
999
|
+
});
|
|
627
1000
|
break;
|
|
628
1001
|
case "test":
|
|
629
|
-
await runCommand(options.dir, {
|
|
1002
|
+
await runCommand(options.dir, {
|
|
1003
|
+
noInstall: true,
|
|
1004
|
+
scriptType: "test",
|
|
1005
|
+
entry: options.entry
|
|
1006
|
+
});
|
|
630
1007
|
break;
|
|
631
1008
|
case "build":
|
|
632
|
-
await runCommand(options.dir, {
|
|
1009
|
+
await runCommand(options.dir, {
|
|
1010
|
+
noInstall: true,
|
|
1011
|
+
scriptType: "build",
|
|
1012
|
+
entry: options.entry
|
|
1013
|
+
});
|
|
633
1014
|
break;
|
|
634
1015
|
case "start":
|
|
635
|
-
await runCommand(options.dir, {
|
|
1016
|
+
await runCommand(options.dir, {
|
|
1017
|
+
noInstall: true,
|
|
1018
|
+
scriptType: "start",
|
|
1019
|
+
entry: options.entry
|
|
1020
|
+
});
|
|
636
1021
|
break;
|
|
637
1022
|
case "info":
|
|
638
1023
|
await infoCommand(options.dir);
|
|
@@ -643,6 +1028,10 @@ async function main() {
|
|
|
643
1028
|
}
|
|
644
1029
|
}
|
|
645
1030
|
main().catch((err) => {
|
|
646
|
-
|
|
1031
|
+
if (err instanceof CliError) {
|
|
1032
|
+
error(err.message);
|
|
1033
|
+
process.exit(err.exitCode);
|
|
1034
|
+
}
|
|
1035
|
+
error(err.message || "\u672A\u77E5\u9519\u8BEF");
|
|
647
1036
|
process.exit(1);
|
|
648
1037
|
});
|