create-einja-app 0.2.18 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +880 -1669
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/.claude/settings.json +1 -0
- package/templates/default/.mcp.json +27 -6
- package/templates/default/.serena/project.yml +4 -0
- package/templates/default/.vscode/settings.json +18 -0
- package/templates/default/CLAUDE.md +126 -387
- package/templates/default/packages/admin-ui/src/ui/sidebar.tsx +4 -4
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { readFileSync as readFileSync5 } from "fs";
|
|
6
|
-
import { fileURLToPath as
|
|
7
|
-
import { dirname as
|
|
6
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7
|
+
import { dirname as dirname6, join as join8 } from "path";
|
|
8
8
|
|
|
9
9
|
// src/commands/create.ts
|
|
10
10
|
import { existsSync as existsSync3, readdirSync } from "fs";
|
|
@@ -167,63 +167,11 @@ import { fileURLToPath } from "url";
|
|
|
167
167
|
// src/utils/fs.ts
|
|
168
168
|
import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from "fs";
|
|
169
169
|
import { join, dirname } from "path";
|
|
170
|
-
function writeWithStrategy(filePath, content, strategy) {
|
|
171
|
-
const exists = existsSync(filePath);
|
|
172
|
-
if (!exists) {
|
|
173
|
-
ensureDir(dirname(filePath));
|
|
174
|
-
writeFileSync(filePath, content, "utf-8");
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
switch (strategy) {
|
|
178
|
-
case "overwrite": {
|
|
179
|
-
writeFileSync(filePath, content, "utf-8");
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
case "merge": {
|
|
183
|
-
const existingContent = readFileSync(filePath, "utf-8");
|
|
184
|
-
const mergedContent = mergeContent(existingContent, content);
|
|
185
|
-
writeFileSync(filePath, mergedContent, "utf-8");
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
case "skip": {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
default: {
|
|
192
|
-
const _exhaustiveCheck = strategy;
|
|
193
|
-
throw new Error(`Unknown strategy: ${_exhaustiveCheck}`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function mergeContent(existing, newContent) {
|
|
198
|
-
if (existing.includes(newContent)) {
|
|
199
|
-
return existing;
|
|
200
|
-
}
|
|
201
|
-
return `${existing}
|
|
202
|
-
${newContent}`;
|
|
203
|
-
}
|
|
204
170
|
function ensureDir(dirPath) {
|
|
205
171
|
if (!existsSync(dirPath)) {
|
|
206
172
|
mkdirSync(dirPath, { recursive: true });
|
|
207
173
|
}
|
|
208
174
|
}
|
|
209
|
-
function appendToGitignore(targetDir, line) {
|
|
210
|
-
const gitignorePath = join(targetDir, ".gitignore");
|
|
211
|
-
if (!existsSync(gitignorePath)) {
|
|
212
|
-
writeFileSync(gitignorePath, `${line}
|
|
213
|
-
`, "utf-8");
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const content = readFileSync(gitignorePath, "utf-8");
|
|
217
|
-
if (content.includes(line)) {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
appendFileSync(gitignorePath, `
|
|
221
|
-
${line}
|
|
222
|
-
`, "utf-8");
|
|
223
|
-
}
|
|
224
|
-
function fileExists(filePath) {
|
|
225
|
-
return existsSync(filePath);
|
|
226
|
-
}
|
|
227
175
|
|
|
228
176
|
// src/utils/logger.ts
|
|
229
177
|
import chalk from "chalk";
|
|
@@ -588,625 +536,603 @@ async function createCommand(projectName, options) {
|
|
|
588
536
|
}
|
|
589
537
|
}
|
|
590
538
|
|
|
591
|
-
// src/commands/
|
|
592
|
-
import {
|
|
593
|
-
import {
|
|
594
|
-
import
|
|
539
|
+
// src/commands/sync.ts
|
|
540
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
541
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
542
|
+
import fsExtra3 from "fs-extra";
|
|
543
|
+
import inquirer5 from "inquirer";
|
|
595
544
|
|
|
596
|
-
// src/
|
|
597
|
-
import
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
545
|
+
// src/generators/sync.ts
|
|
546
|
+
import { glob as glob2 } from "glob";
|
|
547
|
+
var CATEGORY_PATTERNS = {
|
|
548
|
+
env: [".env*", ".envrc", ".volta", ".node-version"],
|
|
549
|
+
tools: ["biome.json", ".prettierrc*", ".editorconfig", ".vscode/**"],
|
|
550
|
+
git: [".gitignore", ".gitattributes"],
|
|
551
|
+
"git-hooks": [".husky/**"],
|
|
552
|
+
github: [".github/workflows/**", ".github/actions/**", ".github/dependabot.yml"],
|
|
553
|
+
docker: ["Dockerfile*", "docker-compose*.yml", ".dockerignore"],
|
|
554
|
+
monorepo: ["turbo.json", "pnpm-workspace.yaml"],
|
|
555
|
+
"root-config": ["package.json", "tsconfig.json"],
|
|
556
|
+
scripts: ["scripts/**"],
|
|
557
|
+
apps: ["apps/**"],
|
|
558
|
+
packages: ["packages/**"],
|
|
559
|
+
docs: ["README.md", "docs/**"]
|
|
560
|
+
};
|
|
561
|
+
var ENV_FILE_PROTECTION = {
|
|
562
|
+
// 同期から保護するファイル(秘密鍵・暗号化済み値を含む)
|
|
563
|
+
protected: [
|
|
564
|
+
".env.keys",
|
|
565
|
+
".env.personal",
|
|
566
|
+
".env.develop",
|
|
567
|
+
".env.local",
|
|
568
|
+
".env.production",
|
|
569
|
+
".env.staging",
|
|
570
|
+
".env.preview"
|
|
571
|
+
],
|
|
572
|
+
// 同期を許可するファイル(テンプレート・例示ファイル)
|
|
573
|
+
allowed: [
|
|
574
|
+
".env.example",
|
|
575
|
+
".env.personal.example",
|
|
576
|
+
".envrc"
|
|
577
|
+
]
|
|
578
|
+
};
|
|
579
|
+
var SYNC_EXCLUDE_PATTERNS = [
|
|
580
|
+
"**/prisma/schema.prisma",
|
|
581
|
+
// DBモデルはユーザー固有
|
|
582
|
+
"**/prisma/migrations/**",
|
|
583
|
+
// マイグレーション履歴
|
|
584
|
+
"pnpm-lock.yaml",
|
|
585
|
+
// lockfile は sync すべきでない
|
|
586
|
+
"**/node_modules/**",
|
|
587
|
+
// 依存関係
|
|
588
|
+
"**/.git/**"
|
|
589
|
+
// Git内部
|
|
590
|
+
];
|
|
591
|
+
function isProtectedEnvFile(filePath) {
|
|
592
|
+
const fileName = filePath.split("/").pop() ?? "";
|
|
593
|
+
if (ENV_FILE_PROTECTION.allowed.some((pattern) => fileName === pattern)) {
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
596
|
+
if (ENV_FILE_PROTECTION.protected.some((pattern) => fileName === pattern)) {
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
if (fileName.startsWith(".env")) {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
function isExcludedFile(filePath) {
|
|
605
|
+
for (const pattern of SYNC_EXCLUDE_PATTERNS) {
|
|
606
|
+
const regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\./g, "\\.");
|
|
607
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
608
|
+
if (regex.test(filePath)) {
|
|
609
|
+
return true;
|
|
651
610
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const tools = {
|
|
655
|
-
direnv: toolsArray.includes("direnv"),
|
|
656
|
-
dotenvx: toolsArray.includes("dotenvx"),
|
|
657
|
-
volta: toolsArray.includes("volta"),
|
|
658
|
-
biome: toolsArray.includes("biome"),
|
|
659
|
-
husky: toolsArray.includes("husky")
|
|
660
|
-
};
|
|
661
|
-
return {
|
|
662
|
-
tools,
|
|
663
|
-
conflictStrategy: answers.conflictStrategy
|
|
664
|
-
};
|
|
611
|
+
}
|
|
612
|
+
return false;
|
|
665
613
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
# Load local overrides
|
|
685
|
-
dotenv_if_exists .env.local
|
|
686
|
-
`;
|
|
687
|
-
function setupDirenv(options) {
|
|
688
|
-
const { targetDir, conflictStrategy } = options;
|
|
689
|
-
const envrcPath = join3(targetDir, ".envrc");
|
|
690
|
-
const envrcExamplePath = join3(targetDir, ".envrc.example");
|
|
691
|
-
writeWithStrategy(envrcPath, ENVRC_CONTENT, conflictStrategy);
|
|
692
|
-
writeWithStrategy(envrcExamplePath, ENVRC_EXAMPLE_CONTENT, conflictStrategy);
|
|
693
|
-
appendToGitignore(targetDir, ".envrc");
|
|
614
|
+
function extractPatternsFromCategories(categories, appsDetail, packagesDetail) {
|
|
615
|
+
const patterns = [];
|
|
616
|
+
for (const category of categories) {
|
|
617
|
+
const categoryPatterns = CATEGORY_PATTERNS[category];
|
|
618
|
+
if (!categoryPatterns) {
|
|
619
|
+
warn(`\u4E0D\u660E\u306A\u30AB\u30C6\u30B4\u30EA: ${category}`);
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (category === "apps" && appsDetail && appsDetail.length > 0) {
|
|
623
|
+
patterns.push(...appsDetail.map((app) => `apps/${app}/**`));
|
|
624
|
+
} else if (category === "packages" && packagesDetail && packagesDetail.length > 0) {
|
|
625
|
+
patterns.push(...packagesDetail.map((pkg) => `packages/${pkg}/**`));
|
|
626
|
+
} else {
|
|
627
|
+
patterns.push(...categoryPatterns);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return patterns;
|
|
694
631
|
}
|
|
695
|
-
async function
|
|
632
|
+
async function collectSyncFiles(templateDir, categories, appsDetail, packagesDetail) {
|
|
696
633
|
try {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
634
|
+
info("\u540C\u671F\u5BFE\u8C61\u30D5\u30A1\u30A4\u30EB\u3092\u53CE\u96C6\u4E2D...");
|
|
635
|
+
const patterns = extractPatternsFromCategories(
|
|
636
|
+
categories,
|
|
637
|
+
appsDetail,
|
|
638
|
+
packagesDetail
|
|
639
|
+
);
|
|
640
|
+
if (patterns.length === 0) {
|
|
641
|
+
warn("\u540C\u671F\u5BFE\u8C61\u306E\u30D1\u30BF\u30FC\u30F3\u304C\u3042\u308A\u307E\u305B\u3093");
|
|
642
|
+
return [];
|
|
643
|
+
}
|
|
644
|
+
const fileSet = /* @__PURE__ */ new Set();
|
|
645
|
+
for (const pattern of patterns) {
|
|
706
646
|
try {
|
|
707
|
-
|
|
708
|
-
|
|
647
|
+
const files = await glob2(pattern, {
|
|
648
|
+
cwd: templateDir,
|
|
649
|
+
dot: true,
|
|
650
|
+
// .で始まるファイルも含める
|
|
651
|
+
nodir: true
|
|
652
|
+
// ディレクトリは除外
|
|
653
|
+
});
|
|
654
|
+
for (const file of files) {
|
|
655
|
+
fileSet.add(file);
|
|
656
|
+
}
|
|
709
657
|
} catch (error2) {
|
|
710
|
-
warn(
|
|
711
|
-
info("\u5F8C\u3067\u624B\u52D5\u3067 'direnv allow' \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
658
|
+
warn(`\u30D1\u30BF\u30FC\u30F3 ${pattern} \u306E\u51E6\u7406\u4E2D\u306B\u30A8\u30E9\u30FC: ${error2}`);
|
|
712
659
|
}
|
|
713
|
-
} else {
|
|
714
|
-
info("direnv allow \u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F");
|
|
715
|
-
info("\u5F8C\u3067\u624B\u52D5\u3067 'direnv allow' \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
716
660
|
}
|
|
661
|
+
const allFiles = Array.from(fileSet);
|
|
662
|
+
const filteredFiles = allFiles.filter((file) => {
|
|
663
|
+
if (isProtectedEnvFile(file)) {
|
|
664
|
+
info(`\u4FDD\u8B77\u5BFE\u8C61\u30D5\u30A1\u30A4\u30EB\u3092\u9664\u5916: ${file}`);
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
if (isExcludedFile(file)) {
|
|
668
|
+
info(`\u9664\u5916\u30D1\u30BF\u30FC\u30F3\u306B\u30DE\u30C3\u30C1: ${file}`);
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
return true;
|
|
672
|
+
});
|
|
673
|
+
success(`${filteredFiles.length}\u500B\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u53CE\u96C6\u3057\u307E\u3057\u305F`);
|
|
674
|
+
return filteredFiles.sort();
|
|
717
675
|
} catch (error2) {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// src/generators/tools/dotenvx.ts
|
|
723
|
-
import { join as join5 } from "path";
|
|
724
|
-
|
|
725
|
-
// src/utils/package-json.ts
|
|
726
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
727
|
-
import { join as join4 } from "path";
|
|
728
|
-
function readPackageJson(targetDir) {
|
|
729
|
-
const packageJsonPath2 = join4(targetDir, "package.json");
|
|
730
|
-
if (!fileExists(packageJsonPath2)) {
|
|
731
|
-
return {};
|
|
732
|
-
}
|
|
733
|
-
const content = readFileSync3(packageJsonPath2, "utf-8");
|
|
734
|
-
return JSON.parse(content);
|
|
735
|
-
}
|
|
736
|
-
function writePackageJson(targetDir, data) {
|
|
737
|
-
const packageJsonPath2 = join4(targetDir, "package.json");
|
|
738
|
-
const content = JSON.stringify(data, null, 2);
|
|
739
|
-
writeFileSync3(packageJsonPath2, `${content}
|
|
740
|
-
`, "utf-8");
|
|
741
|
-
}
|
|
742
|
-
function addScripts(targetDir, scripts) {
|
|
743
|
-
const pkg = readPackageJson(targetDir);
|
|
744
|
-
pkg.scripts = { ...pkg.scripts, ...scripts };
|
|
745
|
-
writePackageJson(targetDir, pkg);
|
|
746
|
-
}
|
|
747
|
-
function addDependencies(targetDir, dependencies, dev = false) {
|
|
748
|
-
const pkg = readPackageJson(targetDir);
|
|
749
|
-
if (dev) {
|
|
750
|
-
pkg.devDependencies = { ...pkg.devDependencies, ...dependencies };
|
|
751
|
-
} else {
|
|
752
|
-
pkg.dependencies = { ...pkg.dependencies, ...dependencies };
|
|
676
|
+
error(`\u30D5\u30A1\u30A4\u30EB\u53CE\u96C6\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F: ${error2}`);
|
|
677
|
+
throw error2;
|
|
753
678
|
}
|
|
754
|
-
writePackageJson(targetDir, pkg);
|
|
755
|
-
}
|
|
756
|
-
function addVoltaField(targetDir, nodeVersion, pnpmVersion) {
|
|
757
|
-
const pkg = readPackageJson(targetDir);
|
|
758
|
-
pkg.volta = {
|
|
759
|
-
node: nodeVersion,
|
|
760
|
-
pnpm: pnpmVersion
|
|
761
|
-
};
|
|
762
|
-
writePackageJson(targetDir, pkg);
|
|
763
|
-
}
|
|
764
|
-
function addLintStaged(targetDir, config) {
|
|
765
|
-
const pkg = readPackageJson(targetDir);
|
|
766
|
-
pkg["lint-staged"] = { ...pkg["lint-staged"], ...config };
|
|
767
|
-
writePackageJson(targetDir, pkg);
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
// src/generators/tools/dotenvx.ts
|
|
771
|
-
var ENV_EXAMPLE_CONTENT = `# Environment variables template
|
|
772
|
-
# Copy this file to .env and fill in the values
|
|
773
|
-
|
|
774
|
-
# Database
|
|
775
|
-
DATABASE_URL="postgresql://user:password@localhost:25432/dbname"
|
|
776
|
-
|
|
777
|
-
# NextAuth
|
|
778
|
-
NEXTAUTH_URL="http://localhost:3000"
|
|
779
|
-
NEXTAUTH_SECRET="your-secret-here"
|
|
780
|
-
|
|
781
|
-
# OAuth (if using)
|
|
782
|
-
# GOOGLE_CLIENT_ID=""
|
|
783
|
-
# GOOGLE_CLIENT_SECRET=""
|
|
784
|
-
# GITHUB_CLIENT_ID=""
|
|
785
|
-
# GITHUB_CLIENT_SECRET=""
|
|
786
|
-
`;
|
|
787
|
-
function setupDotenvx(options) {
|
|
788
|
-
const { targetDir, conflictStrategy } = options;
|
|
789
|
-
addDependencies(targetDir, {
|
|
790
|
-
"@dotenvx/dotenvx": "^1.29.0"
|
|
791
|
-
});
|
|
792
|
-
addScripts(targetDir, {
|
|
793
|
-
"env:encrypt": "dotenvx encrypt",
|
|
794
|
-
"env:decrypt": "dotenvx decrypt"
|
|
795
|
-
});
|
|
796
|
-
const envExamplePath = join5(targetDir, ".env.example");
|
|
797
|
-
writeWithStrategy(envExamplePath, ENV_EXAMPLE_CONTENT, conflictStrategy);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
// src/generators/tools/volta.ts
|
|
801
|
-
import { join as join6 } from "path";
|
|
802
|
-
var NODE_VERSION = "22.16.0";
|
|
803
|
-
var PNPM_VERSION = "9.15.0";
|
|
804
|
-
var NODE_VERSION_CONTENT = `${NODE_VERSION}
|
|
805
|
-
`;
|
|
806
|
-
function setupVolta(options) {
|
|
807
|
-
const { targetDir, conflictStrategy } = options;
|
|
808
|
-
addVoltaField(targetDir, NODE_VERSION, PNPM_VERSION);
|
|
809
|
-
const nodeVersionPath = join6(targetDir, ".node-version");
|
|
810
|
-
writeWithStrategy(nodeVersionPath, NODE_VERSION_CONTENT, conflictStrategy);
|
|
811
679
|
}
|
|
812
680
|
|
|
813
|
-
// src/
|
|
814
|
-
import
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
"
|
|
681
|
+
// src/prompts/sync.ts
|
|
682
|
+
import inquirer3 from "inquirer";
|
|
683
|
+
import * as fs from "fs";
|
|
684
|
+
import * as path from "path";
|
|
685
|
+
var CATEGORY_CONFIGS = {
|
|
686
|
+
env: {
|
|
687
|
+
name: "\u74B0\u5883\u8A2D\u5B9A",
|
|
688
|
+
description: ".env*, .envrc, .volta, .node-version",
|
|
689
|
+
patterns: [".env*", ".envrc", ".volta", ".node-version"],
|
|
690
|
+
defaultChecked: true,
|
|
691
|
+
firstRunDefault: true
|
|
821
692
|
},
|
|
822
|
-
|
|
823
|
-
"
|
|
824
|
-
|
|
693
|
+
tools: {
|
|
694
|
+
name: "\u958B\u767A\u30C4\u30FC\u30EB",
|
|
695
|
+
description: "biome.json, .prettierrc, .editorconfig, .vscode/",
|
|
696
|
+
patterns: ["biome.json", ".prettierrc*", ".editorconfig", ".vscode/"],
|
|
697
|
+
defaultChecked: true,
|
|
698
|
+
firstRunDefault: true
|
|
825
699
|
},
|
|
826
|
-
|
|
827
|
-
"
|
|
828
|
-
|
|
829
|
-
"
|
|
830
|
-
|
|
831
|
-
|
|
700
|
+
git: {
|
|
701
|
+
name: "Git\u8A2D\u5B9A",
|
|
702
|
+
description: ".gitignore, .gitattributes",
|
|
703
|
+
patterns: [".gitignore", ".gitattributes"],
|
|
704
|
+
defaultChecked: false,
|
|
705
|
+
firstRunDefault: true
|
|
832
706
|
},
|
|
833
|
-
"
|
|
834
|
-
"
|
|
707
|
+
"git-hooks": {
|
|
708
|
+
name: "Git Hooks",
|
|
709
|
+
description: ".husky/",
|
|
710
|
+
patterns: [".husky/"],
|
|
711
|
+
defaultChecked: false,
|
|
712
|
+
firstRunDefault: true
|
|
835
713
|
},
|
|
836
|
-
|
|
837
|
-
"
|
|
838
|
-
"
|
|
839
|
-
|
|
840
|
-
|
|
714
|
+
github: {
|
|
715
|
+
name: "CI/CD",
|
|
716
|
+
description: ".github/workflows/, .github/actions/",
|
|
717
|
+
patterns: [".github/workflows/", ".github/actions/", ".github/dependabot.yml"],
|
|
718
|
+
defaultChecked: false
|
|
841
719
|
},
|
|
842
|
-
|
|
843
|
-
"
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
"arrowParentheses": "always"
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
`;
|
|
852
|
-
var VSCODE_SETTINGS = `{
|
|
853
|
-
"editor.defaultFormatter": "biomejs.biome",
|
|
854
|
-
"editor.formatOnSave": true,
|
|
855
|
-
"editor.codeActionsOnSave": {
|
|
856
|
-
"quickfix.biome": "explicit",
|
|
857
|
-
"source.organizeImports.biome": "explicit"
|
|
720
|
+
docker: {
|
|
721
|
+
name: "\u30B3\u30F3\u30C6\u30CA",
|
|
722
|
+
description: "Dockerfile*, docker-compose.yml, .dockerignore",
|
|
723
|
+
patterns: ["Dockerfile*", "docker-compose*.yml", ".dockerignore"],
|
|
724
|
+
defaultChecked: false
|
|
858
725
|
},
|
|
859
|
-
|
|
860
|
-
|
|
726
|
+
monorepo: {
|
|
727
|
+
name: "\u30E2\u30CE\u30EC\u30DD\u69CB\u6210",
|
|
728
|
+
description: "turbo.json, pnpm-workspace.yaml",
|
|
729
|
+
patterns: ["turbo.json", "pnpm-workspace.yaml"],
|
|
730
|
+
defaultChecked: false,
|
|
731
|
+
firstRunDefault: true
|
|
861
732
|
},
|
|
862
|
-
"
|
|
863
|
-
|
|
733
|
+
"root-config": {
|
|
734
|
+
name: "\u30EB\u30FC\u30C8\u8A2D\u5B9A",
|
|
735
|
+
description: "package.json, tsconfig.json",
|
|
736
|
+
patterns: ["package.json", "tsconfig.json"],
|
|
737
|
+
defaultChecked: false,
|
|
738
|
+
firstRunDefault: true
|
|
864
739
|
},
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
// src/generators/tools/husky.ts
|
|
894
|
-
import { join as join8 } from "path";
|
|
895
|
-
import { writeFileSync as writeFileSync4 } from "fs";
|
|
896
|
-
var PRE_COMMIT_HOOK = `#!/usr/bin/env sh
|
|
897
|
-
. "$(dirname -- "$0")/_/husky.sh"
|
|
898
|
-
|
|
899
|
-
pnpm lint-staged
|
|
900
|
-
`;
|
|
901
|
-
function setupHusky(options) {
|
|
902
|
-
const { targetDir } = options;
|
|
903
|
-
addDependencies(
|
|
904
|
-
targetDir,
|
|
905
|
-
{
|
|
906
|
-
husky: "^9.1.7",
|
|
907
|
-
"lint-staged": "^15.2.11"
|
|
908
|
-
},
|
|
909
|
-
true
|
|
910
|
-
);
|
|
911
|
-
addScripts(targetDir, {
|
|
912
|
-
prepare: "husky"
|
|
913
|
-
});
|
|
914
|
-
addLintStaged(targetDir, {
|
|
915
|
-
"*.{js,jsx,ts,tsx}": ["biome format --write", "biome lint --write"],
|
|
916
|
-
"*.{json,md,yml,yaml}": ["biome format --write"]
|
|
917
|
-
});
|
|
918
|
-
const huskyDir = join8(targetDir, ".husky");
|
|
919
|
-
ensureDir(huskyDir);
|
|
920
|
-
const preCommitPath = join8(huskyDir, "pre-commit");
|
|
921
|
-
writeFileSync4(preCommitPath, PRE_COMMIT_HOOK, { mode: 493 });
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
// src/commands/setup.ts
|
|
925
|
-
async function setupCommand() {
|
|
926
|
-
const targetDir = process.cwd();
|
|
927
|
-
const packageJsonPath2 = join9(targetDir, "package.json");
|
|
928
|
-
if (!existsSync4(packageJsonPath2)) {
|
|
929
|
-
error("\u30A8\u30E9\u30FC: package.json\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093");
|
|
930
|
-
info("\u3053\u306E\u30B3\u30DE\u30F3\u30C9\u306F\u65E2\u5B58\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
931
|
-
process.exit(1);
|
|
932
|
-
}
|
|
933
|
-
info("\u65E2\u5B58\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3078\u306E\u30C4\u30FC\u30EB\u8FFD\u52A0\u3092\u958B\u59CB\u3057\u307E\u3059");
|
|
934
|
-
info("");
|
|
935
|
-
const config = await promptSetupConfig();
|
|
936
|
-
info("");
|
|
937
|
-
info("\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u958B\u59CB\u3057\u307E\u3059...");
|
|
938
|
-
info("");
|
|
939
|
-
const options = {
|
|
940
|
-
targetDir,
|
|
941
|
-
conflictStrategy: config.conflictStrategy
|
|
942
|
-
};
|
|
943
|
-
let setupCount = 0;
|
|
944
|
-
if (config.tools.direnv) {
|
|
945
|
-
const spin = ora3("direnv \u3092\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u3066\u3044\u307E\u3059...").start();
|
|
946
|
-
try {
|
|
947
|
-
setupDirenv(options);
|
|
948
|
-
spin.succeed("direnv \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86");
|
|
949
|
-
setupCount++;
|
|
950
|
-
} catch (error2) {
|
|
951
|
-
spin.fail("direnv \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5931\u6557");
|
|
952
|
-
error(
|
|
953
|
-
error2 instanceof Error ? error2.message : "\u4E88\u671F\u3057\u306A\u3044\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
|
|
954
|
-
);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
if (config.tools.dotenvx) {
|
|
958
|
-
const spin = ora3("dotenvx \u3092\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u3066\u3044\u307E\u3059...").start();
|
|
959
|
-
try {
|
|
960
|
-
setupDotenvx(options);
|
|
961
|
-
spin.succeed("dotenvx \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86");
|
|
962
|
-
setupCount++;
|
|
963
|
-
} catch (error2) {
|
|
964
|
-
spin.fail("dotenvx \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5931\u6557");
|
|
965
|
-
error(
|
|
966
|
-
error2 instanceof Error ? error2.message : "\u4E88\u671F\u3057\u306A\u3044\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
|
|
967
|
-
);
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
if (config.tools.volta) {
|
|
971
|
-
const spin = ora3("Volta \u3092\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u3066\u3044\u307E\u3059...").start();
|
|
972
|
-
try {
|
|
973
|
-
setupVolta(options);
|
|
974
|
-
spin.succeed("Volta \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86");
|
|
975
|
-
setupCount++;
|
|
976
|
-
} catch (error2) {
|
|
977
|
-
spin.fail("Volta \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5931\u6557");
|
|
978
|
-
error(
|
|
979
|
-
error2 instanceof Error ? error2.message : "\u4E88\u671F\u3057\u306A\u3044\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
|
|
980
|
-
);
|
|
981
|
-
}
|
|
740
|
+
scripts: {
|
|
741
|
+
name: "\u30B9\u30AF\u30EA\u30D7\u30C8",
|
|
742
|
+
description: "scripts/ \u914D\u4E0B",
|
|
743
|
+
patterns: ["scripts/**"],
|
|
744
|
+
defaultChecked: false
|
|
745
|
+
},
|
|
746
|
+
apps: {
|
|
747
|
+
name: "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3",
|
|
748
|
+
description: "apps/ \u914D\u4E0B\uFF08\u6B21\u306E\u753B\u9762\u3067\u500B\u5225\u9078\u629E\uFF09",
|
|
749
|
+
patterns: ["apps/**"],
|
|
750
|
+
defaultChecked: false,
|
|
751
|
+
firstRunDefault: true,
|
|
752
|
+
requiresDetailSelection: true
|
|
753
|
+
},
|
|
754
|
+
packages: {
|
|
755
|
+
name: "\u5171\u901A\u30D1\u30C3\u30B1\u30FC\u30B8",
|
|
756
|
+
description: "packages/ \u914D\u4E0B\uFF08\u6B21\u306E\u753B\u9762\u3067\u500B\u5225\u9078\u629E\uFF09",
|
|
757
|
+
patterns: ["packages/**"],
|
|
758
|
+
defaultChecked: false,
|
|
759
|
+
firstRunDefault: true,
|
|
760
|
+
requiresDetailSelection: true
|
|
761
|
+
},
|
|
762
|
+
docs: {
|
|
763
|
+
name: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
|
|
764
|
+
description: "README.md, docs/",
|
|
765
|
+
patterns: ["README.md", "docs/**"],
|
|
766
|
+
defaultChecked: false
|
|
982
767
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
} catch (error2) {
|
|
990
|
-
spin.fail("Biome \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5931\u6557");
|
|
991
|
-
error(
|
|
992
|
-
error2 instanceof Error ? error2.message : "\u4E88\u671F\u3057\u306A\u3044\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
|
|
993
|
-
);
|
|
768
|
+
};
|
|
769
|
+
function getAvailableApps(templateDir) {
|
|
770
|
+
const appsDir = path.join(templateDir, "apps");
|
|
771
|
+
try {
|
|
772
|
+
if (!fs.existsSync(appsDir)) {
|
|
773
|
+
return [];
|
|
994
774
|
}
|
|
775
|
+
return fs.readdirSync(appsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
776
|
+
} catch (error2) {
|
|
777
|
+
console.error(`Error reading apps directory: ${error2}`);
|
|
778
|
+
return [];
|
|
995
779
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
} catch (error2) {
|
|
1003
|
-
spin.fail("Husky \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5931\u6557");
|
|
1004
|
-
error(
|
|
1005
|
-
error2 instanceof Error ? error2.message : "\u4E88\u671F\u3057\u306A\u3044\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
|
|
1006
|
-
);
|
|
780
|
+
}
|
|
781
|
+
function getAvailablePackages(templateDir) {
|
|
782
|
+
const packagesDir = path.join(templateDir, "packages");
|
|
783
|
+
try {
|
|
784
|
+
if (!fs.existsSync(packagesDir)) {
|
|
785
|
+
return [];
|
|
1007
786
|
}
|
|
787
|
+
return fs.readdirSync(packagesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
788
|
+
} catch (error2) {
|
|
789
|
+
console.error(`Error reading packages directory: ${error2}`);
|
|
790
|
+
return [];
|
|
1008
791
|
}
|
|
1009
|
-
info("");
|
|
1010
|
-
if (config.tools.direnv) {
|
|
1011
|
-
await promptDirenvAllow(targetDir);
|
|
1012
|
-
info("");
|
|
1013
|
-
}
|
|
1014
|
-
success(`\u2705 \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\uFF01\uFF08${setupCount}\u500B\u306E\u30C4\u30FC\u30EB\uFF09`);
|
|
1015
|
-
info("");
|
|
1016
|
-
info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
|
|
1017
|
-
if (config.tools.direnv) {
|
|
1018
|
-
info(" 1. .envrc \u3092\u7DE8\u96C6\u3057\u3066\u74B0\u5883\u5909\u6570\u3092\u8A2D\u5B9A");
|
|
1019
|
-
info(" 2. direnv allow \u3092\u5B9F\u884C\uFF08\u307E\u3060\u306E\u5834\u5408\uFF09");
|
|
1020
|
-
}
|
|
1021
|
-
if (config.tools.dotenvx) {
|
|
1022
|
-
info(" - .env.example \u3092\u30B3\u30D4\u30FC\u3057\u3066 .env \u3092\u4F5C\u6210");
|
|
1023
|
-
info(" - \u5FC5\u8981\u306B\u5FDC\u3058\u3066 pnpm env:encrypt \u3067\u6697\u53F7\u5316");
|
|
1024
|
-
}
|
|
1025
|
-
if (config.tools.biome) {
|
|
1026
|
-
info(" - pnpm lint \u3067\u30B3\u30FC\u30C9\u3092\u30C1\u30A7\u30C3\u30AF");
|
|
1027
|
-
info(" - pnpm format:fix \u3067\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8");
|
|
1028
|
-
}
|
|
1029
|
-
if (config.tools.husky) {
|
|
1030
|
-
info(" - pnpm install \u3067Husky\u30D5\u30C3\u30AF\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB");
|
|
1031
|
-
}
|
|
1032
|
-
info("");
|
|
1033
|
-
success("\u958B\u767A\u3092\u958B\u59CB\u3067\u304D\u307E\u3059\uFF01");
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
// src/commands/add.ts
|
|
1037
|
-
import { existsSync as existsSync6 } from "fs";
|
|
1038
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
1039
|
-
import { dirname as dirname4, join as join13 } from "path";
|
|
1040
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1041
|
-
import ora4 from "ora";
|
|
1042
|
-
|
|
1043
|
-
// src/prompts/add.ts
|
|
1044
|
-
import inquirer5 from "inquirer";
|
|
1045
|
-
function getDefaultAddConfig() {
|
|
1046
|
-
return {
|
|
1047
|
-
components: {
|
|
1048
|
-
packages: true,
|
|
1049
|
-
apps: true,
|
|
1050
|
-
config: true
|
|
1051
|
-
},
|
|
1052
|
-
packageComponents: ["front-core", "server-core", "config", "ui"],
|
|
1053
|
-
appComponents: ["web"],
|
|
1054
|
-
dryRun: false
|
|
1055
|
-
};
|
|
1056
792
|
}
|
|
1057
|
-
async function
|
|
1058
|
-
const
|
|
793
|
+
async function promptSyncCategories(templateDir, isFirstRun = false) {
|
|
794
|
+
const categoryAnswers = await inquirer3.prompt([
|
|
1059
795
|
{
|
|
1060
796
|
type: "checkbox",
|
|
1061
|
-
name: "
|
|
1062
|
-
message: "\
|
|
1063
|
-
choices: [
|
|
1064
|
-
{
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
797
|
+
name: "categories",
|
|
798
|
+
message: "\u540C\u671F\u3059\u308B\u9805\u76EE\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\uFF08Space\u3067\u9078\u629E\u3001Enter\u3067\u78BA\u5B9A\uFF09:",
|
|
799
|
+
choices: Object.entries(CATEGORY_CONFIGS).map(([key, config]) => ({
|
|
800
|
+
name: `${config.name} - ${config.description}`,
|
|
801
|
+
value: key,
|
|
802
|
+
checked: isFirstRun ? config.firstRunDefault ?? config.defaultChecked ?? false : config.defaultChecked ?? false
|
|
803
|
+
}))
|
|
804
|
+
}
|
|
805
|
+
]);
|
|
806
|
+
const selectedCategories = categoryAnswers.categories;
|
|
807
|
+
const hasApps = selectedCategories.includes("apps");
|
|
808
|
+
const hasPackages = selectedCategories.includes("packages");
|
|
809
|
+
const hasRootConfig = selectedCategories.includes("root-config");
|
|
810
|
+
let appsDetail;
|
|
811
|
+
let packagesDetail;
|
|
812
|
+
let packageJsonSections;
|
|
813
|
+
let conflictStrategy = "merge";
|
|
814
|
+
if (hasApps) {
|
|
815
|
+
const availableApps = getAvailableApps(templateDir);
|
|
816
|
+
if (availableApps.length > 0) {
|
|
817
|
+
const appsAnswers = await inquirer3.prompt([
|
|
1074
818
|
{
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
819
|
+
type: "checkbox",
|
|
820
|
+
name: "apps",
|
|
821
|
+
message: "\u540C\u671F\u3059\u308B\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u9078\u629E:",
|
|
822
|
+
choices: availableApps.map((app) => ({
|
|
823
|
+
name: app,
|
|
824
|
+
value: app,
|
|
825
|
+
checked: true
|
|
826
|
+
})),
|
|
827
|
+
validate: (input) => {
|
|
828
|
+
if (input.length === 0) {
|
|
829
|
+
return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30A2\u30D7\u30EA\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
|
|
830
|
+
}
|
|
831
|
+
return true;
|
|
832
|
+
}
|
|
1078
833
|
}
|
|
1079
|
-
]
|
|
834
|
+
]);
|
|
835
|
+
appsDetail = appsAnswers.apps;
|
|
836
|
+
} else {
|
|
837
|
+
console.warn("\u8B66\u544A: apps/ \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u3001\u30A2\u30D7\u30EA\u304C\u5B58\u5728\u3057\u307E\u305B\u3093");
|
|
838
|
+
appsDetail = [];
|
|
1080
839
|
}
|
|
1081
|
-
|
|
1082
|
-
const selectedComponents = componentAnswers.components;
|
|
1083
|
-
const hasPackages = selectedComponents.includes("packages");
|
|
1084
|
-
const hasApps = selectedComponents.includes("apps");
|
|
1085
|
-
const hasConfig = selectedComponents.includes("config");
|
|
1086
|
-
let packageComponents = [];
|
|
840
|
+
}
|
|
1087
841
|
if (hasPackages) {
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
},
|
|
1099
|
-
{
|
|
1100
|
-
name: "server-core - \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u5171\u901A\u5C64\uFF08Prisma\u3001\u30C9\u30E1\u30A4\u30F3\u30ED\u30B8\u30C3\u30AF\uFF09",
|
|
1101
|
-
value: "server-core",
|
|
1102
|
-
checked: true
|
|
1103
|
-
},
|
|
1104
|
-
{
|
|
1105
|
-
name: "config - \u5171\u901A\u8A2D\u5B9A\uFF08Biome, TypeScript, Panda CSS\uFF09",
|
|
1106
|
-
value: "config",
|
|
1107
|
-
checked: true
|
|
1108
|
-
},
|
|
1109
|
-
{
|
|
1110
|
-
name: "ui - \u5171\u901AUI\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\uFF08shadcn/ui\uFF09",
|
|
1111
|
-
value: "ui",
|
|
842
|
+
const availablePackages = getAvailablePackages(templateDir);
|
|
843
|
+
if (availablePackages.length > 0) {
|
|
844
|
+
const packagesAnswers = await inquirer3.prompt([
|
|
845
|
+
{
|
|
846
|
+
type: "checkbox",
|
|
847
|
+
name: "packages",
|
|
848
|
+
message: "\u540C\u671F\u3059\u308B\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E:",
|
|
849
|
+
choices: availablePackages.map((pkg) => ({
|
|
850
|
+
name: pkg,
|
|
851
|
+
value: pkg,
|
|
1112
852
|
checked: true
|
|
853
|
+
})),
|
|
854
|
+
validate: (input) => {
|
|
855
|
+
if (input.length === 0) {
|
|
856
|
+
return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
|
|
857
|
+
}
|
|
858
|
+
return true;
|
|
1113
859
|
}
|
|
1114
|
-
],
|
|
1115
|
-
validate: (input) => {
|
|
1116
|
-
if (input.length === 0) {
|
|
1117
|
-
return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
|
|
1118
|
-
}
|
|
1119
|
-
return true;
|
|
1120
860
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
861
|
+
]);
|
|
862
|
+
packagesDetail = packagesAnswers.packages;
|
|
863
|
+
} else {
|
|
864
|
+
console.warn("\u8B66\u544A: packages/ \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u3001\u30D1\u30C3\u30B1\u30FC\u30B8\u304C\u5B58\u5728\u3057\u307E\u305B\u3093");
|
|
865
|
+
packagesDetail = [];
|
|
866
|
+
}
|
|
1124
867
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
const appAnswers = await inquirer5.prompt([
|
|
868
|
+
if (hasRootConfig) {
|
|
869
|
+
const packageJsonAnswers = await inquirer3.prompt([
|
|
1128
870
|
{
|
|
1129
871
|
type: "checkbox",
|
|
1130
|
-
name: "
|
|
1131
|
-
message: "\
|
|
872
|
+
name: "sections",
|
|
873
|
+
message: "package.json\u306E\u540C\u671F\u30BB\u30AF\u30B7\u30E7\u30F3\u3092\u9078\u629E:",
|
|
1132
874
|
choices: [
|
|
1133
|
-
{
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
}
|
|
1138
|
-
]
|
|
1139
|
-
validate: (input) => {
|
|
1140
|
-
if (input.length === 0) {
|
|
1141
|
-
return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30A2\u30D7\u30EA\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
|
|
1142
|
-
}
|
|
1143
|
-
return true;
|
|
1144
|
-
}
|
|
875
|
+
{ name: "scripts\uFF08\u63A8\u5968\uFF09", value: "scripts", checked: true },
|
|
876
|
+
{ name: "engines\uFF08\u63A8\u5968\uFF09", value: "engines", checked: true },
|
|
877
|
+
{ name: "dependencies", value: "dependencies", checked: false },
|
|
878
|
+
{ name: "devDependencies", value: "devDependencies", checked: false },
|
|
879
|
+
{ name: "peerDependencies", value: "peerDependencies", checked: false }
|
|
880
|
+
]
|
|
1145
881
|
}
|
|
1146
882
|
]);
|
|
1147
|
-
|
|
883
|
+
packageJsonSections = packageJsonAnswers.sections;
|
|
1148
884
|
}
|
|
885
|
+
const strategyAnswers = await inquirer3.prompt([
|
|
886
|
+
{
|
|
887
|
+
type: "list",
|
|
888
|
+
name: "conflictStrategy",
|
|
889
|
+
message: "\u7AF6\u5408\u89E3\u6C7A\u6226\u7565\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044:",
|
|
890
|
+
choices: [
|
|
891
|
+
{ name: "\u30DE\u30FC\u30AB\u30FC\u30D9\u30FC\u30B9\u30DE\u30FC\u30B8\uFF08\u63A8\u5968\uFF09", value: "merge" },
|
|
892
|
+
{ name: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3067\u4E0A\u66F8\u304D", value: "overwrite" },
|
|
893
|
+
{ name: "\u65E2\u5B58\u30D5\u30A1\u30A4\u30EB\u512A\u5148", value: "skip" }
|
|
894
|
+
],
|
|
895
|
+
default: "merge"
|
|
896
|
+
}
|
|
897
|
+
]);
|
|
898
|
+
conflictStrategy = strategyAnswers.conflictStrategy;
|
|
1149
899
|
return {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
packageComponents,
|
|
1156
|
-
appComponents,
|
|
1157
|
-
dryRun
|
|
900
|
+
categories: selectedCategories,
|
|
901
|
+
appsDetail,
|
|
902
|
+
packagesDetail,
|
|
903
|
+
packageJsonSections,
|
|
904
|
+
conflictStrategy
|
|
1158
905
|
};
|
|
1159
906
|
}
|
|
1160
907
|
|
|
1161
|
-
// src/
|
|
1162
|
-
import
|
|
1163
|
-
import { join as
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
}
|
|
1175
|
-
const normalize = (v) => v.replace(/^[\^~]/, "");
|
|
1176
|
-
return normalize(existingVersion) !== normalize(templateVersion);
|
|
908
|
+
// src/utils/backup.ts
|
|
909
|
+
import fsExtra2 from "fs-extra";
|
|
910
|
+
import { join as join4, dirname as dirname3, relative as relative2 } from "path";
|
|
911
|
+
var { copy, ensureDir: ensureDir2, readdir, remove, pathExists } = fsExtra2;
|
|
912
|
+
function getTimestamp() {
|
|
913
|
+
const now = /* @__PURE__ */ new Date();
|
|
914
|
+
const year = now.getFullYear();
|
|
915
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
916
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
917
|
+
const hours = String(now.getHours()).padStart(2, "0");
|
|
918
|
+
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
919
|
+
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
920
|
+
return `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;
|
|
1177
921
|
}
|
|
1178
|
-
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
if (packageName in existing) {
|
|
1183
|
-
const existingVersion = existing[packageName];
|
|
1184
|
-
if (existingVersion && hasVersionConflict(existingVersion, templateVersion)) {
|
|
1185
|
-
conflicts.push({
|
|
1186
|
-
packageName,
|
|
1187
|
-
existingVersion,
|
|
1188
|
-
templateVersion
|
|
1189
|
-
});
|
|
1190
|
-
} else {
|
|
1191
|
-
merged[packageName] = templateVersion;
|
|
1192
|
-
}
|
|
1193
|
-
} else {
|
|
1194
|
-
merged[packageName] = templateVersion;
|
|
1195
|
-
}
|
|
922
|
+
function parseBackupTimestamp(dirName) {
|
|
923
|
+
const match = dirName.match(/^\.einja-sync-backup-(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})$/);
|
|
924
|
+
if (!match || !match[1]) {
|
|
925
|
+
return null;
|
|
1196
926
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
927
|
+
const timestampStr = match[1];
|
|
928
|
+
const parts = timestampStr.split("_");
|
|
929
|
+
if (parts.length !== 2) {
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
const [datePart, timePart] = parts;
|
|
933
|
+
if (!datePart || !timePart) {
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
const dateParts = datePart.split("-").map(Number);
|
|
937
|
+
const timeParts = timePart.split("-").map(Number);
|
|
938
|
+
if (dateParts.length !== 3 || timeParts.length !== 3) {
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
const [year, month, day] = dateParts;
|
|
942
|
+
const [hours, minutes, seconds] = timeParts;
|
|
943
|
+
if (year === void 0 || month === void 0 || day === void 0 || hours === void 0 || minutes === void 0 || seconds === void 0) {
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
return new Date(year, month - 1, day, hours, minutes, seconds);
|
|
947
|
+
}
|
|
948
|
+
async function createBackup(targetDir, filesToBackup) {
|
|
949
|
+
const timestamp = getTimestamp();
|
|
950
|
+
const backupDirName = `.einja-sync-backup-${timestamp}`;
|
|
951
|
+
const backupDir = join4(targetDir, backupDirName);
|
|
952
|
+
try {
|
|
953
|
+
await ensureDir2(backupDir);
|
|
954
|
+
for (const file of filesToBackup) {
|
|
955
|
+
const sourcePath = join4(targetDir, file);
|
|
956
|
+
const destPath = join4(backupDir, file);
|
|
957
|
+
if (!await pathExists(sourcePath)) {
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
await ensureDir2(dirname3(destPath));
|
|
961
|
+
await copy(sourcePath, destPath);
|
|
962
|
+
}
|
|
963
|
+
return backupDir;
|
|
964
|
+
} catch (error2) {
|
|
965
|
+
error(`\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
966
|
+
throw error2;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
async function restoreFromBackup(backupDir, targetDir) {
|
|
970
|
+
try {
|
|
971
|
+
if (!await pathExists(backupDir)) {
|
|
972
|
+
error(`\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${backupDir}`);
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
const files = await getAllFiles(backupDir);
|
|
976
|
+
for (const file of files) {
|
|
977
|
+
const sourcePath = join4(backupDir, file);
|
|
978
|
+
const destPath = join4(targetDir, file);
|
|
979
|
+
await ensureDir2(dirname3(destPath));
|
|
980
|
+
await copy(sourcePath, destPath, { overwrite: true });
|
|
981
|
+
}
|
|
982
|
+
return true;
|
|
983
|
+
} catch (error2) {
|
|
984
|
+
error(`\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u304B\u3089\u306E\u5FA9\u5143\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
async function getAllFiles(dirPath, baseDir) {
|
|
989
|
+
const base = baseDir ?? dirPath;
|
|
990
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
991
|
+
const files = [];
|
|
992
|
+
for (const entry of entries) {
|
|
993
|
+
const fullPath = join4(dirPath, entry.name);
|
|
994
|
+
if (entry.isDirectory()) {
|
|
995
|
+
const subFiles = await getAllFiles(fullPath, base);
|
|
996
|
+
files.push(...subFiles);
|
|
997
|
+
} else {
|
|
998
|
+
files.push(relative2(base, fullPath));
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return files;
|
|
1002
|
+
}
|
|
1003
|
+
async function listBackups(targetDir) {
|
|
1004
|
+
try {
|
|
1005
|
+
if (!await pathExists(targetDir)) {
|
|
1006
|
+
return [];
|
|
1007
|
+
}
|
|
1008
|
+
const entries = await readdir(targetDir, { withFileTypes: true });
|
|
1009
|
+
const backups = [];
|
|
1010
|
+
for (const entry of entries) {
|
|
1011
|
+
if (!entry.isDirectory()) {
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
const timestamp = parseBackupTimestamp(entry.name);
|
|
1015
|
+
if (!timestamp) {
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
backups.push({
|
|
1019
|
+
path: join4(targetDir, entry.name),
|
|
1020
|
+
name: entry.name,
|
|
1021
|
+
timestamp
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
backups.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
1025
|
+
return backups;
|
|
1026
|
+
} catch (error2) {
|
|
1027
|
+
error(`\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u4E00\u89A7\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
1028
|
+
return [];
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
async function getLatestBackup(targetDir) {
|
|
1032
|
+
const backups = await listBackups(targetDir);
|
|
1033
|
+
return backups.length > 0 ? backups[0] ?? null : null;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// src/utils/git.ts
|
|
1037
|
+
import { execSync } from "child_process";
|
|
1038
|
+
function isGitRepository(targetDir) {
|
|
1039
|
+
try {
|
|
1040
|
+
const cwd = targetDir || process.cwd();
|
|
1041
|
+
execSync("git rev-parse --is-inside-work-tree", {
|
|
1042
|
+
cwd,
|
|
1043
|
+
stdio: "ignore"
|
|
1044
|
+
});
|
|
1045
|
+
return true;
|
|
1046
|
+
} catch {
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function hasUncommittedChanges(targetDir) {
|
|
1051
|
+
if (!isGitRepository(targetDir)) {
|
|
1052
|
+
warn("\u26A0\uFE0F \u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306FGit\u30EA\u30DD\u30B8\u30C8\u30EA\u3067\u306F\u3042\u308A\u307E\u305B\u3093");
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
try {
|
|
1056
|
+
const cwd = targetDir || process.cwd();
|
|
1057
|
+
const output = execSync("git status --porcelain", {
|
|
1058
|
+
cwd,
|
|
1059
|
+
encoding: "utf-8"
|
|
1060
|
+
});
|
|
1061
|
+
return output.trim().length > 0;
|
|
1062
|
+
} catch (error2) {
|
|
1063
|
+
warn(`\u26A0\uFE0F Git\u30B9\u30C6\u30FC\u30BF\u30B9\u78BA\u8A8D\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F: ${error2}`);
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
function checkGitStatusForSync(force, targetDir) {
|
|
1068
|
+
if (!isGitRepository(targetDir)) {
|
|
1069
|
+
warn("\u26A0\uFE0F \u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306FGit\u30EA\u30DD\u30B8\u30C8\u30EA\u3067\u306F\u3042\u308A\u307E\u305B\u3093");
|
|
1070
|
+
warn(
|
|
1071
|
+
" sync\u5B9F\u884C\u5F8C\u306E\u5DEE\u5206\u78BA\u8A8D\u306F `git diff` \u3067\u884C\u3046\u3053\u3068\u3092\u63A8\u5968\u3057\u307E\u3059"
|
|
1072
|
+
);
|
|
1073
|
+
return true;
|
|
1074
|
+
}
|
|
1075
|
+
if (hasUncommittedChanges(targetDir)) {
|
|
1076
|
+
if (!force) {
|
|
1077
|
+
error("\u274C \u672A\u30B3\u30DF\u30C3\u30C8\u306E\u5909\u66F4\u304C\u3042\u308A\u307E\u3059");
|
|
1078
|
+
error(
|
|
1079
|
+
" \u5909\u66F4\u3092\u30B3\u30DF\u30C3\u30C8\u3057\u3066\u304B\u3089\u5B9F\u884C\u3059\u308B\u304B\u3001--force \u30D5\u30E9\u30B0\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
1080
|
+
);
|
|
1081
|
+
process.exit(1);
|
|
1082
|
+
}
|
|
1083
|
+
warn("\u26A0\uFE0F \u672A\u30B3\u30DF\u30C3\u30C8\u306E\u5909\u66F4\u304C\u3042\u308A\u307E\u3059\u304C\u3001--force \u306B\u3088\u308A\u7D9A\u884C\u3057\u307E\u3059");
|
|
1084
|
+
warn(
|
|
1085
|
+
" \u554F\u984C\u304C\u767A\u751F\u3057\u305F\u5834\u5408\u306F `git checkout .` \u3067\u5FA9\u5143\u3067\u304D\u307E\u3059"
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
return true;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// src/utils/merger.ts
|
|
1092
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
|
|
1093
|
+
import { dirname as dirname4, basename } from "path";
|
|
1094
|
+
|
|
1095
|
+
// src/utils/package-json-merger.ts
|
|
1096
|
+
import inquirer4 from "inquirer";
|
|
1097
|
+
function hasVersionConflict(existingVersion, templateVersion) {
|
|
1098
|
+
if (existingVersion === templateVersion) {
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
const normalize = (v) => v.replace(/^[\^~]/, "");
|
|
1102
|
+
return normalize(existingVersion) !== normalize(templateVersion);
|
|
1103
|
+
}
|
|
1104
|
+
async function mergeDependenciesWithConflictDetection(existing, template) {
|
|
1105
|
+
const merged = { ...existing };
|
|
1106
|
+
const conflicts = [];
|
|
1107
|
+
for (const [packageName, templateVersion] of Object.entries(template)) {
|
|
1108
|
+
if (packageName in existing) {
|
|
1109
|
+
const existingVersion = existing[packageName];
|
|
1110
|
+
if (existingVersion && hasVersionConflict(existingVersion, templateVersion)) {
|
|
1111
|
+
conflicts.push({
|
|
1112
|
+
packageName,
|
|
1113
|
+
existingVersion,
|
|
1114
|
+
templateVersion
|
|
1115
|
+
});
|
|
1116
|
+
} else {
|
|
1117
|
+
merged[packageName] = templateVersion;
|
|
1118
|
+
}
|
|
1119
|
+
} else {
|
|
1120
|
+
merged[packageName] = templateVersion;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
return { merged, conflicts };
|
|
1124
|
+
}
|
|
1125
|
+
async function resolveVersionConflicts(conflicts) {
|
|
1126
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
1127
|
+
warn(`
|
|
1128
|
+
\u26A0\uFE0F ${conflicts.length}\u500B\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u3067\u30D0\u30FC\u30B8\u30E7\u30F3\u7AF6\u5408\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F:
|
|
1129
|
+
`);
|
|
1204
1130
|
for (const conflict of conflicts) {
|
|
1205
1131
|
warn(`\u{1F4E6} ${conflict.packageName}`);
|
|
1206
1132
|
warn(` \u65E2\u5B58: ${conflict.existingVersion}`);
|
|
1207
1133
|
warn(` \u30C6\u30F3\u30D7\u30EC\u30FC\u30C8: ${conflict.templateVersion}
|
|
1208
1134
|
`);
|
|
1209
|
-
const answer = await
|
|
1135
|
+
const answer = await inquirer4.prompt([
|
|
1210
1136
|
{
|
|
1211
1137
|
type: "list",
|
|
1212
1138
|
name: "version",
|
|
@@ -1378,23 +1304,6 @@ function deepClone(value) {
|
|
|
1378
1304
|
}
|
|
1379
1305
|
return JSON.parse(JSON.stringify(value));
|
|
1380
1306
|
}
|
|
1381
|
-
async function loadSyncMetadata(targetDir) {
|
|
1382
|
-
const metadataPath = `${targetDir}/.einja-sync.json`;
|
|
1383
|
-
if (!existsSync5(metadataPath)) {
|
|
1384
|
-
return null;
|
|
1385
|
-
}
|
|
1386
|
-
try {
|
|
1387
|
-
const content = readFileSync4(metadataPath, "utf-8");
|
|
1388
|
-
return JSON.parse(content);
|
|
1389
|
-
} catch {
|
|
1390
|
-
return null;
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
async function saveSyncMetadata(targetDir, metadata) {
|
|
1394
|
-
const metadataPath = `${targetDir}/.einja-sync.json`;
|
|
1395
|
-
ensureDir(dirname3(metadataPath));
|
|
1396
|
-
writeFileSync5(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
1397
|
-
}
|
|
1398
1307
|
async function mergePackageJson(existingContent, templateContent, packageJsonSections) {
|
|
1399
1308
|
const existingPkg = JSON.parse(existingContent);
|
|
1400
1309
|
const templatePkg = JSON.parse(templateContent);
|
|
@@ -1430,10 +1339,21 @@ async function mergePackageJson(existingContent, templateContent, packageJsonSec
|
|
|
1430
1339
|
return `${JSON.stringify(result, null, 2)}
|
|
1431
1340
|
`;
|
|
1432
1341
|
}
|
|
1433
|
-
async function mergeAndWriteFile(templatePath, targetPath, syncMetadata, packageJsonSections) {
|
|
1434
|
-
|
|
1342
|
+
async function mergeAndWriteFile(templatePath, targetPath, syncMetadata, packageJsonSections, conflictStrategy = "merge", templateVariables) {
|
|
1343
|
+
let templateContent = readFileSync3(templatePath, "utf-8");
|
|
1344
|
+
if (templateVariables) {
|
|
1345
|
+
templateContent = replacePlaceholders(templateContent, templateVariables);
|
|
1346
|
+
}
|
|
1435
1347
|
const targetExists = existsSync5(targetPath);
|
|
1436
|
-
const existingContent = targetExists ?
|
|
1348
|
+
const existingContent = targetExists ? readFileSync3(targetPath, "utf-8") : null;
|
|
1349
|
+
if (targetExists && conflictStrategy === "skip") {
|
|
1350
|
+
return { action: "skipped", path: targetPath };
|
|
1351
|
+
}
|
|
1352
|
+
if (targetExists && conflictStrategy === "overwrite") {
|
|
1353
|
+
ensureDir(dirname4(targetPath));
|
|
1354
|
+
writeFileSync3(targetPath, templateContent, "utf-8");
|
|
1355
|
+
return { action: "overwritten", path: targetPath };
|
|
1356
|
+
}
|
|
1437
1357
|
const isJsonFile = targetPath.endsWith(".json");
|
|
1438
1358
|
const isPackageJson = basename(targetPath) === "package.json";
|
|
1439
1359
|
let mergedContent;
|
|
@@ -1449,1028 +1369,272 @@ async function mergeAndWriteFile(templatePath, targetPath, syncMetadata, package
|
|
|
1449
1369
|
mergedContent = templateContent;
|
|
1450
1370
|
action = "overwritten";
|
|
1451
1371
|
}
|
|
1452
|
-
} else if (isJsonFile) {
|
|
1453
|
-
try {
|
|
1454
|
-
const templateJson = JSON.parse(templateContent);
|
|
1455
|
-
const existingJson = existingContent ? JSON.parse(existingContent) : null;
|
|
1456
|
-
const jsonPaths = syncMetadata.jsonPaths || { managed: {}, seed: {} };
|
|
1457
|
-
const fileName = targetPath.split("/").pop() || "package.json";
|
|
1458
|
-
const mergedJson = mergeJson(templateJson, existingJson, jsonPaths, fileName);
|
|
1459
|
-
mergedContent = JSON.stringify(mergedJson, null, 2);
|
|
1460
|
-
action = "merged";
|
|
1461
|
-
} catch {
|
|
1462
|
-
mergedContent = templateContent;
|
|
1463
|
-
action = "overwritten";
|
|
1464
|
-
}
|
|
1465
|
-
} else {
|
|
1466
|
-
mergedContent = mergeTextWithMarkers(templateContent, existingContent);
|
|
1467
|
-
if (mergedContent === existingContent) {
|
|
1468
|
-
action = "skipped";
|
|
1469
|
-
} else {
|
|
1470
|
-
action = "merged";
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
if (action !== "skipped") {
|
|
1474
|
-
ensureDir(dirname3(targetPath));
|
|
1475
|
-
writeFileSync5(targetPath, mergedContent, "utf-8");
|
|
1476
|
-
}
|
|
1477
|
-
return { action, path: targetPath };
|
|
1478
|
-
}
|
|
1479
|
-
function parseMarkers(content) {
|
|
1480
|
-
const lines = content.split("\n");
|
|
1481
|
-
const sections = [];
|
|
1482
|
-
let currentType = "unmanaged";
|
|
1483
|
-
let currentStartLine = 1;
|
|
1484
|
-
let currentContent = [];
|
|
1485
|
-
let currentId;
|
|
1486
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1487
|
-
const line = lines[i];
|
|
1488
|
-
const lineNumber = i + 1;
|
|
1489
|
-
const startMarker = parseStartMarker(line);
|
|
1490
|
-
if (startMarker) {
|
|
1491
|
-
if (currentType !== "unmanaged") {
|
|
1492
|
-
currentContent.push(line);
|
|
1493
|
-
continue;
|
|
1494
|
-
}
|
|
1495
|
-
if (currentContent.length > 0 || sections.length === 0) {
|
|
1496
|
-
sections.push({
|
|
1497
|
-
type: "unmanaged",
|
|
1498
|
-
startLine: currentStartLine,
|
|
1499
|
-
endLine: lineNumber - 1,
|
|
1500
|
-
content: currentContent.join("\n")
|
|
1501
|
-
});
|
|
1502
|
-
}
|
|
1503
|
-
currentType = startMarker.type;
|
|
1504
|
-
currentId = startMarker.id;
|
|
1505
|
-
currentStartLine = lineNumber;
|
|
1506
|
-
currentContent = [line];
|
|
1507
|
-
} else if (parseEndMarker(line)) {
|
|
1508
|
-
if (currentType === "unmanaged") {
|
|
1509
|
-
currentContent.push(line);
|
|
1510
|
-
continue;
|
|
1511
|
-
}
|
|
1512
|
-
currentContent.push(line);
|
|
1513
|
-
sections.push({
|
|
1514
|
-
type: currentType,
|
|
1515
|
-
startLine: currentStartLine,
|
|
1516
|
-
endLine: lineNumber,
|
|
1517
|
-
content: currentContent.join("\n"),
|
|
1518
|
-
id: currentId
|
|
1519
|
-
});
|
|
1520
|
-
currentType = "unmanaged";
|
|
1521
|
-
currentId = void 0;
|
|
1522
|
-
currentStartLine = lineNumber + 1;
|
|
1523
|
-
currentContent = [];
|
|
1524
|
-
} else {
|
|
1525
|
-
currentContent.push(line);
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
if (currentContent.length > 0 || sections.length === 0) {
|
|
1529
|
-
sections.push({
|
|
1530
|
-
type: currentType,
|
|
1531
|
-
startLine: currentStartLine,
|
|
1532
|
-
endLine: lines.length,
|
|
1533
|
-
content: currentContent.join("\n"),
|
|
1534
|
-
id: currentId
|
|
1535
|
-
});
|
|
1536
|
-
}
|
|
1537
|
-
return sections;
|
|
1538
|
-
}
|
|
1539
|
-
function parseStartMarker(line) {
|
|
1540
|
-
const markdownManagedPattern = /^<!--\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*-->$/;
|
|
1541
|
-
let match = line.match(markdownManagedPattern);
|
|
1542
|
-
if (match) {
|
|
1543
|
-
return { type: "managed", id: match[1] || void 0 };
|
|
1544
|
-
}
|
|
1545
|
-
const markdownSeedPattern = /^<!--\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*-->$/;
|
|
1546
|
-
match = line.match(markdownSeedPattern);
|
|
1547
|
-
if (match) {
|
|
1548
|
-
return { type: "seed", id: match[1] || void 0 };
|
|
1549
|
-
}
|
|
1550
|
-
const yamlManagedPattern = /^\s*#\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*$/;
|
|
1551
|
-
match = line.match(yamlManagedPattern);
|
|
1552
|
-
if (match) {
|
|
1553
|
-
return { type: "managed", id: match[1] || void 0 };
|
|
1554
|
-
}
|
|
1555
|
-
const yamlSeedPattern = /^\s*#\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*$/;
|
|
1556
|
-
match = line.match(yamlSeedPattern);
|
|
1557
|
-
if (match) {
|
|
1558
|
-
return { type: "seed", id: match[1] || void 0 };
|
|
1559
|
-
}
|
|
1560
|
-
return null;
|
|
1561
|
-
}
|
|
1562
|
-
function parseEndMarker(line) {
|
|
1563
|
-
if (/^<!--\s*@einja:managed:end\s*-->$/.test(line)) {
|
|
1564
|
-
return "managed";
|
|
1565
|
-
}
|
|
1566
|
-
if (/^<!--\s*@einja:seed:end\s*-->$/.test(line)) {
|
|
1567
|
-
return "seed";
|
|
1568
|
-
}
|
|
1569
|
-
if (/^\s*#\s*@einja:managed:end\s*$/.test(line)) {
|
|
1570
|
-
return "managed";
|
|
1571
|
-
}
|
|
1572
|
-
if (/^\s*#\s*@einja:seed:end\s*$/.test(line)) {
|
|
1573
|
-
return "seed";
|
|
1574
|
-
}
|
|
1575
|
-
return null;
|
|
1576
|
-
}
|
|
1577
|
-
function isPathManaged(filePath, keyPath, jsonPaths) {
|
|
1578
|
-
const managedPaths = jsonPaths.managed[filePath] || [];
|
|
1579
|
-
return managedPaths.some(
|
|
1580
|
-
(p) => keyPath === p || keyPath.startsWith(`${p}.`)
|
|
1581
|
-
);
|
|
1582
|
-
}
|
|
1583
|
-
function isPathSeed(filePath, keyPath, jsonPaths) {
|
|
1584
|
-
const seedPaths = jsonPaths.seed[filePath] || [];
|
|
1585
|
-
return seedPaths.some((p) => keyPath === p || keyPath.startsWith(`${p}.`));
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
// src/generators/partials/packages.ts
|
|
1589
|
-
async function addPackages(options, components, syncMetadata) {
|
|
1590
|
-
const added = [];
|
|
1591
|
-
const skipped = [];
|
|
1592
|
-
const merged = [];
|
|
1593
|
-
const { targetDir, templateDir, config } = options;
|
|
1594
|
-
for (const component of components) {
|
|
1595
|
-
const componentName = component === "front-core" ? "front-core" : component === "server-core" ? "server-core" : component === "config" ? "config" : "ui";
|
|
1596
|
-
const srcDir = join10(templateDir, "packages", componentName);
|
|
1597
|
-
const destDir = join10(targetDir, "packages", componentName);
|
|
1598
|
-
info(`Adding package component: ${componentName}`);
|
|
1599
|
-
await copyDirectory(
|
|
1600
|
-
srcDir,
|
|
1601
|
-
destDir,
|
|
1602
|
-
{ added, skipped, merged },
|
|
1603
|
-
config.dryRun,
|
|
1604
|
-
syncMetadata
|
|
1605
|
-
);
|
|
1606
|
-
}
|
|
1607
|
-
return { added, skipped, merged };
|
|
1608
|
-
}
|
|
1609
|
-
async function copyDirectory(srcDir, destDir, result, dryRun, syncMetadata) {
|
|
1610
|
-
const entries = await readdir(srcDir, { withFileTypes: true });
|
|
1611
|
-
for (const entry of entries) {
|
|
1612
|
-
const srcPath = join10(srcDir, entry.name);
|
|
1613
|
-
const destPath = join10(destDir, entry.name);
|
|
1614
|
-
if (entry.isDirectory()) {
|
|
1615
|
-
await copyDirectory(srcPath, destPath, result, dryRun, syncMetadata);
|
|
1616
|
-
} else {
|
|
1617
|
-
if (!dryRun) {
|
|
1618
|
-
const mergeResult = await mergeAndWriteFile(
|
|
1619
|
-
srcPath,
|
|
1620
|
-
destPath,
|
|
1621
|
-
syncMetadata
|
|
1622
|
-
);
|
|
1623
|
-
if (mergeResult.action === "created") {
|
|
1624
|
-
result.added.push(destPath);
|
|
1625
|
-
} else if (mergeResult.action === "skipped") {
|
|
1626
|
-
result.skipped.push(destPath);
|
|
1627
|
-
} else if (mergeResult.action === "merged") {
|
|
1628
|
-
result.merged.push(destPath);
|
|
1629
|
-
}
|
|
1630
|
-
} else {
|
|
1631
|
-
result.skipped.push(destPath);
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
// src/generators/partials/apps.ts
|
|
1638
|
-
import { readdir as readdir2 } from "fs/promises";
|
|
1639
|
-
import { join as join11 } from "path";
|
|
1640
|
-
async function addApps(options, components, syncMetadata) {
|
|
1641
|
-
const added = [];
|
|
1642
|
-
const skipped = [];
|
|
1643
|
-
const merged = [];
|
|
1644
|
-
const { targetDir, templateDir, config } = options;
|
|
1645
|
-
for (const component of components) {
|
|
1646
|
-
const componentName = component === "web" ? "web" : component;
|
|
1647
|
-
const srcDir = join11(templateDir, "apps", componentName);
|
|
1648
|
-
const destDir = join11(targetDir, "apps", componentName);
|
|
1649
|
-
info(`Adding app component: ${componentName}`);
|
|
1650
|
-
await copyDirectory2(
|
|
1651
|
-
srcDir,
|
|
1652
|
-
destDir,
|
|
1653
|
-
{ added, skipped, merged },
|
|
1654
|
-
config.dryRun,
|
|
1655
|
-
syncMetadata
|
|
1656
|
-
);
|
|
1657
|
-
}
|
|
1658
|
-
return { added, skipped, merged };
|
|
1659
|
-
}
|
|
1660
|
-
async function copyDirectory2(srcDir, destDir, result, dryRun, syncMetadata) {
|
|
1661
|
-
const entries = await readdir2(srcDir, { withFileTypes: true });
|
|
1662
|
-
for (const entry of entries) {
|
|
1663
|
-
const srcPath = join11(srcDir, entry.name);
|
|
1664
|
-
const destPath = join11(destDir, entry.name);
|
|
1665
|
-
if (entry.isDirectory()) {
|
|
1666
|
-
await copyDirectory2(srcPath, destPath, result, dryRun, syncMetadata);
|
|
1667
|
-
} else {
|
|
1668
|
-
if (!dryRun) {
|
|
1669
|
-
const mergeResult = await mergeAndWriteFile(
|
|
1670
|
-
srcPath,
|
|
1671
|
-
destPath,
|
|
1672
|
-
syncMetadata
|
|
1673
|
-
);
|
|
1674
|
-
if (mergeResult.action === "created") {
|
|
1675
|
-
result.added.push(destPath);
|
|
1676
|
-
} else if (mergeResult.action === "skipped") {
|
|
1677
|
-
result.skipped.push(destPath);
|
|
1678
|
-
} else if (mergeResult.action === "merged") {
|
|
1679
|
-
result.merged.push(destPath);
|
|
1680
|
-
}
|
|
1681
|
-
} else {
|
|
1682
|
-
result.skipped.push(destPath);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
// src/generators/partials/config.ts
|
|
1689
|
-
import { readdir as readdir3, readFile } from "fs/promises";
|
|
1690
|
-
import { join as join12, relative as relative2, sep } from "path";
|
|
1691
|
-
async function addConfigFiles(options, syncMetadata) {
|
|
1692
|
-
const added = [];
|
|
1693
|
-
const skipped = [];
|
|
1694
|
-
const merged = [];
|
|
1695
|
-
const { targetDir, templateDir, config } = options;
|
|
1696
|
-
info("Adding config files from template root");
|
|
1697
|
-
const excludedPaths = /* @__PURE__ */ new Set([
|
|
1698
|
-
".claude",
|
|
1699
|
-
"docs/einja",
|
|
1700
|
-
"CLAUDE.md",
|
|
1701
|
-
".mcp.json",
|
|
1702
|
-
"node_modules",
|
|
1703
|
-
".turbo",
|
|
1704
|
-
"next-env.d.ts",
|
|
1705
|
-
"styled-system",
|
|
1706
|
-
"pnpm-lock.yaml",
|
|
1707
|
-
"package-lock.json",
|
|
1708
|
-
"packages",
|
|
1709
|
-
"apps"
|
|
1710
|
-
]);
|
|
1711
|
-
const gitignorePatterns = await loadGitignorePatterns(templateDir);
|
|
1712
|
-
await copyConfigDirectory(
|
|
1713
|
-
templateDir,
|
|
1714
|
-
targetDir,
|
|
1715
|
-
templateDir,
|
|
1716
|
-
// rootDir として templateDir を渡す
|
|
1717
|
-
{ added, skipped, merged },
|
|
1718
|
-
config.dryRun,
|
|
1719
|
-
excludedPaths,
|
|
1720
|
-
gitignorePatterns,
|
|
1721
|
-
syncMetadata
|
|
1722
|
-
);
|
|
1723
|
-
return { added, skipped, merged };
|
|
1724
|
-
}
|
|
1725
|
-
async function loadGitignorePatterns(templateDir) {
|
|
1726
|
-
const patterns = /* @__PURE__ */ new Set();
|
|
1727
|
-
const gitignorePath = join12(templateDir, ".gitignore");
|
|
1728
|
-
try {
|
|
1729
|
-
const content = await readFile(gitignorePath, "utf-8");
|
|
1730
|
-
const lines = content.split("\n");
|
|
1731
|
-
for (const line of lines) {
|
|
1732
|
-
const trimmed = line.trim();
|
|
1733
|
-
if (trimmed && !trimmed.startsWith("#")) {
|
|
1734
|
-
const pattern = trimmed.startsWith("/") ? trimmed.slice(1) : trimmed;
|
|
1735
|
-
patterns.add(pattern);
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
} catch {
|
|
1739
|
-
}
|
|
1740
|
-
return patterns;
|
|
1741
|
-
}
|
|
1742
|
-
async function copyConfigDirectory(srcDir, destDir, rootDir, result, dryRun, excludedPaths, gitignorePatterns, syncMetadata) {
|
|
1743
|
-
const entries = await readdir3(srcDir, { withFileTypes: true });
|
|
1744
|
-
for (const entry of entries) {
|
|
1745
|
-
const srcPath = join12(srcDir, entry.name);
|
|
1746
|
-
const destPath = join12(destDir, entry.name);
|
|
1747
|
-
const rawRelativePath = relative2(rootDir, srcPath);
|
|
1748
|
-
const relativePath = rawRelativePath.split(sep).join("/");
|
|
1749
|
-
if (shouldExclude(relativePath, excludedPaths, gitignorePatterns)) {
|
|
1750
|
-
continue;
|
|
1751
|
-
}
|
|
1752
|
-
if (entry.isDirectory()) {
|
|
1753
|
-
await copyConfigDirectory(
|
|
1754
|
-
srcPath,
|
|
1755
|
-
destPath,
|
|
1756
|
-
rootDir,
|
|
1757
|
-
// rootDir を引き継ぐ
|
|
1758
|
-
result,
|
|
1759
|
-
dryRun,
|
|
1760
|
-
excludedPaths,
|
|
1761
|
-
gitignorePatterns,
|
|
1762
|
-
syncMetadata
|
|
1763
|
-
);
|
|
1764
|
-
} else {
|
|
1765
|
-
if (!dryRun) {
|
|
1766
|
-
const mergeResult = await mergeAndWriteFile(
|
|
1767
|
-
srcPath,
|
|
1768
|
-
destPath,
|
|
1769
|
-
syncMetadata
|
|
1770
|
-
);
|
|
1771
|
-
if (mergeResult.action === "created") {
|
|
1772
|
-
result.added.push(destPath);
|
|
1773
|
-
} else if (mergeResult.action === "skipped") {
|
|
1774
|
-
result.skipped.push(destPath);
|
|
1775
|
-
} else if (mergeResult.action === "merged") {
|
|
1776
|
-
result.merged.push(destPath);
|
|
1777
|
-
}
|
|
1778
|
-
} else {
|
|
1779
|
-
result.skipped.push(destPath);
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
function shouldExclude(relativePath, excludedPaths, gitignorePatterns) {
|
|
1785
|
-
for (const excluded of excludedPaths) {
|
|
1786
|
-
if (relativePath === excluded || relativePath.startsWith(`${excluded}/`)) {
|
|
1787
|
-
return true;
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
for (const pattern of gitignorePatterns) {
|
|
1791
|
-
if (matchPattern(relativePath, pattern)) {
|
|
1792
|
-
return true;
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
return false;
|
|
1796
|
-
}
|
|
1797
|
-
function matchPattern(path2, pattern) {
|
|
1798
|
-
if (pattern.endsWith("/")) {
|
|
1799
|
-
const dirPattern = pattern.slice(0, -1);
|
|
1800
|
-
return path2 === dirPattern || path2.startsWith(`${dirPattern}/`);
|
|
1801
|
-
}
|
|
1802
|
-
if (pattern.includes("*")) {
|
|
1803
|
-
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
|
|
1804
|
-
return new RegExp(`^${regexPattern}$`).test(path2);
|
|
1805
|
-
}
|
|
1806
|
-
return path2 === pattern || path2.startsWith(`${pattern}/`);
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
// src/commands/add.ts
|
|
1810
|
-
function getTemplatePath2(templateName) {
|
|
1811
|
-
const __filename3 = fileURLToPath2(import.meta.url);
|
|
1812
|
-
const __dirname3 = dirname4(__filename3);
|
|
1813
|
-
const distPath = join13(__dirname3, "../templates", templateName);
|
|
1814
|
-
const srcPath = join13(__dirname3, "../../templates", templateName);
|
|
1815
|
-
if (existsSync6(distPath)) {
|
|
1816
|
-
return distPath;
|
|
1817
|
-
}
|
|
1818
|
-
if (existsSync6(srcPath)) {
|
|
1819
|
-
return srcPath;
|
|
1820
|
-
}
|
|
1821
|
-
return distPath;
|
|
1822
|
-
}
|
|
1823
|
-
async function loadTemplateSyncMetadata(templateDir) {
|
|
1824
|
-
const syncFilePath = join13(templateDir, ".einja-sync.json");
|
|
1825
|
-
try {
|
|
1826
|
-
const content = await readFile2(syncFilePath, "utf-8");
|
|
1827
|
-
return JSON.parse(content);
|
|
1828
|
-
} catch {
|
|
1829
|
-
return null;
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
function mergeSyncMetadata(template, existing) {
|
|
1833
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1834
|
-
const jsonPaths = template?.jsonPaths ?? existing?.jsonPaths ?? { managed: {}, seed: {} };
|
|
1835
|
-
return {
|
|
1836
|
-
version: template?.version ?? existing?.version ?? "1.0.0",
|
|
1837
|
-
lastSync: now,
|
|
1838
|
-
templateVersion: template?.templateVersion ?? "1.0.0",
|
|
1839
|
-
files: { ...existing?.files ?? {}, ...template?.files ?? {} },
|
|
1840
|
-
jsonPaths
|
|
1841
|
-
};
|
|
1842
|
-
}
|
|
1843
|
-
async function addCommand(options) {
|
|
1844
|
-
try {
|
|
1845
|
-
const targetDir = process.cwd();
|
|
1846
|
-
let config;
|
|
1847
|
-
if (options.skipPrompts) {
|
|
1848
|
-
config = getDefaultAddConfig();
|
|
1849
|
-
info("\u30C7\u30D5\u30A9\u30EB\u30C8\u8A2D\u5B9A\u3092\u4F7F\u7528\u3057\u307E\u3059\uFF08\u3059\u3079\u3066\u306E\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u3092\u9078\u629E\uFF09");
|
|
1850
|
-
} else {
|
|
1851
|
-
config = await promptAddConfig(options.dryRun);
|
|
1852
|
-
}
|
|
1853
|
-
config.dryRun = options.dryRun;
|
|
1854
|
-
if (config.dryRun) {
|
|
1855
|
-
warn("dry-run\u30E2\u30FC\u30C9: \u5B9F\u969B\u306E\u30D5\u30A1\u30A4\u30EB\u64CD\u4F5C\u306F\u884C\u3044\u307E\u305B\u3093");
|
|
1856
|
-
info("\n--- \u8FFD\u52A0\u4E88\u5B9A\u306E\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8 ---");
|
|
1857
|
-
if (config.components.packages) {
|
|
1858
|
-
info(
|
|
1859
|
-
`- packages/: ${config.packageComponents.join(", ")}`
|
|
1860
|
-
);
|
|
1861
|
-
}
|
|
1862
|
-
if (config.components.apps) {
|
|
1863
|
-
info(`- apps/: ${config.appComponents.join(", ")}`);
|
|
1864
|
-
}
|
|
1865
|
-
if (config.components.config) {
|
|
1866
|
-
info("- \u76F4\u4E0B\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB: turbo.json, pnpm-workspace.yaml \u7B49");
|
|
1867
|
-
}
|
|
1868
|
-
info("---\n");
|
|
1869
|
-
}
|
|
1870
|
-
const templateDir = getTemplatePath2("default");
|
|
1871
|
-
if (!existsSync6(templateDir)) {
|
|
1872
|
-
throw new Error("\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: default");
|
|
1873
|
-
}
|
|
1874
|
-
const templateMetadata = await loadTemplateSyncMetadata(templateDir);
|
|
1875
|
-
const existingMetadata = await loadSyncMetadata(targetDir);
|
|
1876
|
-
const syncMetadata = mergeSyncMetadata(templateMetadata, existingMetadata);
|
|
1877
|
-
const addOptions = {
|
|
1878
|
-
targetDir,
|
|
1879
|
-
templateDir,
|
|
1880
|
-
config
|
|
1881
|
-
};
|
|
1882
|
-
let totalAdded = 0;
|
|
1883
|
-
let totalMerged = 0;
|
|
1884
|
-
let totalSkipped = 0;
|
|
1885
|
-
if (config.components.packages && config.packageComponents.length > 0) {
|
|
1886
|
-
const spinner = ora4("\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u8FFD\u52A0\u4E2D...").start();
|
|
1887
|
-
try {
|
|
1888
|
-
const result = await addPackages(
|
|
1889
|
-
addOptions,
|
|
1890
|
-
config.packageComponents,
|
|
1891
|
-
syncMetadata
|
|
1892
|
-
);
|
|
1893
|
-
totalAdded += result.added.length;
|
|
1894
|
-
totalMerged += result.merged.length;
|
|
1895
|
-
totalSkipped += result.skipped.length;
|
|
1896
|
-
spinner.succeed(
|
|
1897
|
-
`\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F\uFF08\u8FFD\u52A0: ${result.added.length}, \u30DE\u30FC\u30B8: ${result.merged.length}, \u30B9\u30AD\u30C3\u30D7: ${result.skipped.length}\uFF09`
|
|
1898
|
-
);
|
|
1899
|
-
} catch (error2) {
|
|
1900
|
-
spinner.fail("\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
1901
|
-
throw error2;
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
if (config.components.apps && config.appComponents.length > 0) {
|
|
1905
|
-
const spinner = ora4("\u30A2\u30D7\u30EA\u3092\u8FFD\u52A0\u4E2D...").start();
|
|
1906
|
-
try {
|
|
1907
|
-
const result = await addApps(
|
|
1908
|
-
addOptions,
|
|
1909
|
-
config.appComponents,
|
|
1910
|
-
syncMetadata
|
|
1911
|
-
);
|
|
1912
|
-
totalAdded += result.added.length;
|
|
1913
|
-
totalMerged += result.merged.length;
|
|
1914
|
-
totalSkipped += result.skipped.length;
|
|
1915
|
-
spinner.succeed(
|
|
1916
|
-
`\u30A2\u30D7\u30EA\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F\uFF08\u8FFD\u52A0: ${result.added.length}, \u30DE\u30FC\u30B8: ${result.merged.length}, \u30B9\u30AD\u30C3\u30D7: ${result.skipped.length}\uFF09`
|
|
1917
|
-
);
|
|
1918
|
-
} catch (error2) {
|
|
1919
|
-
spinner.fail("\u30A2\u30D7\u30EA\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
1920
|
-
throw error2;
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
if (config.components.config) {
|
|
1924
|
-
const spinner = ora4("\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0\u4E2D...").start();
|
|
1925
|
-
try {
|
|
1926
|
-
const result = await addConfigFiles(addOptions, syncMetadata);
|
|
1927
|
-
totalAdded += result.added.length;
|
|
1928
|
-
totalMerged += result.merged.length;
|
|
1929
|
-
totalSkipped += result.skipped.length;
|
|
1930
|
-
spinner.succeed(
|
|
1931
|
-
`\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F\uFF08\u8FFD\u52A0: ${result.added.length}, \u30DE\u30FC\u30B8: ${result.merged.length}, \u30B9\u30AD\u30C3\u30D7: ${result.skipped.length}\uFF09`
|
|
1932
|
-
);
|
|
1933
|
-
} catch (error2) {
|
|
1934
|
-
spinner.fail("\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
1935
|
-
throw error2;
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
if (!config.dryRun) {
|
|
1939
|
-
syncMetadata.lastSync = (/* @__PURE__ */ new Date()).toISOString();
|
|
1940
|
-
await saveSyncMetadata(targetDir, syncMetadata);
|
|
1941
|
-
}
|
|
1942
|
-
success("\n\u2713 \u8FFD\u52A0\u5B8C\u4E86\uFF01\n");
|
|
1943
|
-
if (config.dryRun) {
|
|
1944
|
-
info("\uFF08dry-run\u30E2\u30FC\u30C9\u306E\u305F\u3081\u3001\u5B9F\u969B\u306E\u5909\u66F4\u306F\u884C\u308F\u308C\u3066\u3044\u307E\u305B\u3093\uFF09\n");
|
|
1945
|
-
}
|
|
1946
|
-
info(`\u8FFD\u52A0\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalAdded}\u500B`);
|
|
1947
|
-
info(`\u30DE\u30FC\u30B8\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalMerged}\u500B`);
|
|
1948
|
-
info(`\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalSkipped}\u500B
|
|
1949
|
-
`);
|
|
1950
|
-
const packageJsonPath2 = join13(targetDir, "package.json");
|
|
1951
|
-
if (existsSync6(packageJsonPath2)) {
|
|
1952
|
-
const packageJson2 = await import(packageJsonPath2);
|
|
1953
|
-
const hasEinjaCli = packageJson2.default?.devDependencies?.["@einja/dev-cli"] || packageJson2.default?.dependencies?.["@einja/dev-cli"];
|
|
1954
|
-
if (!hasEinjaCli) {
|
|
1955
|
-
info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
|
|
1956
|
-
info("1. pnpm install");
|
|
1957
|
-
info("2. pnpm dev:setup");
|
|
1958
|
-
info("\n\u63A8\u5968:");
|
|
1959
|
-
info(" einja\u958B\u767A\u652F\u63F4CLI (@einja/dev-cli) \u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB:");
|
|
1960
|
-
info(" pnpm add -D @einja/dev-cli\n");
|
|
1961
|
-
} else {
|
|
1962
|
-
info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
|
|
1963
|
-
info("1. pnpm install");
|
|
1964
|
-
info("2. pnpm dev:setup\n");
|
|
1965
|
-
}
|
|
1966
|
-
} else {
|
|
1967
|
-
info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
|
|
1968
|
-
info("1. pnpm install");
|
|
1969
|
-
info("2. pnpm dev:setup\n");
|
|
1970
|
-
}
|
|
1971
|
-
} catch (error2) {
|
|
1972
|
-
error("\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F:");
|
|
1973
|
-
if (error2 instanceof Error) {
|
|
1974
|
-
error(error2.message);
|
|
1975
|
-
} else {
|
|
1976
|
-
error(String(error2));
|
|
1977
|
-
}
|
|
1978
|
-
process.exit(1);
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
// src/commands/sync.ts
|
|
1983
|
-
import { dirname as dirname6, join as join16 } from "path";
|
|
1984
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1985
|
-
import fsExtra3 from "fs-extra";
|
|
1986
|
-
import inquirer8 from "inquirer";
|
|
1987
|
-
|
|
1988
|
-
// src/generators/sync.ts
|
|
1989
|
-
import { glob as glob2 } from "glob";
|
|
1990
|
-
var CATEGORY_PATTERNS = {
|
|
1991
|
-
env: [".env*", ".envrc", ".volta", ".node-version"],
|
|
1992
|
-
tools: ["biome.json", ".prettierrc*", ".editorconfig", ".vscode/**"],
|
|
1993
|
-
git: [".gitignore", ".gitattributes"],
|
|
1994
|
-
"git-hooks": [".husky/**"],
|
|
1995
|
-
github: [".github/workflows/**", ".github/actions/**", ".github/dependabot.yml"],
|
|
1996
|
-
docker: ["Dockerfile*", "docker-compose*.yml", ".dockerignore"],
|
|
1997
|
-
monorepo: ["turbo.json", "pnpm-workspace.yaml"],
|
|
1998
|
-
"root-config": ["package.json", "tsconfig.json"],
|
|
1999
|
-
scripts: ["scripts/**"],
|
|
2000
|
-
apps: ["apps/**"],
|
|
2001
|
-
packages: ["packages/**"],
|
|
2002
|
-
docs: ["README.md", "docs/**"]
|
|
2003
|
-
};
|
|
2004
|
-
var ENV_FILE_PROTECTION = {
|
|
2005
|
-
protected: [".env.keys", ".env.personal"]
|
|
2006
|
-
};
|
|
2007
|
-
function isProtectedEnvFile(filePath) {
|
|
2008
|
-
return ENV_FILE_PROTECTION.protected.some(
|
|
2009
|
-
(pattern) => filePath.endsWith(pattern)
|
|
2010
|
-
);
|
|
2011
|
-
}
|
|
2012
|
-
function extractPatternsFromCategories(categories, appsDetail, packagesDetail) {
|
|
2013
|
-
const patterns = [];
|
|
2014
|
-
for (const category of categories) {
|
|
2015
|
-
const categoryPatterns = CATEGORY_PATTERNS[category];
|
|
2016
|
-
if (!categoryPatterns) {
|
|
2017
|
-
warn(`\u4E0D\u660E\u306A\u30AB\u30C6\u30B4\u30EA: ${category}`);
|
|
2018
|
-
continue;
|
|
2019
|
-
}
|
|
2020
|
-
if (category === "apps" && appsDetail && appsDetail.length > 0) {
|
|
2021
|
-
patterns.push(...appsDetail.map((app) => `apps/${app}/**`));
|
|
2022
|
-
} else if (category === "packages" && packagesDetail && packagesDetail.length > 0) {
|
|
2023
|
-
patterns.push(...packagesDetail.map((pkg) => `packages/${pkg}/**`));
|
|
2024
|
-
} else {
|
|
2025
|
-
patterns.push(...categoryPatterns);
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
return patterns;
|
|
2029
|
-
}
|
|
2030
|
-
async function collectSyncFiles(templateDir, categories, appsDetail, packagesDetail) {
|
|
2031
|
-
try {
|
|
2032
|
-
info("\u540C\u671F\u5BFE\u8C61\u30D5\u30A1\u30A4\u30EB\u3092\u53CE\u96C6\u4E2D...");
|
|
2033
|
-
const patterns = extractPatternsFromCategories(
|
|
2034
|
-
categories,
|
|
2035
|
-
appsDetail,
|
|
2036
|
-
packagesDetail
|
|
2037
|
-
);
|
|
2038
|
-
if (patterns.length === 0) {
|
|
2039
|
-
warn("\u540C\u671F\u5BFE\u8C61\u306E\u30D1\u30BF\u30FC\u30F3\u304C\u3042\u308A\u307E\u305B\u3093");
|
|
2040
|
-
return [];
|
|
2041
|
-
}
|
|
2042
|
-
const fileSet = /* @__PURE__ */ new Set();
|
|
2043
|
-
for (const pattern of patterns) {
|
|
2044
|
-
try {
|
|
2045
|
-
const files = await glob2(pattern, {
|
|
2046
|
-
cwd: templateDir,
|
|
2047
|
-
dot: true,
|
|
2048
|
-
// .で始まるファイルも含める
|
|
2049
|
-
nodir: true
|
|
2050
|
-
// ディレクトリは除外
|
|
2051
|
-
});
|
|
2052
|
-
for (const file of files) {
|
|
2053
|
-
fileSet.add(file);
|
|
2054
|
-
}
|
|
2055
|
-
} catch (error2) {
|
|
2056
|
-
warn(`\u30D1\u30BF\u30FC\u30F3 ${pattern} \u306E\u51E6\u7406\u4E2D\u306B\u30A8\u30E9\u30FC: ${error2}`);
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
|
-
const allFiles = Array.from(fileSet);
|
|
2060
|
-
const filteredFiles = allFiles.filter((file) => {
|
|
2061
|
-
if (isProtectedEnvFile(file)) {
|
|
2062
|
-
info(`\u4FDD\u8B77\u5BFE\u8C61\u30D5\u30A1\u30A4\u30EB\u3092\u9664\u5916: ${file}`);
|
|
2063
|
-
return false;
|
|
2064
|
-
}
|
|
2065
|
-
return true;
|
|
2066
|
-
});
|
|
2067
|
-
success(`${filteredFiles.length}\u500B\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u53CE\u96C6\u3057\u307E\u3057\u305F`);
|
|
2068
|
-
return filteredFiles.sort();
|
|
2069
|
-
} catch (error2) {
|
|
2070
|
-
error(`\u30D5\u30A1\u30A4\u30EB\u53CE\u96C6\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F: ${error2}`);
|
|
2071
|
-
throw error2;
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
// src/prompts/sync.ts
|
|
2076
|
-
import inquirer7 from "inquirer";
|
|
2077
|
-
import * as fs from "fs";
|
|
2078
|
-
import * as path from "path";
|
|
2079
|
-
var CATEGORY_CONFIGS = {
|
|
2080
|
-
env: {
|
|
2081
|
-
name: "\u74B0\u5883\u8A2D\u5B9A",
|
|
2082
|
-
description: ".env*, .envrc, .volta, .node-version",
|
|
2083
|
-
patterns: [".env*", ".envrc", ".volta", ".node-version"],
|
|
2084
|
-
defaultChecked: true
|
|
2085
|
-
},
|
|
2086
|
-
tools: {
|
|
2087
|
-
name: "\u958B\u767A\u30C4\u30FC\u30EB",
|
|
2088
|
-
description: "biome.json, .prettierrc, .editorconfig, .vscode/",
|
|
2089
|
-
patterns: ["biome.json", ".prettierrc*", ".editorconfig", ".vscode/"],
|
|
2090
|
-
defaultChecked: true
|
|
2091
|
-
},
|
|
2092
|
-
git: {
|
|
2093
|
-
name: "Git\u8A2D\u5B9A",
|
|
2094
|
-
description: ".gitignore, .gitattributes",
|
|
2095
|
-
patterns: [".gitignore", ".gitattributes"],
|
|
2096
|
-
defaultChecked: false
|
|
2097
|
-
},
|
|
2098
|
-
"git-hooks": {
|
|
2099
|
-
name: "Git Hooks",
|
|
2100
|
-
description: ".husky/",
|
|
2101
|
-
patterns: [".husky/"],
|
|
2102
|
-
defaultChecked: false
|
|
2103
|
-
},
|
|
2104
|
-
github: {
|
|
2105
|
-
name: "CI/CD",
|
|
2106
|
-
description: ".github/workflows/, .github/actions/",
|
|
2107
|
-
patterns: [".github/workflows/", ".github/actions/", ".github/dependabot.yml"],
|
|
2108
|
-
defaultChecked: false
|
|
2109
|
-
},
|
|
2110
|
-
docker: {
|
|
2111
|
-
name: "\u30B3\u30F3\u30C6\u30CA",
|
|
2112
|
-
description: "Dockerfile*, docker-compose.yml, .dockerignore",
|
|
2113
|
-
patterns: ["Dockerfile*", "docker-compose*.yml", ".dockerignore"],
|
|
2114
|
-
defaultChecked: false
|
|
2115
|
-
},
|
|
2116
|
-
monorepo: {
|
|
2117
|
-
name: "\u30E2\u30CE\u30EC\u30DD\u69CB\u6210",
|
|
2118
|
-
description: "turbo.json, pnpm-workspace.yaml",
|
|
2119
|
-
patterns: ["turbo.json", "pnpm-workspace.yaml"],
|
|
2120
|
-
defaultChecked: false
|
|
2121
|
-
},
|
|
2122
|
-
"root-config": {
|
|
2123
|
-
name: "\u30EB\u30FC\u30C8\u8A2D\u5B9A",
|
|
2124
|
-
description: "package.json, tsconfig.json",
|
|
2125
|
-
patterns: ["package.json", "tsconfig.json"],
|
|
2126
|
-
defaultChecked: false
|
|
2127
|
-
},
|
|
2128
|
-
scripts: {
|
|
2129
|
-
name: "\u30B9\u30AF\u30EA\u30D7\u30C8",
|
|
2130
|
-
description: "scripts/ \u914D\u4E0B",
|
|
2131
|
-
patterns: ["scripts/**"],
|
|
2132
|
-
defaultChecked: false
|
|
2133
|
-
},
|
|
2134
|
-
apps: {
|
|
2135
|
-
name: "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3",
|
|
2136
|
-
description: "apps/ \u914D\u4E0B\uFF08\u6B21\u306E\u753B\u9762\u3067\u500B\u5225\u9078\u629E\uFF09",
|
|
2137
|
-
patterns: ["apps/**"],
|
|
2138
|
-
defaultChecked: false,
|
|
2139
|
-
requiresDetailSelection: true
|
|
2140
|
-
},
|
|
2141
|
-
packages: {
|
|
2142
|
-
name: "\u5171\u901A\u30D1\u30C3\u30B1\u30FC\u30B8",
|
|
2143
|
-
description: "packages/ \u914D\u4E0B\uFF08\u6B21\u306E\u753B\u9762\u3067\u500B\u5225\u9078\u629E\uFF09",
|
|
2144
|
-
patterns: ["packages/**"],
|
|
2145
|
-
defaultChecked: false,
|
|
2146
|
-
requiresDetailSelection: true
|
|
2147
|
-
},
|
|
2148
|
-
docs: {
|
|
2149
|
-
name: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
|
|
2150
|
-
description: "README.md, docs/",
|
|
2151
|
-
patterns: ["README.md", "docs/**"],
|
|
2152
|
-
defaultChecked: false
|
|
2153
|
-
}
|
|
2154
|
-
};
|
|
2155
|
-
function getAvailableApps(templateDir) {
|
|
2156
|
-
const appsDir = path.join(templateDir, "apps");
|
|
2157
|
-
try {
|
|
2158
|
-
if (!fs.existsSync(appsDir)) {
|
|
2159
|
-
return [];
|
|
2160
|
-
}
|
|
2161
|
-
return fs.readdirSync(appsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
2162
|
-
} catch (error2) {
|
|
2163
|
-
console.error(`Error reading apps directory: ${error2}`);
|
|
2164
|
-
return [];
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
function getAvailablePackages(templateDir) {
|
|
2168
|
-
const packagesDir = path.join(templateDir, "packages");
|
|
2169
|
-
try {
|
|
2170
|
-
if (!fs.existsSync(packagesDir)) {
|
|
2171
|
-
return [];
|
|
2172
|
-
}
|
|
2173
|
-
return fs.readdirSync(packagesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
2174
|
-
} catch (error2) {
|
|
2175
|
-
console.error(`Error reading packages directory: ${error2}`);
|
|
2176
|
-
return [];
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
async function promptSyncCategories(templateDir) {
|
|
2180
|
-
const categoryAnswers = await inquirer7.prompt([
|
|
2181
|
-
{
|
|
2182
|
-
type: "checkbox",
|
|
2183
|
-
name: "categories",
|
|
2184
|
-
message: "\u540C\u671F\u3059\u308B\u9805\u76EE\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\uFF08Space\u3067\u9078\u629E\u3001Enter\u3067\u78BA\u5B9A\uFF09:",
|
|
2185
|
-
choices: Object.entries(CATEGORY_CONFIGS).map(([key, config]) => ({
|
|
2186
|
-
name: `${config.name} - ${config.description}`,
|
|
2187
|
-
value: key,
|
|
2188
|
-
checked: config.defaultChecked ?? false
|
|
2189
|
-
}))
|
|
2190
|
-
}
|
|
2191
|
-
]);
|
|
2192
|
-
const selectedCategories = categoryAnswers.categories;
|
|
2193
|
-
const hasApps = selectedCategories.includes("apps");
|
|
2194
|
-
const hasPackages = selectedCategories.includes("packages");
|
|
2195
|
-
const hasRootConfig = selectedCategories.includes("root-config");
|
|
2196
|
-
let appsDetail;
|
|
2197
|
-
let packagesDetail;
|
|
2198
|
-
let packageJsonSections;
|
|
2199
|
-
let conflictStrategy = "merge";
|
|
2200
|
-
if (hasApps) {
|
|
2201
|
-
const availableApps = getAvailableApps(templateDir);
|
|
2202
|
-
if (availableApps.length > 0) {
|
|
2203
|
-
const appsAnswers = await inquirer7.prompt([
|
|
2204
|
-
{
|
|
2205
|
-
type: "checkbox",
|
|
2206
|
-
name: "apps",
|
|
2207
|
-
message: "\u540C\u671F\u3059\u308B\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u9078\u629E:",
|
|
2208
|
-
choices: availableApps.map((app) => ({
|
|
2209
|
-
name: app,
|
|
2210
|
-
value: app,
|
|
2211
|
-
checked: true
|
|
2212
|
-
})),
|
|
2213
|
-
validate: (input) => {
|
|
2214
|
-
if (input.length === 0) {
|
|
2215
|
-
return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30A2\u30D7\u30EA\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
|
|
2216
|
-
}
|
|
2217
|
-
return true;
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
]);
|
|
2221
|
-
appsDetail = appsAnswers.apps;
|
|
2222
|
-
} else {
|
|
2223
|
-
console.warn("\u8B66\u544A: apps/ \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u3001\u30A2\u30D7\u30EA\u304C\u5B58\u5728\u3057\u307E\u305B\u3093");
|
|
2224
|
-
appsDetail = [];
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
if (hasPackages) {
|
|
2228
|
-
const availablePackages = getAvailablePackages(templateDir);
|
|
2229
|
-
if (availablePackages.length > 0) {
|
|
2230
|
-
const packagesAnswers = await inquirer7.prompt([
|
|
2231
|
-
{
|
|
2232
|
-
type: "checkbox",
|
|
2233
|
-
name: "packages",
|
|
2234
|
-
message: "\u540C\u671F\u3059\u308B\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E:",
|
|
2235
|
-
choices: availablePackages.map((pkg) => ({
|
|
2236
|
-
name: pkg,
|
|
2237
|
-
value: pkg,
|
|
2238
|
-
checked: true
|
|
2239
|
-
})),
|
|
2240
|
-
validate: (input) => {
|
|
2241
|
-
if (input.length === 0) {
|
|
2242
|
-
return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
|
|
2243
|
-
}
|
|
2244
|
-
return true;
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
]);
|
|
2248
|
-
packagesDetail = packagesAnswers.packages;
|
|
2249
|
-
} else {
|
|
2250
|
-
console.warn("\u8B66\u544A: packages/ \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u3001\u30D1\u30C3\u30B1\u30FC\u30B8\u304C\u5B58\u5728\u3057\u307E\u305B\u3093");
|
|
2251
|
-
packagesDetail = [];
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
if (hasRootConfig) {
|
|
2255
|
-
const packageJsonAnswers = await inquirer7.prompt([
|
|
2256
|
-
{
|
|
2257
|
-
type: "checkbox",
|
|
2258
|
-
name: "sections",
|
|
2259
|
-
message: "package.json\u306E\u540C\u671F\u30BB\u30AF\u30B7\u30E7\u30F3\u3092\u9078\u629E:",
|
|
2260
|
-
choices: [
|
|
2261
|
-
{ name: "scripts\uFF08\u63A8\u5968\uFF09", value: "scripts", checked: true },
|
|
2262
|
-
{ name: "engines\uFF08\u63A8\u5968\uFF09", value: "engines", checked: true },
|
|
2263
|
-
{ name: "dependencies", value: "dependencies", checked: false },
|
|
2264
|
-
{ name: "devDependencies", value: "devDependencies", checked: false }
|
|
2265
|
-
]
|
|
2266
|
-
}
|
|
2267
|
-
]);
|
|
2268
|
-
packageJsonSections = packageJsonAnswers.sections;
|
|
2269
|
-
}
|
|
2270
|
-
const strategyAnswers = await inquirer7.prompt([
|
|
2271
|
-
{
|
|
2272
|
-
type: "list",
|
|
2273
|
-
name: "conflictStrategy",
|
|
2274
|
-
message: "\u7AF6\u5408\u89E3\u6C7A\u6226\u7565\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044:",
|
|
2275
|
-
choices: [
|
|
2276
|
-
{ name: "\u30DE\u30FC\u30AB\u30FC\u30D9\u30FC\u30B9\u30DE\u30FC\u30B8\uFF08\u63A8\u5968\uFF09", value: "merge" },
|
|
2277
|
-
{ name: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3067\u4E0A\u66F8\u304D", value: "overwrite" },
|
|
2278
|
-
{ name: "\u65E2\u5B58\u30D5\u30A1\u30A4\u30EB\u512A\u5148", value: "skip" }
|
|
2279
|
-
],
|
|
2280
|
-
default: "merge"
|
|
2281
|
-
}
|
|
2282
|
-
]);
|
|
2283
|
-
conflictStrategy = strategyAnswers.conflictStrategy;
|
|
2284
|
-
return {
|
|
2285
|
-
categories: selectedCategories,
|
|
2286
|
-
appsDetail,
|
|
2287
|
-
packagesDetail,
|
|
2288
|
-
packageJsonSections,
|
|
2289
|
-
conflictStrategy
|
|
2290
|
-
};
|
|
2291
|
-
}
|
|
2292
|
-
|
|
2293
|
-
// src/utils/backup.ts
|
|
2294
|
-
import fsExtra2 from "fs-extra";
|
|
2295
|
-
import { join as join15, dirname as dirname5, relative as relative3 } from "path";
|
|
2296
|
-
var { copy, ensureDir: ensureDir2, readdir: readdir4, remove, pathExists } = fsExtra2;
|
|
2297
|
-
function getTimestamp() {
|
|
2298
|
-
const now = /* @__PURE__ */ new Date();
|
|
2299
|
-
const year = now.getFullYear();
|
|
2300
|
-
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
2301
|
-
const day = String(now.getDate()).padStart(2, "0");
|
|
2302
|
-
const hours = String(now.getHours()).padStart(2, "0");
|
|
2303
|
-
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
2304
|
-
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
2305
|
-
return `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;
|
|
2306
|
-
}
|
|
2307
|
-
function parseBackupTimestamp(dirName) {
|
|
2308
|
-
const match = dirName.match(/^\.einja-sync-backup-(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})$/);
|
|
2309
|
-
if (!match || !match[1]) {
|
|
2310
|
-
return null;
|
|
2311
|
-
}
|
|
2312
|
-
const timestampStr = match[1];
|
|
2313
|
-
const parts = timestampStr.split("_");
|
|
2314
|
-
if (parts.length !== 2) {
|
|
2315
|
-
return null;
|
|
2316
|
-
}
|
|
2317
|
-
const [datePart, timePart] = parts;
|
|
2318
|
-
if (!datePart || !timePart) {
|
|
2319
|
-
return null;
|
|
2320
|
-
}
|
|
2321
|
-
const dateParts = datePart.split("-").map(Number);
|
|
2322
|
-
const timeParts = timePart.split("-").map(Number);
|
|
2323
|
-
if (dateParts.length !== 3 || timeParts.length !== 3) {
|
|
2324
|
-
return null;
|
|
2325
|
-
}
|
|
2326
|
-
const [year, month, day] = dateParts;
|
|
2327
|
-
const [hours, minutes, seconds] = timeParts;
|
|
2328
|
-
if (year === void 0 || month === void 0 || day === void 0 || hours === void 0 || minutes === void 0 || seconds === void 0) {
|
|
2329
|
-
return null;
|
|
2330
|
-
}
|
|
2331
|
-
return new Date(year, month - 1, day, hours, minutes, seconds);
|
|
2332
|
-
}
|
|
2333
|
-
async function createBackup(targetDir, filesToBackup) {
|
|
2334
|
-
const timestamp = getTimestamp();
|
|
2335
|
-
const backupDirName = `.einja-sync-backup-${timestamp}`;
|
|
2336
|
-
const backupDir = join15(targetDir, backupDirName);
|
|
2337
|
-
try {
|
|
2338
|
-
await ensureDir2(backupDir);
|
|
2339
|
-
for (const file of filesToBackup) {
|
|
2340
|
-
const sourcePath = join15(targetDir, file);
|
|
2341
|
-
const destPath = join15(backupDir, file);
|
|
2342
|
-
if (!await pathExists(sourcePath)) {
|
|
2343
|
-
continue;
|
|
2344
|
-
}
|
|
2345
|
-
await ensureDir2(dirname5(destPath));
|
|
2346
|
-
await copy(sourcePath, destPath);
|
|
2347
|
-
}
|
|
2348
|
-
return backupDir;
|
|
2349
|
-
} catch (error2) {
|
|
2350
|
-
error(`\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
2351
|
-
throw error2;
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
|
-
async function restoreFromBackup(backupDir, targetDir) {
|
|
2355
|
-
try {
|
|
2356
|
-
if (!await pathExists(backupDir)) {
|
|
2357
|
-
error(`\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${backupDir}`);
|
|
2358
|
-
return false;
|
|
2359
|
-
}
|
|
2360
|
-
const files = await getAllFiles(backupDir);
|
|
2361
|
-
for (const file of files) {
|
|
2362
|
-
const sourcePath = join15(backupDir, file);
|
|
2363
|
-
const destPath = join15(targetDir, file);
|
|
2364
|
-
await ensureDir2(dirname5(destPath));
|
|
2365
|
-
await copy(sourcePath, destPath, { overwrite: true });
|
|
1372
|
+
} else if (isJsonFile) {
|
|
1373
|
+
try {
|
|
1374
|
+
const templateJson = JSON.parse(templateContent);
|
|
1375
|
+
const existingJson = existingContent ? JSON.parse(existingContent) : null;
|
|
1376
|
+
const jsonPaths = syncMetadata.jsonPaths || { managed: {}, seed: {} };
|
|
1377
|
+
const fileName = targetPath.split("/").pop() || "package.json";
|
|
1378
|
+
const mergedJson = mergeJson(templateJson, existingJson, jsonPaths, fileName);
|
|
1379
|
+
mergedContent = JSON.stringify(mergedJson, null, 2);
|
|
1380
|
+
action = "merged";
|
|
1381
|
+
} catch {
|
|
1382
|
+
mergedContent = templateContent;
|
|
1383
|
+
action = "overwritten";
|
|
2366
1384
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2373
|
-
async function getAllFiles(dirPath, baseDir) {
|
|
2374
|
-
const base = baseDir ?? dirPath;
|
|
2375
|
-
const entries = await readdir4(dirPath, { withFileTypes: true });
|
|
2376
|
-
const files = [];
|
|
2377
|
-
for (const entry of entries) {
|
|
2378
|
-
const fullPath = join15(dirPath, entry.name);
|
|
2379
|
-
if (entry.isDirectory()) {
|
|
2380
|
-
const subFiles = await getAllFiles(fullPath, base);
|
|
2381
|
-
files.push(...subFiles);
|
|
1385
|
+
} else {
|
|
1386
|
+
mergedContent = mergeTextWithMarkers(templateContent, existingContent);
|
|
1387
|
+
if (mergedContent === existingContent) {
|
|
1388
|
+
action = "skipped";
|
|
2382
1389
|
} else {
|
|
2383
|
-
|
|
1390
|
+
action = "merged";
|
|
2384
1391
|
}
|
|
2385
1392
|
}
|
|
2386
|
-
|
|
1393
|
+
if (action !== "skipped") {
|
|
1394
|
+
ensureDir(dirname4(targetPath));
|
|
1395
|
+
writeFileSync3(targetPath, mergedContent, "utf-8");
|
|
1396
|
+
}
|
|
1397
|
+
return { action, path: targetPath };
|
|
2387
1398
|
}
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
1399
|
+
function parseMarkers(content) {
|
|
1400
|
+
const lines = content.split("\n");
|
|
1401
|
+
const sections = [];
|
|
1402
|
+
let currentType = "unmanaged";
|
|
1403
|
+
let currentStartLine = 1;
|
|
1404
|
+
let currentContent = [];
|
|
1405
|
+
let currentId;
|
|
1406
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1407
|
+
const line = lines[i];
|
|
1408
|
+
const lineNumber = i + 1;
|
|
1409
|
+
const startMarker = parseStartMarker(line);
|
|
1410
|
+
if (startMarker) {
|
|
1411
|
+
if (currentType !== "unmanaged") {
|
|
1412
|
+
currentContent.push(line);
|
|
2397
1413
|
continue;
|
|
2398
1414
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
1415
|
+
if (currentContent.length > 0 || sections.length === 0) {
|
|
1416
|
+
sections.push({
|
|
1417
|
+
type: "unmanaged",
|
|
1418
|
+
startLine: currentStartLine,
|
|
1419
|
+
endLine: lineNumber - 1,
|
|
1420
|
+
content: currentContent.join("\n")
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
currentType = startMarker.type;
|
|
1424
|
+
currentId = startMarker.id;
|
|
1425
|
+
currentStartLine = lineNumber;
|
|
1426
|
+
currentContent = [line];
|
|
1427
|
+
} else if (parseEndMarker(line)) {
|
|
1428
|
+
if (currentType === "unmanaged") {
|
|
1429
|
+
currentContent.push(line);
|
|
2401
1430
|
continue;
|
|
2402
1431
|
}
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
1432
|
+
currentContent.push(line);
|
|
1433
|
+
sections.push({
|
|
1434
|
+
type: currentType,
|
|
1435
|
+
startLine: currentStartLine,
|
|
1436
|
+
endLine: lineNumber,
|
|
1437
|
+
content: currentContent.join("\n"),
|
|
1438
|
+
id: currentId
|
|
2407
1439
|
});
|
|
1440
|
+
currentType = "unmanaged";
|
|
1441
|
+
currentId = void 0;
|
|
1442
|
+
currentStartLine = lineNumber + 1;
|
|
1443
|
+
currentContent = [];
|
|
1444
|
+
} else {
|
|
1445
|
+
currentContent.push(line);
|
|
2408
1446
|
}
|
|
2409
|
-
backups.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
2410
|
-
return backups;
|
|
2411
|
-
} catch (error2) {
|
|
2412
|
-
error(`\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u4E00\u89A7\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
2413
|
-
return [];
|
|
2414
1447
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
import { execSync as execSync2 } from "child_process";
|
|
2423
|
-
function isGitRepository(targetDir) {
|
|
2424
|
-
try {
|
|
2425
|
-
const cwd = targetDir || process.cwd();
|
|
2426
|
-
execSync2("git rev-parse --is-inside-work-tree", {
|
|
2427
|
-
cwd,
|
|
2428
|
-
stdio: "ignore"
|
|
1448
|
+
if (currentContent.length > 0 || sections.length === 0) {
|
|
1449
|
+
sections.push({
|
|
1450
|
+
type: currentType,
|
|
1451
|
+
startLine: currentStartLine,
|
|
1452
|
+
endLine: lines.length,
|
|
1453
|
+
content: currentContent.join("\n"),
|
|
1454
|
+
id: currentId
|
|
2429
1455
|
});
|
|
2430
|
-
return true;
|
|
2431
|
-
} catch {
|
|
2432
|
-
return false;
|
|
2433
1456
|
}
|
|
1457
|
+
return sections;
|
|
2434
1458
|
}
|
|
2435
|
-
function
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
1459
|
+
function parseStartMarker(line) {
|
|
1460
|
+
const markdownManagedPattern = /^<!--\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*-->$/;
|
|
1461
|
+
let match = line.match(markdownManagedPattern);
|
|
1462
|
+
if (match) {
|
|
1463
|
+
return { type: "managed", id: match[1] || void 0 };
|
|
2439
1464
|
}
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
1465
|
+
const markdownSeedPattern = /^<!--\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*-->$/;
|
|
1466
|
+
match = line.match(markdownSeedPattern);
|
|
1467
|
+
if (match) {
|
|
1468
|
+
return { type: "seed", id: match[1] || void 0 };
|
|
1469
|
+
}
|
|
1470
|
+
const yamlManagedPattern = /^\s*#\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*$/;
|
|
1471
|
+
match = line.match(yamlManagedPattern);
|
|
1472
|
+
if (match) {
|
|
1473
|
+
return { type: "managed", id: match[1] || void 0 };
|
|
1474
|
+
}
|
|
1475
|
+
const yamlSeedPattern = /^\s*#\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*$/;
|
|
1476
|
+
match = line.match(yamlSeedPattern);
|
|
1477
|
+
if (match) {
|
|
1478
|
+
return { type: "seed", id: match[1] || void 0 };
|
|
2450
1479
|
}
|
|
1480
|
+
return null;
|
|
2451
1481
|
}
|
|
2452
|
-
function
|
|
2453
|
-
if (
|
|
2454
|
-
|
|
2455
|
-
warn(
|
|
2456
|
-
" sync\u5B9F\u884C\u5F8C\u306E\u5DEE\u5206\u78BA\u8A8D\u306F `git diff` \u3067\u884C\u3046\u3053\u3068\u3092\u63A8\u5968\u3057\u307E\u3059"
|
|
2457
|
-
);
|
|
2458
|
-
return true;
|
|
1482
|
+
function parseEndMarker(line) {
|
|
1483
|
+
if (/^<!--\s*@einja:managed:end\s*-->$/.test(line)) {
|
|
1484
|
+
return "managed";
|
|
2459
1485
|
}
|
|
2460
|
-
if (
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
1486
|
+
if (/^<!--\s*@einja:seed:end\s*-->$/.test(line)) {
|
|
1487
|
+
return "seed";
|
|
1488
|
+
}
|
|
1489
|
+
if (/^\s*#\s*@einja:managed:end\s*$/.test(line)) {
|
|
1490
|
+
return "managed";
|
|
1491
|
+
}
|
|
1492
|
+
if (/^\s*#\s*@einja:seed:end\s*$/.test(line)) {
|
|
1493
|
+
return "seed";
|
|
1494
|
+
}
|
|
1495
|
+
return null;
|
|
1496
|
+
}
|
|
1497
|
+
function isPathManaged(filePath, keyPath, jsonPaths) {
|
|
1498
|
+
const managedPaths = jsonPaths.managed[filePath] || [];
|
|
1499
|
+
return managedPaths.some(
|
|
1500
|
+
(p) => keyPath === p || keyPath.startsWith(`${p}.`)
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
function isPathSeed(filePath, keyPath, jsonPaths) {
|
|
1504
|
+
const seedPaths = jsonPaths.seed[filePath] || [];
|
|
1505
|
+
return seedPaths.some((p) => keyPath === p || keyPath.startsWith(`${p}.`));
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
// src/utils/placeholder-validator.ts
|
|
1509
|
+
import { readFile } from "fs/promises";
|
|
1510
|
+
import { join as join5 } from "path";
|
|
1511
|
+
var PLACEHOLDER_PATTERNS = [
|
|
1512
|
+
"@repo/",
|
|
1513
|
+
"{{packageName}}",
|
|
1514
|
+
"{{projectName}}",
|
|
1515
|
+
"{{description}}"
|
|
1516
|
+
];
|
|
1517
|
+
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1518
|
+
".png",
|
|
1519
|
+
".jpg",
|
|
1520
|
+
".jpeg",
|
|
1521
|
+
".gif",
|
|
1522
|
+
".ico",
|
|
1523
|
+
".svg",
|
|
1524
|
+
".woff",
|
|
1525
|
+
".woff2",
|
|
1526
|
+
".ttf",
|
|
1527
|
+
".eot",
|
|
1528
|
+
".zip",
|
|
1529
|
+
".tar",
|
|
1530
|
+
".gz",
|
|
1531
|
+
".pdf",
|
|
1532
|
+
".mp4",
|
|
1533
|
+
".mp3"
|
|
1534
|
+
]);
|
|
1535
|
+
var EXCLUDED_DIRS = ["node_modules/", ".git/", "dist/", ".next/"];
|
|
1536
|
+
function isBinaryFile(filePath) {
|
|
1537
|
+
const lastDot = filePath.lastIndexOf(".");
|
|
1538
|
+
if (lastDot === -1) return false;
|
|
1539
|
+
const ext = filePath.substring(lastDot).toLowerCase();
|
|
1540
|
+
return BINARY_EXTENSIONS.has(ext);
|
|
1541
|
+
}
|
|
1542
|
+
function isExcludedDir(filePath) {
|
|
1543
|
+
return EXCLUDED_DIRS.some((dir) => filePath.includes(dir));
|
|
1544
|
+
}
|
|
1545
|
+
async function validatePlaceholders(targetDir, filePaths) {
|
|
1546
|
+
const violations = [];
|
|
1547
|
+
for (const filePath of filePaths) {
|
|
1548
|
+
if (isBinaryFile(filePath) || isExcludedDir(filePath)) {
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1551
|
+
try {
|
|
1552
|
+
const fullPath = join5(targetDir, filePath);
|
|
1553
|
+
const content = await readFile(fullPath, "utf-8");
|
|
1554
|
+
const lines = content.split("\n");
|
|
1555
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1556
|
+
const line = lines[i] ?? "";
|
|
1557
|
+
for (const pattern of PLACEHOLDER_PATTERNS) {
|
|
1558
|
+
if (line.includes(pattern)) {
|
|
1559
|
+
violations.push({
|
|
1560
|
+
filePath,
|
|
1561
|
+
line: i + 1,
|
|
1562
|
+
placeholder: pattern,
|
|
1563
|
+
context: line.trim()
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
} catch {
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return {
|
|
1572
|
+
isValid: violations.length === 0,
|
|
1573
|
+
violations
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// src/utils/project-detector.ts
|
|
1578
|
+
import { readFileSync as readFileSync4, existsSync as existsSync6, readdirSync as readdirSync3, statSync } from "fs";
|
|
1579
|
+
import { join as join6 } from "path";
|
|
1580
|
+
async function detectProjectConfig(targetDir) {
|
|
1581
|
+
let projectName = null;
|
|
1582
|
+
let packageScope = null;
|
|
1583
|
+
const rootPackageJsonPath = join6(targetDir, "package.json");
|
|
1584
|
+
if (existsSync6(rootPackageJsonPath)) {
|
|
1585
|
+
try {
|
|
1586
|
+
const rootPkg = JSON.parse(
|
|
1587
|
+
readFileSync4(rootPackageJsonPath, "utf-8")
|
|
2465
1588
|
);
|
|
2466
|
-
|
|
1589
|
+
if (rootPkg.name) {
|
|
1590
|
+
projectName = rootPkg.name;
|
|
1591
|
+
}
|
|
1592
|
+
} catch {
|
|
2467
1593
|
}
|
|
2468
|
-
warn("\u26A0\uFE0F \u672A\u30B3\u30DF\u30C3\u30C8\u306E\u5909\u66F4\u304C\u3042\u308A\u307E\u3059\u304C\u3001--force \u306B\u3088\u308A\u7D9A\u884C\u3057\u307E\u3059");
|
|
2469
|
-
warn(
|
|
2470
|
-
" \u554F\u984C\u304C\u767A\u751F\u3057\u305F\u5834\u5408\u306F `git checkout .` \u3067\u5FA9\u5143\u3067\u304D\u307E\u3059"
|
|
2471
|
-
);
|
|
2472
1594
|
}
|
|
2473
|
-
|
|
1595
|
+
const candidateDirs = [join6(targetDir, "apps"), join6(targetDir, "packages")];
|
|
1596
|
+
for (const dir of candidateDirs) {
|
|
1597
|
+
if (!existsSync6(dir)) {
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
try {
|
|
1601
|
+
const entries = readdirSync3(dir);
|
|
1602
|
+
for (const entry of entries) {
|
|
1603
|
+
const entryPath = join6(dir, entry);
|
|
1604
|
+
if (!statSync(entryPath).isDirectory()) {
|
|
1605
|
+
continue;
|
|
1606
|
+
}
|
|
1607
|
+
const pkgJsonPath = join6(entryPath, "package.json");
|
|
1608
|
+
if (!existsSync6(pkgJsonPath)) {
|
|
1609
|
+
continue;
|
|
1610
|
+
}
|
|
1611
|
+
try {
|
|
1612
|
+
const pkg = JSON.parse(
|
|
1613
|
+
readFileSync4(pkgJsonPath, "utf-8")
|
|
1614
|
+
);
|
|
1615
|
+
if (pkg.name) {
|
|
1616
|
+
const match = pkg.name.match(/^(@[^/]+)\//);
|
|
1617
|
+
if (match?.[1]) {
|
|
1618
|
+
const detectedScope = match[1];
|
|
1619
|
+
if (packageScope && packageScope !== detectedScope) {
|
|
1620
|
+
console.warn(
|
|
1621
|
+
`\u8B66\u544A: \u7570\u306A\u308B\u30B9\u30B3\u30FC\u30D7\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F\uFF08${packageScope} vs ${detectedScope}\uFF09\u3002\u6700\u521D\u306B\u898B\u3064\u304B\u3063\u305F ${packageScope} \u3092\u4F7F\u7528\u3057\u307E\u3059\u3002`
|
|
1622
|
+
);
|
|
1623
|
+
} else if (!packageScope) {
|
|
1624
|
+
packageScope = detectedScope;
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
} catch {
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
} catch {
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
if (projectName && packageScope) {
|
|
1635
|
+
return { projectName, packageScope };
|
|
1636
|
+
}
|
|
1637
|
+
return null;
|
|
2474
1638
|
}
|
|
2475
1639
|
|
|
2476
1640
|
// src/commands/sync.ts
|
|
@@ -2486,7 +1650,7 @@ async function handleInterrupt() {
|
|
|
2486
1650
|
info("\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u304C\u4F5C\u6210\u3055\u308C\u3066\u3044\u306A\u3044\u305F\u3081\u3001\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306F\u4E0D\u8981\u3067\u3059");
|
|
2487
1651
|
process.exit(0);
|
|
2488
1652
|
}
|
|
2489
|
-
const answer = await
|
|
1653
|
+
const answer = await inquirer5.prompt([
|
|
2490
1654
|
{
|
|
2491
1655
|
type: "confirm",
|
|
2492
1656
|
name: "rollback",
|
|
@@ -2507,22 +1671,22 @@ async function handleInterrupt() {
|
|
|
2507
1671
|
}
|
|
2508
1672
|
process.exit(0);
|
|
2509
1673
|
}
|
|
2510
|
-
function
|
|
2511
|
-
const __filename3 =
|
|
2512
|
-
const __dirname3 =
|
|
2513
|
-
const { existsSync:
|
|
2514
|
-
const distPath =
|
|
2515
|
-
const srcPath =
|
|
2516
|
-
if (
|
|
1674
|
+
function getTemplatePath2() {
|
|
1675
|
+
const __filename3 = fileURLToPath2(import.meta.url);
|
|
1676
|
+
const __dirname3 = dirname5(__filename3);
|
|
1677
|
+
const { existsSync: existsSync7 } = fsExtra3;
|
|
1678
|
+
const distPath = join7(__dirname3, "../templates/default");
|
|
1679
|
+
const srcPath = join7(__dirname3, "../../templates/default");
|
|
1680
|
+
if (existsSync7(distPath)) {
|
|
2517
1681
|
return distPath;
|
|
2518
1682
|
}
|
|
2519
|
-
if (
|
|
1683
|
+
if (existsSync7(srcPath)) {
|
|
2520
1684
|
return srcPath;
|
|
2521
1685
|
}
|
|
2522
1686
|
throw new Error("\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093");
|
|
2523
1687
|
}
|
|
2524
1688
|
async function syncCommand(options) {
|
|
2525
|
-
const { existsSync:
|
|
1689
|
+
const { existsSync: existsSync7 } = fsExtra3;
|
|
2526
1690
|
const sigintHandler = () => {
|
|
2527
1691
|
handleInterrupt().catch((error2) => {
|
|
2528
1692
|
error(`\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u4E2D\u306B\u30A8\u30E9\u30FC: ${error2}`);
|
|
@@ -2554,7 +1718,7 @@ async function syncCommand(options) {
|
|
|
2554
1718
|
}
|
|
2555
1719
|
const targetDir = process.cwd();
|
|
2556
1720
|
checkGitStatusForSync(options.force || false, targetDir);
|
|
2557
|
-
const templatePath =
|
|
1721
|
+
const templatePath = getTemplatePath2();
|
|
2558
1722
|
let categories;
|
|
2559
1723
|
let appsDetail;
|
|
2560
1724
|
let packagesDetail;
|
|
@@ -2599,6 +1763,52 @@ async function syncCommand(options) {
|
|
|
2599
1763
|
return;
|
|
2600
1764
|
}
|
|
2601
1765
|
info(`\u540C\u671F\u5BFE\u8C61: ${filesToSync.length}\u500B\u306E\u30D5\u30A1\u30A4\u30EB`);
|
|
1766
|
+
let templateVariables;
|
|
1767
|
+
info("\u{1F50D} \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u8A2D\u5B9A\u3092\u691C\u51FA\u4E2D...");
|
|
1768
|
+
const detectedConfig = await detectProjectConfig(targetDir);
|
|
1769
|
+
if (detectedConfig) {
|
|
1770
|
+
templateVariables = {
|
|
1771
|
+
projectName: detectedConfig.projectName,
|
|
1772
|
+
packageName: detectedConfig.packageScope,
|
|
1773
|
+
description: `${detectedConfig.projectName} - Einja Management Template`
|
|
1774
|
+
};
|
|
1775
|
+
info(` \u2713 \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D: ${detectedConfig.projectName}`);
|
|
1776
|
+
info(` \u2713 \u30D1\u30C3\u30B1\u30FC\u30B8\u30B9\u30B3\u30FC\u30D7: ${detectedConfig.packageScope}`);
|
|
1777
|
+
} else {
|
|
1778
|
+
warn("\u26A0\uFE0F \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u8A2D\u5B9A\u3092\u81EA\u52D5\u691C\u51FA\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F");
|
|
1779
|
+
const inputAnswers = await inquirer5.prompt([
|
|
1780
|
+
{
|
|
1781
|
+
type: "input",
|
|
1782
|
+
name: "projectName",
|
|
1783
|
+
message: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044:",
|
|
1784
|
+
validate: (input) => {
|
|
1785
|
+
if (!input.trim()) {
|
|
1786
|
+
return "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D\u306F\u5FC5\u9808\u3067\u3059";
|
|
1787
|
+
}
|
|
1788
|
+
return true;
|
|
1789
|
+
}
|
|
1790
|
+
},
|
|
1791
|
+
{
|
|
1792
|
+
type: "input",
|
|
1793
|
+
name: "packageScope",
|
|
1794
|
+
message: "\u30D1\u30C3\u30B1\u30FC\u30B8\u30B9\u30B3\u30FC\u30D7\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: @mycompany\uFF09:",
|
|
1795
|
+
validate: (input) => {
|
|
1796
|
+
if (!input.trim()) {
|
|
1797
|
+
return "\u30D1\u30C3\u30B1\u30FC\u30B8\u30B9\u30B3\u30FC\u30D7\u306F\u5FC5\u9808\u3067\u3059";
|
|
1798
|
+
}
|
|
1799
|
+
if (!input.startsWith("@")) {
|
|
1800
|
+
return "\u30D1\u30C3\u30B1\u30FC\u30B8\u30B9\u30B3\u30FC\u30D7\u306F @ \u3067\u59CB\u307E\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u4F8B: @mycompany\uFF09";
|
|
1801
|
+
}
|
|
1802
|
+
return true;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
]);
|
|
1806
|
+
templateVariables = {
|
|
1807
|
+
projectName: inputAnswers.projectName,
|
|
1808
|
+
packageName: inputAnswers.packageScope,
|
|
1809
|
+
description: `${inputAnswers.projectName} - Einja Management Template`
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
2602
1812
|
if (options.dryRun) {
|
|
2603
1813
|
info("\n\u{1F4CB} \u540C\u671F\u30D7\u30EC\u30D3\u30E5\u30FC (--dry-run)\n");
|
|
2604
1814
|
for (const file of filesToSync) {
|
|
@@ -2612,7 +1822,7 @@ async function syncCommand(options) {
|
|
|
2612
1822
|
let backupDir;
|
|
2613
1823
|
if (options.backup !== false) {
|
|
2614
1824
|
info("\u{1F4BE} \u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u4F5C\u6210\u4E2D...");
|
|
2615
|
-
const existingFiles = filesToSync.filter((file) =>
|
|
1825
|
+
const existingFiles = filesToSync.filter((file) => existsSync7(join7(targetDir, file)));
|
|
2616
1826
|
if (existingFiles.length > 0) {
|
|
2617
1827
|
backupDir = await createBackup(targetDir, existingFiles);
|
|
2618
1828
|
currentBackupDir = backupDir;
|
|
@@ -2641,9 +1851,9 @@ async function syncCommand(options) {
|
|
|
2641
1851
|
};
|
|
2642
1852
|
for (const file of filesToSync) {
|
|
2643
1853
|
try {
|
|
2644
|
-
const sourcePath =
|
|
2645
|
-
const targetPath =
|
|
2646
|
-
if (!
|
|
1854
|
+
const sourcePath = join7(templatePath, file);
|
|
1855
|
+
const targetPath = join7(targetDir, file);
|
|
1856
|
+
if (!existsSync7(sourcePath)) {
|
|
2647
1857
|
warn(`\u30B9\u30AD\u30C3\u30D7: ${file} (\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u305B\u3093)`);
|
|
2648
1858
|
result.skipped++;
|
|
2649
1859
|
result.files.push({
|
|
@@ -2657,7 +1867,9 @@ async function syncCommand(options) {
|
|
|
2657
1867
|
sourcePath,
|
|
2658
1868
|
targetPath,
|
|
2659
1869
|
syncMetadata,
|
|
2660
|
-
packageJsonSections
|
|
1870
|
+
packageJsonSections,
|
|
1871
|
+
conflictStrategy,
|
|
1872
|
+
templateVariables
|
|
2661
1873
|
);
|
|
2662
1874
|
const mappedAction = mergeResult.action === "created" || mergeResult.action === "overwritten" ? "copied" : mergeResult.action;
|
|
2663
1875
|
result.success++;
|
|
@@ -2676,6 +1888,16 @@ async function syncCommand(options) {
|
|
|
2676
1888
|
error(` \u2717 ${file}: ${error2}`);
|
|
2677
1889
|
}
|
|
2678
1890
|
}
|
|
1891
|
+
const syncedFiles = result.files.filter((f) => f.action === "copied" || f.action === "merged").map((f) => f.path);
|
|
1892
|
+
if (syncedFiles.length > 0) {
|
|
1893
|
+
const validation = await validatePlaceholders(targetDir, syncedFiles);
|
|
1894
|
+
if (!validation.isValid) {
|
|
1895
|
+
warn("\n\u26A0 \u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u5909\u6570\u306E\u7F6E\u63DB\u6F0F\u308C\u3092\u691C\u51FA:");
|
|
1896
|
+
for (const v of validation.violations) {
|
|
1897
|
+
warn(` ${v.filePath}:${v.line} \u2014 ${v.placeholder}`);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
2679
1901
|
info("\n\u{1F4CA} \u540C\u671F\u7D50\u679C:");
|
|
2680
1902
|
info(` \u6210\u529F: ${result.success}\u30D5\u30A1\u30A4\u30EB`);
|
|
2681
1903
|
if (result.skipped > 0) {
|
|
@@ -2706,9 +1928,9 @@ async function syncCommand(options) {
|
|
|
2706
1928
|
}
|
|
2707
1929
|
|
|
2708
1930
|
// src/cli.ts
|
|
2709
|
-
var __filename2 =
|
|
2710
|
-
var __dirname2 =
|
|
2711
|
-
var packageJsonPath =
|
|
1931
|
+
var __filename2 = fileURLToPath3(import.meta.url);
|
|
1932
|
+
var __dirname2 = dirname6(__filename2);
|
|
1933
|
+
var packageJsonPath = join8(__dirname2, "../package.json");
|
|
2712
1934
|
var packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
|
|
2713
1935
|
var program = new Command();
|
|
2714
1936
|
program.name("create-einja-app").description("CLI tool to create new projects with Einja Management Template").version(packageJson.version);
|
|
@@ -2717,17 +1939,6 @@ program.argument("[project-name]", "Project name").option("--skip-git", "Skip gi
|
|
|
2717
1939
|
await createCommand(projectName, options);
|
|
2718
1940
|
}
|
|
2719
1941
|
);
|
|
2720
|
-
program.command("setup").description("Setup tools for existing project").action(async () => {
|
|
2721
|
-
await setupCommand();
|
|
2722
|
-
});
|
|
2723
|
-
program.command("add").description("Add einja components to existing monorepo").option("--all", "Select all components").option("--dry-run", "Preview changes without making them").action(
|
|
2724
|
-
async (options) => {
|
|
2725
|
-
await addCommand({
|
|
2726
|
-
skipPrompts: options.all || false,
|
|
2727
|
-
dryRun: options.dryRun || false
|
|
2728
|
-
});
|
|
2729
|
-
}
|
|
2730
|
-
);
|
|
2731
1942
|
program.command("sync").description("Sync template files to existing project").option("--categories <categories>", "Comma-separated list of categories to sync").option("--all", "Sync all categories").option("--dry-run", "Preview changes without making them").option("--backup", "Create backup before syncing (default: true)", true).option("--rollback", "Rollback to previous backup").option("--force", "Force sync even with uncommitted changes").action(
|
|
2732
1943
|
async (options) => {
|
|
2733
1944
|
await syncCommand({
|