@lark-apaas/fullstack-cli 1.1.16-beta.12 → 1.1.16-beta.14
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/config/drizzle.config.js +1 -1
- package/dist/index.js +177 -134
- package/package.json +2 -3
- package/templates/drizzle.config.ts +1 -1
- package/templates/scripts/build.sh +126 -45
- package/templates/scripts/lint.js +150 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/config/drizzle.config.ts
|
|
2
2
|
import { defineConfig } from "drizzle-kit";
|
|
3
|
-
var outputDir = process.env.__DRIZZLE_OUT_DIR__ || "./
|
|
3
|
+
var outputDir = process.env.__DRIZZLE_OUT_DIR__ || "./tmp/.introspect";
|
|
4
4
|
var schemaPath = process.env.__DRIZZLE_SCHEMA_PATH__ || "./server/database/schema.ts";
|
|
5
5
|
var parsedUrl = new URL(process.env.SUDA_DATABASE_URL || "");
|
|
6
6
|
var envSchemaFilter = process.env.DRIZZLE_SCHEMA_FILTER;
|
package/dist/index.js
CHANGED
|
@@ -2167,7 +2167,9 @@ async function run(options = {}) {
|
|
|
2167
2167
|
process.exit(1);
|
|
2168
2168
|
}
|
|
2169
2169
|
const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || "server/database/schema.ts";
|
|
2170
|
-
const
|
|
2170
|
+
const INTROSPECT_ROOT = path2.resolve(process.cwd(), "tmp/.introspect");
|
|
2171
|
+
fs4.mkdirSync(INTROSPECT_ROOT, { recursive: true });
|
|
2172
|
+
const OUT_DIR = fs4.mkdtempSync(path2.join(INTROSPECT_ROOT, "run-"));
|
|
2171
2173
|
const SCHEMA_FILE = path2.resolve(process.cwd(), outputPath);
|
|
2172
2174
|
console.log("[gen-db-schema] Starting...");
|
|
2173
2175
|
const __filename = fileURLToPath2(import.meta.url);
|
|
@@ -2363,8 +2365,8 @@ var genDbSchemaCommand = {
|
|
|
2363
2365
|
};
|
|
2364
2366
|
|
|
2365
2367
|
// src/commands/sync/run.handler.ts
|
|
2366
|
-
import
|
|
2367
|
-
import
|
|
2368
|
+
import path5 from "path";
|
|
2369
|
+
import fs7 from "fs";
|
|
2368
2370
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2369
2371
|
|
|
2370
2372
|
// src/config/sync.ts
|
|
@@ -2525,18 +2527,93 @@ function deepMergeJson(user, template, arrayMerge = {}, currentPath = "") {
|
|
|
2525
2527
|
return result;
|
|
2526
2528
|
}
|
|
2527
2529
|
|
|
2530
|
+
// src/utils/package-json.ts
|
|
2531
|
+
import fs6 from "fs";
|
|
2532
|
+
import path4 from "path";
|
|
2533
|
+
var LEGACY_LINT_SCRIPT = 'concurrently "npm run eslint" "npm run type:check" "npm run stylelint"';
|
|
2534
|
+
var PATCHED_LINT_SCRIPT = "node ./scripts/lint.js";
|
|
2535
|
+
function readPackageJson(cwd = process.cwd()) {
|
|
2536
|
+
const pkgPath = path4.join(cwd, "package.json");
|
|
2537
|
+
if (!fs6.existsSync(pkgPath)) {
|
|
2538
|
+
throw new Error(`package.json not found at ${pkgPath}`);
|
|
2539
|
+
}
|
|
2540
|
+
const content = fs6.readFileSync(pkgPath, "utf-8");
|
|
2541
|
+
return JSON.parse(content);
|
|
2542
|
+
}
|
|
2543
|
+
function writePackageJson(pkg2, cwd = process.cwd()) {
|
|
2544
|
+
const pkgPath = path4.join(cwd, "package.json");
|
|
2545
|
+
const content = JSON.stringify(pkg2, null, 2) + "\n";
|
|
2546
|
+
fs6.writeFileSync(pkgPath, content, "utf-8");
|
|
2547
|
+
}
|
|
2548
|
+
function patchLintScriptForFilesSupport(pkg2) {
|
|
2549
|
+
const currentLint = pkg2.scripts?.lint;
|
|
2550
|
+
if (!currentLint) {
|
|
2551
|
+
return "skipped-missing";
|
|
2552
|
+
}
|
|
2553
|
+
if (currentLint === PATCHED_LINT_SCRIPT || currentLint === "node scripts/lint.js") {
|
|
2554
|
+
return "already-patched";
|
|
2555
|
+
}
|
|
2556
|
+
if (currentLint !== LEGACY_LINT_SCRIPT) {
|
|
2557
|
+
return "skipped-custom";
|
|
2558
|
+
}
|
|
2559
|
+
pkg2.scripts = pkg2.scripts || {};
|
|
2560
|
+
pkg2.scripts.lint = PATCHED_LINT_SCRIPT;
|
|
2561
|
+
return "patched";
|
|
2562
|
+
}
|
|
2563
|
+
function removeUpgradeScript(pkg2) {
|
|
2564
|
+
if (!pkg2.scripts?.upgrade) {
|
|
2565
|
+
return false;
|
|
2566
|
+
}
|
|
2567
|
+
delete pkg2.scripts.upgrade;
|
|
2568
|
+
return true;
|
|
2569
|
+
}
|
|
2570
|
+
function cleanDevScript(pkg2) {
|
|
2571
|
+
if (!pkg2.scripts?.dev) {
|
|
2572
|
+
return false;
|
|
2573
|
+
}
|
|
2574
|
+
const originalDev = pkg2.scripts.dev;
|
|
2575
|
+
const cleanedDev = originalDev.replace(/npm\s+run\s+upgrade\s*&&\s*/g, "").replace(/npm\s+run\s+upgrade\s*$/g, "").trim();
|
|
2576
|
+
if (cleanedDev !== originalDev) {
|
|
2577
|
+
pkg2.scripts.dev = cleanedDev;
|
|
2578
|
+
return true;
|
|
2579
|
+
}
|
|
2580
|
+
return false;
|
|
2581
|
+
}
|
|
2582
|
+
function cleanupPackageJson(cwd = process.cwd()) {
|
|
2583
|
+
try {
|
|
2584
|
+
const pkg2 = readPackageJson(cwd);
|
|
2585
|
+
let changed = false;
|
|
2586
|
+
if (removeUpgradeScript(pkg2)) {
|
|
2587
|
+
console.log("[fullstack-cli] \u2713 Removed scripts.upgrade");
|
|
2588
|
+
changed = true;
|
|
2589
|
+
}
|
|
2590
|
+
if (cleanDevScript(pkg2)) {
|
|
2591
|
+
console.log("[fullstack-cli] \u2713 Cleaned scripts.dev (removed npm run upgrade)");
|
|
2592
|
+
changed = true;
|
|
2593
|
+
}
|
|
2594
|
+
if (changed) {
|
|
2595
|
+
writePackageJson(pkg2, cwd);
|
|
2596
|
+
}
|
|
2597
|
+
return changed;
|
|
2598
|
+
} catch (error) {
|
|
2599
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2600
|
+
console.log(`[fullstack-cli] \u26A0 Could not cleanup package.json: ${message}`);
|
|
2601
|
+
return false;
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2528
2605
|
// src/commands/sync/run.handler.ts
|
|
2529
2606
|
async function run2(options) {
|
|
2530
2607
|
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
2531
2608
|
const __filename = fileURLToPath3(import.meta.url);
|
|
2532
|
-
const __dirname2 =
|
|
2533
|
-
const pluginRoot =
|
|
2609
|
+
const __dirname2 = path5.dirname(__filename);
|
|
2610
|
+
const pluginRoot = path5.resolve(__dirname2, "..");
|
|
2534
2611
|
if (userProjectRoot === pluginRoot) {
|
|
2535
2612
|
console.log("[fullstack-cli] Skip syncing (installing plugin itself)");
|
|
2536
2613
|
process.exit(0);
|
|
2537
2614
|
}
|
|
2538
|
-
const userPackageJson =
|
|
2539
|
-
if (!
|
|
2615
|
+
const userPackageJson = path5.join(userProjectRoot, "package.json");
|
|
2616
|
+
if (!fs7.existsSync(userPackageJson)) {
|
|
2540
2617
|
console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
|
|
2541
2618
|
process.exit(0);
|
|
2542
2619
|
}
|
|
@@ -2552,6 +2629,7 @@ async function run2(options) {
|
|
|
2552
2629
|
for (const rule of config.sync) {
|
|
2553
2630
|
await syncRule(rule, pluginRoot, userProjectRoot);
|
|
2554
2631
|
}
|
|
2632
|
+
patchUserPackageJson(userProjectRoot);
|
|
2555
2633
|
if (config.permissions) {
|
|
2556
2634
|
setPermissions(config.permissions, userProjectRoot);
|
|
2557
2635
|
}
|
|
@@ -2562,9 +2640,32 @@ async function run2(options) {
|
|
|
2562
2640
|
process.exit(1);
|
|
2563
2641
|
}
|
|
2564
2642
|
}
|
|
2643
|
+
function patchUserPackageJson(userProjectRoot) {
|
|
2644
|
+
try {
|
|
2645
|
+
const pkg2 = readPackageJson(userProjectRoot);
|
|
2646
|
+
const lintPatchResult = patchLintScriptForFilesSupport(pkg2);
|
|
2647
|
+
if (lintPatchResult === "patched") {
|
|
2648
|
+
writePackageJson(pkg2, userProjectRoot);
|
|
2649
|
+
console.log("[fullstack-cli] \u2713 Patched scripts.lint to support --files");
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
if (lintPatchResult === "already-patched") {
|
|
2653
|
+
console.log("[fullstack-cli] \u25CB scripts.lint already supports --files");
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
if (lintPatchResult === "skipped-custom") {
|
|
2657
|
+
console.warn(
|
|
2658
|
+
"[fullstack-cli] \u26A0 Skipped patching scripts.lint because it has been customized"
|
|
2659
|
+
);
|
|
2660
|
+
}
|
|
2661
|
+
} catch (error) {
|
|
2662
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2663
|
+
console.warn(`[fullstack-cli] \u26A0 Could not patch package.json: ${message}`);
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2565
2666
|
async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
2566
2667
|
if (rule.type === "delete-file" || rule.type === "delete-directory") {
|
|
2567
|
-
const destPath2 =
|
|
2668
|
+
const destPath2 = path5.join(userProjectRoot, rule.to);
|
|
2568
2669
|
if (rule.type === "delete-file") {
|
|
2569
2670
|
deleteFile(destPath2);
|
|
2570
2671
|
} else {
|
|
@@ -2573,32 +2674,32 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2573
2674
|
return;
|
|
2574
2675
|
}
|
|
2575
2676
|
if (rule.type === "remove-line") {
|
|
2576
|
-
const destPath2 =
|
|
2677
|
+
const destPath2 = path5.join(userProjectRoot, rule.to);
|
|
2577
2678
|
removeLineFromFile(destPath2, rule.pattern);
|
|
2578
2679
|
return;
|
|
2579
2680
|
}
|
|
2580
2681
|
if (rule.type === "add-script") {
|
|
2581
|
-
const packageJsonPath =
|
|
2682
|
+
const packageJsonPath = path5.join(userProjectRoot, "package.json");
|
|
2582
2683
|
addScript(packageJsonPath, rule.name, rule.command, rule.overwrite ?? false);
|
|
2583
2684
|
return;
|
|
2584
2685
|
}
|
|
2585
2686
|
if (rule.type === "add-line") {
|
|
2586
|
-
const destPath2 =
|
|
2687
|
+
const destPath2 = path5.join(userProjectRoot, rule.to);
|
|
2587
2688
|
addLineToFile(destPath2, rule.line);
|
|
2588
2689
|
return;
|
|
2589
2690
|
}
|
|
2590
2691
|
if (rule.type === "merge-json") {
|
|
2591
|
-
const srcPath2 =
|
|
2592
|
-
const destPath2 =
|
|
2692
|
+
const srcPath2 = path5.join(pluginRoot, rule.from);
|
|
2693
|
+
const destPath2 = path5.join(userProjectRoot, rule.to);
|
|
2593
2694
|
mergeJsonFile(srcPath2, destPath2, rule.arrayMerge);
|
|
2594
2695
|
return;
|
|
2595
2696
|
}
|
|
2596
2697
|
if (!("from" in rule)) {
|
|
2597
2698
|
return;
|
|
2598
2699
|
}
|
|
2599
|
-
const srcPath =
|
|
2600
|
-
const destPath =
|
|
2601
|
-
if (!
|
|
2700
|
+
const srcPath = path5.join(pluginRoot, rule.from);
|
|
2701
|
+
const destPath = path5.join(userProjectRoot, rule.to);
|
|
2702
|
+
if (!fs7.existsSync(srcPath)) {
|
|
2602
2703
|
console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
|
|
2603
2704
|
return;
|
|
2604
2705
|
}
|
|
@@ -2615,68 +2716,68 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2615
2716
|
}
|
|
2616
2717
|
}
|
|
2617
2718
|
function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
|
|
2618
|
-
if (onlyIfExists && !
|
|
2619
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
2719
|
+
if (onlyIfExists && !fs7.existsSync(dest)) {
|
|
2720
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, target not exists)`);
|
|
2620
2721
|
return;
|
|
2621
2722
|
}
|
|
2622
|
-
const destDir =
|
|
2623
|
-
if (!
|
|
2624
|
-
|
|
2723
|
+
const destDir = path5.dirname(dest);
|
|
2724
|
+
if (!fs7.existsSync(destDir)) {
|
|
2725
|
+
fs7.mkdirSync(destDir, { recursive: true });
|
|
2625
2726
|
}
|
|
2626
|
-
if (
|
|
2627
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
2727
|
+
if (fs7.existsSync(dest) && !overwrite) {
|
|
2728
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`);
|
|
2628
2729
|
return;
|
|
2629
2730
|
}
|
|
2630
|
-
|
|
2631
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
2731
|
+
fs7.copyFileSync(src, dest);
|
|
2732
|
+
console.log(`[fullstack-cli] \u2713 ${path5.basename(dest)}`);
|
|
2632
2733
|
}
|
|
2633
2734
|
function syncDirectory(src, dest, overwrite = true) {
|
|
2634
|
-
if (!
|
|
2635
|
-
|
|
2735
|
+
if (!fs7.existsSync(dest)) {
|
|
2736
|
+
fs7.mkdirSync(dest, { recursive: true });
|
|
2636
2737
|
}
|
|
2637
|
-
const files =
|
|
2738
|
+
const files = fs7.readdirSync(src);
|
|
2638
2739
|
let count = 0;
|
|
2639
2740
|
files.forEach((file) => {
|
|
2640
|
-
const srcFile =
|
|
2641
|
-
const destFile =
|
|
2642
|
-
const stats =
|
|
2741
|
+
const srcFile = path5.join(src, file);
|
|
2742
|
+
const destFile = path5.join(dest, file);
|
|
2743
|
+
const stats = fs7.statSync(srcFile);
|
|
2643
2744
|
if (stats.isDirectory()) {
|
|
2644
2745
|
syncDirectory(srcFile, destFile, overwrite);
|
|
2645
2746
|
} else {
|
|
2646
|
-
if (overwrite || !
|
|
2647
|
-
|
|
2648
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
2747
|
+
if (overwrite || !fs7.existsSync(destFile)) {
|
|
2748
|
+
fs7.copyFileSync(srcFile, destFile);
|
|
2749
|
+
console.log(`[fullstack-cli] \u2713 ${path5.relative(dest, destFile)}`);
|
|
2649
2750
|
count++;
|
|
2650
2751
|
}
|
|
2651
2752
|
}
|
|
2652
2753
|
});
|
|
2653
2754
|
if (count > 0) {
|
|
2654
|
-
console.log(`[fullstack-cli] Synced ${count} files to ${
|
|
2755
|
+
console.log(`[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`);
|
|
2655
2756
|
}
|
|
2656
2757
|
}
|
|
2657
2758
|
function appendToFile(src, dest) {
|
|
2658
|
-
const content =
|
|
2759
|
+
const content = fs7.readFileSync(src, "utf-8");
|
|
2659
2760
|
let existingContent = "";
|
|
2660
|
-
if (
|
|
2661
|
-
existingContent =
|
|
2761
|
+
if (fs7.existsSync(dest)) {
|
|
2762
|
+
existingContent = fs7.readFileSync(dest, "utf-8");
|
|
2662
2763
|
}
|
|
2663
2764
|
if (existingContent.includes(content.trim())) {
|
|
2664
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
2765
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`);
|
|
2665
2766
|
return;
|
|
2666
2767
|
}
|
|
2667
|
-
|
|
2668
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
2768
|
+
fs7.appendFileSync(dest, content);
|
|
2769
|
+
console.log(`[fullstack-cli] \u2713 ${path5.basename(dest)} (appended)`);
|
|
2669
2770
|
}
|
|
2670
2771
|
function setPermissions(permissions, projectRoot) {
|
|
2671
2772
|
for (const [pattern, mode] of Object.entries(permissions)) {
|
|
2672
2773
|
if (pattern === "**/*.sh") {
|
|
2673
|
-
const scriptsDir =
|
|
2674
|
-
if (
|
|
2675
|
-
const files =
|
|
2774
|
+
const scriptsDir = path5.join(projectRoot, "scripts");
|
|
2775
|
+
if (fs7.existsSync(scriptsDir)) {
|
|
2776
|
+
const files = fs7.readdirSync(scriptsDir);
|
|
2676
2777
|
files.forEach((file) => {
|
|
2677
2778
|
if (file.endsWith(".sh")) {
|
|
2678
|
-
const filePath =
|
|
2679
|
-
|
|
2779
|
+
const filePath = path5.join(scriptsDir, file);
|
|
2780
|
+
fs7.chmodSync(filePath, mode);
|
|
2680
2781
|
}
|
|
2681
2782
|
});
|
|
2682
2783
|
}
|
|
@@ -2684,27 +2785,27 @@ function setPermissions(permissions, projectRoot) {
|
|
|
2684
2785
|
}
|
|
2685
2786
|
}
|
|
2686
2787
|
function deleteFile(filePath) {
|
|
2687
|
-
if (
|
|
2688
|
-
|
|
2689
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
2788
|
+
if (fs7.existsSync(filePath)) {
|
|
2789
|
+
fs7.unlinkSync(filePath);
|
|
2790
|
+
console.log(`[fullstack-cli] \u2713 ${path5.basename(filePath)} (deleted)`);
|
|
2690
2791
|
} else {
|
|
2691
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
2792
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(filePath)} (not found)`);
|
|
2692
2793
|
}
|
|
2693
2794
|
}
|
|
2694
2795
|
function deleteDirectory(dirPath) {
|
|
2695
|
-
if (
|
|
2696
|
-
|
|
2697
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
2796
|
+
if (fs7.existsSync(dirPath)) {
|
|
2797
|
+
fs7.rmSync(dirPath, { recursive: true });
|
|
2798
|
+
console.log(`[fullstack-cli] \u2713 ${path5.basename(dirPath)} (deleted)`);
|
|
2698
2799
|
} else {
|
|
2699
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
2800
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dirPath)} (not found)`);
|
|
2700
2801
|
}
|
|
2701
2802
|
}
|
|
2702
2803
|
function addScript(packageJsonPath, name, command, overwrite) {
|
|
2703
|
-
if (!
|
|
2804
|
+
if (!fs7.existsSync(packageJsonPath)) {
|
|
2704
2805
|
console.log(`[fullstack-cli] \u25CB package.json (not found)`);
|
|
2705
2806
|
return;
|
|
2706
2807
|
}
|
|
2707
|
-
const content =
|
|
2808
|
+
const content = fs7.readFileSync(packageJsonPath, "utf-8");
|
|
2708
2809
|
const pkg2 = JSON.parse(content);
|
|
2709
2810
|
if (!pkg2.scripts) {
|
|
2710
2811
|
pkg2.scripts = {};
|
|
@@ -2716,42 +2817,42 @@ function addScript(packageJsonPath, name, command, overwrite) {
|
|
|
2716
2817
|
}
|
|
2717
2818
|
}
|
|
2718
2819
|
pkg2.scripts[name] = command;
|
|
2719
|
-
|
|
2820
|
+
fs7.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
|
|
2720
2821
|
console.log(`[fullstack-cli] \u2713 scripts.${name}`);
|
|
2721
2822
|
}
|
|
2722
2823
|
function addLineToFile(filePath, line) {
|
|
2723
|
-
const fileName =
|
|
2724
|
-
if (!
|
|
2824
|
+
const fileName = path5.basename(filePath);
|
|
2825
|
+
if (!fs7.existsSync(filePath)) {
|
|
2725
2826
|
console.log(`[fullstack-cli] \u25CB ${fileName} (not found, skipped)`);
|
|
2726
2827
|
return;
|
|
2727
2828
|
}
|
|
2728
|
-
const content =
|
|
2829
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
2729
2830
|
const lines = content.split("\n").map((l) => l.trim());
|
|
2730
2831
|
if (lines.includes(line)) {
|
|
2731
2832
|
console.log(`[fullstack-cli] \u25CB ${fileName} (line already exists: ${line})`);
|
|
2732
2833
|
return;
|
|
2733
2834
|
}
|
|
2734
2835
|
const appendContent = (content.endsWith("\n") ? "" : "\n") + line + "\n";
|
|
2735
|
-
|
|
2836
|
+
fs7.appendFileSync(filePath, appendContent);
|
|
2736
2837
|
console.log(`[fullstack-cli] \u2713 ${fileName} (added: ${line})`);
|
|
2737
2838
|
}
|
|
2738
2839
|
function mergeJsonFile(src, dest, arrayMerge) {
|
|
2739
|
-
const fileName =
|
|
2740
|
-
if (!
|
|
2840
|
+
const fileName = path5.basename(dest);
|
|
2841
|
+
if (!fs7.existsSync(src)) {
|
|
2741
2842
|
console.warn(`[fullstack-cli] Source not found: ${src}`);
|
|
2742
2843
|
return;
|
|
2743
2844
|
}
|
|
2744
|
-
const templateContent = JSON.parse(
|
|
2745
|
-
if (!
|
|
2746
|
-
const destDir =
|
|
2747
|
-
if (!
|
|
2748
|
-
|
|
2845
|
+
const templateContent = JSON.parse(fs7.readFileSync(src, "utf-8"));
|
|
2846
|
+
if (!fs7.existsSync(dest)) {
|
|
2847
|
+
const destDir = path5.dirname(dest);
|
|
2848
|
+
if (!fs7.existsSync(destDir)) {
|
|
2849
|
+
fs7.mkdirSync(destDir, { recursive: true });
|
|
2749
2850
|
}
|
|
2750
|
-
|
|
2851
|
+
fs7.writeFileSync(dest, JSON.stringify(templateContent, null, 2) + "\n");
|
|
2751
2852
|
console.log(`[fullstack-cli] \u2713 ${fileName} (created)`);
|
|
2752
2853
|
return;
|
|
2753
2854
|
}
|
|
2754
|
-
const userContent = JSON.parse(
|
|
2855
|
+
const userContent = JSON.parse(fs7.readFileSync(dest, "utf-8"));
|
|
2755
2856
|
const merged = deepMergeJson(userContent, templateContent, arrayMerge ?? {});
|
|
2756
2857
|
const userStr = JSON.stringify(userContent, null, 2);
|
|
2757
2858
|
const mergedStr = JSON.stringify(merged, null, 2);
|
|
@@ -2759,7 +2860,7 @@ function mergeJsonFile(src, dest, arrayMerge) {
|
|
|
2759
2860
|
console.log(`[fullstack-cli] \u25CB ${fileName} (already up to date)`);
|
|
2760
2861
|
return;
|
|
2761
2862
|
}
|
|
2762
|
-
|
|
2863
|
+
fs7.writeFileSync(dest, mergedStr + "\n");
|
|
2763
2864
|
console.log(`[fullstack-cli] \u2713 ${fileName} (merged)`);
|
|
2764
2865
|
}
|
|
2765
2866
|
|
|
@@ -2820,12 +2921,12 @@ async function reportCreateInstanceEvent(pluginKey, version) {
|
|
|
2820
2921
|
|
|
2821
2922
|
// src/utils/git.ts
|
|
2822
2923
|
import { execSync, spawnSync as spawnSync2 } from "child_process";
|
|
2823
|
-
import
|
|
2824
|
-
import
|
|
2924
|
+
import fs8 from "fs";
|
|
2925
|
+
import path6 from "path";
|
|
2825
2926
|
function isGitRepository(cwd = process.cwd()) {
|
|
2826
2927
|
try {
|
|
2827
|
-
const gitDir =
|
|
2828
|
-
if (
|
|
2928
|
+
const gitDir = path6.join(cwd, ".git");
|
|
2929
|
+
if (fs8.existsSync(gitDir)) {
|
|
2829
2930
|
return true;
|
|
2830
2931
|
}
|
|
2831
2932
|
const result = spawnSync2("git", ["rev-parse", "--git-dir"], {
|
|
@@ -2854,7 +2955,7 @@ function getChangedFiles(cwd = process.cwd()) {
|
|
|
2854
2955
|
function gitAddUpgradeFiles(cwd = process.cwd(), filesToStage) {
|
|
2855
2956
|
const filteredFiles = [];
|
|
2856
2957
|
for (const filePath of filesToStage) {
|
|
2857
|
-
if (
|
|
2958
|
+
if (fs8.existsSync(path6.join(cwd, filePath))) {
|
|
2858
2959
|
filteredFiles.push(filePath);
|
|
2859
2960
|
continue;
|
|
2860
2961
|
}
|
|
@@ -2938,64 +3039,6 @@ Auto-committed by fullstack-cli`;
|
|
|
2938
3039
|
}
|
|
2939
3040
|
}
|
|
2940
3041
|
|
|
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
3042
|
// src/commands/upgrade/shared/utils.ts
|
|
3000
3043
|
import path7 from "path";
|
|
3001
3044
|
import fs9 from "fs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-cli",
|
|
3
|
-
"version": "1.1.16-beta.
|
|
3
|
+
"version": "1.1.16-beta.14",
|
|
4
4
|
"description": "CLI tool for fullstack template management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,6 +56,5 @@
|
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"typescript": "^5.9.2"
|
|
59
|
-
}
|
|
60
|
-
"migrationVersion": 1
|
|
59
|
+
}
|
|
61
60
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineConfig, Config } from 'drizzle-kit';
|
|
2
2
|
require('dotenv').config();
|
|
3
3
|
|
|
4
|
-
const outputDir = process.env.__DRIZZLE_OUT_DIR__ || './
|
|
4
|
+
const outputDir = process.env.__DRIZZLE_OUT_DIR__ || './tmp/.introspect';
|
|
5
5
|
const schemaPath = process.env.__DRIZZLE_SCHEMA_PATH__ || './server/database/schema.ts';
|
|
6
6
|
|
|
7
7
|
const parsedUrl = new URL(process.env.SUDA_DATABASE_URL || '');
|
|
@@ -19,82 +19,149 @@ print_time() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
# ==================== 步骤 0 ====================
|
|
22
|
-
echo "🗑️ [0/
|
|
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/
|
|
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/
|
|
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 "
|
|
43
|
+
echo "🗺️ [3/6] 生成路由定义"
|
|
44
44
|
STEP_START=$(node -e "console.log(Date.now())")
|
|
45
45
|
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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 " ├─
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
CLIENT_EXIT=0
|
|
56
|
+
API_ROUTES_EXIT=0
|
|
57
|
+
PAGE_ROUTES_EXIT=0
|
|
58
58
|
|
|
59
|
-
wait $
|
|
60
|
-
wait $
|
|
59
|
+
wait $API_ROUTES_PID || API_ROUTES_EXIT=$?
|
|
60
|
+
wait $PAGE_ROUTES_PID || PAGE_ROUTES_EXIT=$?
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 [ $
|
|
70
|
-
echo "
|
|
71
|
-
cat /tmp/
|
|
72
|
-
|
|
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 "
|
|
79
|
+
echo "🔨 [4/6] 并行构建 server 和 client"
|
|
82
80
|
STEP_START=$(node -e "console.log(Date.now())")
|
|
83
81
|
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
# 给 server/client 构建子进程预留 8GB heap,缓解 vite build transform 阶段 OOM
|
|
83
|
+
# (典型错误:Reached heap limit Allocation failed)。
|
|
84
|
+
# 仅在外部未设置 NODE_OPTIONS 时注入,允许 CI / 用户通过外部环境变量完全覆盖
|
|
85
|
+
BUILD_NODE_OPTIONS="${NODE_OPTIONS:---max-old-space-size=8192}"
|
|
86
|
+
|
|
87
|
+
# 根据 only_frontend_change 决定是否构建 server
|
|
88
|
+
if [[ "${only_frontend_change:-false}" == "true" ]]; then
|
|
89
|
+
echo "🔨 [4/6] 仅构建 client (only_frontend_change=true)"
|
|
90
|
+
|
|
91
|
+
echo " ├─ 启动 client 构建..."
|
|
92
|
+
NODE_OPTIONS="$BUILD_NODE_OPTIONS" npm run build:client > /tmp/build-client.log 2>&1
|
|
93
|
+
CLIENT_EXIT=$?
|
|
94
|
+
|
|
95
|
+
if [ $CLIENT_EXIT -ne 0 ]; then
|
|
96
|
+
echo " ❌ Client 构建失败"
|
|
97
|
+
cat /tmp/build-client.log
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
echo " ✅ Client 构建完成"
|
|
102
|
+
else
|
|
103
|
+
echo "🔨 [4/6] 并行构建 server 和 client"
|
|
104
|
+
|
|
105
|
+
# 并行构建
|
|
106
|
+
echo " ├─ 启动 server 构建..."
|
|
107
|
+
NODE_OPTIONS="$BUILD_NODE_OPTIONS" npm run build:server > /tmp/build-server.log 2>&1 &
|
|
108
|
+
SERVER_PID=$!
|
|
109
|
+
|
|
110
|
+
echo " ├─ 启动 client 构建..."
|
|
111
|
+
NODE_OPTIONS="$BUILD_NODE_OPTIONS" npm run build:client > /tmp/build-client.log 2>&1 &
|
|
112
|
+
CLIENT_PID=$!
|
|
113
|
+
|
|
114
|
+
# 等待两个构建完成
|
|
115
|
+
SERVER_EXIT=0
|
|
116
|
+
CLIENT_EXIT=0
|
|
117
|
+
|
|
118
|
+
wait $SERVER_PID || SERVER_EXIT=$?
|
|
119
|
+
wait $CLIENT_PID || CLIENT_EXIT=$?
|
|
120
|
+
|
|
121
|
+
# 检查构建结果
|
|
122
|
+
if [ $SERVER_EXIT -ne 0 ]; then
|
|
123
|
+
echo " ❌ Server 构建失败"
|
|
124
|
+
cat /tmp/build-server.log
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
if [ $CLIENT_EXIT -ne 0 ]; then
|
|
129
|
+
echo " ❌ Client 构建失败"
|
|
130
|
+
cat /tmp/build-client.log
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
echo " ✅ Server 构建完成"
|
|
135
|
+
echo " ✅ Client 构建完成"
|
|
90
136
|
fi
|
|
91
137
|
|
|
138
|
+
print_time $STEP_START
|
|
139
|
+
echo ""
|
|
140
|
+
|
|
141
|
+
# ==================== 步骤 5 ====================
|
|
142
|
+
echo "📦 [5/6] 准备产物"
|
|
143
|
+
STEP_START=$(node -e "console.log(Date.now())")
|
|
144
|
+
|
|
92
145
|
# 移动 client 下的 HTML 文件到 dist/dist/client,保证 views 路径在 dev/prod 下一致
|
|
146
|
+
# 使用 mv 而非 cp:HTML 不能上传到公网 CDN,移走后 dist/client 中不再包含 HTML
|
|
93
147
|
if [ -d "$DIST_DIR/client" ]; then
|
|
94
148
|
mkdir -p "$DIST_DIR/dist/client"
|
|
95
149
|
find "$DIST_DIR/client" -maxdepth 1 -name "*.html" -exec mv {} "$DIST_DIR/dist/client/" \;
|
|
96
150
|
fi
|
|
97
151
|
|
|
152
|
+
# server 相关产物准备(only_frontend_change=true 时跳过)
|
|
153
|
+
if [[ "${only_frontend_change:-false}" == "true" ]]; then
|
|
154
|
+
echo " [skip] 跳过 run.sh/.env 复制 (only_frontend_change=true)"
|
|
155
|
+
else
|
|
156
|
+
# 拷贝 run.sh 到 dist/(prod 从 dist/ 启动,确保 cwd 一致性)
|
|
157
|
+
cp "$ROOT_DIR/scripts/run.sh" "$DIST_DIR/"
|
|
158
|
+
|
|
159
|
+
# 拷贝 .env 文件(如果存在)
|
|
160
|
+
if [ -f "$ROOT_DIR/.env" ]; then
|
|
161
|
+
cp "$ROOT_DIR/.env" "$DIST_DIR/"
|
|
162
|
+
fi
|
|
163
|
+
fi
|
|
164
|
+
|
|
98
165
|
# 清理无用文件
|
|
99
166
|
rm -rf "$DIST_DIR/scripts"
|
|
100
167
|
rm -rf "$DIST_DIR/tsconfig.node.tsbuildinfo"
|
|
@@ -102,12 +169,19 @@ rm -rf "$DIST_DIR/tsconfig.node.tsbuildinfo"
|
|
|
102
169
|
print_time $STEP_START
|
|
103
170
|
echo ""
|
|
104
171
|
|
|
105
|
-
# ==================== 步骤
|
|
106
|
-
echo "✂️ [
|
|
172
|
+
# ==================== 步骤 6 ====================
|
|
173
|
+
echo "✂️ [6/6] 智能依赖裁剪"
|
|
107
174
|
STEP_START=$(node -e "console.log(Date.now())")
|
|
108
175
|
|
|
109
|
-
#
|
|
110
|
-
|
|
176
|
+
# 智能依赖裁剪(仅全量构建时执行,纯前端场景不打包 server,无需裁剪)
|
|
177
|
+
if [[ "${only_frontend_change:-false}" == "true" ]]; then
|
|
178
|
+
echo "✂️ [6/6] 跳过智能依赖裁剪 (only_frontend_change=true)"
|
|
179
|
+
else
|
|
180
|
+
echo "✂️ [6/6] 智能依赖裁剪"
|
|
181
|
+
|
|
182
|
+
# 分析实际依赖、复制并裁剪 node_modules、生成精简的 package.json
|
|
183
|
+
node "$ROOT_DIR/scripts/prune-smart.js"
|
|
184
|
+
fi
|
|
111
185
|
|
|
112
186
|
print_time $STEP_START
|
|
113
187
|
echo ""
|
|
@@ -118,9 +192,16 @@ print_time $TOTAL_START
|
|
|
118
192
|
|
|
119
193
|
# 输出产物信息
|
|
120
194
|
DIST_SIZE=$(du -sh "$DIST_DIR" | cut -f1)
|
|
121
|
-
|
|
122
|
-
echo ""
|
|
123
|
-
echo "📊 构建产物统计:"
|
|
124
|
-
echo " 产物大小: $DIST_SIZE"
|
|
125
|
-
echo "
|
|
126
|
-
|
|
195
|
+
if [[ "${only_frontend_change:-false}" == "true" ]]; then
|
|
196
|
+
echo ""
|
|
197
|
+
echo "📊 构建产物统计:"
|
|
198
|
+
echo " 产物大小: $DIST_SIZE"
|
|
199
|
+
echo ""
|
|
200
|
+
else
|
|
201
|
+
NODE_MODULES_SIZE=$(du -sh "$DIST_DIR/node_modules" | cut -f1)
|
|
202
|
+
echo ""
|
|
203
|
+
echo "📊 构建产物统计:"
|
|
204
|
+
echo " 产物大小: $DIST_SIZE"
|
|
205
|
+
echo " node_modules: $NODE_MODULES_SIZE"
|
|
206
|
+
echo ""
|
|
207
|
+
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
|
+
});
|