create-einja-app 0.1.2 → 0.2.2
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 +80 -1
- package/dist/cli.js +875 -23
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/.env.development +25 -0
- package/templates/default/.env.local +24 -0
- package/templates/default/.env.production +25 -0
- package/templates/default/.env.staging +25 -0
- package/templates/default/.github/dependabot.yml +63 -0
- package/templates/default/.github/workflows/ci.yml +52 -0
- package/templates/default/.github/workflows/claude.yml +38 -0
- package/templates/default/.husky/_/applypatch-msg +2 -0
- package/templates/default/.husky/_/commit-msg +2 -0
- package/templates/default/.husky/_/gitignore +1 -0
- package/templates/default/.husky/_/h +22 -0
- package/templates/default/.husky/_/husky.sh +9 -0
- package/templates/default/.husky/_/post-applypatch +2 -0
- package/templates/default/.husky/_/post-checkout +2 -0
- package/templates/default/.husky/_/post-commit +2 -0
- package/templates/default/.husky/_/post-merge +2 -0
- package/templates/default/.husky/_/post-rewrite +2 -0
- package/templates/default/.husky/_/pre-applypatch +2 -0
- package/templates/default/.husky/_/pre-auto-gc +2 -0
- package/templates/default/.husky/_/pre-commit +2 -0
- package/templates/default/.husky/_/pre-merge-commit +2 -0
- package/templates/default/.husky/_/pre-push +2 -0
- package/templates/default/.husky/_/pre-rebase +2 -0
- package/templates/default/.husky/_/prepare-commit-msg +2 -0
- package/templates/default/.serena/gitignore +1 -0
- package/templates/default/.serena/project.yml +84 -0
- package/templates/default/CLAUDE.md +27 -0
- package/templates/default/README.md +150 -45
- package/templates/default/apps/web/server/presentation/routes/userRoutes.ts +2 -5
- package/templates/default/apps/web/src/app/(authenticated)/data/_components/UserTable.tsx +110 -113
- package/templates/default/apps/web/src/app/(authenticated)/data/_components/UserTableContainer.tsx +5 -17
- package/templates/default/apps/web/src/app/(authenticated)/data/page.tsx +9 -7
- package/templates/default/apps/web/src/app/api/rpc/[[...route]]/route.ts +1 -1
- package/templates/default/apps/web/src/hooks/api/prefetch-users.ts +63 -0
- package/templates/default/apps/web/src/hooks/{use-users.ts → api/use-users.ts} +11 -46
- package/templates/default/apps/web/src/lib/api/parse-response.ts +114 -0
- package/templates/default/apps/web/src/shared/schemas/user.ts +36 -0
- package/templates/default/gitignore +86 -0
- package/templates/default/package.json +1 -1
- package/templates/default/worktree.config.json +14 -0
- package/templates/default/.templateignore +0 -60
- package/templates/default/middleware.ts +0 -32
- package/templates/default/apps/web/src/lib/{api-client.ts → api/client.ts} +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import { readFileSync as
|
|
6
|
-
import { fileURLToPath as
|
|
7
|
-
import { dirname as
|
|
5
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
6
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7
|
+
import { dirname as dirname5, join as join14 } from "path";
|
|
8
8
|
|
|
9
9
|
// src/commands/create.ts
|
|
10
|
-
import { existsSync as existsSync3 } from "fs";
|
|
10
|
+
import { existsSync as existsSync3, readdirSync } from "fs";
|
|
11
11
|
import { resolve } from "path";
|
|
12
12
|
import ora2 from "ora";
|
|
13
13
|
|
|
@@ -28,6 +28,12 @@ async function promptProjectConfig(defaultProjectName) {
|
|
|
28
28
|
return true;
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
+
{
|
|
32
|
+
type: "confirm",
|
|
33
|
+
name: "useCurrentDir",
|
|
34
|
+
message: "\u4ECA\u3044\u308B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u76F4\u63A5\u4F5C\u6210\u3057\u307E\u3059\u304B\uFF1F\uFF08No\u306A\u3089\u30B5\u30D6\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u4F5C\u6210\uFF09",
|
|
35
|
+
default: false
|
|
36
|
+
},
|
|
31
37
|
{
|
|
32
38
|
type: "input",
|
|
33
39
|
name: "packageScope",
|
|
@@ -54,7 +60,7 @@ async function promptProjectConfig(defaultProjectName) {
|
|
|
54
60
|
{
|
|
55
61
|
type: "confirm",
|
|
56
62
|
name: "setupEinjaCli",
|
|
57
|
-
message: "@einja/cli \u3092\u81EA\u52D5\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u307E\u3059\u304B\uFF1F",
|
|
63
|
+
message: "@einja/dev-cli \u3092\u81EA\u52D5\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u307E\u3059\u304B\uFF1F",
|
|
58
64
|
default: true
|
|
59
65
|
},
|
|
60
66
|
{
|
|
@@ -147,7 +153,8 @@ async function promptProjectConfig(defaultProjectName) {
|
|
|
147
153
|
authMethod: answers.authMethod,
|
|
148
154
|
tools,
|
|
149
155
|
setupEinjaCli: answers.setupEinjaCli,
|
|
150
|
-
worktreeConfig
|
|
156
|
+
worktreeConfig,
|
|
157
|
+
useCurrentDir: answers.useCurrentDir
|
|
151
158
|
};
|
|
152
159
|
}
|
|
153
160
|
|
|
@@ -295,6 +302,21 @@ function renameTemplateFiles(targetPath) {
|
|
|
295
302
|
removeSync(file);
|
|
296
303
|
}
|
|
297
304
|
}
|
|
305
|
+
function renameSpecialFiles(targetPath) {
|
|
306
|
+
const gitignoreFiles = glob.sync("**/gitignore", {
|
|
307
|
+
cwd: targetPath,
|
|
308
|
+
absolute: true,
|
|
309
|
+
dot: true
|
|
310
|
+
});
|
|
311
|
+
for (const file of gitignoreFiles) {
|
|
312
|
+
const dir = dirname2(file);
|
|
313
|
+
const newPath = join2(dir, ".gitignore");
|
|
314
|
+
if (existsSync2(file)) {
|
|
315
|
+
copySync(file, newPath);
|
|
316
|
+
removeSync(file);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
298
320
|
function excludeAuthFiles(targetPath, authMethod) {
|
|
299
321
|
const excludePatterns = getAuthExcludePatterns(authMethod);
|
|
300
322
|
if (excludePatterns.length === 0) {
|
|
@@ -331,7 +353,7 @@ async function generateTemplate(config, targetPath) {
|
|
|
331
353
|
"dist",
|
|
332
354
|
"logs",
|
|
333
355
|
".env",
|
|
334
|
-
|
|
356
|
+
// フェイルセーフ(暗号化キーファイル)
|
|
335
357
|
".DS_Store",
|
|
336
358
|
"Thumbs.db",
|
|
337
359
|
"coverage"
|
|
@@ -349,6 +371,7 @@ async function generateTemplate(config, targetPath) {
|
|
|
349
371
|
});
|
|
350
372
|
excludeAuthFiles(targetPath, config.authMethod);
|
|
351
373
|
renameTemplateFiles(targetPath);
|
|
374
|
+
renameSpecialFiles(targetPath);
|
|
352
375
|
info("\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u5909\u6570\u3092\u7F6E\u63DB\u4E2D...");
|
|
353
376
|
const variables = {
|
|
354
377
|
projectName: config.projectName,
|
|
@@ -413,10 +436,15 @@ function printCompletionMessage(config) {
|
|
|
413
436
|
console.log(chalk2.bold("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:"));
|
|
414
437
|
console.log();
|
|
415
438
|
console.log(chalk2.cyan(` cd ${config.projectName}`));
|
|
439
|
+
console.log(chalk2.cyan(" pnpm env:update # \u74B0\u5883\u5909\u6570\u3092\u8A2D\u5B9A"));
|
|
416
440
|
console.log(chalk2.cyan(" docker-compose up -d postgres"));
|
|
417
441
|
console.log(chalk2.cyan(" pnpm dev"));
|
|
418
442
|
console.log();
|
|
419
|
-
console.log(chalk2.gray("\u958B\u767A\u30B5\u30FC\u30D0\u30FC:
|
|
443
|
+
console.log(chalk2.gray("\u958B\u767A\u30B5\u30FC\u30D0\u30FC: \u30BF\u30FC\u30DF\u30CA\u30EB\u306B\u8868\u793A\u3055\u308C\u308BURL\u3092\u78BA\u8A8D"));
|
|
444
|
+
console.log();
|
|
445
|
+
console.log(
|
|
446
|
+
chalk2.yellow("\u26A0 \u91CD\u8981: ") + chalk2.gray("pnpm env:update \u3067\u74B0\u5883\u5909\u6570\u3092\u81EA\u5206\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u7528\u306B\u518D\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044")
|
|
447
|
+
);
|
|
420
448
|
console.log();
|
|
421
449
|
console.log(chalk2.gray("\u8A73\u7D30\u306F README.md \u3092\u3054\u78BA\u8A8D\u304F\u3060\u3055\u3044\u3002"));
|
|
422
450
|
console.log();
|
|
@@ -457,19 +485,29 @@ async function execPostSetup(config, targetPath, options) {
|
|
|
457
485
|
await promptAndExecuteDirenvAllow(targetPath);
|
|
458
486
|
}
|
|
459
487
|
if (config.setupEinjaCli) {
|
|
460
|
-
const einjaSpinner = ora("@einja/cli \u3092\u521D\u671F\u5316\u4E2D...").start();
|
|
488
|
+
const einjaSpinner = ora("@einja/dev-cli \u3092\u521D\u671F\u5316\u4E2D...").start();
|
|
461
489
|
try {
|
|
462
|
-
await execa("npx", ["@einja/cli", "init"], { cwd: targetPath });
|
|
463
|
-
einjaSpinner.succeed("@einja/cli \u3092\u521D\u671F\u5316\u3057\u307E\u3057\u305F");
|
|
490
|
+
await execa("npx", ["@einja/dev-cli", "init", "--force"], { cwd: targetPath });
|
|
491
|
+
einjaSpinner.succeed("@einja/dev-cli \u3092\u521D\u671F\u5316\u3057\u307E\u3057\u305F");
|
|
464
492
|
} catch (error2) {
|
|
465
|
-
einjaSpinner.fail("@einja/cli \u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
466
|
-
warn("\u5F8C\u3067\u624B\u52D5\u3067 'npx @einja/cli init' \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
493
|
+
einjaSpinner.fail("@einja/dev-cli \u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
494
|
+
warn("\u5F8C\u3067\u624B\u52D5\u3067 'npx @einja/dev-cli init' \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
467
495
|
}
|
|
468
496
|
}
|
|
469
497
|
printCompletionMessage(config);
|
|
470
498
|
}
|
|
471
499
|
|
|
472
500
|
// src/commands/create.ts
|
|
501
|
+
function isDirectoryEmpty(dirPath) {
|
|
502
|
+
if (!existsSync3(dirPath)) {
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
const files = readdirSync(dirPath);
|
|
506
|
+
const significantFiles = files.filter(
|
|
507
|
+
(f) => !f.startsWith(".")
|
|
508
|
+
);
|
|
509
|
+
return significantFiles.length === 0;
|
|
510
|
+
}
|
|
473
511
|
function validateProjectName(projectName) {
|
|
474
512
|
const regex = /^[a-zA-Z][a-zA-Z0-9_-]{0,49}$/;
|
|
475
513
|
if (!regex.test(projectName)) {
|
|
@@ -502,7 +540,8 @@ async function createCommand(projectName, options) {
|
|
|
502
540
|
husky: true
|
|
503
541
|
},
|
|
504
542
|
setupEinjaCli: true,
|
|
505
|
-
worktreeConfig: void 0
|
|
543
|
+
worktreeConfig: void 0,
|
|
544
|
+
useCurrentDir: false
|
|
506
545
|
};
|
|
507
546
|
info(`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D: ${config.projectName}`);
|
|
508
547
|
info(`\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8: ${config.template}`);
|
|
@@ -510,11 +549,19 @@ async function createCommand(projectName, options) {
|
|
|
510
549
|
} else {
|
|
511
550
|
config = await promptProjectConfig(projectName);
|
|
512
551
|
}
|
|
513
|
-
const targetPath = resolve(process.cwd(), config.projectName);
|
|
514
|
-
if (
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
552
|
+
const targetPath = config.useCurrentDir ? process.cwd() : resolve(process.cwd(), config.projectName);
|
|
553
|
+
if (config.useCurrentDir) {
|
|
554
|
+
if (!isDirectoryEmpty(targetPath)) {
|
|
555
|
+
error("\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u3059");
|
|
556
|
+
info("\u7A7A\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u5B9F\u884C\u3059\u308B\u304B\u3001\u30B5\u30D6\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u4F5C\u6210\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
if (checkProjectExists(targetPath)) {
|
|
561
|
+
error(`\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA '${config.projectName}' \u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059`);
|
|
562
|
+
info("\u5225\u306E\u540D\u524D\u3092\u6307\u5B9A\u3059\u308B\u304B\u3001\u65E2\u5B58\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
563
|
+
process.exit(1);
|
|
564
|
+
}
|
|
518
565
|
}
|
|
519
566
|
const spinner = ora2("\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4F5C\u6210\u4E2D...").start();
|
|
520
567
|
try {
|
|
@@ -984,11 +1031,808 @@ async function setupCommand() {
|
|
|
984
1031
|
success("\u958B\u767A\u3092\u958B\u59CB\u3067\u304D\u307E\u3059\uFF01");
|
|
985
1032
|
}
|
|
986
1033
|
|
|
1034
|
+
// src/commands/add.ts
|
|
1035
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1036
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1037
|
+
import { dirname as dirname4, join as join13 } from "path";
|
|
1038
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1039
|
+
import ora4 from "ora";
|
|
1040
|
+
|
|
1041
|
+
// src/prompts/add.ts
|
|
1042
|
+
import inquirer5 from "inquirer";
|
|
1043
|
+
function getDefaultAddConfig() {
|
|
1044
|
+
return {
|
|
1045
|
+
components: {
|
|
1046
|
+
packages: true,
|
|
1047
|
+
apps: true,
|
|
1048
|
+
config: true
|
|
1049
|
+
},
|
|
1050
|
+
packageComponents: ["front-core", "server-core", "config", "ui"],
|
|
1051
|
+
appComponents: ["web"],
|
|
1052
|
+
dryRun: false
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
async function promptAddConfig(dryRun) {
|
|
1056
|
+
const componentAnswers = await inquirer5.prompt([
|
|
1057
|
+
{
|
|
1058
|
+
type: "checkbox",
|
|
1059
|
+
name: "components",
|
|
1060
|
+
message: "\u8FFD\u52A0\u3059\u308B\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u3092\u9078\u629E\uFF08Space\u3067\u9078\u629E\u3001Enter\u3067\u78BA\u5B9A\uFF09:",
|
|
1061
|
+
choices: [
|
|
1062
|
+
{
|
|
1063
|
+
name: "packages/ - \u5171\u901A\u30D1\u30C3\u30B1\u30FC\u30B8\uFF08front-core, server-core, config, ui\uFF09",
|
|
1064
|
+
value: "packages",
|
|
1065
|
+
checked: true
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
name: "apps/ - \u30A2\u30D7\u30EA\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8",
|
|
1069
|
+
value: "apps",
|
|
1070
|
+
checked: true
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
name: "\u76F4\u4E0B\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB - turbo.json, pnpm-workspace.yaml \u7B49",
|
|
1074
|
+
value: "config",
|
|
1075
|
+
checked: true
|
|
1076
|
+
}
|
|
1077
|
+
]
|
|
1078
|
+
}
|
|
1079
|
+
]);
|
|
1080
|
+
const selectedComponents = componentAnswers.components;
|
|
1081
|
+
const hasPackages = selectedComponents.includes("packages");
|
|
1082
|
+
const hasApps = selectedComponents.includes("apps");
|
|
1083
|
+
const hasConfig = selectedComponents.includes("config");
|
|
1084
|
+
let packageComponents = [];
|
|
1085
|
+
if (hasPackages) {
|
|
1086
|
+
const packageAnswers = await inquirer5.prompt([
|
|
1087
|
+
{
|
|
1088
|
+
type: "checkbox",
|
|
1089
|
+
name: "packages",
|
|
1090
|
+
message: "\u8FFD\u52A0\u3059\u308B\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E:",
|
|
1091
|
+
choices: [
|
|
1092
|
+
{
|
|
1093
|
+
name: "front-core - \u30D5\u30ED\u30F3\u30C8\u30A8\u30F3\u30C9\u5171\u901A\u5C64\uFF08\u8A8D\u8A3C\u8A2D\u5B9A\u3001hooks\u3001utils\uFF09",
|
|
1094
|
+
value: "front-core",
|
|
1095
|
+
checked: true
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
name: "server-core - \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u5171\u901A\u5C64\uFF08Prisma\u3001\u30C9\u30E1\u30A4\u30F3\u30ED\u30B8\u30C3\u30AF\uFF09",
|
|
1099
|
+
value: "server-core",
|
|
1100
|
+
checked: true
|
|
1101
|
+
},
|
|
1102
|
+
{
|
|
1103
|
+
name: "config - \u5171\u901A\u8A2D\u5B9A\uFF08Biome, TypeScript, Panda CSS\uFF09",
|
|
1104
|
+
value: "config",
|
|
1105
|
+
checked: true
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
name: "ui - \u5171\u901AUI\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\uFF08shadcn/ui\uFF09",
|
|
1109
|
+
value: "ui",
|
|
1110
|
+
checked: true
|
|
1111
|
+
}
|
|
1112
|
+
],
|
|
1113
|
+
validate: (input) => {
|
|
1114
|
+
if (input.length === 0) {
|
|
1115
|
+
return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
|
|
1116
|
+
}
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
]);
|
|
1121
|
+
packageComponents = packageAnswers.packages;
|
|
1122
|
+
}
|
|
1123
|
+
let appComponents = [];
|
|
1124
|
+
if (hasApps) {
|
|
1125
|
+
const appAnswers = await inquirer5.prompt([
|
|
1126
|
+
{
|
|
1127
|
+
type: "checkbox",
|
|
1128
|
+
name: "apps",
|
|
1129
|
+
message: "\u8FFD\u52A0\u3059\u308B\u30A2\u30D7\u30EA\u3092\u9078\u629E:",
|
|
1130
|
+
choices: [
|
|
1131
|
+
{
|
|
1132
|
+
name: "web - \u30E1\u30A4\u30F3\u7BA1\u7406\u753B\u9762\u30A2\u30D7\u30EA\uFF08Next.js + App Router\uFF09",
|
|
1133
|
+
value: "web",
|
|
1134
|
+
checked: true
|
|
1135
|
+
}
|
|
1136
|
+
],
|
|
1137
|
+
validate: (input) => {
|
|
1138
|
+
if (input.length === 0) {
|
|
1139
|
+
return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30A2\u30D7\u30EA\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
|
|
1140
|
+
}
|
|
1141
|
+
return true;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
]);
|
|
1145
|
+
appComponents = appAnswers.apps;
|
|
1146
|
+
}
|
|
1147
|
+
return {
|
|
1148
|
+
components: {
|
|
1149
|
+
packages: hasPackages,
|
|
1150
|
+
apps: hasApps,
|
|
1151
|
+
config: hasConfig
|
|
1152
|
+
},
|
|
1153
|
+
packageComponents,
|
|
1154
|
+
appComponents,
|
|
1155
|
+
dryRun
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// src/generators/partials/packages.ts
|
|
1160
|
+
import { readdir } from "fs/promises";
|
|
1161
|
+
import { join as join10 } from "path";
|
|
1162
|
+
|
|
1163
|
+
// src/utils/merger.ts
|
|
1164
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
|
|
1165
|
+
import { dirname as dirname3 } from "path";
|
|
1166
|
+
function mergeTextWithMarkers(templateContent, existingContent) {
|
|
1167
|
+
if (existingContent === null) {
|
|
1168
|
+
return templateContent;
|
|
1169
|
+
}
|
|
1170
|
+
const templateSections = parseMarkers(templateContent);
|
|
1171
|
+
const localSections = parseMarkers(existingContent);
|
|
1172
|
+
const hasMarkers = templateSections.some(
|
|
1173
|
+
(s) => s.type === "managed" || s.type === "seed"
|
|
1174
|
+
);
|
|
1175
|
+
if (!hasMarkers) {
|
|
1176
|
+
return existingContent;
|
|
1177
|
+
}
|
|
1178
|
+
const templateManagedById = /* @__PURE__ */ new Map();
|
|
1179
|
+
const templateSeedById = /* @__PURE__ */ new Map();
|
|
1180
|
+
const processedTemplateIds = /* @__PURE__ */ new Set();
|
|
1181
|
+
for (const section of templateSections) {
|
|
1182
|
+
if (section.type === "managed" && section.id) {
|
|
1183
|
+
templateManagedById.set(section.id, section);
|
|
1184
|
+
} else if (section.type === "seed" && section.id) {
|
|
1185
|
+
templateSeedById.set(section.id, section);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
const result = [];
|
|
1189
|
+
for (const localSection of localSections) {
|
|
1190
|
+
if (localSection.type === "managed") {
|
|
1191
|
+
const match = localSection.id ? templateManagedById.get(localSection.id) : void 0;
|
|
1192
|
+
if (localSection.id && match) {
|
|
1193
|
+
processedTemplateIds.add(localSection.id);
|
|
1194
|
+
result.push(match.content);
|
|
1195
|
+
} else if (!localSection.id) {
|
|
1196
|
+
result.push(localSection.content);
|
|
1197
|
+
}
|
|
1198
|
+
} else if (localSection.type === "seed") {
|
|
1199
|
+
if (localSection.id) {
|
|
1200
|
+
processedTemplateIds.add(localSection.id);
|
|
1201
|
+
}
|
|
1202
|
+
result.push(localSection.content);
|
|
1203
|
+
} else {
|
|
1204
|
+
result.push(localSection.content);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
for (const [id, section] of templateManagedById) {
|
|
1208
|
+
if (!processedTemplateIds.has(id)) {
|
|
1209
|
+
result.push(section.content);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
for (const [id, section] of templateSeedById) {
|
|
1213
|
+
if (!processedTemplateIds.has(id)) {
|
|
1214
|
+
result.push(section.content);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
const firstElement = result[0];
|
|
1218
|
+
if (result.length > 0 && firstElement !== void 0 && firstElement.length === 0) {
|
|
1219
|
+
result.shift();
|
|
1220
|
+
}
|
|
1221
|
+
return result.join("\n");
|
|
1222
|
+
}
|
|
1223
|
+
function mergeJson(templateJson, existingJson, jsonPaths, filePath = "package.json") {
|
|
1224
|
+
if (existingJson === null) {
|
|
1225
|
+
return JSON.parse(JSON.stringify(templateJson));
|
|
1226
|
+
}
|
|
1227
|
+
return deepMergeWithPaths(
|
|
1228
|
+
templateJson,
|
|
1229
|
+
existingJson,
|
|
1230
|
+
jsonPaths,
|
|
1231
|
+
filePath,
|
|
1232
|
+
""
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
function deepMergeWithPaths(template, existing, jsonPaths, filePath, currentPath) {
|
|
1236
|
+
const result = JSON.parse(JSON.stringify(existing));
|
|
1237
|
+
for (const [key, templateValue] of Object.entries(template)) {
|
|
1238
|
+
const keyPath = currentPath ? `${currentPath}.${key}` : key;
|
|
1239
|
+
const existingValue = existing[key];
|
|
1240
|
+
if (isPathManaged(filePath, keyPath, jsonPaths)) {
|
|
1241
|
+
result[key] = deepClone(templateValue);
|
|
1242
|
+
} else if (isPathSeed(filePath, keyPath, jsonPaths)) {
|
|
1243
|
+
if (typeof templateValue === "object" && templateValue !== null && !Array.isArray(templateValue) && typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) {
|
|
1244
|
+
result[key] = deepMergeWithPaths(
|
|
1245
|
+
templateValue,
|
|
1246
|
+
existingValue,
|
|
1247
|
+
jsonPaths,
|
|
1248
|
+
filePath,
|
|
1249
|
+
keyPath
|
|
1250
|
+
);
|
|
1251
|
+
} else if (!(key in existing)) {
|
|
1252
|
+
result[key] = deepClone(templateValue);
|
|
1253
|
+
}
|
|
1254
|
+
} else if (typeof templateValue === "object" && templateValue !== null && !Array.isArray(templateValue) && typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) {
|
|
1255
|
+
result[key] = deepMergeWithPaths(
|
|
1256
|
+
templateValue,
|
|
1257
|
+
existingValue,
|
|
1258
|
+
jsonPaths,
|
|
1259
|
+
filePath,
|
|
1260
|
+
keyPath
|
|
1261
|
+
);
|
|
1262
|
+
} else if (!(key in existing)) {
|
|
1263
|
+
result[key] = deepClone(templateValue);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
return result;
|
|
1267
|
+
}
|
|
1268
|
+
function deepClone(value) {
|
|
1269
|
+
if (value === void 0) {
|
|
1270
|
+
return void 0;
|
|
1271
|
+
}
|
|
1272
|
+
return JSON.parse(JSON.stringify(value));
|
|
1273
|
+
}
|
|
1274
|
+
async function loadSyncMetadata(targetDir) {
|
|
1275
|
+
const metadataPath = `${targetDir}/.einja-sync.json`;
|
|
1276
|
+
if (!existsSync5(metadataPath)) {
|
|
1277
|
+
return null;
|
|
1278
|
+
}
|
|
1279
|
+
try {
|
|
1280
|
+
const content = readFileSync4(metadataPath, "utf-8");
|
|
1281
|
+
return JSON.parse(content);
|
|
1282
|
+
} catch {
|
|
1283
|
+
return null;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
async function saveSyncMetadata(targetDir, metadata) {
|
|
1287
|
+
const metadataPath = `${targetDir}/.einja-sync.json`;
|
|
1288
|
+
ensureDir(dirname3(metadataPath));
|
|
1289
|
+
writeFileSync5(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
1290
|
+
}
|
|
1291
|
+
async function mergeAndWriteFile(templatePath, targetPath, syncMetadata) {
|
|
1292
|
+
const templateContent = readFileSync4(templatePath, "utf-8");
|
|
1293
|
+
const targetExists = existsSync5(targetPath);
|
|
1294
|
+
const existingContent = targetExists ? readFileSync4(targetPath, "utf-8") : null;
|
|
1295
|
+
const isJsonFile = targetPath.endsWith(".json");
|
|
1296
|
+
let mergedContent;
|
|
1297
|
+
let action;
|
|
1298
|
+
if (!targetExists) {
|
|
1299
|
+
mergedContent = templateContent;
|
|
1300
|
+
action = "created";
|
|
1301
|
+
} else if (isJsonFile) {
|
|
1302
|
+
try {
|
|
1303
|
+
const templateJson = JSON.parse(templateContent);
|
|
1304
|
+
const existingJson = existingContent ? JSON.parse(existingContent) : null;
|
|
1305
|
+
const jsonPaths = syncMetadata.jsonPaths || { managed: {}, seed: {} };
|
|
1306
|
+
const fileName = targetPath.split("/").pop() || "package.json";
|
|
1307
|
+
const mergedJson = mergeJson(templateJson, existingJson, jsonPaths, fileName);
|
|
1308
|
+
mergedContent = JSON.stringify(mergedJson, null, 2);
|
|
1309
|
+
action = "merged";
|
|
1310
|
+
} catch {
|
|
1311
|
+
mergedContent = templateContent;
|
|
1312
|
+
action = "overwritten";
|
|
1313
|
+
}
|
|
1314
|
+
} else {
|
|
1315
|
+
mergedContent = mergeTextWithMarkers(templateContent, existingContent);
|
|
1316
|
+
if (mergedContent === existingContent) {
|
|
1317
|
+
action = "skipped";
|
|
1318
|
+
} else {
|
|
1319
|
+
action = "merged";
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
if (action !== "skipped") {
|
|
1323
|
+
ensureDir(dirname3(targetPath));
|
|
1324
|
+
writeFileSync5(targetPath, mergedContent, "utf-8");
|
|
1325
|
+
}
|
|
1326
|
+
return { action, path: targetPath };
|
|
1327
|
+
}
|
|
1328
|
+
function parseMarkers(content) {
|
|
1329
|
+
const lines = content.split("\n");
|
|
1330
|
+
const sections = [];
|
|
1331
|
+
let currentType = "unmanaged";
|
|
1332
|
+
let currentStartLine = 1;
|
|
1333
|
+
let currentContent = [];
|
|
1334
|
+
let currentId;
|
|
1335
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1336
|
+
const line = lines[i];
|
|
1337
|
+
const lineNumber = i + 1;
|
|
1338
|
+
const startMarker = parseStartMarker(line);
|
|
1339
|
+
if (startMarker) {
|
|
1340
|
+
if (currentType !== "unmanaged") {
|
|
1341
|
+
currentContent.push(line);
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
if (currentContent.length > 0 || sections.length === 0) {
|
|
1345
|
+
sections.push({
|
|
1346
|
+
type: "unmanaged",
|
|
1347
|
+
startLine: currentStartLine,
|
|
1348
|
+
endLine: lineNumber - 1,
|
|
1349
|
+
content: currentContent.join("\n")
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
currentType = startMarker.type;
|
|
1353
|
+
currentId = startMarker.id;
|
|
1354
|
+
currentStartLine = lineNumber;
|
|
1355
|
+
currentContent = [line];
|
|
1356
|
+
} else if (parseEndMarker(line)) {
|
|
1357
|
+
if (currentType === "unmanaged") {
|
|
1358
|
+
currentContent.push(line);
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
currentContent.push(line);
|
|
1362
|
+
sections.push({
|
|
1363
|
+
type: currentType,
|
|
1364
|
+
startLine: currentStartLine,
|
|
1365
|
+
endLine: lineNumber,
|
|
1366
|
+
content: currentContent.join("\n"),
|
|
1367
|
+
id: currentId
|
|
1368
|
+
});
|
|
1369
|
+
currentType = "unmanaged";
|
|
1370
|
+
currentId = void 0;
|
|
1371
|
+
currentStartLine = lineNumber + 1;
|
|
1372
|
+
currentContent = [];
|
|
1373
|
+
} else {
|
|
1374
|
+
currentContent.push(line);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
if (currentContent.length > 0 || sections.length === 0) {
|
|
1378
|
+
sections.push({
|
|
1379
|
+
type: currentType,
|
|
1380
|
+
startLine: currentStartLine,
|
|
1381
|
+
endLine: lines.length,
|
|
1382
|
+
content: currentContent.join("\n"),
|
|
1383
|
+
id: currentId
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
return sections;
|
|
1387
|
+
}
|
|
1388
|
+
function parseStartMarker(line) {
|
|
1389
|
+
const markdownManagedPattern = /^<!--\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*-->$/;
|
|
1390
|
+
let match = line.match(markdownManagedPattern);
|
|
1391
|
+
if (match) {
|
|
1392
|
+
return { type: "managed", id: match[1] || void 0 };
|
|
1393
|
+
}
|
|
1394
|
+
const markdownSeedPattern = /^<!--\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*-->$/;
|
|
1395
|
+
match = line.match(markdownSeedPattern);
|
|
1396
|
+
if (match) {
|
|
1397
|
+
return { type: "seed", id: match[1] || void 0 };
|
|
1398
|
+
}
|
|
1399
|
+
const yamlManagedPattern = /^\s*#\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*$/;
|
|
1400
|
+
match = line.match(yamlManagedPattern);
|
|
1401
|
+
if (match) {
|
|
1402
|
+
return { type: "managed", id: match[1] || void 0 };
|
|
1403
|
+
}
|
|
1404
|
+
const yamlSeedPattern = /^\s*#\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*$/;
|
|
1405
|
+
match = line.match(yamlSeedPattern);
|
|
1406
|
+
if (match) {
|
|
1407
|
+
return { type: "seed", id: match[1] || void 0 };
|
|
1408
|
+
}
|
|
1409
|
+
return null;
|
|
1410
|
+
}
|
|
1411
|
+
function parseEndMarker(line) {
|
|
1412
|
+
if (/^<!--\s*@einja:managed:end\s*-->$/.test(line)) {
|
|
1413
|
+
return "managed";
|
|
1414
|
+
}
|
|
1415
|
+
if (/^<!--\s*@einja:seed:end\s*-->$/.test(line)) {
|
|
1416
|
+
return "seed";
|
|
1417
|
+
}
|
|
1418
|
+
if (/^\s*#\s*@einja:managed:end\s*$/.test(line)) {
|
|
1419
|
+
return "managed";
|
|
1420
|
+
}
|
|
1421
|
+
if (/^\s*#\s*@einja:seed:end\s*$/.test(line)) {
|
|
1422
|
+
return "seed";
|
|
1423
|
+
}
|
|
1424
|
+
return null;
|
|
1425
|
+
}
|
|
1426
|
+
function isPathManaged(filePath, keyPath, jsonPaths) {
|
|
1427
|
+
const managedPaths = jsonPaths.managed[filePath] || [];
|
|
1428
|
+
return managedPaths.some(
|
|
1429
|
+
(p) => keyPath === p || keyPath.startsWith(`${p}.`)
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
function isPathSeed(filePath, keyPath, jsonPaths) {
|
|
1433
|
+
const seedPaths = jsonPaths.seed[filePath] || [];
|
|
1434
|
+
return seedPaths.some((p) => keyPath === p || keyPath.startsWith(`${p}.`));
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// src/generators/partials/packages.ts
|
|
1438
|
+
async function addPackages(options, components, syncMetadata) {
|
|
1439
|
+
const added = [];
|
|
1440
|
+
const skipped = [];
|
|
1441
|
+
const merged = [];
|
|
1442
|
+
const { targetDir, templateDir, config } = options;
|
|
1443
|
+
for (const component of components) {
|
|
1444
|
+
const componentName = component === "front-core" ? "front-core" : component === "server-core" ? "server-core" : component === "config" ? "config" : "ui";
|
|
1445
|
+
const srcDir = join10(templateDir, "packages", componentName);
|
|
1446
|
+
const destDir = join10(targetDir, "packages", componentName);
|
|
1447
|
+
info(`Adding package component: ${componentName}`);
|
|
1448
|
+
await copyDirectory(
|
|
1449
|
+
srcDir,
|
|
1450
|
+
destDir,
|
|
1451
|
+
{ added, skipped, merged },
|
|
1452
|
+
config.dryRun,
|
|
1453
|
+
syncMetadata
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
return { added, skipped, merged };
|
|
1457
|
+
}
|
|
1458
|
+
async function copyDirectory(srcDir, destDir, result, dryRun, syncMetadata) {
|
|
1459
|
+
const entries = await readdir(srcDir, { withFileTypes: true });
|
|
1460
|
+
for (const entry of entries) {
|
|
1461
|
+
const srcPath = join10(srcDir, entry.name);
|
|
1462
|
+
const destPath = join10(destDir, entry.name);
|
|
1463
|
+
if (entry.isDirectory()) {
|
|
1464
|
+
await copyDirectory(srcPath, destPath, result, dryRun, syncMetadata);
|
|
1465
|
+
} else {
|
|
1466
|
+
if (!dryRun) {
|
|
1467
|
+
const mergeResult = await mergeAndWriteFile(
|
|
1468
|
+
srcPath,
|
|
1469
|
+
destPath,
|
|
1470
|
+
syncMetadata
|
|
1471
|
+
);
|
|
1472
|
+
if (mergeResult.action === "created") {
|
|
1473
|
+
result.added.push(destPath);
|
|
1474
|
+
} else if (mergeResult.action === "skipped") {
|
|
1475
|
+
result.skipped.push(destPath);
|
|
1476
|
+
} else if (mergeResult.action === "merged") {
|
|
1477
|
+
result.merged.push(destPath);
|
|
1478
|
+
}
|
|
1479
|
+
} else {
|
|
1480
|
+
result.skipped.push(destPath);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// src/generators/partials/apps.ts
|
|
1487
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
1488
|
+
import { join as join11 } from "path";
|
|
1489
|
+
async function addApps(options, components, syncMetadata) {
|
|
1490
|
+
const added = [];
|
|
1491
|
+
const skipped = [];
|
|
1492
|
+
const merged = [];
|
|
1493
|
+
const { targetDir, templateDir, config } = options;
|
|
1494
|
+
for (const component of components) {
|
|
1495
|
+
const componentName = component === "web" ? "web" : component;
|
|
1496
|
+
const srcDir = join11(templateDir, "apps", componentName);
|
|
1497
|
+
const destDir = join11(targetDir, "apps", componentName);
|
|
1498
|
+
info(`Adding app component: ${componentName}`);
|
|
1499
|
+
await copyDirectory2(
|
|
1500
|
+
srcDir,
|
|
1501
|
+
destDir,
|
|
1502
|
+
{ added, skipped, merged },
|
|
1503
|
+
config.dryRun,
|
|
1504
|
+
syncMetadata
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
return { added, skipped, merged };
|
|
1508
|
+
}
|
|
1509
|
+
async function copyDirectory2(srcDir, destDir, result, dryRun, syncMetadata) {
|
|
1510
|
+
const entries = await readdir2(srcDir, { withFileTypes: true });
|
|
1511
|
+
for (const entry of entries) {
|
|
1512
|
+
const srcPath = join11(srcDir, entry.name);
|
|
1513
|
+
const destPath = join11(destDir, entry.name);
|
|
1514
|
+
if (entry.isDirectory()) {
|
|
1515
|
+
await copyDirectory2(srcPath, destPath, result, dryRun, syncMetadata);
|
|
1516
|
+
} else {
|
|
1517
|
+
if (!dryRun) {
|
|
1518
|
+
const mergeResult = await mergeAndWriteFile(
|
|
1519
|
+
srcPath,
|
|
1520
|
+
destPath,
|
|
1521
|
+
syncMetadata
|
|
1522
|
+
);
|
|
1523
|
+
if (mergeResult.action === "created") {
|
|
1524
|
+
result.added.push(destPath);
|
|
1525
|
+
} else if (mergeResult.action === "skipped") {
|
|
1526
|
+
result.skipped.push(destPath);
|
|
1527
|
+
} else if (mergeResult.action === "merged") {
|
|
1528
|
+
result.merged.push(destPath);
|
|
1529
|
+
}
|
|
1530
|
+
} else {
|
|
1531
|
+
result.skipped.push(destPath);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
// src/generators/partials/config.ts
|
|
1538
|
+
import { readdir as readdir3, readFile } from "fs/promises";
|
|
1539
|
+
import { join as join12, relative as relative2, sep } from "path";
|
|
1540
|
+
async function addConfigFiles(options, syncMetadata) {
|
|
1541
|
+
const added = [];
|
|
1542
|
+
const skipped = [];
|
|
1543
|
+
const merged = [];
|
|
1544
|
+
const { targetDir, templateDir, config } = options;
|
|
1545
|
+
info("Adding config files from template root");
|
|
1546
|
+
const excludedPaths = /* @__PURE__ */ new Set([
|
|
1547
|
+
".claude",
|
|
1548
|
+
"docs/einja",
|
|
1549
|
+
"CLAUDE.md",
|
|
1550
|
+
".mcp.json",
|
|
1551
|
+
"node_modules",
|
|
1552
|
+
".turbo",
|
|
1553
|
+
"next-env.d.ts",
|
|
1554
|
+
"styled-system",
|
|
1555
|
+
"pnpm-lock.yaml",
|
|
1556
|
+
"package-lock.json",
|
|
1557
|
+
"packages",
|
|
1558
|
+
"apps"
|
|
1559
|
+
]);
|
|
1560
|
+
const gitignorePatterns = await loadGitignorePatterns(templateDir);
|
|
1561
|
+
await copyConfigDirectory(
|
|
1562
|
+
templateDir,
|
|
1563
|
+
targetDir,
|
|
1564
|
+
templateDir,
|
|
1565
|
+
// rootDir として templateDir を渡す
|
|
1566
|
+
{ added, skipped, merged },
|
|
1567
|
+
config.dryRun,
|
|
1568
|
+
excludedPaths,
|
|
1569
|
+
gitignorePatterns,
|
|
1570
|
+
syncMetadata
|
|
1571
|
+
);
|
|
1572
|
+
return { added, skipped, merged };
|
|
1573
|
+
}
|
|
1574
|
+
async function loadGitignorePatterns(templateDir) {
|
|
1575
|
+
const patterns = /* @__PURE__ */ new Set();
|
|
1576
|
+
const gitignorePath = join12(templateDir, ".gitignore");
|
|
1577
|
+
try {
|
|
1578
|
+
const content = await readFile(gitignorePath, "utf-8");
|
|
1579
|
+
const lines = content.split("\n");
|
|
1580
|
+
for (const line of lines) {
|
|
1581
|
+
const trimmed = line.trim();
|
|
1582
|
+
if (trimmed && !trimmed.startsWith("#")) {
|
|
1583
|
+
const pattern = trimmed.startsWith("/") ? trimmed.slice(1) : trimmed;
|
|
1584
|
+
patterns.add(pattern);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
} catch {
|
|
1588
|
+
}
|
|
1589
|
+
return patterns;
|
|
1590
|
+
}
|
|
1591
|
+
async function copyConfigDirectory(srcDir, destDir, rootDir, result, dryRun, excludedPaths, gitignorePatterns, syncMetadata) {
|
|
1592
|
+
const entries = await readdir3(srcDir, { withFileTypes: true });
|
|
1593
|
+
for (const entry of entries) {
|
|
1594
|
+
const srcPath = join12(srcDir, entry.name);
|
|
1595
|
+
const destPath = join12(destDir, entry.name);
|
|
1596
|
+
const rawRelativePath = relative2(rootDir, srcPath);
|
|
1597
|
+
const relativePath = rawRelativePath.split(sep).join("/");
|
|
1598
|
+
if (shouldExclude(relativePath, excludedPaths, gitignorePatterns)) {
|
|
1599
|
+
continue;
|
|
1600
|
+
}
|
|
1601
|
+
if (entry.isDirectory()) {
|
|
1602
|
+
await copyConfigDirectory(
|
|
1603
|
+
srcPath,
|
|
1604
|
+
destPath,
|
|
1605
|
+
rootDir,
|
|
1606
|
+
// rootDir を引き継ぐ
|
|
1607
|
+
result,
|
|
1608
|
+
dryRun,
|
|
1609
|
+
excludedPaths,
|
|
1610
|
+
gitignorePatterns,
|
|
1611
|
+
syncMetadata
|
|
1612
|
+
);
|
|
1613
|
+
} else {
|
|
1614
|
+
if (!dryRun) {
|
|
1615
|
+
const mergeResult = await mergeAndWriteFile(
|
|
1616
|
+
srcPath,
|
|
1617
|
+
destPath,
|
|
1618
|
+
syncMetadata
|
|
1619
|
+
);
|
|
1620
|
+
if (mergeResult.action === "created") {
|
|
1621
|
+
result.added.push(destPath);
|
|
1622
|
+
} else if (mergeResult.action === "skipped") {
|
|
1623
|
+
result.skipped.push(destPath);
|
|
1624
|
+
} else if (mergeResult.action === "merged") {
|
|
1625
|
+
result.merged.push(destPath);
|
|
1626
|
+
}
|
|
1627
|
+
} else {
|
|
1628
|
+
result.skipped.push(destPath);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
function shouldExclude(relativePath, excludedPaths, gitignorePatterns) {
|
|
1634
|
+
for (const excluded of excludedPaths) {
|
|
1635
|
+
if (relativePath === excluded || relativePath.startsWith(`${excluded}/`)) {
|
|
1636
|
+
return true;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
for (const pattern of gitignorePatterns) {
|
|
1640
|
+
if (matchPattern(relativePath, pattern)) {
|
|
1641
|
+
return true;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
return false;
|
|
1645
|
+
}
|
|
1646
|
+
function matchPattern(path, pattern) {
|
|
1647
|
+
if (pattern.endsWith("/")) {
|
|
1648
|
+
const dirPattern = pattern.slice(0, -1);
|
|
1649
|
+
return path === dirPattern || path.startsWith(`${dirPattern}/`);
|
|
1650
|
+
}
|
|
1651
|
+
if (pattern.includes("*")) {
|
|
1652
|
+
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
|
|
1653
|
+
return new RegExp(`^${regexPattern}$`).test(path);
|
|
1654
|
+
}
|
|
1655
|
+
return path === pattern || path.startsWith(`${pattern}/`);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// src/commands/add.ts
|
|
1659
|
+
function getTemplatePath2(templateName) {
|
|
1660
|
+
const __filename3 = fileURLToPath2(import.meta.url);
|
|
1661
|
+
const __dirname3 = dirname4(__filename3);
|
|
1662
|
+
const distPath = join13(__dirname3, "../templates", templateName);
|
|
1663
|
+
const srcPath = join13(__dirname3, "../../templates", templateName);
|
|
1664
|
+
if (existsSync6(distPath)) {
|
|
1665
|
+
return distPath;
|
|
1666
|
+
}
|
|
1667
|
+
if (existsSync6(srcPath)) {
|
|
1668
|
+
return srcPath;
|
|
1669
|
+
}
|
|
1670
|
+
return distPath;
|
|
1671
|
+
}
|
|
1672
|
+
async function loadTemplateSyncMetadata(templateDir) {
|
|
1673
|
+
const syncFilePath = join13(templateDir, ".einja-sync.json");
|
|
1674
|
+
try {
|
|
1675
|
+
const content = await readFile2(syncFilePath, "utf-8");
|
|
1676
|
+
return JSON.parse(content);
|
|
1677
|
+
} catch {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
function mergeSyncMetadata(template, existing) {
|
|
1682
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1683
|
+
const jsonPaths = template?.jsonPaths ?? existing?.jsonPaths ?? { managed: {}, seed: {} };
|
|
1684
|
+
return {
|
|
1685
|
+
version: template?.version ?? existing?.version ?? "1.0.0",
|
|
1686
|
+
lastSync: now,
|
|
1687
|
+
templateVersion: template?.templateVersion ?? "1.0.0",
|
|
1688
|
+
files: { ...existing?.files ?? {}, ...template?.files ?? {} },
|
|
1689
|
+
jsonPaths
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
async function addCommand(options) {
|
|
1693
|
+
try {
|
|
1694
|
+
const targetDir = process.cwd();
|
|
1695
|
+
let config;
|
|
1696
|
+
if (options.skipPrompts) {
|
|
1697
|
+
config = getDefaultAddConfig();
|
|
1698
|
+
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");
|
|
1699
|
+
} else {
|
|
1700
|
+
config = await promptAddConfig(options.dryRun);
|
|
1701
|
+
}
|
|
1702
|
+
config.dryRun = options.dryRun;
|
|
1703
|
+
if (config.dryRun) {
|
|
1704
|
+
warn("dry-run\u30E2\u30FC\u30C9: \u5B9F\u969B\u306E\u30D5\u30A1\u30A4\u30EB\u64CD\u4F5C\u306F\u884C\u3044\u307E\u305B\u3093");
|
|
1705
|
+
info("\n--- \u8FFD\u52A0\u4E88\u5B9A\u306E\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8 ---");
|
|
1706
|
+
if (config.components.packages) {
|
|
1707
|
+
info(
|
|
1708
|
+
`- packages/: ${config.packageComponents.join(", ")}`
|
|
1709
|
+
);
|
|
1710
|
+
}
|
|
1711
|
+
if (config.components.apps) {
|
|
1712
|
+
info(`- apps/: ${config.appComponents.join(", ")}`);
|
|
1713
|
+
}
|
|
1714
|
+
if (config.components.config) {
|
|
1715
|
+
info("- \u76F4\u4E0B\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB: turbo.json, pnpm-workspace.yaml \u7B49");
|
|
1716
|
+
}
|
|
1717
|
+
info("---\n");
|
|
1718
|
+
}
|
|
1719
|
+
const templateDir = getTemplatePath2("default");
|
|
1720
|
+
if (!existsSync6(templateDir)) {
|
|
1721
|
+
throw new Error("\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: default");
|
|
1722
|
+
}
|
|
1723
|
+
const templateMetadata = await loadTemplateSyncMetadata(templateDir);
|
|
1724
|
+
const existingMetadata = await loadSyncMetadata(targetDir);
|
|
1725
|
+
const syncMetadata = mergeSyncMetadata(templateMetadata, existingMetadata);
|
|
1726
|
+
const addOptions = {
|
|
1727
|
+
targetDir,
|
|
1728
|
+
templateDir,
|
|
1729
|
+
config
|
|
1730
|
+
};
|
|
1731
|
+
let totalAdded = 0;
|
|
1732
|
+
let totalMerged = 0;
|
|
1733
|
+
let totalSkipped = 0;
|
|
1734
|
+
if (config.components.packages && config.packageComponents.length > 0) {
|
|
1735
|
+
const spinner = ora4("\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u8FFD\u52A0\u4E2D...").start();
|
|
1736
|
+
try {
|
|
1737
|
+
const result = await addPackages(
|
|
1738
|
+
addOptions,
|
|
1739
|
+
config.packageComponents,
|
|
1740
|
+
syncMetadata
|
|
1741
|
+
);
|
|
1742
|
+
totalAdded += result.added.length;
|
|
1743
|
+
totalMerged += result.merged.length;
|
|
1744
|
+
totalSkipped += result.skipped.length;
|
|
1745
|
+
spinner.succeed(
|
|
1746
|
+
`\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`
|
|
1747
|
+
);
|
|
1748
|
+
} catch (error2) {
|
|
1749
|
+
spinner.fail("\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
1750
|
+
throw error2;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
if (config.components.apps && config.appComponents.length > 0) {
|
|
1754
|
+
const spinner = ora4("\u30A2\u30D7\u30EA\u3092\u8FFD\u52A0\u4E2D...").start();
|
|
1755
|
+
try {
|
|
1756
|
+
const result = await addApps(
|
|
1757
|
+
addOptions,
|
|
1758
|
+
config.appComponents,
|
|
1759
|
+
syncMetadata
|
|
1760
|
+
);
|
|
1761
|
+
totalAdded += result.added.length;
|
|
1762
|
+
totalMerged += result.merged.length;
|
|
1763
|
+
totalSkipped += result.skipped.length;
|
|
1764
|
+
spinner.succeed(
|
|
1765
|
+
`\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`
|
|
1766
|
+
);
|
|
1767
|
+
} catch (error2) {
|
|
1768
|
+
spinner.fail("\u30A2\u30D7\u30EA\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
1769
|
+
throw error2;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
if (config.components.config) {
|
|
1773
|
+
const spinner = ora4("\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0\u4E2D...").start();
|
|
1774
|
+
try {
|
|
1775
|
+
const result = await addConfigFiles(addOptions, syncMetadata);
|
|
1776
|
+
totalAdded += result.added.length;
|
|
1777
|
+
totalMerged += result.merged.length;
|
|
1778
|
+
totalSkipped += result.skipped.length;
|
|
1779
|
+
spinner.succeed(
|
|
1780
|
+
`\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`
|
|
1781
|
+
);
|
|
1782
|
+
} catch (error2) {
|
|
1783
|
+
spinner.fail("\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
1784
|
+
throw error2;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
if (!config.dryRun) {
|
|
1788
|
+
syncMetadata.lastSync = (/* @__PURE__ */ new Date()).toISOString();
|
|
1789
|
+
await saveSyncMetadata(targetDir, syncMetadata);
|
|
1790
|
+
}
|
|
1791
|
+
success("\n\u2713 \u8FFD\u52A0\u5B8C\u4E86\uFF01\n");
|
|
1792
|
+
if (config.dryRun) {
|
|
1793
|
+
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");
|
|
1794
|
+
}
|
|
1795
|
+
info(`\u8FFD\u52A0\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalAdded}\u500B`);
|
|
1796
|
+
info(`\u30DE\u30FC\u30B8\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalMerged}\u500B`);
|
|
1797
|
+
info(`\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalSkipped}\u500B
|
|
1798
|
+
`);
|
|
1799
|
+
const packageJsonPath2 = join13(targetDir, "package.json");
|
|
1800
|
+
if (existsSync6(packageJsonPath2)) {
|
|
1801
|
+
const packageJson2 = await import(packageJsonPath2);
|
|
1802
|
+
const hasEinjaCli = packageJson2.default?.devDependencies?.["@einja/dev-cli"] || packageJson2.default?.dependencies?.["@einja/dev-cli"];
|
|
1803
|
+
if (!hasEinjaCli) {
|
|
1804
|
+
info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
|
|
1805
|
+
info("1. pnpm install");
|
|
1806
|
+
info("2. pnpm dev:setup");
|
|
1807
|
+
info("\n\u63A8\u5968:");
|
|
1808
|
+
info(" einja\u958B\u767A\u652F\u63F4CLI (@einja/dev-cli) \u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB:");
|
|
1809
|
+
info(" pnpm add -D @einja/dev-cli\n");
|
|
1810
|
+
} else {
|
|
1811
|
+
info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
|
|
1812
|
+
info("1. pnpm install");
|
|
1813
|
+
info("2. pnpm dev:setup\n");
|
|
1814
|
+
}
|
|
1815
|
+
} else {
|
|
1816
|
+
info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
|
|
1817
|
+
info("1. pnpm install");
|
|
1818
|
+
info("2. pnpm dev:setup\n");
|
|
1819
|
+
}
|
|
1820
|
+
} catch (error2) {
|
|
1821
|
+
error("\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F:");
|
|
1822
|
+
if (error2 instanceof Error) {
|
|
1823
|
+
error(error2.message);
|
|
1824
|
+
} else {
|
|
1825
|
+
error(String(error2));
|
|
1826
|
+
}
|
|
1827
|
+
process.exit(1);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
|
|
987
1831
|
// src/cli.ts
|
|
988
|
-
var __filename2 =
|
|
989
|
-
var __dirname2 =
|
|
990
|
-
var packageJsonPath =
|
|
991
|
-
var packageJson = JSON.parse(
|
|
1832
|
+
var __filename2 = fileURLToPath3(import.meta.url);
|
|
1833
|
+
var __dirname2 = dirname5(__filename2);
|
|
1834
|
+
var packageJsonPath = join14(__dirname2, "../package.json");
|
|
1835
|
+
var packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
|
|
992
1836
|
var program = new Command();
|
|
993
1837
|
program.name("create-einja-app").description("CLI tool to create new projects with Einja Management Template").version(packageJson.version);
|
|
994
1838
|
program.argument("[project-name]", "Project name").option("--skip-git", "Skip git initialization").option("--skip-install", "Skip package installation").option("-y, --yes", "Skip interactive prompts").action(
|
|
@@ -999,5 +1843,13 @@ program.argument("[project-name]", "Project name").option("--skip-git", "Skip gi
|
|
|
999
1843
|
program.command("setup").description("Setup tools for existing project").action(async () => {
|
|
1000
1844
|
await setupCommand();
|
|
1001
1845
|
});
|
|
1846
|
+
program.command("add").description("Add einja components to existing monorepo").option("-y, --yes", "Skip prompts and use defaults (select all)").option("--all", "Select all components (same as -y)").option("--dry-run", "Preview changes without making them").action(
|
|
1847
|
+
async (options) => {
|
|
1848
|
+
await addCommand({
|
|
1849
|
+
skipPrompts: options.yes || options.all || false,
|
|
1850
|
+
dryRun: options.dryRun || false
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
);
|
|
1002
1854
|
program.parse();
|
|
1003
1855
|
//# sourceMappingURL=cli.js.map
|