@lark-apaas/fullstack-cli 1.1.16-beta.11 → 1.1.16-beta.13

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/index.js CHANGED
@@ -2363,8 +2363,8 @@ var genDbSchemaCommand = {
2363
2363
  };
2364
2364
 
2365
2365
  // src/commands/sync/run.handler.ts
2366
- import path4 from "path";
2367
- import fs6 from "fs";
2366
+ import path5 from "path";
2367
+ import fs7 from "fs";
2368
2368
  import { fileURLToPath as fileURLToPath3 } from "url";
2369
2369
 
2370
2370
  // src/config/sync.ts
@@ -2525,18 +2525,93 @@ function deepMergeJson(user, template, arrayMerge = {}, currentPath = "") {
2525
2525
  return result;
2526
2526
  }
2527
2527
 
2528
+ // src/utils/package-json.ts
2529
+ import fs6 from "fs";
2530
+ import path4 from "path";
2531
+ var LEGACY_LINT_SCRIPT = 'concurrently "npm run eslint" "npm run type:check" "npm run stylelint"';
2532
+ var PATCHED_LINT_SCRIPT = "node ./scripts/lint.js";
2533
+ function readPackageJson(cwd = process.cwd()) {
2534
+ const pkgPath = path4.join(cwd, "package.json");
2535
+ if (!fs6.existsSync(pkgPath)) {
2536
+ throw new Error(`package.json not found at ${pkgPath}`);
2537
+ }
2538
+ const content = fs6.readFileSync(pkgPath, "utf-8");
2539
+ return JSON.parse(content);
2540
+ }
2541
+ function writePackageJson(pkg2, cwd = process.cwd()) {
2542
+ const pkgPath = path4.join(cwd, "package.json");
2543
+ const content = JSON.stringify(pkg2, null, 2) + "\n";
2544
+ fs6.writeFileSync(pkgPath, content, "utf-8");
2545
+ }
2546
+ function patchLintScriptForFilesSupport(pkg2) {
2547
+ const currentLint = pkg2.scripts?.lint;
2548
+ if (!currentLint) {
2549
+ return "skipped-missing";
2550
+ }
2551
+ if (currentLint === PATCHED_LINT_SCRIPT || currentLint === "node scripts/lint.js") {
2552
+ return "already-patched";
2553
+ }
2554
+ if (currentLint !== LEGACY_LINT_SCRIPT) {
2555
+ return "skipped-custom";
2556
+ }
2557
+ pkg2.scripts = pkg2.scripts || {};
2558
+ pkg2.scripts.lint = PATCHED_LINT_SCRIPT;
2559
+ return "patched";
2560
+ }
2561
+ function removeUpgradeScript(pkg2) {
2562
+ if (!pkg2.scripts?.upgrade) {
2563
+ return false;
2564
+ }
2565
+ delete pkg2.scripts.upgrade;
2566
+ return true;
2567
+ }
2568
+ function cleanDevScript(pkg2) {
2569
+ if (!pkg2.scripts?.dev) {
2570
+ return false;
2571
+ }
2572
+ const originalDev = pkg2.scripts.dev;
2573
+ const cleanedDev = originalDev.replace(/npm\s+run\s+upgrade\s*&&\s*/g, "").replace(/npm\s+run\s+upgrade\s*$/g, "").trim();
2574
+ if (cleanedDev !== originalDev) {
2575
+ pkg2.scripts.dev = cleanedDev;
2576
+ return true;
2577
+ }
2578
+ return false;
2579
+ }
2580
+ function cleanupPackageJson(cwd = process.cwd()) {
2581
+ try {
2582
+ const pkg2 = readPackageJson(cwd);
2583
+ let changed = false;
2584
+ if (removeUpgradeScript(pkg2)) {
2585
+ console.log("[fullstack-cli] \u2713 Removed scripts.upgrade");
2586
+ changed = true;
2587
+ }
2588
+ if (cleanDevScript(pkg2)) {
2589
+ console.log("[fullstack-cli] \u2713 Cleaned scripts.dev (removed npm run upgrade)");
2590
+ changed = true;
2591
+ }
2592
+ if (changed) {
2593
+ writePackageJson(pkg2, cwd);
2594
+ }
2595
+ return changed;
2596
+ } catch (error) {
2597
+ const message = error instanceof Error ? error.message : String(error);
2598
+ console.log(`[fullstack-cli] \u26A0 Could not cleanup package.json: ${message}`);
2599
+ return false;
2600
+ }
2601
+ }
2602
+
2528
2603
  // src/commands/sync/run.handler.ts
