project-runner 0.2.0 → 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.
Files changed (3) hide show
  1. package/README.md +24 -0
  2. package/dist/index.js +335 -36
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -106,9 +106,33 @@ pr info
106
106
  | `-d, --dir <path>` | 指定项目目录(默认:当前目录)|
107
107
  | `-i, --install` | 强制执行依赖安装 |
108
108
  | `--no-install` | 跳过依赖安装步骤 |
109
+ | `-e, --entry <name>` | 指定 MPA 入口(也可用 `PR_ENTRY`) |
109
110
  | `-h, --help` | 显示帮助信息 |
110
111
  | `-V, --version` | 显示版本号 |
111
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
+
112
136
  ## 示例输出
113
137
 
114
138
  ### `pr info`
package/dist/index.js CHANGED
@@ -124,9 +124,12 @@ function setupSignalHandlers() {
124
124
  });
125
125
  }
126
126
 
127
+ // src/cli/run.ts
128
+ import { createInterface as createInterface2 } from "node:readline/promises";
129
+
127
130
  // src/analyzer/index.ts
128
- import { readFile } from "fs/promises";
129
- import { join as join3 } from "path";
131
+ import { readFile as readFile2 } from "fs/promises";
132
+ import { join as join4 } from "path";
130
133
 
131
134
  // src/analyzer/package-manager.ts
132
135
  import { join } from "path";
@@ -223,7 +226,8 @@ var DEV_PATTERNS = ["dev", "serve", "start:dev", "develop", "watch"];
223
226
  var TEST_PATTERNS = ["test", "test:unit", "test:all", "spec"];
224
227
  var BUILD_PATTERNS = ["build", "compile", "bundle", "dist"];
225
228
  var START_PATTERNS = ["start", "preview", "production"];
226
- function analyzeScripts(packageJson) {
229
+ var STANDARD_TYPES = ["dev", "test", "build", "start"];
230
+ function analyzeScripts(packageJson, localConfig) {
227
231
  if (!packageJson) {
228
232
  return null;
229
233
  }
@@ -235,11 +239,81 @@ function analyzeScripts(packageJson) {
235
239
  build: findMatchingScript(scripts, BUILD_PATTERNS),
236
240
  start: findMatchingScript(scripts, START_PATTERNS)
237
241
  };
238
- return { scripts, detected };
242
+ const mpa = analyzeMpa(packageJson, scripts, localConfig);
243
+ return { scripts, detected, mpa };
239
244
  } catch {
240
245
  return null;
241
246
  }
242
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
+ }
243
317
  function findMatchingScript(scripts, patterns) {
244
318
  const scriptNames = Object.keys(scripts);
245
319
  for (const pattern of patterns) {
@@ -252,8 +326,12 @@ function findMatchingScript(scripts, patterns) {
252
326
  if (!name.toLowerCase().includes(pattern.toLowerCase())) {
253
327
  return false;
254
328
  }
255
- const scriptContent = scripts[name].toLowerCase();
256
- if (scriptContent.includes("npm i") || scriptContent.includes("npm install") || scriptContent.includes("yarn install") || scriptContent.includes("pnpm install") || scriptContent.includes("bun install")) {
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")) {
257
335
  return false;
258
336
  }
259
337
  return true;
@@ -264,6 +342,9 @@ function findMatchingScript(scripts, patterns) {
264
342
  }
265
343
  return void 0;
266
344
  }
345
+ function dedupe(items) {
346
+ return [...new Set(items)];
347
+ }
267
348
 
268
349
  // src/analyzer/dependencies.ts
269
350
  import { stat as stat2 } from "fs/promises";
@@ -329,34 +410,84 @@ async function getModifiedTime(path) {
329
410
  }
330
411
  }
331
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;
423
+ try {
424
+ const content = await readFile(configPath, "utf-8");
425
+ parsed = JSON.parse(content.replace(/^\uFEFF/, ""));
426
+ } catch {
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);
436
+ }
437
+ if (typeof parsed.defaultEntry === "string" && parsed.defaultEntry.trim()) {
438
+ config.defaultEntry = parsed.defaultEntry.trim();
439
+ }
440
+ return config;
441
+ }
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;
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");
458
+ }
459
+
332
460
  // src/analyzer/index.ts
