create-einja-app 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
- import { readFileSync as readFileSync4 } from "fs";
6
- import { fileURLToPath as fileURLToPath2 } from "url";
7
- import { dirname as dirname3, join as join10 } from "path";
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
 
@@ -457,19 +464,29 @@ async function execPostSetup(config, targetPath, options) {
457
464
  await promptAndExecuteDirenvAllow(targetPath);
458
465
  }
459
466
  if (config.setupEinjaCli) {
460
- const einjaSpinner = ora("@einja/cli \u3092\u521D\u671F\u5316\u4E2D...").start();
467
+ const einjaSpinner = ora("@einja/dev-cli \u3092\u521D\u671F\u5316\u4E2D...").start();
461
468
  try {
462
- await execa("npx", ["@einja/cli", "init"], { cwd: targetPath });
463
- einjaSpinner.succeed("@einja/cli \u3092\u521D\u671F\u5316\u3057\u307E\u3057\u305F");
469
+ await execa("npx", ["@einja/dev-cli", "init", "--force"], { cwd: targetPath });
470
+ einjaSpinner.succeed("@einja/dev-cli \u3092\u521D\u671F\u5316\u3057\u307E\u3057\u305F");
464
471
  } 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");
472
+ einjaSpinner.fail("@einja/dev-cli \u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
473
+ warn("\u5F8C\u3067\u624B\u52D5\u3067 'npx @einja/dev-cli init' \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
467
474
  }
468
475
  }
469
476
  printCompletionMessage(config);
470
477
  }
471
478
 
472
479
  // src/commands/create.ts