2529
2604
  async function run2(options) {
2530
2605
  const userProjectRoot = process.env.INIT_CWD || process.cwd();
2531
2606
  const __filename = fileURLToPath3(import.meta.url);
2532
- const __dirname2 = path4.dirname(__filename);
2533
- const pluginRoot = path4.resolve(__dirname2, "..");
2607
+ const __dirname2 = path5.dirname(__filename);
2608
+ const pluginRoot = path5.resolve(__dirname2, "..");
2534
2609
  if (userProjectRoot === pluginRoot) {
2535
2610
  console.log("[fullstack-cli] Skip syncing (installing plugin itself)");
2536
2611
  process.exit(0);
2537
2612
  }
2538
- const userPackageJson = path4.join(userProjectRoot, "package.json");
2539
- if (!fs6.existsSync(userPackageJson)) {
2613
+ const userPackageJson = path5.join(userProjectRoot, "package.json");
2614
+ if (!fs7.existsSync(userPackageJson)) {
2540
2615
  console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
2541
2616
  process.exit(0);
2542
2617
  }
@@ -2552,6 +2627,7 @@ async function run2(options) {
2552
2627
  for (const rule of config.sync) {
2553
2628
  await syncRule(rule, pluginRoot, userProjectRoot);
2554
2629
  }
2630
+ patchUserPackageJson(userProjectRoot);
2555
2631
  if (config.permissions) {
2556
2632
  setPermissions(config.permissions, userProjectRoot);
2557
2633
  }
@@ -2562,9 +2638,32 @@ async function run2(options) {
2562
2638
  process.exit(1);
2563
2639
  }
2564
2640
  }
2641
+ function patchUserPackageJson(userProjectRoot) {
2642
+ try {
2643
+ const pkg2 = readPackageJson(userProjectRoot);
2644
+ const lintPatchResult = patchLintScriptForFilesSupport(pkg2);
2645
+ if (lintPatchResult === "patched") {
2646
+ writePackageJson(pkg2, userProjectRoot);
2647
+ console.log("[fullstack-cli] \u2713 Patched scripts.lint to support --files");
2648
+ return;
2649
+ }
2650
+ if (lintPatchResult === "already-patched") {
2651
+ console.log("[fullstack-cli] \u25CB scripts.lint already supports --files");
2652
+ return;
2653
+ }
2654
+ if (lintPatchResult === "skipped-custom") {
2655
+ console.warn(
2656
+ "[fullstack-cli] \u26A0 Skipped patching scripts.lint because it has been customized"
2657
+ );
2658
+ }
2659
+ } catch (error) {
2660
+ const message = error instanceof Error ? error.message : String(error);
2661
+ console.warn(`[fullstack-cli] \u26A0 Could not patch package.json: ${message}`);
2662
+ }
2663
+ }
2565
2664
  async function syncRule(rule, pluginRoot, userProjectRoot) {
2566
2665
  if (rule.type === "delete-file" || rule.type === "delete-directory") {
2567
- const destPath2 = path4.join(userProjectRoot, rule.to);
2666
+ const destPath2 = path5.join(userProjectRoot, rule.to);
2568
2667
  if (rule.type === "delete-file") {
2569
2668
  deleteFile(destPath2);
2570
2669
  } else {
@@ -2573,32 +2672,32 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2573
2672
  return;
2574
2673
  }
2575
2674
  if (rule.type === "remove-line") {
2576
- const destPath2 = path4.join(userProjectRoot, rule.to);
2675
+ const destPath2 = path5.join(userProjectRoot, rule.to);
2577
2676
  removeLineFromFile(destPath2, rule.pattern);
2578
2677
  return;
2579
2678
  }
2580
2679
  if (rule.type === "add-script") {
2581
- const packageJsonPath = path4.join(userProjectRoot, "package.json");
2680
+ const packageJsonPath = path5.join(userProjectRoot, "package.json");
2582
2681
  addScript(packageJsonPath, rule.name, rule.command, rule.overwrite ?? false);
2583
2682
  return;
2584
2683
  }
2585
2684
  if (rule.type === "add-line") {
2586
- const destPath2 = path4.join(userProjectRoot, rule.to);
2685
+ const destPath2 = path5.join(userProjectRoot, rule.to);
2587
2686
  addLineToFile(destPath2, rule.line);
2588
2687
  return;
2589
2688
  }
2590
2689
  if (rule.type === "merge-json") {
2591
- const srcPath2 = path4.join(pluginRoot, rule.from);
2592
- const destPath2 = path4.join(userProjectRoot, rule.to);
2690
+ const srcPath2 = path5.join(pluginRoot, rule.from);
2691
+ const destPath2 = path5.join(userProjectRoot, rule.to);
2593
2692
  mergeJsonFile(srcPath2, destPath2, rule.arrayMerge);
2594
2693
  return;
2595
2694
  }
2596
2695
  if (!("from" in rule)) {
2597
2696
  return;
2598
2697
  }
2599
- const srcPath = path4.join(pluginRoot, rule.from);
2600
- const destPath = path4.join(userProjectRoot, rule.to);
2601
- if (!fs6.existsSync(srcPath)) {
2698
+ const srcPath = path5.join(pluginRoot, rule.from);
2699
+ const destPath = path5.join(userProjectRoot, rule.to);
2700
+ if (!fs7.existsSync(srcPath)) {
2602
2701
  console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
2603
2702
  return;
2604
2703
  }
@@ -2615,68 +2714,68 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2615
2714
  }
2616
2715
  }
2617
2716
  function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
2618
- if (onlyIfExists && !fs6.existsSync(dest)) {
2619
- console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (skipped, target not exists)`);
2717
+ if (onlyIfExists && !fs7.existsSync(dest)) {
2718
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, target not exists)`);
2620
2719
  return;
2621
2720
  }
2622
- const destDir = path4.dirname(dest);
2623
- if (!fs6.existsSync(destDir)) {
2624
- fs6.mkdirSync(destDir, { recursive: true });
2721
+ const destDir = path5.dirname(dest);
2722
+ if (!fs7.existsSync(destDir)) {
2723
+ fs7.mkdirSync(destDir, { recursive: true });
2625
2724
  }
2626
- if (fs6.existsSync(dest) && !overwrite) {
2627
- console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (skipped, already exists)`);
2725
+ if (fs7.existsSync(dest) && !overwrite) {
2726
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`);
2628
2727
  return;
2629
2728
  }
2630
- fs6.copyFileSync(src, dest);
2631
- console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)}`);
2729
+ fs7.copyFileSync(src, dest);
2730
+ console.log(`[fullstack-cli] \u2713 ${path5.basename(dest)}`);
2632
2731
  }
2633
2732
  function syncDirectory(src, dest, overwrite = true) {
2634
- if (!fs6.existsSync(dest)) {
2635
- fs6.mkdirSync(dest, { recursive: true });
2733
+ if (!fs7.existsSync(dest)) {
2734
+ fs7.mkdirSync(dest, { recursive: true });
2636
2735
  }
2637
- const files = fs6.readdirSync(src);
2736
+ const files = fs7.readdirSync(src);
2638
2737
  let count = 0;
2639
2738
  files.forEach((file) => {
2640
- const srcFile = path4.join(src, file);
2641
- const destFile = path4.join(dest, file);
2642
- const stats = fs6.statSync(srcFile);
2739
+ const srcFile = path5.join(src, file);
2740
+ const destFile = path5.join(dest, file);
2741
+ const stats = fs7.statSync(srcFile);
2643
2742
  if (stats.isDirectory()) {
2644
2743
  syncDirectory(srcFile, destFile, overwrite);
2645
2744
  } else {
2646
- if (overwrite || !fs6.existsSync(destFile)) {
2647
- fs6.copyFileSync(srcFile, destFile);
2648
- console.log(`[fullstack-cli] \u2713 ${path4.relative(dest, destFile)}`);
2745
+ if (overwrite || !fs7.existsSync(destFile)) {
2746
+ fs7.copyFileSync(srcFile, destFile);
2747
+ console.log(`[fullstack-cli] \u2713 ${path5.relative(dest, destFile)}`);
2649
2748
  count++;
2650
2749
  }
2651
2750
  }
2652
2751
  });
2653
2752
  if (count > 0) {
2654
- console.log(`[fullstack-cli] Synced ${count} files to ${path4.basename(dest)}/`);
2753
+ console.log(`[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`);
2655
2754
  }
2656
2755
  }
2657
2756
  function appendToFile(src, dest) {
2658
- const content = fs6.readFileSync(src, "utf-8");
2757
+ const content = fs7.readFileSync(src, "utf-8");
2659
2758
  let existingContent = "";
2660
- if (fs6.existsSync(dest)) {
2661
- existingContent = fs6.readFileSync(dest, "utf-8");
2759
+ if (fs7.existsSync(dest)) {
2760
+ existingContent = fs7.readFileSync(dest, "utf-8");
2662
2761
  }
2663
2762
  if (existingContent.includes(content.trim())) {
2664
- console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (already contains content)`);
2763
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`);
2665
2764
  return;
2666
2765
  }
2667
- fs6.appendFileSync(dest, content);
2668
- console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)} (appended)`);
2766
+ fs7.appendFileSync(dest, content);
2767
+ console.log(`[fullstack-cli] \u2713 ${path5.basename(dest)} (appended)`);
2669
2768
  }