333
461
  async function analyzeProject(projectDir) {
334
- const packageJsonPath = join3(projectDir, "package.json");
462
+ const packageJsonPath = join4(projectDir, "package.json");
335
463
  const hasPackageJson = await fileExists(packageJsonPath);
336
464
  if (!hasPackageJson) {
337
465
  return {
338
466
  type: "unknown",
339
467
  packageManager: { name: "npm", source: "default" },
340
468
  scripts: null,
341
- dependencies: { hasNodeModules: false, needsInstall: false }
469
+ dependencies: { hasNodeModules: false, needsInstall: false },
470
+ localConfig: null
342
471
  };
343
472
  }
344
473
  let packageJson = {};
345
474
  try {
346
- const content = await readFile(packageJsonPath, "utf-8");
347
- packageJson = JSON.parse(content);
475
+ const content = await readFile2(packageJsonPath, "utf-8");
476
+ packageJson = JSON.parse(content.replace(/^\uFEFF/, ""));
348
477
  } catch {
349
478
  }
350
- const [packageManager, dependencies] = await Promise.all([
479
+ const [packageManager, dependencies, localConfig] = await Promise.all([
351
480
  detectPackageManager(projectDir, packageJson),
352
- checkDependencyStatus(projectDir)
481
+ checkDependencyStatus(projectDir),
482
+ loadPrLocalConfig(projectDir)
353
483
  ]);
354
- const scripts = analyzeScripts(packageJson);
484
+ const scripts = analyzeScripts(packageJson, localConfig);
355
485
  return {
356
486
  type: "nodejs",
357
487
  packageManager,
358
488
  scripts,
359
489
  dependencies,
490
+ localConfig,
360
491
  name: packageJson.name,
361
492
  version: packageJson.version,
362
493
  description: packageJson.description
@@ -451,7 +582,7 @@ async function autoInstallPm(pm) {
451
582
 
452
583
  // src/cli/run.ts
453
584
  async function runCommand(projectDir, options = {}) {
454
- const { noInstall = false, forceInstall = false, scriptType = "dev" } = options;
585
+ const { noInstall = false, forceInstall = false, scriptType = "dev", entry } = options;
455
586
  log("\u6B63\u5728\u5206\u6790\u9879\u76EE...");
456
587
  const project = await analyzeProject(projectDir);
457
588
  if (project.type === "unknown") {
@@ -462,10 +593,14 @@ async function runCommand(projectDir, options = {}) {
462
593
  if (!project.scripts) {
463
594
  throw new CliError("\u65E0\u6CD5\u8BFB\u53D6 package.json \u7684 scripts");
464
595
  }
465
- const scriptName = findScript(project, scriptType);
596
+ const resolvedEntry = await resolveEntry(project, scriptType, entry);
597
+ const scriptName = findScript(project, scriptType, resolvedEntry);
466
598
  if (!scriptName) {
467
- showAvailableScripts(project);
468
- throw new CliError(`\u672A\u627E\u5230 ${scriptType} \u76F8\u5173\u7684\u811A\u672C`);
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}`);
469
604
  }
470
605
  log(`\u5C06\u6267\u884C\u811A\u672C: ${scriptName}`);
471
606
  const resolvedPm = await ensurePmAvailable(project.packageManager.name);
@@ -496,10 +631,16 @@ async function runCommand(projectDir, options = {}) {
496
631
  throw new CliError("\u811A\u672C\u6267\u884C\u5931\u8D25", exitCode);
497
632
  }
498
633
  }
499
- function findScript(project, scriptType) {
634
+ function findScript(project, scriptType, entry) {
500
635
  const scripts = project.scripts;
501
636
  if (!scripts)
502
637
  return void 0;
638
+ if (entry) {
639
+ const entryScript = `${scriptType}:${entry}`;
640
+ if (scripts.scripts[entryScript]) {
641
+ return entryScript;
642
+ }
643
+ }
503
644
  const detected = scripts.detected[scriptType];
504
645
  if (detected) {
505
646
  return detected;
@@ -509,9 +650,115 @@ function findScript(project, scriptType) {
509
650
  }
510
651
  return void 0;
511
652
  }
512
- function showAvailableScripts(project) {
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) {
513
753
  if (!project.scripts)
514
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
+ }
515
762
  const scriptNames = Object.keys(project.scripts.scripts);
516
763
  if (scriptNames.length === 0) {
517
764
  warn("package.json \u4E2D\u6CA1\u6709\u5B9A\u4E49\u4EFB\u4F55 scripts");
@@ -529,8 +776,8 @@ function showAvailableScripts(project) {
529
776
  async function infoCommand(projectDir) {
530
777
  const project = await analyzeProject(projectDir);
531
778
  if (project.type === "unknown") {
532
- console.log(`${colors.red}\u2717${colors.reset} \u672A\u68C0\u6D4B\u5230\u9879\u76EE\u7C7B\u578B`);
533
- console.log(" \u8BF7\u786E\u4FDD\u5F53\u524D\u76EE\u5F55\u5305\u542B package.json \u6216\u5176\u4ED6\u9879\u76EE\u914D\u7F6E\u6587\u4EF6");
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");
534
781
  return;
535
782
  }
536
783
  console.log();
@@ -558,6 +805,22 @@ async function infoCommand(projectDir) {
558
805
  const deps = project.dependencies;
559
806
  const depsStatus = deps.needsInstall ? `${colors.yellow}\u9700\u8981\u5B89\u88C5${colors.reset} (${deps.reason})` : `${colors.green}\u5DF2\u5C31\u7EEA${colors.reset}`;
560
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
+ }
561
824
  console.log();
562
825
  if (project.scripts) {
563
826
  const { scripts, detected } = project.scripts;
@@ -580,7 +843,10 @@ async function infoCommand(projectDir) {
580
843
  console.log(`${colors.bold}\u6240\u6709\u811A\u672C:${colors.reset}`);
581
844
  for (const name of allScripts) {
582
845
  const cmd = scripts[name];
583
- const displayCmd = cmd.length > 40 ? cmd.slice(0, 40) + "..." : cmd;
846
+ if (!cmd) {
847
+ continue;
848
+ }
849
+ const displayCmd = cmd.length > 60 ? cmd.slice(0, 60) + "..." : cmd;
584
850
  console.log(` ${colors.cyan}${name}${colors.reset} ${colors.dim}\u2192 ${displayCmd}${colors.reset}`);
585
851
  }
586
852
  }
@@ -624,7 +890,7 @@ function showAvailableScripts2(scripts) {
624
890
  }
625
891
 
626
892
  // src/index.ts
627
- var VERSION = true ? "0.2.0" : "0.0.0-dev";
893
+ var VERSION = true ? "0.3.0" : "0.0.0-dev";
628
894
  function parseArgs(args) {
629
895
  const options = {
630
896
  verbose: false,
@@ -637,14 +903,30 @@ function parseArgs(args) {
637
903
  let i = 0;
638
904
  while (i < args.length) {
639
905
  const arg = args[i];
906
+ if (!arg) {
907
+ i++;
908
+ continue;
909
+ }
640
910
  if (arg === "-v" || arg === "--verbose") {
641
911
  options.verbose = true;
642
912
  } else if (arg === "-d" || arg === "--dir") {
643
- options.dir = resolve(args[++i] || ".");
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++;
644
919
  } else if (arg === "--no-install") {
645
920
  options.noInstall = true;
646
921
  } else if (arg === "-i" || arg === "--install") {
647
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++;
648
930
  } else if (arg === "-h" || arg === "--help") {
649
931
  command = "help";
650
932
  } else if (arg === "-V" || arg === "--version") {
@@ -669,7 +951,7 @@ ${"\x1B[36m"}pr${"\x1B[0m"} v${VERSION} - \u96F6\u914D\u7F6E\u667A\u80FD\u9879\u
669
951
  ${"\x1B[1m"}\u7528\u6CD5:${"\x1B[0m"} pr <command> [options]
670
952
 
671
953
  ${"\x1B[1m"}\u547D\u4EE4:${"\x1B[0m"}
672
- run \u5B8C\u6574\u6D41\u7A0B\uFF1A\u68C0\u6D4B \u2192 install \u2192 \u542F\u52A8\u5F00\u53D1\u670D\u52A1\u5668
954
+ run \u5B8C\u6574\u6D41\u7A0B\uFF1A\u68C0\u6D4B -> install -> \u542F\u52A8\u5F00\u53D1\u811A\u672C
673
955
  test \u8FD0\u884C\u6D4B\u8BD5
674
956
  build \u6784\u5EFA\u9879\u76EE
675
957
  start \u751F\u4EA7\u6A21\u5F0F\u542F\u52A8
@@ -681,16 +963,16 @@ ${"\x1B[1m"}\u9009\u9879:${"\x1B[0m"}
681
963
  -d, --dir <path> \u6307\u5B9A\u9879\u76EE\u76EE\u5F55 (\u9ED8\u8BA4: \u5F53\u524D\u76EE\u5F55)
682
964
  -i, --install \u5F3A\u5236\u6267\u884C\u4F9D\u8D56\u5B89\u88C5
683
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)
684
967
  -h, --help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
685
968
  -V, --version \u663E\u793A\u7248\u672C\u53F7
686
969
 
687
970
  ${"\x1B[1m"}\u793A\u4F8B:${"\x1B[0m"}
688
- pr run \u4E00\u952E\u542F\u52A8\u9879\u76EE
689
- pr run -i \u5F3A\u5236\u5B89\u88C5\u4F9D\u8D56\u540E\u542F\u52A8
690
- pr run -v \u663E\u793A\u8BE6\u7EC6\u68C0\u6D4B\u8FC7\u7A0B
691
- pr test \u8FD0\u884C\u6D4B\u8BD5
692
- pr lint \u8FD0\u884C lint \u811A\u672C
693
- 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
694
976
  `);
695
977
  }
696
978
  function showVersion() {
@@ -698,7 +980,7 @@ function showVersion() {
698
980
  }
699
981
  async function main() {
700
982
  setupSignalHandlers();
701
- const { command, options, args } = parseArgs(process.argv.slice(2));
983
+ const { command, options } = parseArgs(process.argv.slice(2));
702
984
  setVerbose(options.verbose);
703
985
  switch (command) {
704
986
  case "":
@@ -709,16 +991,33 @@ async function main() {
709
991
  showVersion();
710
992
  break;
711
993
  case "run":
712
- await runCommand(options.dir, { noInstall: options.noInstall, forceInstall: options.install, scriptType: "dev" });
994
+ await runCommand(options.dir, {
995
+ noInstall: options.noInstall,
996
+ forceInstall: options.install,
997
+ scriptType: "dev",
998
+ entry: options.entry
999
+ });
713
1000
  break;
714
1001
  case "test":
715
- await runCommand(options.dir, { noInstall: true, scriptType: "test" });
1002
+ await runCommand(options.dir, {
1003
+ noInstall: true,
1004
+ scriptType: "test",
1005
+ entry: options.entry
1006
+ });
716
1007
  break;
717
1008
  case "build":
718
- await runCommand(options.dir, { noInstall: true, scriptType: "build" });
1009
+ await runCommand(options.dir, {
1010
+ noInstall: true,
1011
+ scriptType: "build",
1012
+ entry: options.entry
1013
+ });
719
1014
  break;
720
1015
  case "start":
721
- await runCommand(options.dir, { noInstall: true, scriptType: "start" });
1016
+ await runCommand(options.dir, {
1017
+ noInstall: true,
1018
+ scriptType: "start",
1019
+ entry: options.entry
1020
+ });
722
1021
  break;
723
1022
  case "info":
724
1023
  await infoCommand(options.dir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-runner",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "零配置智能项目运行器 - 一键运行任意 Node.js 项目",
5
5
  "author": "liangzhenqi",
6
6
  "license": "MIT",