480
+ function isDirectoryEmpty(dirPath) {
481
+ if (!existsSync3(dirPath)) {
482
+ return true;
483
+ }
484
+ const files = readdirSync(dirPath);
485
+ const significantFiles = files.filter(
486
+ (f) => !f.startsWith(".")
487
+ );
488
+ return significantFiles.length === 0;
489
+ }
473
490
  function validateProjectName(projectName) {
474
491
  const regex = /^[a-zA-Z][a-zA-Z0-9_-]{0,49}$/;
475
492
  if (!regex.test(projectName)) {
@@ -502,7 +519,8 @@ async function createCommand(projectName, options) {
502
519
  husky: true
503
520
  },
504
521
  setupEinjaCli: true,
505
- worktreeConfig: void 0
522
+ worktreeConfig: void 0,
523
+ useCurrentDir: false
506
524
  };
507
525
  info(`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D: ${config.projectName}`);
508
526
  info(`\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8: ${config.template}`);
@@ -510,11 +528,19 @@ async function createCommand(projectName, options) {
510
528
  } else {
511
529
  config = await promptProjectConfig(projectName);
512
530
  }
513
- const targetPath = resolve(process.cwd(), config.projectName);
514
- if (checkProjectExists(targetPath)) {
515
- error(`\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA '${config.projectName}' \u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059`);
516
- 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");
517
- process.exit(1);
531
+ const targetPath = config.useCurrentDir ? process.cwd() : resolve(process.cwd(), config.projectName);
532
+ if (config.useCurrentDir) {
533
+ if (!isDirectoryEmpty(targetPath)) {
534
+ error("\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u3059");
535
+ 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");
536
+ process.exit(1);
537
+ }
538
+ } else {
539
+ if (checkProjectExists(targetPath)) {
540
+ error(`\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA '${config.projectName}' \u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059`);
541
+ 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");
542
+ process.exit(1);
543
+ }
518
544
  }
519
545
  const spinner = ora2("\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4F5C\u6210\u4E2D...").start();
520
546
  try {
@@ -984,11 +1010,808 @@ async function setupCommand() {
984
1010
  success("\u958B\u767A\u3092\u958B\u59CB\u3067\u304D\u307E\u3059\uFF01");
985
1011
  }
986
1012
 
1013
+ // src/commands/add.ts
1014
+ import { existsSync as existsSync6 } from "fs";
1015
+ import { readFile as readFile2 } from "fs/promises";
1016
+ import { dirname as dirname4, join as join13 } from "path";
1017
+ import { fileURLToPath as fileURLToPath2 } from "url";
1018
+ import ora4 from "ora";
1019
+
1020
+ // src/prompts/add.ts
1021
+ import inquirer5 from "inquirer";
1022
+ function getDefaultAddConfig() {
1023
+ return {
1024
+ components: {
1025
+ packages: true,
1026
+ apps: true,
1027
+ config: true
1028
+ },
1029
+ packageComponents: ["front-core", "server-core", "config", "ui"],
1030
+ appComponents: ["web"],
1031
+ dryRun: false
1032
+ };
1033
+ }
1034
+ async function promptAddConfig(dryRun) {
1035
+ const componentAnswers = await inquirer5.prompt([
1036
+ {
1037
+ type: "checkbox",
1038
+ name: "components",
1039
+ message: "\u8FFD\u52A0\u3059\u308B\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u3092\u9078\u629E\uFF08Space\u3067\u9078\u629E\u3001Enter\u3067\u78BA\u5B9A\uFF09:",
1040
+ choices: [
1041
+ {
1042
+ name: "packages/ - \u5171\u901A\u30D1\u30C3\u30B1\u30FC\u30B8\uFF08front-core, server-core, config, ui\uFF09",
1043
+ value: "packages",
1044
+ checked: true
1045
+ },
1046
+ {
1047
+ name: "apps/ - \u30A2\u30D7\u30EA\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8",
1048
+ value: "apps",
1049
+ checked: true
1050
+ },
1051
+ {
1052
+ name: "\u76F4\u4E0B\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB - turbo.json, pnpm-workspace.yaml \u7B49",
1053
+ value: "config",
1054
+ checked: true
1055
+ }
1056
+ ]
1057
+ }
1058
+ ]);
1059
+ const selectedComponents = componentAnswers.components;
1060
+ const hasPackages = selectedComponents.includes("packages");
1061
+ const hasApps = selectedComponents.includes("apps");
1062
+ const hasConfig = selectedComponents.includes("config");
1063
+ let packageComponents = [];
1064
+ if (hasPackages) {
1065
+ const packageAnswers = await inquirer5.prompt([
1066
+ {
1067
+ type: "checkbox",
1068
+ name: "packages",
1069
+ message: "\u8FFD\u52A0\u3059\u308B\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E:",
1070
+ choices: [
1071
+ {
1072
+ name: "front-core - \u30D5\u30ED\u30F3\u30C8\u30A8\u30F3\u30C9\u5171\u901A\u5C64\uFF08\u8A8D\u8A3C\u8A2D\u5B9A\u3001hooks\u3001utils\uFF09",
1073
+ value: "front-core",
1074
+ checked: true
1075
+ },
1076
+ {
1077
+ name: "server-core - \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u5171\u901A\u5C64\uFF08Prisma\u3001\u30C9\u30E1\u30A4\u30F3\u30ED\u30B8\u30C3\u30AF\uFF09",
1078
+ value: "server-core",
1079
+ checked: true
1080
+ },
1081
+ {
1082
+ name: "config - \u5171\u901A\u8A2D\u5B9A\uFF08Biome, TypeScript, Panda CSS\uFF09",
1083
+ value: "config",
1084
+ checked: true
1085
+ },
1086
+ {
1087
+ name: "ui - \u5171\u901AUI\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\uFF08shadcn/ui\uFF09",
1088
+ value: "ui",
1089
+ checked: true
1090
+ }
1091
+ ],
1092
+ validate: (input) => {
1093
+ if (input.length === 0) {
1094
+ return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
1095
+ }
1096
+ return true;
1097
+ }
1098
+ }
1099
+ ]);
1100
+ packageComponents = packageAnswers.packages;
1101
+ }
1102
+ let appComponents = [];
1103
+ if (hasApps) {
1104
+ const appAnswers = await inquirer5.prompt([
1105
+ {
1106
+ type: "checkbox",
1107
+ name: "apps",
1108
+ message: "\u8FFD\u52A0\u3059\u308B\u30A2\u30D7\u30EA\u3092\u9078\u629E:",
1109
+ choices: [
1110
+ {
1111
+ name: "web - \u30E1\u30A4\u30F3\u7BA1\u7406\u753B\u9762\u30A2\u30D7\u30EA\uFF08Next.js + App Router\uFF09",
1112
+ value: "web",
1113
+ checked: true
1114
+ }
1115
+ ],
1116
+ validate: (input) => {
1117
+ if (input.length === 0) {
1118
+ return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30A2\u30D7\u30EA\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
1119
+ }
1120
+ return true;
1121
+ }
1122
+ }
1123
+ ]);
1124
+ appComponents = appAnswers.apps;
1125
+ }
1126
+ return {
1127
+ components: {
1128
+ packages: hasPackages,
1129
+ apps: hasApps,
1130
+ config: hasConfig
1131
+ },
1132
+ packageComponents,
1133
+ appComponents,
1134
+ dryRun
1135
+ };
1136
+ }
1137
+
1138
+ // src/generators/partials/packages.ts
1139
+ import { readdir } from "fs/promises";
1140
+ import { join as join10 } from "path";
1141
+
1142
+ // src/utils/merger.ts
1143
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
1144
+ import { dirname as dirname3 } from "path";
1145
+ function mergeTextWithMarkers(templateContent, existingContent) {
1146
+ if (existingContent === null) {
1147
+ return templateContent;
1148
+ }
1149
+ const templateSections = parseMarkers(templateContent);
1150
+ const localSections = parseMarkers(existingContent);
1151
+ const hasMarkers = templateSections.some(
1152
+ (s) => s.type === "managed" || s.type === "seed"
1153
+ );
1154
+ if (!hasMarkers) {
1155
+ return existingContent;
1156
+ }
1157
+ const templateManagedById = /* @__PURE__ */ new Map();
1158
+ const templateSeedById = /* @__PURE__ */ new Map();
1159
+ const processedTemplateIds = /* @__PURE__ */ new Set();
1160
+ for (const section of templateSections) {
1161
+ if (section.type === "managed" && section.id) {
1162
+ templateManagedById.set(section.id, section);
1163
+ } else if (section.type === "seed" && section.id) {
1164
+ templateSeedById.set(section.id, section);
1165
+ }
1166
+ }
1167
+ const result = [];
1168
+ for (const localSection of localSections) {
1169
+ if (localSection.type === "managed") {
1170
+ const match = localSection.id ? templateManagedById.get(localSection.id) : void 0;
1171
+ if (localSection.id && match) {
1172
+ processedTemplateIds.add(localSection.id);
1173
+ result.push(match.content);
1174
+ } else if (!localSection.id) {
1175
+ result.push(localSection.content);
1176
+ }
1177
+ } else if (localSection.type === "seed") {
1178
+ if (localSection.id) {
1179
+ processedTemplateIds.add(localSection.id);
1180
+ }
1181
+ result.push(localSection.content);
1182
+ } else {
1183
+ result.push(localSection.content);
1184
+ }
1185
+ }
1186
+ for (const [id, section] of templateManagedById) {
1187
+ if (!processedTemplateIds.has(id)) {
1188
+ result.push(section.content);
1189
+ }
1190
+ }
1191
+ for (const [id, section] of templateSeedById) {
1192
+ if (!processedTemplateIds.has(id)) {
1193
+ result.push(section.content);
1194
+ }
1195
+ }
1196
+ const firstElement = result[0];
1197
+ if (result.length > 0 && firstElement !== void 0 && firstElement.length === 0) {
1198
+ result.shift();
1199
+ }
1200
+ return result.join("\n");
1201
+ }
1202
+ function mergeJson(templateJson, existingJson, jsonPaths, filePath = "package.json") {
1203
+ if (existingJson === null) {
1204
+ return JSON.parse(JSON.stringify(templateJson));
1205
+ }
1206
+ return deepMergeWithPaths(
1207
+ templateJson,
1208
+ existingJson,
1209
+ jsonPaths,
1210
+ filePath,
1211
+ ""
1212
+ );
1213
+ }
1214
+ function deepMergeWithPaths(template, existing, jsonPaths, filePath, currentPath) {
1215
+ const result = JSON.parse(JSON.stringify(existing));
1216
+ for (const [key, templateValue] of Object.entries(template)) {
1217
+ const keyPath = currentPath ? `${currentPath}.${key}` : key;
1218
+ const existingValue = existing[key];
1219
+ if (isPathManaged(filePath, keyPath, jsonPaths)) {
1220
+ result[key] = deepClone(templateValue);
1221
+ } else if (isPathSeed(filePath, keyPath, jsonPaths)) {
1222
+ if (typeof templateValue === "object" && templateValue !== null && !Array.isArray(templateValue) && typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) {
1223
+ result[key] = deepMergeWithPaths(
1224
+ templateValue,
1225
+ existingValue,
1226
+ jsonPaths,
1227
+ filePath,
1228
+ keyPath
1229
+ );
1230
+ } else if (!(key in existing)) {
1231
+ result[key] = deepClone(templateValue);
1232
+ }
1233
+ } else if (typeof templateValue === "object" && templateValue !== null && !Array.isArray(templateValue) && typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) {
1234
+ result[key] = deepMergeWithPaths(
1235
+ templateValue,
1236
+ existingValue,
1237
+ jsonPaths,
1238
+ filePath,
1239
+ keyPath
1240
+ );
1241
+ } else if (!(key in existing)) {
1242
+ result[key] = deepClone(templateValue);
1243
+ }
1244
+ }
1245
+ return result;
1246
+ }
1247
+ function deepClone(value) {
1248
+ if (value === void 0) {
1249
+ return void 0;
1250
+ }
1251
+ return JSON.parse(JSON.stringify(value));
1252
+ }
1253
+ async function loadSyncMetadata(targetDir) {
1254
+ const metadataPath = `${targetDir}/.einja-sync.json`;
1255
+ if (!existsSync5(metadataPath)) {
1256
+ return null;
1257
+ }
1258
+ try {
1259
+ const content = readFileSync4(metadataPath, "utf-8");
1260
+ return JSON.parse(content);
1261
+ } catch {
1262
+ return null;
1263
+ }
1264
+ }
1265
+ async function saveSyncMetadata(targetDir, metadata) {
1266
+ const metadataPath = `${targetDir}/.einja-sync.json`;
1267
+ ensureDir(dirname3(metadataPath));
1268
+ writeFileSync5(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
1269
+ }
1270
+ async function mergeAndWriteFile(templatePath, targetPath, syncMetadata) {
1271
+ const templateContent = readFileSync4(templatePath, "utf-8");
1272
+ const targetExists = existsSync5(targetPath);
1273
+ const existingContent = targetExists ? readFileSync4(targetPath, "utf-8") : null;
1274
+ const isJsonFile = targetPath.endsWith(".json");
1275
+ let mergedContent;
1276
+ let action;
1277
+ if (!targetExists) {
1278
+ mergedContent = templateContent;
1279
+ action = "created";
1280
+ } else if (isJsonFile) {
1281
+ try {
1282
+ const templateJson = JSON.parse(templateContent);
1283
+ const existingJson = existingContent ? JSON.parse(existingContent) : null;
1284
+ const jsonPaths = syncMetadata.jsonPaths || { managed: {}, seed: {} };
1285
+ const fileName = targetPath.split("/").pop() || "package.json";
1286
+ const mergedJson = mergeJson(templateJson, existingJson, jsonPaths, fileName);
1287
+ mergedContent = JSON.stringify(mergedJson, null, 2);
1288
+ action = "merged";
1289
+ } catch {
1290
+ mergedContent = templateContent;
1291
+ action = "overwritten";
1292
+ }
1293
+ } else {
1294
+ mergedContent = mergeTextWithMarkers(templateContent, existingContent);
1295
+ if (mergedContent === existingContent) {
1296
+ action = "skipped";
1297
+ } else {
1298
+ action = "merged";
1299
+ }
1300
+ }
1301
+ if (action !== "skipped") {
1302
+ ensureDir(dirname3(targetPath));
1303
+ writeFileSync5(targetPath, mergedContent, "utf-8");
1304
+ }
1305
+ return { action, path: targetPath };
1306
+ }
1307
+ function parseMarkers(content) {
1308
+ const lines = content.split("\n");
1309
+ const sections = [];
1310
+ let currentType = "unmanaged";
1311
+ let currentStartLine = 1;
1312
+ let currentContent = [];
1313
+ let currentId;
1314
+ for (let i = 0; i < lines.length; i++) {
1315
+ const line = lines[i];
1316
+ const lineNumber = i + 1;
1317
+ const startMarker = parseStartMarker(line);
1318
+ if (startMarker) {
1319
+ if (currentType !== "unmanaged") {
1320
+ currentContent.push(line);
1321
+ continue;
1322
+ }
1323
+ if (currentContent.length > 0 || sections.length === 0) {
1324
+ sections.push({
1325
+ type: "unmanaged",
1326
+ startLine: currentStartLine,
1327
+ endLine: lineNumber - 1,
1328
+ content: currentContent.join("\n")
1329
+ });
1330
+ }
1331
+ currentType = startMarker.type;
1332
+ currentId = startMarker.id;
1333
+ currentStartLine = lineNumber;
1334
+ currentContent = [line];
1335
+ } else if (parseEndMarker(line)) {
1336
+ if (currentType === "unmanaged") {
1337
+ currentContent.push(line);
1338
+ continue;
1339
+ }
1340
+ currentContent.push(line);
1341
+ sections.push({
1342
+ type: currentType,
1343
+ startLine: currentStartLine,
1344
+ endLine: lineNumber,
1345
+ content: currentContent.join("\n"),
1346
+ id: currentId
1347
+ });
1348
+ currentType = "unmanaged";
1349
+ currentId = void 0;
1350
+ currentStartLine = lineNumber + 1;
1351
+ currentContent = [];
1352
+ } else {
1353
+ currentContent.push(line);
1354
+ }
1355
+ }
1356
+ if (currentContent.length > 0 || sections.length === 0) {
1357
+ sections.push({
1358
+ type: currentType,
1359
+ startLine: currentStartLine,
1360
+ endLine: lines.length,
1361
+ content: currentContent.join("\n"),
1362
+ id: currentId
1363
+ });
1364
+ }
1365
+ return sections;
1366
+ }
1367
+ function parseStartMarker(line) {
1368
+ const markdownManagedPattern = /^<!--\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*-->$/;
1369
+ let match = line.match(markdownManagedPattern);
1370
+ if (match) {
1371
+ return { type: "managed", id: match[1] || void 0 };
1372
+ }
1373
+ const markdownSeedPattern = /^<!--\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*-->$/;
1374
+ match = line.match(markdownSeedPattern);
1375
+ if (match) {
1376
+ return { type: "seed", id: match[1] || void 0 };
1377
+ }
1378
+ const yamlManagedPattern = /^\s*#\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*$/;
1379
+ match = line.match(yamlManagedPattern);
1380
+ if (match) {
1381
+ return { type: "managed", id: match[1] || void 0 };
1382
+ }
1383
+ const yamlSeedPattern = /^\s*#\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*$/;
1384
+ match = line.match(yamlSeedPattern);
1385
+ if (match) {
1386
+ return { type: "seed", id: match[1] || void 0 };
1387
+ }
1388
+ return null;
1389
+ }
1390
+ function parseEndMarker(line) {
1391
+ if (/^<!--\s*@einja:managed:end\s*-->$/.test(line)) {
1392
+ return "managed";
1393
+ }
1394
+ if (/^<!--\s*@einja:seed:end\s*-->$/.test(line)) {
1395
+ return "seed";
1396
+ }
1397
+ if (/^\s*#\s*@einja:managed:end\s*$/.test(line)) {
1398
+ return "managed";
1399
+ }
1400
+ if (/^\s*#\s*@einja:seed:end\s*$/.test(line)) {
1401
+ return "seed";
1402
+ }
1403
+ return null;
1404
+ }
1405
+ function isPathManaged(filePath, keyPath, jsonPaths) {
1406
+ const managedPaths = jsonPaths.managed[filePath] || [];
1407
+ return managedPaths.some(
1408
+ (p) => keyPath === p || keyPath.startsWith(`${p}.`)
1409
+ );
1410
+ }
1411
+ function isPathSeed(filePath, keyPath, jsonPaths) {
1412
+ const seedPaths = jsonPaths.seed[filePath] || [];
1413
+ return seedPaths.some((p) => keyPath === p || keyPath.startsWith(`${p}.`));
1414
+ }
1415
+
1416
+ // src/generators/partials/packages.ts
1417
+ async function addPackages(options, components, syncMetadata) {
1418
+ const added = [];
1419
+ const skipped = [];
1420
+ const merged = [];
1421
+ const { targetDir, templateDir, config } = options;
1422
+ for (const component of components) {
1423
+ const componentName = component === "front-core" ? "front-core" : component === "server-core" ? "server-core" : component === "config" ? "config" : "ui";
1424
+ const srcDir = join10(templateDir, "packages", componentName);
1425
+ const destDir = join10(targetDir, "packages", componentName);
1426
+ info(`Adding package component: ${componentName}`);
1427
+ await copyDirectory(
1428
+ srcDir,
1429
+ destDir,
1430
+ { added, skipped, merged },
1431
+ config.dryRun,
1432
+ syncMetadata
1433
+ );
1434
+ }
1435
+ return { added, skipped, merged };
1436
+ }
1437
+ async function copyDirectory(srcDir, destDir, result, dryRun, syncMetadata) {
1438
+ const entries = await readdir(srcDir, { withFileTypes: true });
1439
+ for (const entry of entries) {
1440
+ const srcPath = join10(srcDir, entry.name);
1441
+ const destPath = join10(destDir, entry.name);
1442
+ if (entry.isDirectory()) {
1443
+ await copyDirectory(srcPath, destPath, result, dryRun, syncMetadata);
1444
+ } else {
1445
+ if (!dryRun) {
1446
+ const mergeResult = await mergeAndWriteFile(
1447
+ srcPath,
1448
+ destPath,
1449
+ syncMetadata
1450
+ );
1451
+ if (mergeResult.action === "created") {
1452
+ result.added.push(destPath);
1453
+ } else if (mergeResult.action === "skipped") {
1454
+ result.skipped.push(destPath);
1455
+ } else if (mergeResult.action === "merged") {
1456
+ result.merged.push(destPath);
1457
+ }
1458
+ } else {
1459
+ result.skipped.push(destPath);
1460
+ }
1461
+ }
1462
+ }
1463
+ }
1464
+
1465
+ // src/generators/partials/apps.ts
1466
+ import { readdir as readdir2 } from "fs/promises";
1467
+ import { join as join11 } from "path";
1468
+ async function addApps(options, components, syncMetadata) {
1469
+ const added = [];
1470
+ const skipped = [];
1471
+ const merged = [];
1472
+ const { targetDir, templateDir, config } = options;
1473
+ for (const component of components) {
1474
+ const componentName = component === "web" ? "web" : component;
1475
+ const srcDir = join11(templateDir, "apps", componentName);
1476
+ const destDir = join11(targetDir, "apps", componentName);
1477
+ info(`Adding app component: ${componentName}`);
1478
+ await copyDirectory2(
1479
+ srcDir,
1480
+ destDir,
1481
+ { added, skipped, merged },
1482
+ config.dryRun,
1483
+ syncMetadata
1484
+ );
1485
+ }
1486
+ return { added, skipped, merged };
1487
+ }
1488
+ async function copyDirectory2(srcDir, destDir, result, dryRun, syncMetadata) {
1489
+ const entries = await readdir2(srcDir, { withFileTypes: true });
1490
+ for (const entry of entries) {
1491
+ const srcPath = join11(srcDir, entry.name);
1492
+ const destPath = join11(destDir, entry.name);
1493
+ if (entry.isDirectory()) {
1494
+ await copyDirectory2(srcPath, destPath, result, dryRun, syncMetadata);
1495
+ } else {
1496
+ if (!dryRun) {
1497
+ const mergeResult = await mergeAndWriteFile(
1498
+ srcPath,
1499
+ destPath,
1500
+ syncMetadata
1501
+ );
1502
+ if (mergeResult.action === "created") {
1503
+ result.added.push(destPath);
1504
+ } else if (mergeResult.action === "skipped") {
1505
+ result.skipped.push(destPath);
1506
+ } else if (mergeResult.action === "merged") {
1507
+ result.merged.push(destPath);
1508
+ }
1509
+ } else {
1510
+ result.skipped.push(destPath);
1511
+ }
1512
+ }
1513
+ }
1514
+ }
1515
+
1516
+ // src/generators/partials/config.ts
1517
+ import { readdir as readdir3, readFile } from "fs/promises";
1518
+ import { join as join12, relative as relative2, sep } from "path";
1519
+ async function addConfigFiles(options, syncMetadata) {
1520
+ const added = [];
1521
+ const skipped = [];
1522
+ const merged = [];
1523
+ const { targetDir, templateDir, config } = options;
1524
+ info("Adding config files from template root");
1525
+ const excludedPaths = /* @__PURE__ */ new Set([
1526
+ ".claude",
1527
+ "docs/einja",
1528
+ "CLAUDE.md",
1529
+ ".mcp.json",
1530
+ "node_modules",
1531
+ ".turbo",
1532
+ "next-env.d.ts",
1533
+ "styled-system",
1534
+ "pnpm-lock.yaml",
1535
+ "package-lock.json",
1536
+ "packages",
1537
+ "apps"
1538
+ ]);
1539
+ const gitignorePatterns = await loadGitignorePatterns(templateDir);
1540
+ await copyConfigDirectory(
1541
+ templateDir,
1542
+ targetDir,
1543
+ templateDir,
1544
+ // rootDir として templateDir を渡す
1545
+ { added, skipped, merged },
1546
+ config.dryRun,
1547
+ excludedPaths,
1548
+ gitignorePatterns,
1549
+ syncMetadata
1550
+ );
1551
+ return { added, skipped, merged };
1552
+ }
1553
+ async function loadGitignorePatterns(templateDir) {
1554
+ const patterns = /* @__PURE__ */ new Set();
1555
+ const gitignorePath = join12(templateDir, ".gitignore");
1556
+ try {
1557
+ const content = await readFile(gitignorePath, "utf-8");
1558
+ const lines = content.split("\n");
1559
+ for (const line of lines) {
1560
+ const trimmed = line.trim();
1561
+ if (trimmed && !trimmed.startsWith("#")) {
1562
+ const pattern = trimmed.startsWith("/") ? trimmed.slice(1) : trimmed;
1563
+ patterns.add(pattern);
1564
+ }
1565
+ }
1566
+ } catch {
1567
+ }
1568
+ return patterns;
1569
+ }
1570
+ async function copyConfigDirectory(srcDir, destDir, rootDir, result, dryRun, excludedPaths, gitignorePatterns, syncMetadata) {
1571
+ const entries = await readdir3(srcDir, { withFileTypes: true });
1572
+ for (const entry of entries) {
1573
+ const srcPath = join12(srcDir, entry.name);
1574
+ const destPath = join12(destDir, entry.name);
1575
+ const rawRelativePath = relative2(rootDir, srcPath);
1576
+ const relativePath = rawRelativePath.split(sep).join("/");
1577
+ if (shouldExclude(relativePath, excludedPaths, gitignorePatterns)) {
1578
+ continue;
1579
+ }
1580
+ if (entry.isDirectory()) {
1581
+ await copyConfigDirectory(
1582
+ srcPath,
1583
+ destPath,
1584
+ rootDir,
1585
+ // rootDir を引き継ぐ
1586
+ result,
1587
+ dryRun,
1588
+ excludedPaths,
1589
+ gitignorePatterns,
1590
+ syncMetadata
1591
+ );
1592
+ } else {
1593
+ if (!dryRun) {
1594
+ const mergeResult = await mergeAndWriteFile(
1595
+ srcPath,
1596
+ destPath,
1597
+ syncMetadata
1598
+ );
1599
+ if (mergeResult.action === "created") {
1600
+ result.added.push(destPath);
1601
+ } else if (mergeResult.action === "skipped") {
1602
+ result.skipped.push(destPath);
1603
+ } else if (mergeResult.action === "merged") {
1604
+ result.merged.push(destPath);
1605
+ }
1606
+ } else {
1607
+ result.skipped.push(destPath);
1608
+ }
1609
+ }
1610
+ }
1611
+ }
1612
+ function shouldExclude(relativePath, excludedPaths, gitignorePatterns) {
1613
+ for (const excluded of excludedPaths) {
1614
+ if (relativePath === excluded || relativePath.startsWith(`${excluded}/`)) {
1615
+ return true;
1616
+ }
1617
+ }
1618
+ for (const pattern of gitignorePatterns) {
1619
+ if (matchPattern(relativePath, pattern)) {
1620
+ return true;
1621
+ }
1622
+ }
1623
+ return false;
1624
+ }
1625
+ function matchPattern(path, pattern) {
1626
+ if (pattern.endsWith("/")) {
1627
+ const dirPattern = pattern.slice(0, -1);
1628
+ return path === dirPattern || path.startsWith(`${dirPattern}/`);
1629
+ }
1630
+ if (pattern.includes("*")) {
1631
+ const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
1632
+ return new RegExp(`^${regexPattern}$`).test(path);
1633
+ }
1634
+ return path === pattern || path.startsWith(`${pattern}/`);
1635
+ }
1636
+
1637
+ // src/commands/add.ts
1638
+ function getTemplatePath2(templateName) {
1639
+ const __filename3 = fileURLToPath2(import.meta.url);
1640
+ const __dirname3 = dirname4(__filename3);
1641
+ const distPath = join13(__dirname3, "../templates", templateName);
1642
+ const srcPath = join13(__dirname3, "../../templates", templateName);
1643
+ if (existsSync6(distPath)) {
1644
+ return distPath;
1645
+ }
1646
+ if (existsSync6(srcPath)) {
1647
+ return srcPath;
1648
+ }
1649
+ return distPath;
1650
+ }
1651
+ async function loadTemplateSyncMetadata(templateDir) {
1652
+ const syncFilePath = join13(templateDir, ".einja-sync.json");
1653
+ try {
1654
+ const content = await readFile2(syncFilePath, "utf-8");
1655
+ return JSON.parse(content);
1656
+ } catch {
1657
+ return null;
1658
+ }
1659
+ }
1660
+ function mergeSyncMetadata(template, existing) {
1661
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1662
+ const jsonPaths = template?.jsonPaths ?? existing?.jsonPaths ?? { managed: {}, seed: {} };
1663
+ return {
1664
+ version: template?.version ?? existing?.version ?? "1.0.0",
1665
+ lastSync: now,
1666
+ templateVersion: template?.templateVersion ?? "1.0.0",
1667
+ files: { ...existing?.files ?? {}, ...template?.files ?? {} },
1668
+ jsonPaths
1669
+ };
1670
+ }
1671
+ async function addCommand(options) {
1672
+ try {
1673
+ const targetDir = process.cwd();
1674
+ let config;
1675
+ if (options.skipPrompts) {
1676
+ config = getDefaultAddConfig();
1677
+ 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");
1678
+ } else {
1679
+ config = await promptAddConfig(options.dryRun);
1680
+ }
1681
+ config.dryRun = options.dryRun;
1682
+ if (config.dryRun) {
1683
+ warn("dry-run\u30E2\u30FC\u30C9: \u5B9F\u969B\u306E\u30D5\u30A1\u30A4\u30EB\u64CD\u4F5C\u306F\u884C\u3044\u307E\u305B\u3093");
1684
+ info("\n--- \u8FFD\u52A0\u4E88\u5B9A\u306E\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8 ---");
1685
+ if (config.components.packages) {
1686
+ info(
1687
+ `- packages/: ${config.packageComponents.join(", ")}`
1688
+ );
1689
+ }
1690
+ if (config.components.apps) {
1691
+ info(`- apps/: ${config.appComponents.join(", ")}`);
1692
+ }
1693
+ if (config.components.config) {
1694
+ info("- \u76F4\u4E0B\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB: turbo.json, pnpm-workspace.yaml \u7B49");
1695
+ }
1696
+ info("---\n");
1697
+ }
1698
+ const templateDir = getTemplatePath2("default");
1699
+ if (!existsSync6(templateDir)) {
1700
+ throw new Error("\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: default");
1701
+ }
1702
+ const templateMetadata = await loadTemplateSyncMetadata(templateDir);
1703
+ const existingMetadata = await loadSyncMetadata(targetDir);
1704
+ const syncMetadata = mergeSyncMetadata(templateMetadata, existingMetadata);
1705
+ const addOptions = {
1706
+ targetDir,
1707
+ templateDir,
1708
+ config
1709
+ };
1710
+ let totalAdded = 0;
1711
+ let totalMerged = 0;
1712
+ let totalSkipped = 0;
1713
+ if (config.components.packages && config.packageComponents.length > 0) {
1714
+ const spinner = ora4("\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u8FFD\u52A0\u4E2D...").start();
1715
+ try {
1716
+ const result = await addPackages(
1717
+ addOptions,
1718
+ config.packageComponents,
1719
+ syncMetadata
1720
+ );
1721
+ totalAdded += result.added.length;
1722
+ totalMerged += result.merged.length;
1723
+ totalSkipped += result.skipped.length;
1724
+ spinner.succeed(
1725
+ `\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`
1726
+ );
1727
+ } catch (error2) {
1728
+ spinner.fail("\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
1729
+ throw error2;
1730
+ }
1731
+ }
1732
+ if (config.components.apps && config.appComponents.length > 0) {
1733
+ const spinner = ora4("\u30A2\u30D7\u30EA\u3092\u8FFD\u52A0\u4E2D...").start();
1734
+ try {
1735
+ const result = await addApps(
1736
+ addOptions,
1737
+ config.appComponents,
1738
+ syncMetadata
1739
+ );
1740
+ totalAdded += result.added.length;
1741
+ totalMerged += result.merged.length;
1742
+ totalSkipped += result.skipped.length;
1743
+ spinner.succeed(
1744
+ `\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`
1745
+ );
1746
+ } catch (error2) {
1747
+ spinner.fail("\u30A2\u30D7\u30EA\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
1748
+ throw error2;
1749
+ }
1750
+ }
1751
+ if (config.components.config) {
1752
+ const spinner = ora4("\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0\u4E2D...").start();
1753
+ try {
1754
+ const result = await addConfigFiles(addOptions, syncMetadata);
1755
+ totalAdded += result.added.length;
1756
+ totalMerged += result.merged.length;
1757
+ totalSkipped += result.skipped.length;
1758
+ spinner.succeed(
1759
+ `\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`
1760
+ );
1761
+ } catch (error2) {
1762
+ spinner.fail("\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
1763
+ throw error2;
1764
+ }
1765
+ }
1766
+ if (!config.dryRun) {
1767
+ syncMetadata.lastSync = (/* @__PURE__ */ new Date()).toISOString();
1768
+ await saveSyncMetadata(targetDir, syncMetadata);
1769
+ }
1770
+ success("\n\u2713 \u8FFD\u52A0\u5B8C\u4E86\uFF01\n");
1771
+ if (config.dryRun) {
1772
+ 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");
1773
+ }
1774
+ info(`\u8FFD\u52A0\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalAdded}\u500B`);
1775
+ info(`\u30DE\u30FC\u30B8\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalMerged}\u500B`);
1776
+ info(`\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalSkipped}\u500B
1777
+ `);
1778
+ const packageJsonPath2 = join13(targetDir, "package.json");
1779
+ if (existsSync6(packageJsonPath2)) {
1780
+ const packageJson2 = await import(packageJsonPath2);
1781
+ const hasEinjaCli = packageJson2.default?.devDependencies?.["@einja/dev-cli"] || packageJson2.default?.dependencies?.["@einja/dev-cli"];
1782
+ if (!hasEinjaCli) {
1783
+ info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
1784
+ info("1. pnpm install");
1785
+ info("2. pnpm dev:setup");
1786
+ info("\n\u63A8\u5968:");
1787
+ info(" einja\u958B\u767A\u652F\u63F4CLI (@einja/dev-cli) \u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB:");
1788
+ info(" pnpm add -D @einja/dev-cli\n");
1789
+ } else {
1790
+ info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
1791
+ info("1. pnpm install");
1792
+ info("2. pnpm dev:setup\n");
1793
+ }
1794
+ } else {
1795
+ info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
1796
+ info("1. pnpm install");
1797
+ info("2. pnpm dev:setup\n");
1798
+ }
1799
+ } catch (error2) {
1800
+ error("\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F:");
1801
+ if (error2 instanceof Error) {
1802
+ error(error2.message);
1803
+ } else {
1804
+ error(String(error2));
1805
+ }
1806
+ process.exit(1);
1807
+ }
1808
+ }
1809
+
987
1810
  // src/cli.ts
988
- var __filename2 = fileURLToPath2(import.meta.url);
989
- var __dirname2 = dirname3(__filename2);
990
- var packageJsonPath = join10(__dirname2, "../package.json");
991
- var packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
1811
+ var __filename2 = fileURLToPath3(import.meta.url);
1812
+ var __dirname2 = dirname5(__filename2);
1813
+ var packageJsonPath = join14(__dirname2, "../package.json");
1814
+ var packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
992
1815
  var program = new Command();
993
1816
  program.name("create-einja-app").description("CLI tool to create new projects with Einja Management Template").version(packageJson.version);
994
1817
  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 +1822,13 @@ program.argument("[project-name]", "Project name").option("--skip-git", "Skip gi
999
1822
  program.command("setup").description("Setup tools for existing project").action(async () => {
1000
1823
  await setupCommand();
1001
1824
  });
1825
+ 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(
1826
+ async (options) => {
1827
+ await addCommand({
1828
+ skipPrompts: options.yes || options.all || false,
1829
+ dryRun: options.dryRun || false
1830
+ });
1831
+ }
1832
+ );
1002
1833
  program.parse();
1003
1834
  //# sourceMappingURL=cli.js.map