2670
2769
  function setPermissions(permissions, projectRoot) {
2671
2770
  for (const [pattern, mode] of Object.entries(permissions)) {
2672
2771
  if (pattern === "**/*.sh") {
2673
- const scriptsDir = path4.join(projectRoot, "scripts");
2674
- if (fs6.existsSync(scriptsDir)) {
2675
- const files = fs6.readdirSync(scriptsDir);
2772
+ const scriptsDir = path5.join(projectRoot, "scripts");
2773
+ if (fs7.existsSync(scriptsDir)) {
2774
+ const files = fs7.readdirSync(scriptsDir);
2676
2775
  files.forEach((file) => {
2677
2776
  if (file.endsWith(".sh")) {
2678
- const filePath = path4.join(scriptsDir, file);
2679
- fs6.chmodSync(filePath, mode);
2777
+ const filePath = path5.join(scriptsDir, file);
2778
+ fs7.chmodSync(filePath, mode);
2680
2779
  }
2681
2780
  });
2682
2781
  }
@@ -2684,27 +2783,27 @@ function setPermissions(permissions, projectRoot) {
2684
2783
  }
2685
2784
  }
2686
2785
  function deleteFile(filePath) {
2687
- if (fs6.existsSync(filePath)) {
2688
- fs6.unlinkSync(filePath);
2689
- console.log(`[fullstack-cli] \u2713 ${path4.basename(filePath)} (deleted)`);
2786
+ if (fs7.existsSync(filePath)) {
2787
+ fs7.unlinkSync(filePath);
2788
+ console.log(`[fullstack-cli] \u2713 ${path5.basename(filePath)} (deleted)`);
2690
2789
  } else {
2691
- console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (not found)`);
2790
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(filePath)} (not found)`);
2692
2791
  }
2693
2792
  }
2694
2793
  function deleteDirectory(dirPath) {
2695
- if (fs6.existsSync(dirPath)) {
2696
- fs6.rmSync(dirPath, { recursive: true });
2697
- console.log(`[fullstack-cli] \u2713 ${path4.basename(dirPath)} (deleted)`);
2794
+ if (fs7.existsSync(dirPath)) {
2795
+ fs7.rmSync(dirPath, { recursive: true });
2796
+ console.log(`[fullstack-cli] \u2713 ${path5.basename(dirPath)} (deleted)`);
2698
2797
  } else {
2699
- console.log(`[fullstack-cli] \u25CB ${path4.basename(dirPath)} (not found)`);
2798
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(dirPath)} (not found)`);
2700
2799
  }
2701
2800
  }
2702
2801
  function addScript(packageJsonPath, name, command, overwrite) {
2703
- if (!fs6.existsSync(packageJsonPath)) {
2802
+ if (!fs7.existsSync(packageJsonPath)) {
2704
2803
  console.log(`[fullstack-cli] \u25CB package.json (not found)`);
2705
2804
  return;
2706
2805
  }
2707
- const content = fs6.readFileSync(packageJsonPath, "utf-8");
2806
+ const content = fs7.readFileSync(packageJsonPath, "utf-8");
2708
2807
  const pkg2 = JSON.parse(content);
2709
2808
  if (!pkg2.scripts) {
2710
2809
  pkg2.scripts = {};
@@ -2716,42 +2815,42 @@ function addScript(packageJsonPath, name, command, overwrite) {
2716
2815
  }
2717
2816
  }
2718
2817
  pkg2.scripts[name] = command;
2719
- fs6.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
2818
+ fs7.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
2720
2819
  console.log(`[fullstack-cli] \u2713 scripts.${name}`);
2721
2820
  }
2722
2821
  function addLineToFile(filePath, line) {
2723
- const fileName = path4.basename(filePath);
2724
- if (!fs6.existsSync(filePath)) {
2822
+ const fileName = path5.basename(filePath);
2823
+ if (!fs7.existsSync(filePath)) {
2725
2824
  console.log(`[fullstack-cli] \u25CB ${fileName} (not found, skipped)`);
2726
2825
  return;
2727
2826
  }
2728
- const content = fs6.readFileSync(filePath, "utf-8");
2827
+ const content = fs7.readFileSync(filePath, "utf-8");
2729
2828
  const lines = content.split("\n").map((l) => l.trim());
2730
2829
  if (lines.includes(line)) {
2731
2830
  console.log(`[fullstack-cli] \u25CB ${fileName} (line already exists: ${line})`);
2732
2831
  return;
2733
2832
  }
2734
2833
  const appendContent = (content.endsWith("\n") ? "" : "\n") + line + "\n";
2735
- fs6.appendFileSync(filePath, appendContent);
2834
+ fs7.appendFileSync(filePath, appendContent);
2736
2835
  console.log(`[fullstack-cli] \u2713 ${fileName} (added: ${line})`);
2737
2836
  }
2738
2837
  function mergeJsonFile(src, dest, arrayMerge) {
2739
- const fileName = path4.basename(dest);
2740
- if (!fs6.existsSync(src)) {
2838
+ const fileName = path5.basename(dest);
2839
+ if (!fs7.existsSync(src)) {
2741
2840
  console.warn(`[fullstack-cli] Source not found: ${src}`);
2742
2841
  return;
2743
2842
  }
2744
- const templateContent = JSON.parse(fs6.readFileSync(src, "utf-8"));
2745
- if (!fs6.existsSync(dest)) {
2746
- const destDir = path4.dirname(dest);
2747
- if (!fs6.existsSync(destDir)) {
2748
- fs6.mkdirSync(destDir, { recursive: true });
2843
+ const templateContent = JSON.parse(fs7.readFileSync(src, "utf-8"));
2844
+ if (!fs7.existsSync(dest)) {
2845
+ const destDir = path5.dirname(dest);
2846
+ if (!fs7.existsSync(destDir)) {
2847
+ fs7.mkdirSync(destDir, { recursive: true });
2749
2848
  }
2750
- fs6.writeFileSync(dest, JSON.stringify(templateContent, null, 2) + "\n");
2849
+ fs7.writeFileSync(dest, JSON.stringify(templateContent, null, 2) + "\n");
2751
2850
  console.log(`[fullstack-cli] \u2713 ${fileName} (created)`);
2752
2851
  return;
2753
2852
  }
2754
- const userContent = JSON.parse(fs6.readFileSync(dest, "utf-8"));
2853
+ const userContent = JSON.parse(fs7.readFileSync(dest, "utf-8"));
2755
2854
  const merged = deepMergeJson(userContent, templateContent, arrayMerge ?? {});
2756
2855
  const userStr = JSON.stringify(userContent, null, 2);
2757
2856
  const mergedStr = JSON.stringify(merged, null, 2);
@@ -2759,7 +2858,7 @@ function mergeJsonFile(src, dest, arrayMerge) {
2759
2858
  console.log(`[fullstack-cli] \u25CB ${fileName} (already up to date)`);
2760
2859
  return;
2761
2860
  }
2762
- fs6.writeFileSync(dest, mergedStr + "\n");
2861
+ fs7.writeFileSync(dest, mergedStr + "\n");
2763
2862
  console.log(`[fullstack-cli] \u2713 ${fileName} (merged)`);
2764
2863
  }
2765
2864
 
@@ -2820,12 +2919,12 @@ async function reportCreateInstanceEvent(pluginKey, version) {
2820
2919
 
2821
2920
  // src/utils/git.ts
2822
2921
  import { execSync, spawnSync as spawnSync2 } from "child_process";
2823
- import fs7 from "fs";
2824
- import path5 from "path";
2922
+ import fs8 from "fs";
2923
+ import path6 from "path";
2825
2924
  function isGitRepository(cwd = process.cwd()) {
2826
2925
  try {
2827
- const gitDir = path5.join(cwd, ".git");
2828
- if (fs7.existsSync(gitDir)) {
2926
+ const gitDir = path6.join(cwd, ".git");
2927
+ if (fs8.existsSync(gitDir)) {
2829
2928
  return true;
2830
2929
  }
2831
2930
  const result = spawnSync2("git", ["rev-parse", "--git-dir"], {
@@ -2854,7 +2953,7 @@ function getChangedFiles(cwd = process.cwd()) {
2854
2953
  function gitAddUpgradeFiles(cwd = process.cwd(), filesToStage) {
2855
2954
  const filteredFiles = [];
2856
2955
  for (const filePath of filesToStage) {
2857
- if (fs7.existsSync(path5.join(cwd, filePath))) {
2956
+ if (fs8.existsSync(path6.join(cwd, filePath))) {
2858
2957
  filteredFiles.push(filePath);
2859
2958
  continue;
2860
2959
  }
@@ -2938,64 +3037,6 @@ Auto-committed by fullstack-cli`;
2938
3037
  }
2939
3038
  }
2940
3039
 
2941
- // src/utils/package-json.ts
2942
- import fs8 from "fs";
2943
- import path6 from "path";
2944
- function readPackageJson(cwd = process.cwd()) {
2945
- const pkgPath = path6.join(cwd, "package.json");
2946
- if (!fs8.existsSync(pkgPath)) {
2947
- throw new Error(`package.json not found at ${pkgPath}`);
2948
- }
2949
- const content = fs8.readFileSync(pkgPath, "utf-8");
2950
- return JSON.parse(content);
2951
- }
2952
- function writePackageJson(pkg2, cwd = process.cwd()) {
2953
- const pkgPath = path6.join(cwd, "package.json");
2954
- const content = JSON.stringify(pkg2, null, 2) + "\n";
2955
- fs8.writeFileSync(pkgPath, content, "utf-8");
2956
- }
2957
- function removeUpgradeScript(pkg2) {
2958
- if (!pkg2.scripts?.upgrade) {
2959
- return false;
2960
- }
2961
- delete pkg2.scripts.upgrade;
2962
- return true;
2963
- }
2964
- function cleanDevScript(pkg2) {
2965
- if (!pkg2.scripts?.dev) {
2966
- return false;
2967
- }
2968
- const originalDev = pkg2.scripts.dev;
2969
- const cleanedDev = originalDev.replace(/npm\s+run\s+upgrade\s*&&\s*/g, "").replace(/npm\s+run\s+upgrade\s*$/g, "").trim();
2970
- if (cleanedDev !== originalDev) {
2971
- pkg2.scripts.dev = cleanedDev;
2972
- return true;
2973
- }
2974
- return false;
2975
- }
2976
- function cleanupPackageJson(cwd = process.cwd()) {
2977
- try {
2978
- const pkg2 = readPackageJson(cwd);
2979
- let changed = false;
2980
- if (removeUpgradeScript(pkg2)) {
2981
- console.log("[fullstack-cli] \u2713 Removed scripts.upgrade");
2982
- changed = true;
2983
- }
2984
- if (cleanDevScript(pkg2)) {
2985
- console.log("[fullstack-cli] \u2713 Cleaned scripts.dev (removed npm run upgrade)");
2986
- changed = true;
2987
- }
2988
- if (changed) {
2989
- writePackageJson(pkg2, cwd);
2990
- }
2991
- return changed;
2992
- } catch (error) {
2993
- const message = error instanceof Error ? error.message : String(error);
2994
- console.log(`[fullstack-cli] \u26A0 Could not cleanup package.json: ${message}`);
2995
- return false;
2996
- }
2997
- }
2998
-
2999
3040
  // src/commands/upgrade/shared/utils.ts
3000
3041
  import path7 from "path";
3001
3042
  import fs9 from "fs";
@@ -7264,26 +7305,48 @@ var UPLOAD_STATIC_DEFAULTS = {
7264
7305
  };
7265
7306
 
7266
7307
  // src/commands/build/api-client.ts
7308
+ var ERROR_RESPONSE_PREVIEW_LIMIT = 2e3;
7309
+ async function getErrorResponseDetails(response) {
7310
+ try {
7311
+ if (typeof response.text === "function") {
7312
+ const text = await response.text();
7313
+ if (!text) {
7314
+ return "";
7315
+ }
7316
+ return `, response: ${text.slice(0, ERROR_RESPONSE_PREVIEW_LIMIT)}`;
7317
+ }
7318
+ if (typeof response.json === "function") {
7319
+ const data = await response.json();
7320
+ const serialized = JSON.stringify(data);
7321
+ if (!serialized) {
7322
+ return "";
7323
+ }
7324
+ return `, response: ${serialized.slice(0, ERROR_RESPONSE_PREVIEW_LIMIT)}`;
7325
+ }
7326
+ } catch {
7327
+ return "";
7328
+ }
7329
+ return "";
7330
+ }
7331
+ async function throwIfResponseNotOk(response, message) {
7332
+ if (response.ok && response.status === 200) {
7333
+ return;
7334
+ }
7335
+ const details = await getErrorResponseDetails(response);
7336
+ throw new Error(`${message}: ${response.status} ${response.statusText}${details}`);
7337
+ }
7267
7338
  async function genArtifactUploadCredential(appId, body) {
7268
7339
  const client = getHttpClient();
7269
7340
  const url = `/v1/app/${appId}/pipeline/gen_artifact_upload_credential`;
7270
7341
  const response = await client.post(url, body);
7271
- if (!response.ok || response.status !== 200) {
7272
- throw new Error(
7273
- `gen_artifact_upload_credential \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
7274
- );
7275
- }
7342
+ await throwIfResponseNotOk(response, "gen_artifact_upload_credential \u8BF7\u6C42\u5931\u8D25");
7276
7343
  return response.json();
7277
7344
  }
7278
7345
  async function getDefaultBucketId(appId) {
7279
7346
  const client = getHttpClient();
7280
7347
  const url = `/v1/app/${appId}/storage/inner/staticBucket`;
7281
7348
  const response = await client.post(url, {});
7282
- if (!response.ok || response.status !== 200) {
7283
- throw new Error(
7284
- `getOrCreateStaticBucket \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
7285
- );
7286
- }
7349
+ await throwIfResponseNotOk(response, "getOrCreateStaticBucket \u8BF7\u6C42\u5931\u8D25");
7287
7350
  const data = await response.json();
7288
7351
  if (data.status_code !== "0") {
7289
7352
  throw new Error(`getOrCreateStaticBucket \u8FD4\u56DE\u5F02\u5E38, status_code: ${data.status_code}`);
@@ -7298,22 +7361,14 @@ async function preUploadStaticAttachment(appId, bucketId) {
7298
7361
  const client = getHttpClient();
7299
7362
  const url = `/v1/app/${appId}/storage/bucket/${bucketId}/preUploadStatic`;
7300
7363
  const response = await client.post(url, {});
7301
- if (!response.ok || response.status !== 200) {
7302
- throw new Error(
7303
- `preUploadStatic \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
7304
- );
7305
- }
7364
+ await throwIfResponseNotOk(response, "preUploadStatic \u8BF7\u6C42\u5931\u8D25");
7306
7365
  return response.json();
7307
7366
  }
7308
7367
  async function uploadStaticAttachmentCallback(appId, bucketId, body) {
7309
7368
  const client = getHttpClient();
7310
7369
  const url = `/v1/app/${appId}/storage/bucket/${bucketId}/object/callbackStatic`;
7311
7370
  const response = await client.post(url, body);
7312
- if (!response.ok || response.status !== 200) {
7313
- throw new Error(
7314
- `callbackStatic \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
7315
- );
7316
- }
7371
+ await throwIfResponseNotOk(response, "callbackStatic \u8BF7\u6C42\u5931\u8D25");
7317
7372
  return response.json();
7318
7373
  }
7319
7374
 
@@ -7433,7 +7488,7 @@ async function uploadStatic(options) {
7433
7488
  console.error(`${LOG_PREFIX} \u8C03\u7528 callbackStatic (uploadID: ${uploadID})...`);
7434
7489
  const callbackResp = await uploadStaticAttachmentCallback(appId, bucketId, { uploadID });
7435
7490
  if (callbackResp.status_code !== "0") {
7436
- throw new Error(`callbackStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${callbackResp.status_code}`);
7491
+ throw new Error(`callbackStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${callbackResp.status_code}, response: ${JSON.stringify(callbackResp)}`);
7437
7492
  }
7438
7493
  const attachments = callbackResp.data?.attachments || [];
7439
7494
  console.error(`${LOG_PREFIX} \u4E0A\u4F20\u5B8C\u6210\uFF0C\u5171 ${attachments.length} \u4E2A\u6587\u4EF6`);
@@ -7458,14 +7513,14 @@ function resolveTosutilPath(tosutilPath) {
7458
7513
  async function fetchPreUpload(appId, bucketId) {
7459
7514
  const response = await preUploadStaticAttachment(appId, bucketId);
7460
7515
  if (response.status_code !== "0") {
7461
- throw new Error(`preUploadStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${response.status_code}`);
7516
+ throw new Error(`preUploadStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${response.status_code}, response: ${JSON.stringify(response)}`);
7462
7517
  }
7463
7518
  const { uploadPrefix, uploadID, uploadCredential } = response.data || {};
7464
7519
  if (!uploadPrefix || !uploadID) {
7465
- throw new Error("preUploadStatic \u8FD4\u56DE\u6570\u636E\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 uploadPrefix \u6216 uploadID");
7520
+ throw new Error(`preUploadStatic \u8FD4\u56DE\u6570\u636E\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 uploadPrefix \u6216 uploadID, response: ${JSON.stringify(response)}`);
7466
7521
  }
7467
7522
  if (!uploadCredential?.AccessKeyID || !uploadCredential?.SecretAccessKey || !uploadCredential?.SessionToken) {
7468
- throw new Error("preUploadStatic \u8FD4\u56DE\u7684\u51ED\u8BC1\u5B57\u6BB5\u4E0D\u5B8C\u6574");
7523
+ throw new Error(`preUploadStatic \u8FD4\u56DE\u7684\u51ED\u8BC1\u5B57\u6BB5\u4E0D\u5B8C\u6574, response: ${JSON.stringify(response)}`);
7469
7524
  }
7470
7525
  return response;
7471
7526
  }
@@ -7510,15 +7565,18 @@ async function preUploadStatic(options) {
7510
7565
  const response = await preUploadStaticAttachment(appId, bucketId);
7511
7566
  if (response.status_code !== "0") {
7512
7567
  console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${response.status_code}`);
7568
+ console.error(`${LOG_PREFIX2} preUploadStatic \u54CD\u5E94: ${JSON.stringify(response)}`);
7513
7569
  return;
7514
7570
  }
7515
7571
  const { downloadURLPrefix, uploadPrefix, uploadID, uploadCredential } = response.data || {};
7516
7572
  if (!downloadURLPrefix || !uploadPrefix || !uploadID) {
7517
7573
  console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u6570\u636E\u4E0D\u5B8C\u6574`);
7574
+ console.error(`${LOG_PREFIX2} preUploadStatic \u54CD\u5E94: ${JSON.stringify(response)}`);
7518
7575
  return;
7519
7576
  }
7520
7577
  if (!uploadCredential?.AccessKeyID || !uploadCredential?.SecretAccessKey || !uploadCredential?.SessionToken) {
7521
7578
  console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u7684\u51ED\u8BC1\u5B57\u6BB5\u4E0D\u5B8C\u6574`);
7579
+ console.error(`${LOG_PREFIX2} preUploadStatic \u54CD\u5E94: ${JSON.stringify(response)}`);
7522
7580
  return;
7523
7581
  }
7524
7582
  console.log(`export STATIC_ASSETS_BASE_URL="${shellEscape(downloadURLPrefix)}"`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.16-beta.11",
3
+ "version": "1.1.16-beta.13",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,7 +52,7 @@
52
52
  "@types/node": "^22.0.0",
53
53
  "tsup": "^8.3.5",
54
54
  "typescript": "^5.9.2",
55
- "vitest": "^2.1.8"
55
+ "vitest": "^3.2.4"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "typescript": "^5.9.2"
@@ -19,82 +19,144 @@ print_time() {
19
19
  }
20
20
 
21
21
  # ==================== 步骤 0 ====================
22
- echo "🗑️ [0/5] 安装插件"
22
+ echo "🗑️ [0/6] 安装插件"
23
23
  STEP_START=$(node -e "console.log(Date.now())")
24
24
  npx fullstack-cli action-plugin init
25
25
  print_time $STEP_START
26
26
  echo ""
27
27
 
28
28
  # ==================== 步骤 1 ====================
29
- echo "📝 [1/5] 更新 openapi 代码"
29
+ echo "📝 [1/6] 更新 openapi 代码"
30
30
  STEP_START=$(node -e "console.log(Date.now())")
31
31
  npm run gen:openapi
32
32
  print_time $STEP_START
33
33
  echo ""
34
34
 
35
35
  # ==================== 步骤 2 ====================
36
- echo "🗑️ [2/5] 清理 dist 目录"
36
+ echo "🗑️ [2/6] 清理 dist 目录"
37
37
  STEP_START=$(node -e "console.log(Date.now())")
38
38
  rm -rf "$ROOT_DIR/dist"
39
39
  print_time $STEP_START
40
40
  echo ""
41
41
 
42
42
  # ==================== 步骤 3 ====================
43
- echo "🔨 [3/5] 并行构建 server 和 client"
43
+ echo "🗺️ [3/6] 生成路由定义"
44
44
  STEP_START=$(node -e "console.log(Date.now())")
45
45
 
46
- # 并行构建
47
- echo " ├─ 启动 server 构建..."
48
- npm run build:server > /tmp/build-server.log 2>&1 &
49
- SERVER_PID=$!
46
+ # 在 client/server 构建之前生成到 dist/,供 DefinePlugin 注入前端 bundle
47
+ # 注意:nest-cli.json deleteOutDir 必须为 false(模板默认值),否则 nest build 会清掉 dist/
48
+ echo " ├─ 生成 API 路由定义..."
49
+ npx generate-api-routes --server-dir ./server --out-dir ./dist > /tmp/gen-api-routes.log 2>&1 &
50
+ API_ROUTES_PID=$!
50
51
 
51
- echo " ├─ 启动 client 构建..."
52
- npm run build:client > /tmp/build-client.log 2>&1 &
53
- CLIENT_PID=$!
52
+ echo " ├─ 生成页面路由定义..."
53
+ npx generate-page-routes --app-path ./client/src/app.tsx --out-dir ./dist > /tmp/gen-page-routes.log 2>&1 &
54
+ PAGE_ROUTES_PID=$!
54
55
 
55
- # 等待两个构建完成
56
- SERVER_EXIT=0
57
- CLIENT_EXIT=0
56
+ API_ROUTES_EXIT=0
57
+ PAGE_ROUTES_EXIT=0
58
58
 
59
- wait $SERVER_PID || SERVER_EXIT=$?
60
- wait $CLIENT_PID || CLIENT_EXIT=$?
59
+ wait $API_ROUTES_PID || API_ROUTES_EXIT=$?
60
+ wait $PAGE_ROUTES_PID || PAGE_ROUTES_EXIT=$?
61
61
 
62
- # 检查构建结果
63
- if [ $SERVER_EXIT -ne 0 ]; then
64
- echo " ❌ Server 构建失败"
65
- cat /tmp/build-server.log
66
- exit 1
62
+ if [ $API_ROUTES_EXIT -ne 0 ]; then
63
+ echo " ⚠️ API 路由生成失败(不影响构建)"
64
+ cat /tmp/gen-api-routes.log
65
+ else
66
+ echo " ✅ API 路由生成完成"
67
67
  fi
68
68
 
69
- if [ $CLIENT_EXIT -ne 0 ]; then
70
- echo " ❌ Client 构建失败"
71
- cat /tmp/build-client.log
72
- exit 1
69
+ if [ $PAGE_ROUTES_EXIT -ne 0 ]; then
70
+ echo " ⚠️ 页面路由生成失败(不影响构建)"
71
+ cat /tmp/gen-page-routes.log
72
+ else
73
+ echo " ✅ 页面路由生成完成"
73
74
  fi
74
-
75
- echo " ✅ Server 构建完成"
76
- echo " ✅ Client 构建完成"
77
75
  print_time $STEP_START
78
76
  echo ""
79
77
 
80
78
  # ==================== 步骤 4 ====================
81
- echo "📦 [4/5] 准备产物"
79
+ echo "🔨 [4/6] 并行构建 server 和 client"
82
80
  STEP_START=$(node -e "console.log(Date.now())")
83
81
 
84
- # 拷贝 run.sh dist/(prod 从 dist/ 启动,确保 cwd 一致性)
85
- cp "$ROOT_DIR/scripts/run.sh" "$DIST_DIR/"
86
-
87
- # 拷贝 .env 文件(如果存在)
88
- if [ -f "$ROOT_DIR/.env" ]; then
89
- cp "$ROOT_DIR/.env" "$DIST_DIR/"
82
+ # 根据 only_frontend_change 决定是否构建 server
83
+ if [[ "${only_frontend_change:-false}" == "true" ]]; then
84
+ echo "🔨 [4/6] 仅构建 client (only_frontend_change=true)"
85
+
86
+ echo " ├─ 启动 client 构建..."
87
+ npm run build:client > /tmp/build-client.log 2>&1
88
+ CLIENT_EXIT=$?
89
+
90
+ if [ $CLIENT_EXIT -ne 0 ]; then
91
+ echo " ❌ Client 构建失败"
92
+ cat /tmp/build-client.log
93
+ exit 1
94
+ fi
95
+
96
+ echo " ✅ Client 构建完成"
97
+ else
98
+ echo "🔨 [4/6] 并行构建 server 和 client"
99
+
100
+ # 并行构建
101
+ echo " ├─ 启动 server 构建..."
102
+ npm run build:server > /tmp/build-server.log 2>&1 &
103
+ SERVER_PID=$!
104
+
105
+ echo " ├─ 启动 client 构建..."
106
+ npm run build:client > /tmp/build-client.log 2>&1 &
107
+ CLIENT_PID=$!
108
+
109
+ # 等待两个构建完成
110
+ SERVER_EXIT=0
111
+ CLIENT_EXIT=0
112
+
113
+ wait $SERVER_PID || SERVER_EXIT=$?
114
+ wait $CLIENT_PID || CLIENT_EXIT=$?
115
+
116
+ # 检查构建结果
117
+ if [ $SERVER_EXIT -ne 0 ]; then
118
+ echo " ❌ Server 构建失败"
119
+ cat /tmp/build-server.log
120
+ exit 1
121
+ fi
122
+
123
+ if [ $CLIENT_EXIT -ne 0 ]; then
124
+ echo " ❌ Client 构建失败"
125
+ cat /tmp/build-client.log
126
+ exit 1
127
+ fi
128
+
129
+ echo " ✅ Server 构建完成"
130
+ echo " ✅ Client 构建完成"
90
131
  fi
91
132
 
133
+ print_time $STEP_START
134
+ echo ""
135
+
136
+ # ==================== 步骤 5 ====================
137
+ echo "📦 [5/6] 准备产物"
138
+ STEP_START=$(node -e "console.log(Date.now())")
139
+
92
140
  # 移动 client 下的 HTML 文件到 dist/dist/client,保证 views 路径在 dev/prod 下一致
141
+ # 使用 mv 而非 cp:HTML 不能上传到公网 CDN,移走后 dist/client 中不再包含 HTML
93
142
  if [ -d "$DIST_DIR/client" ]; then
94
143
  mkdir -p "$DIST_DIR/dist/client"
95
144
  find "$DIST_DIR/client" -maxdepth 1 -name "*.html" -exec mv {} "$DIST_DIR/dist/client/" \;
96
145
  fi
97
146
 
147
+ # server 相关产物准备(only_frontend_change=true 时跳过)
148
+ if [[ "${only_frontend_change:-false}" == "true" ]]; then
149
+ echo " [skip] 跳过 run.sh/.env 复制 (only_frontend_change=true)"
150
+ else
151
+ # 拷贝 run.sh 到 dist/(prod 从 dist/ 启动,确保 cwd 一致性)
152
+ cp "$ROOT_DIR/scripts/run.sh" "$DIST_DIR/"
153
+
154
+ # 拷贝 .env 文件(如果存在)
155
+ if [ -f "$ROOT_DIR/.env" ]; then
156
+ cp "$ROOT_DIR/.env" "$DIST_DIR/"
157
+ fi
158
+ fi
159
+
98
160
  # 清理无用文件
99
161
  rm -rf "$DIST_DIR/scripts"
100
162
  rm -rf "$DIST_DIR/tsconfig.node.tsbuildinfo"
@@ -102,12 +164,19 @@ rm -rf "$DIST_DIR/tsconfig.node.tsbuildinfo"
102
164
  print_time $STEP_START
103
165
  echo ""
104
166
 
105
- # ==================== 步骤 5 ====================
106
- echo "✂️ [5/5] 智能依赖裁剪"
167
+ # ==================== 步骤 6 ====================
168
+ echo "✂️ [6/6] 智能依赖裁剪"
107
169
  STEP_START=$(node -e "console.log(Date.now())")
108
170
 
109
- # 分析实际依赖、复制并裁剪 node_modules、生成精简的 package.json
110
- node "$ROOT_DIR/scripts/prune-smart.js"
171
+ # 智能依赖裁剪(仅全量构建时执行,纯前端场景不打包 server,无需裁剪)
172
+ if [[ "${only_frontend_change:-false}" == "true" ]]; then
173
+ echo "✂️ [6/6] 跳过智能依赖裁剪 (only_frontend_change=true)"
174
+ else
175
+ echo "✂️ [6/6] 智能依赖裁剪"
176
+
177
+ # 分析实际依赖、复制并裁剪 node_modules、生成精简的 package.json
178
+ node "$ROOT_DIR/scripts/prune-smart.js"
179
+ fi
111
180
 
112
181
  print_time $STEP_START
113
182
  echo ""
@@ -118,9 +187,16 @@ print_time $TOTAL_START
118
187
 
119
188
  # 输出产物信息
120
189
  DIST_SIZE=$(du -sh "$DIST_DIR" | cut -f1)
121
- NODE_MODULES_SIZE=$(du -sh "$DIST_DIR/node_modules" | cut -f1)
122
- echo ""
123
- echo "📊 构建产物统计:"
124
- echo " 产物大小: $DIST_SIZE"
125
- echo " node_modules: $NODE_MODULES_SIZE"
126
- echo ""
190
+ if [[ "${only_frontend_change:-false}" == "true" ]]; then
191
+ echo ""
192
+ echo "📊 构建产物统计:"
193
+ echo " 产物大小: $DIST_SIZE"
194
+ echo ""
195
+ else
196
+ NODE_MODULES_SIZE=$(du -sh "$DIST_DIR/node_modules" | cut -f1)
197
+ echo ""
198
+ echo "📊 构建产物统计:"
199
+ echo " 产物大小: $DIST_SIZE"
200
+ echo " node_modules: $NODE_MODULES_SIZE"
201
+ echo ""
202
+ fi
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('node:path');
4
+ const { spawn } = require('node:child_process');
5
+ const fs = require('node:fs');
6
+
7
+ const cwd = process.cwd();
8
+
9
+ function getBinName(name) {
10
+ return process.platform === 'win32' ? `${name}.cmd` : name;
11
+ }
12
+
13
+ function runCommand(command, args) {
14
+ return new Promise((resolve) => {
15
+ const child = spawn(command, args, {
16
+ cwd,
17
+ stdio: 'inherit',
18
+ shell: false,
19
+ });
20
+
21
+ child.on('close', (code) => resolve(code || 0));
22
+ child.on('error', () => resolve(1));
23
+ });
24
+ }
25
+
26
+ function normalizeProjectFile(filePath) {
27
+ const absolutePath = path.isAbsolute(filePath)
28
+ ? filePath
29
+ : path.resolve(cwd, filePath);
30
+
31
+ if (!fs.existsSync(absolutePath)) {
32
+ console.warn(`[lint] Skip missing file: ${filePath}`);
33
+ return null;
34
+ }
35
+
36
+ const relativePath = path.relative(cwd, absolutePath);
37
+ if (relativePath.startsWith('..')) {
38
+ console.warn(`[lint] Skip file outside project: ${filePath}`);
39
+ return null;
40
+ }
41
+
42
+ return relativePath.split(path.sep).join('/');
43
+ }
44
+
45
+ function parseFilesArg(argv) {
46
+ const filesIndex = argv.indexOf('--files');
47
+ if (filesIndex === -1) {
48
+ return null;
49
+ }
50
+
51
+ return argv.slice(filesIndex + 1).filter(Boolean);
52
+ }
53
+
54
+ function isEslintTarget(filePath) {
55
+ return /\.(c|m)?(j|t)sx?$/.test(filePath);
56
+ }
57
+
58
+ function isTypeCheckTarget(filePath) {
59
+ return /\.(ts|tsx|mts|cts)$/.test(filePath);
60
+ }
61
+
62
+ function isStylelintTarget(filePath) {
63
+ return filePath.endsWith('.css');
64
+ }
65
+
66
+ async function runDefaultLint() {
67
+ const code = await runCommand(getBinName('npx'), [
68
+ 'concurrently',
69
+ 'npm run eslint',
70
+ 'npm run type:check',
71
+ 'npm run stylelint',
72
+ ]);
73
+ process.exit(code);
74
+ }
75
+
76
+ async function runSelectiveLint(inputFiles) {
77
+ const normalizedFiles = Array.from(
78
+ new Set(inputFiles.map(normalizeProjectFile).filter(Boolean)),
79
+ );
80
+
81
+ if (normalizedFiles.length === 0) {
82
+ console.log('[lint] No supported project files found');
83
+ process.exit(0);
84
+ }
85
+
86
+ const eslintFiles = normalizedFiles.filter(isEslintTarget);
87
+ const stylelintFiles = normalizedFiles.filter(isStylelintTarget);
88
+ const typeCheckFiles = normalizedFiles.filter(isTypeCheckTarget);
89
+
90
+ const clientTypeFiles = [];
91
+ const serverTypeFiles = [];
92
+
93
+ for (const filePath of typeCheckFiles) {
94
+ if (filePath.startsWith('client/')) {
95
+ clientTypeFiles.push(filePath);
96
+ } else if (filePath.startsWith('server/')) {
97
+ serverTypeFiles.push(filePath);
98
+ } else if (filePath.startsWith('shared/')) {
99
+ clientTypeFiles.push(filePath);
100
+ serverTypeFiles.push(filePath);
101
+ }
102
+ }
103
+
104
+ const tasks = [];
105
+
106
+ if (eslintFiles.length > 0) {
107
+ tasks.push(runCommand(getBinName('npx'), ['eslint', '--quiet', ...eslintFiles]));
108
+ }
109
+
110
+ if (stylelintFiles.length > 0) {
111
+ tasks.push(runCommand(getBinName('npx'), ['stylelint', '--quiet', ...stylelintFiles]));
112
+ }
113
+
114
+ if (clientTypeFiles.length > 0) {
115
+ tasks.push(runCommand(getBinName('npm'), ['run', 'type:check:client']));
116
+ }
117
+
118
+ if (serverTypeFiles.length > 0) {
119
+ tasks.push(runCommand(getBinName('npm'), ['run', 'type:check:server']));
120
+ }
121
+
122
+ if (tasks.length === 0) {
123
+ console.log('[lint] No supported files matched for lint');
124
+ process.exit(0);
125
+ }
126
+
127
+ const results = await Promise.all(tasks);
128
+ process.exit(results.some(code => code !== 0) ? 1 : 0);
129
+ }
130
+
131
+ async function main() {
132
+ const files = parseFilesArg(process.argv.slice(2));
133
+ if (files === null) {
134
+ await runDefaultLint();
135
+ return;
136
+ }
137
+
138
+ if (files.length === 0) {
139
+ console.error('[lint] --files requires at least one file path');
140
+ process.exit(1);
141
+ }
142
+
143
+ await runSelectiveLint(files);
144
+ }
145
+
146
+ main().catch((error) => {
147
+ const message = error instanceof Error ? error.message : String(error);
148
+ console.error(`[lint] Failed to run lint: ${message}`);
149
+ process.exit(1);
150
+ });