create-m5kdev 0.30.0 → 0.31.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.
Files changed (60) hide show
  1. package/dist/src/catalog.cjs +140 -0
  2. package/dist/src/catalog.cjs.map +1 -0
  3. package/dist/src/catalog.d.cts +31 -0
  4. package/dist/src/changes.cjs +70 -0
  5. package/dist/src/changes.cjs.map +1 -0
  6. package/dist/src/changes.d.cts +26 -0
  7. package/dist/src/create.cjs +37 -23
  8. package/dist/src/create.cjs.map +1 -1
  9. package/dist/src/diagnostics.cjs +33 -0
  10. package/dist/src/diagnostics.cjs.map +1 -0
  11. package/dist/src/diagnostics.d.cts +20 -0
  12. package/dist/src/doctor.cjs +246 -0
  13. package/dist/src/doctor.cjs.map +1 -0
  14. package/dist/src/doctor.d.cts +13 -0
  15. package/dist/src/fs.cjs +56 -26
  16. package/dist/src/fs.cjs.map +1 -1
  17. package/dist/src/fs.d.cts +11 -4
  18. package/dist/src/init.cjs +110 -0
  19. package/dist/src/init.cjs.map +1 -0
  20. package/dist/src/init.d.cts +36 -0
  21. package/dist/src/migrations/ast.cjs +49 -0
  22. package/dist/src/migrations/ast.cjs.map +1 -0
  23. package/dist/src/migrations/ast.d.cts +10 -0
  24. package/dist/src/migrations/registry.cjs +41 -0
  25. package/dist/src/migrations/registry.cjs.map +1 -0
  26. package/dist/src/migrations/registry.d.cts +12 -0
  27. package/dist/src/migrations/types.cjs +0 -0
  28. package/dist/src/migrations/types.d.cts +22 -0
  29. package/dist/src/prompts.cjs +1 -1
  30. package/dist/src/prompts.cjs.map +1 -1
  31. package/dist/src/reconcile.cjs +361 -0
  32. package/dist/src/reconcile.cjs.map +1 -0
  33. package/dist/src/reconcile.d.cts +26 -0
  34. package/dist/src/runCli.cjs +155 -27
  35. package/dist/src/runCli.cjs.map +1 -1
  36. package/dist/src/state.cjs +107 -0
  37. package/dist/src/state.cjs.map +1 -0
  38. package/dist/src/state.d.cts +50 -0
  39. package/dist/src/template.cjs +45 -0
  40. package/dist/src/template.cjs.map +1 -0
  41. package/dist/src/template.d.cts +10 -0
  42. package/dist/src/types.d.cts +20 -1
  43. package/dist/src/update.cjs +91 -0
  44. package/dist/src/update.cjs.map +1 -0
  45. package/dist/src/update.d.cts +33 -0
  46. package/package.json +12 -4
  47. package/templates/minimal-app/README.md.tpl +18 -3
  48. package/templates/minimal-app/apps/email/src/emails/accountDeletionEmail.tsx.tpl +36 -35
  49. package/templates/minimal-app/apps/email/src/emails/organizationInviteEmail.tsx.tpl +35 -34
  50. package/templates/minimal-app/apps/email/src/emails/passwordResetEmail.tsx.tpl +36 -35
  51. package/templates/minimal-app/apps/email/src/emails/verificationEmail.tsx.tpl +38 -37
  52. package/templates/minimal-app/apps/email/src/emails/waitlistEmails.tsx.tpl +88 -87
  53. package/templates/minimal-app/apps/email/src/index.ts.tpl +74 -72
  54. package/templates/minimal-app/apps/email/tsconfig.json.tpl +11 -16
  55. package/templates/minimal-app/apps/server/tsconfig.json.tpl +13 -20
  56. package/templates/minimal-app/apps/shared/tsconfig.json.tpl +12 -16
  57. package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsList.ts.tpl +98 -98
  58. package/templates/minimal-app/biome.json.tpl +9 -2
  59. package/templates/minimal-app/pnpm-workspace.yaml.tpl +61 -63
  60. package/templates/minimal-app/template.manifest.json +30 -0
@@ -0,0 +1,36 @@
1
+ import { DiagnosticReport } from "./diagnostics.cjs";
2
+
3
+ //#region src/init.d.ts
4
+ interface InitCommandOptions {
5
+ repoRoot: string;
6
+ yes: boolean;
7
+ force: boolean;
8
+ }
9
+ declare function initializeManagedRepo(options: InitCommandOptions): Promise<{
10
+ initialized: boolean;
11
+ state: {
12
+ schemaVersion: 1;
13
+ template: {
14
+ name: string;
15
+ version: string;
16
+ features: string[];
17
+ context: {
18
+ appName: string;
19
+ appDescription: string;
20
+ appSlug: string;
21
+ packageScope: string;
22
+ };
23
+ };
24
+ catalog: Record<string, string>;
25
+ files: Record<string, {
26
+ kind: "text" | "binary";
27
+ policy: "merge" | "ensure" | "ignore";
28
+ sha256?: string | undefined;
29
+ }>;
30
+ appliedMigrations: string[];
31
+ };
32
+ report: DiagnosticReport;
33
+ }>;
34
+ //#endregion
35
+ export { InitCommandOptions, initializeManagedRepo };
36
+ //# sourceMappingURL=init.d.cts.map
@@ -0,0 +1,49 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
3
+ let jscodeshift = require("jscodeshift");
4
+ jscodeshift = require_runtime.__toESM(jscodeshift);
5
+ //#region src/migrations/ast.ts
6
+ function transformTypeScript(source, filePath, transform) {
7
+ const parser = filePath.endsWith(".tsx") ? "tsx" : "ts";
8
+ const j = jscodeshift.default.withParser(parser);
9
+ return transform({
10
+ path: filePath,
11
+ source
12
+ }, {
13
+ jscodeshift: j,
14
+ j,
15
+ stats: () => void 0,
16
+ report: () => void 0
17
+ }, { parser }) ?? source;
18
+ }
19
+ function renameImportSource(from, to) {
20
+ return (file, api) => {
21
+ const root = api.jscodeshift(file.source);
22
+ for (const declaration of root.find(api.jscodeshift.ImportDeclaration, { source: { value: from } }).nodes()) declaration.source.value = to;
23
+ return root.toSource();
24
+ };
25
+ }
26
+ function ensureNamedImport(moduleName, importedName) {
27
+ return (file, api) => {
28
+ const j = api.jscodeshift;
29
+ const root = j(file.source);
30
+ const declarations = root.find(j.ImportDeclaration, { source: { value: moduleName } });
31
+ if (declarations.find(j.ImportSpecifier, { imported: {
32
+ type: "Identifier",
33
+ name: importedName
34
+ } }).size() > 0) return file.source;
35
+ const first = declarations.at(0);
36
+ if (first.size() > 0) {
37
+ const declaration = first.nodes()[0];
38
+ declaration.specifiers ??= [];
39
+ declaration.specifiers.push(j.importSpecifier(j.identifier(importedName)));
40
+ } else root.get().node.program.body.unshift(j.importDeclaration([j.importSpecifier(j.identifier(importedName))], j.literal(moduleName)));
41
+ return root.toSource();
42
+ };
43
+ }
44
+ //#endregion
45
+ exports.ensureNamedImport = ensureNamedImport;
46
+ exports.renameImportSource = renameImportSource;
47
+ exports.transformTypeScript = transformTypeScript;
48
+
49
+ //# sourceMappingURL=ast.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.cjs","names":[],"sources":["../../../src/migrations/ast.ts"],"sourcesContent":["import jscodeshift, { type API, type FileInfo, type Options } from \"jscodeshift\";\r\n\r\nexport type SourceTransform = (\r\n file: FileInfo,\r\n api: API,\r\n options: Options\r\n) => string | null | undefined;\r\n\r\nexport function transformTypeScript(\r\n source: string,\r\n filePath: string,\r\n transform: SourceTransform\r\n): string {\r\n const parser = filePath.endsWith(\".tsx\") ? \"tsx\" : \"ts\";\r\n const j = jscodeshift.withParser(parser);\r\n return (\r\n transform(\r\n { path: filePath, source },\r\n { jscodeshift: j, j, stats: () => undefined, report: () => undefined },\r\n { parser }\r\n ) ?? source\r\n );\r\n}\r\n\r\nexport function renameImportSource(from: string, to: string): SourceTransform {\r\n return (file, api) => {\r\n const root = api.jscodeshift(file.source);\r\n for (const declaration of root\r\n .find(api.jscodeshift.ImportDeclaration, { source: { value: from } })\r\n .nodes()) {\r\n declaration.source.value = to;\r\n }\r\n return root.toSource();\r\n };\r\n}\r\n\r\nexport function ensureNamedImport(moduleName: string, importedName: string): SourceTransform {\r\n return (file, api) => {\r\n const j = api.jscodeshift;\r\n const root = j(file.source);\r\n const declarations = root.find(j.ImportDeclaration, { source: { value: moduleName } });\r\n const existing = declarations.find(j.ImportSpecifier, {\r\n imported: { type: \"Identifier\", name: importedName },\r\n });\r\n if (existing.size() > 0) return file.source;\r\n\r\n const first = declarations.at(0);\r\n if (first.size() > 0) {\r\n const declaration = first.nodes()[0];\r\n declaration.specifiers ??= [];\r\n declaration.specifiers.push(j.importSpecifier(j.identifier(importedName)));\r\n } else {\r\n root\r\n .get()\r\n .node.program.body.unshift(\r\n j.importDeclaration(\r\n [j.importSpecifier(j.identifier(importedName))],\r\n j.literal(moduleName)\r\n )\r\n );\r\n }\r\n return root.toSource();\r\n };\r\n}\r\n"],"mappings":";;;;;AAQA,SAAgB,oBACd,QACA,UACA,WACQ;CACR,MAAM,SAAS,SAAS,SAAS,OAAO,GAAG,QAAQ;CACnD,MAAM,IAAI,YAAA,QAAY,WAAW,OAAO;AACxC,QACE,UACE;EAAE,MAAM;EAAU;EAAQ,EAC1B;EAAE,aAAa;EAAG;EAAG,aAAa,KAAA;EAAW,cAAc,KAAA;EAAW,EACtE,EAAE,QAAQ,CACX,IAAI;;AAIT,SAAgB,mBAAmB,MAAc,IAA6B;AAC5E,SAAQ,MAAM,QAAQ;EACpB,MAAM,OAAO,IAAI,YAAY,KAAK,OAAO;AACzC,OAAK,MAAM,eAAe,KACvB,KAAK,IAAI,YAAY,mBAAmB,EAAE,QAAQ,EAAE,OAAO,MAAM,EAAE,CAAC,CACpE,OAAO,CACR,aAAY,OAAO,QAAQ;AAE7B,SAAO,KAAK,UAAU;;;AAI1B,SAAgB,kBAAkB,YAAoB,cAAuC;AAC3F,SAAQ,MAAM,QAAQ;EACpB,MAAM,IAAI,IAAI;EACd,MAAM,OAAO,EAAE,KAAK,OAAO;EAC3B,MAAM,eAAe,KAAK,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,OAAO,YAAY,EAAE,CAAC;AAItF,MAHiB,aAAa,KAAK,EAAE,iBAAiB,EACpD,UAAU;GAAE,MAAM;GAAc,MAAM;GAAc,EACrD,CAAC,CACW,MAAM,GAAG,EAAG,QAAO,KAAK;EAErC,MAAM,QAAQ,aAAa,GAAG,EAAE;AAChC,MAAI,MAAM,MAAM,GAAG,GAAG;GACpB,MAAM,cAAc,MAAM,OAAO,CAAC;AAClC,eAAY,eAAe,EAAE;AAC7B,eAAY,WAAW,KAAK,EAAE,gBAAgB,EAAE,WAAW,aAAa,CAAC,CAAC;QAE1E,MACG,KAAK,CACL,KAAK,QAAQ,KAAK,QACjB,EAAE,kBACA,CAAC,EAAE,gBAAgB,EAAE,WAAW,aAAa,CAAC,CAAC,EAC/C,EAAE,QAAQ,WAAW,CACtB,CACF;AAEL,SAAO,KAAK,UAAU"}
@@ -0,0 +1,10 @@
1
+ import { API, FileInfo, Options } from "jscodeshift";
2
+
3
+ //#region src/migrations/ast.d.ts
4
+ type SourceTransform = (file: FileInfo, api: API, options: Options) => string | null | undefined;
5
+ declare function transformTypeScript(source: string, filePath: string, transform: SourceTransform): string;
6
+ declare function renameImportSource(from: string, to: string): SourceTransform;
7
+ declare function ensureNamedImport(moduleName: string, importedName: string): SourceTransform;
8
+ //#endregion
9
+ export { SourceTransform, ensureNamedImport, renameImportSource, transformTypeScript };
10
+ //# sourceMappingURL=ast.d.cts.map
@@ -0,0 +1,41 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
3
+ let semver = require("semver");
4
+ semver = require_runtime.__toESM(semver);
5
+ //#region src/migrations/registry.ts
6
+ const migrations = [];
7
+ function validateMigrationRegistry(registry = migrations) {
8
+ const ids = /* @__PURE__ */ new Set();
9
+ let previousVersion;
10
+ for (const migration of registry) {
11
+ if (ids.has(migration.id)) throw new Error(`Duplicate migration id: ${migration.id}`);
12
+ if (!semver.default.valid(migration.targetVersion)) throw new Error(`Invalid target version for migration ${migration.id}: ${migration.targetVersion}`);
13
+ if (previousVersion && semver.default.gt(previousVersion, migration.targetVersion)) throw new Error(`Migration registry is not ordered at ${migration.id}`);
14
+ ids.add(migration.id);
15
+ previousVersion = migration.targetVersion;
16
+ }
17
+ }
18
+ function getPendingMigrations(state, targetVersion, registry = migrations) {
19
+ validateMigrationRegistry(registry);
20
+ const applied = new Set(state.appliedMigrations);
21
+ return registry.filter((migration) => !applied.has(migration.id) && semver.default.gt(migration.targetVersion, state.template.version) && semver.default.lte(migration.targetVersion, targetVersion));
22
+ }
23
+ async function runMigrationValidators(context, registry = migrations) {
24
+ const diagnostics = [];
25
+ const applied = new Set(context.state.appliedMigrations);
26
+ for (const migration of registry) {
27
+ if (!applied.has(migration.id) || !migration.validate) continue;
28
+ diagnostics.push(...await migration.validate(context));
29
+ }
30
+ return diagnostics;
31
+ }
32
+ function getMigrationRegistry() {
33
+ return migrations;
34
+ }
35
+ //#endregion
36
+ exports.getMigrationRegistry = getMigrationRegistry;
37
+ exports.getPendingMigrations = getPendingMigrations;
38
+ exports.runMigrationValidators = runMigrationValidators;
39
+ exports.validateMigrationRegistry = validateMigrationRegistry;
40
+
41
+ //# sourceMappingURL=registry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.cjs","names":[],"sources":["../../../src/migrations/registry.ts"],"sourcesContent":["import semver from \"semver\";\r\nimport type { Diagnostic } from \"../diagnostics\";\r\nimport type { ManagedState } from \"../state\";\r\nimport type { MigrationContext, MigrationDefinition } from \"./types\";\r\n\r\nconst migrations: MigrationDefinition[] = [];\r\n\r\nexport function validateMigrationRegistry(\r\n registry: readonly MigrationDefinition[] = migrations\r\n): void {\r\n const ids = new Set<string>();\r\n let previousVersion: string | undefined;\r\n for (const migration of registry) {\r\n if (ids.has(migration.id)) throw new Error(`Duplicate migration id: ${migration.id}`);\r\n if (!semver.valid(migration.targetVersion)) {\r\n throw new Error(\r\n `Invalid target version for migration ${migration.id}: ${migration.targetVersion}`\r\n );\r\n }\r\n if (previousVersion && semver.gt(previousVersion, migration.targetVersion)) {\r\n throw new Error(`Migration registry is not ordered at ${migration.id}`);\r\n }\r\n ids.add(migration.id);\r\n previousVersion = migration.targetVersion;\r\n }\r\n}\r\n\r\nexport function getPendingMigrations(\r\n state: ManagedState,\r\n targetVersion: string,\r\n registry: readonly MigrationDefinition[] = migrations\r\n): MigrationDefinition[] {\r\n validateMigrationRegistry(registry);\r\n const applied = new Set(state.appliedMigrations);\r\n return registry.filter(\r\n (migration) =>\r\n !applied.has(migration.id) &&\r\n semver.gt(migration.targetVersion, state.template.version) &&\r\n semver.lte(migration.targetVersion, targetVersion)\r\n );\r\n}\r\n\r\nexport async function runMigrationValidators(\r\n context: MigrationContext,\r\n registry: readonly MigrationDefinition[] = migrations\r\n): Promise<Diagnostic[]> {\r\n const diagnostics: Diagnostic[] = [];\r\n const applied = new Set(context.state.appliedMigrations);\r\n for (const migration of registry) {\r\n if (!applied.has(migration.id) || !migration.validate) continue;\r\n diagnostics.push(...(await migration.validate(context)));\r\n }\r\n return diagnostics;\r\n}\r\n\r\nexport function getMigrationRegistry(): readonly MigrationDefinition[] {\r\n return migrations;\r\n}\r\n"],"mappings":";;;;;AAKA,MAAM,aAAoC,EAAE;AAE5C,SAAgB,0BACd,WAA2C,YACrC;CACN,MAAM,sBAAM,IAAI,KAAa;CAC7B,IAAI;AACJ,MAAK,MAAM,aAAa,UAAU;AAChC,MAAI,IAAI,IAAI,UAAU,GAAG,CAAE,OAAM,IAAI,MAAM,2BAA2B,UAAU,KAAK;AACrF,MAAI,CAAC,OAAA,QAAO,MAAM,UAAU,cAAc,CACxC,OAAM,IAAI,MACR,wCAAwC,UAAU,GAAG,IAAI,UAAU,gBACpE;AAEH,MAAI,mBAAmB,OAAA,QAAO,GAAG,iBAAiB,UAAU,cAAc,CACxE,OAAM,IAAI,MAAM,wCAAwC,UAAU,KAAK;AAEzE,MAAI,IAAI,UAAU,GAAG;AACrB,oBAAkB,UAAU;;;AAIhC,SAAgB,qBACd,OACA,eACA,WAA2C,YACpB;AACvB,2BAA0B,SAAS;CACnC,MAAM,UAAU,IAAI,IAAI,MAAM,kBAAkB;AAChD,QAAO,SAAS,QACb,cACC,CAAC,QAAQ,IAAI,UAAU,GAAG,IAC1B,OAAA,QAAO,GAAG,UAAU,eAAe,MAAM,SAAS,QAAQ,IAC1D,OAAA,QAAO,IAAI,UAAU,eAAe,cAAc,CACrD;;AAGH,eAAsB,uBACpB,SACA,WAA2C,YACpB;CACvB,MAAM,cAA4B,EAAE;CACpC,MAAM,UAAU,IAAI,IAAI,QAAQ,MAAM,kBAAkB;AACxD,MAAK,MAAM,aAAa,UAAU;AAChC,MAAI,CAAC,QAAQ,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,SAAU;AACvD,cAAY,KAAK,GAAI,MAAM,UAAU,SAAS,QAAQ,CAAE;;AAE1D,QAAO;;AAGT,SAAgB,uBAAuD;AACrE,QAAO"}
@@ -0,0 +1,12 @@
1
+ import { Diagnostic } from "../diagnostics.cjs";
2
+ import { ManagedState } from "../state.cjs";
3
+ import { MigrationContext, MigrationDefinition } from "./types.cjs";
4
+
5
+ //#region src/migrations/registry.d.ts
6
+ declare function validateMigrationRegistry(registry?: readonly MigrationDefinition[]): void;
7
+ declare function getPendingMigrations(state: ManagedState, targetVersion: string, registry?: readonly MigrationDefinition[]): MigrationDefinition[];
8
+ declare function runMigrationValidators(context: MigrationContext, registry?: readonly MigrationDefinition[]): Promise<Diagnostic[]>;
9
+ declare function getMigrationRegistry(): readonly MigrationDefinition[];
10
+ //#endregion
11
+ export { getMigrationRegistry, getPendingMigrations, runMigrationValidators, validateMigrationRegistry };
12
+ //# sourceMappingURL=registry.d.cts.map
File without changes
@@ -0,0 +1,22 @@
1
+ import { ChangeSet } from "../changes.cjs";
2
+ import { Diagnostic } from "../diagnostics.cjs";
3
+ import { ManagedState } from "../state.cjs";
4
+
5
+ //#region src/migrations/types.d.ts
6
+ interface MigrationContext {
7
+ repoRoot: string;
8
+ state: ManagedState;
9
+ changes: ChangeSet;
10
+ }
11
+ interface MigrationDefinition {
12
+ id: string;
13
+ targetVersion: string;
14
+ description: string;
15
+ guide: string;
16
+ applies?: (context: MigrationContext) => boolean | Promise<boolean>;
17
+ plan: (context: MigrationContext) => void | Promise<void>;
18
+ validate?: (context: MigrationContext) => Diagnostic[] | Promise<Diagnostic[]>;
19
+ }
20
+ //#endregion
21
+ export { MigrationContext, MigrationDefinition };
22
+ //# sourceMappingURL=types.d.cts.map
@@ -2,9 +2,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
3
3
  const require_src_constants = require("./constants.cjs");
4
4
  const require_src_strings = require("./strings.cjs");
5
+ let node_process = require("node:process");
5
6
  let node_readline_promises = require("node:readline/promises");
6
7
  node_readline_promises = require_runtime.__toESM(node_readline_promises);
7
- let node_process = require("node:process");
8
8
  //#region src/prompts.ts
9
9
  function requireInteractive(yes) {
10
10
  if (!yes && !process.stdin.isTTY) throw new Error("Missing required values in a non-interactive shell. Pass --yes or provide flags.");
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.cjs","names":["readline","toDisplayName","DEFAULT_APP_NAME","slugifyAppName","getDefaultDescription"],"sources":["../../src/prompts.ts"],"sourcesContent":["import readline from \"node:readline/promises\";\nimport { stdin as input, stdout as output } from \"node:process\";\nimport { DEFAULT_APP_NAME, getDefaultDescription } from \"./constants\";\nimport { slugifyAppName, toDisplayName } from \"./strings\";\nimport type { AppPlatform, CreateCommandOptions } from \"./types\";\n\nfunction requireInteractive(yes: boolean): void {\n if (!yes && !process.stdin.isTTY) {\n throw new Error(\"Missing required values in a non-interactive shell. Pass --yes or provide flags.\");\n }\n}\n\nasync function promptValue(question: string, fallback?: string): Promise<string> {\n const rl = readline.createInterface({ input, output });\n\n try {\n const suffix = fallback ? ` (${fallback})` : \"\";\n const response = await rl.question(`${question}${suffix}: `);\n return response.trim() || fallback || \"\";\n } finally {\n rl.close();\n }\n}\n\nfunction parsePlatform(value: string): AppPlatform | undefined {\n const normalized = value.trim().toLowerCase();\n if (normalized === \"web\" || normalized === \"expo\" || normalized === \"both\") {\n return normalized;\n }\n return undefined;\n}\n\nexport async function resolveCreateCommandOptions(\n options: CreateCommandOptions\n): Promise<CreateCommandOptions> {\n const resolved = { ...options };\n\n if (!resolved.appName) {\n requireInteractive(resolved.yes);\n resolved.appName = resolved.yes\n ? resolved.targetDirectory\n ? toDisplayName(pathBaseName(resolved.targetDirectory))\n : DEFAULT_APP_NAME\n : await promptValue(\"App name\", resolved.targetDirectory ? pathBaseName(resolved.targetDirectory) : DEFAULT_APP_NAME);\n }\n\n resolved.appName = toDisplayName(resolved.appName);\n\n if (!resolved.targetDirectory) {\n requireInteractive(resolved.yes);\n resolved.targetDirectory = resolved.yes\n ? slugifyAppName(resolved.appName)\n : await promptValue(\"Target directory\", slugifyAppName(resolved.appName));\n }\n\n if (!resolved.appDescription) {\n requireInteractive(resolved.yes);\n const fallback = getDefaultDescription(resolved.appName);\n resolved.appDescription = resolved.yes\n ? fallback\n : await promptValue(\"App description\", fallback);\n }\n\n if (!resolved.platform) {\n requireInteractive(resolved.yes);\n if (resolved.yes) {\n resolved.platform = \"web\";\n } else {\n const answer = await promptValue(\"App platform — web, expo, or both\", \"web\");\n const platform = parsePlatform(answer);\n if (!platform) {\n throw new Error(`Invalid platform \"${answer}\". Use web, expo, or both.`);\n }\n resolved.platform = platform;\n }\n }\n\n if (resolved.testHarness === undefined) {\n requireInteractive(resolved.yes);\n if (resolved.yes) {\n resolved.testHarness = false;\n } else {\n const answer = await promptValue(\"Include the e2e test harness? y/N\", \"n\");\n resolved.testHarness = /^y(es)?$/i.test(answer.trim());\n }\n }\n\n return resolved;\n}\n\nfunction pathBaseName(value: string): string {\n const normalized = value.replace(/[\\\\/]+$/g, \"\");\n const segments = normalized.split(/[\\\\/]/).filter(Boolean);\n return segments.at(-1) || DEFAULT_APP_NAME;\n}\n"],"mappings":";;;;;;;;AAMA,SAAS,mBAAmB,KAAoB;AAC9C,KAAI,CAAC,OAAO,CAAC,QAAQ,MAAM,MACzB,OAAM,IAAI,MAAM,mFAAmF;;AAIvG,eAAe,YAAY,UAAkB,UAAoC;CAC/E,MAAM,KAAKA,uBAAAA,QAAS,gBAAgB;EAAE,OAAA,aAAA;EAAO,QAAA,aAAA;EAAQ,CAAC;AAEtD,KAAI;EACF,MAAM,SAAS,WAAW,KAAK,SAAS,KAAK;AAE7C,UADiB,MAAM,GAAG,SAAS,GAAG,WAAW,OAAO,IAAI,EAC5C,MAAM,IAAI,YAAY;WAC9B;AACR,KAAG,OAAO;;;AAId,SAAS,cAAc,OAAwC;CAC7D,MAAM,aAAa,MAAM,MAAM,CAAC,aAAa;AAC7C,KAAI,eAAe,SAAS,eAAe,UAAU,eAAe,OAClE,QAAO;;AAKX,eAAsB,4BACpB,SAC+B;CAC/B,MAAM,WAAW,EAAE,GAAG,SAAS;AAE/B,KAAI,CAAC,SAAS,SAAS;AACrB,qBAAmB,SAAS,IAAI;AAChC,WAAS,UAAU,SAAS,MACxB,SAAS,kBACPC,oBAAAA,cAAc,aAAa,SAAS,gBAAgB,CAAC,GACrDC,sBAAAA,mBACF,MAAM,YAAY,YAAY,SAAS,kBAAkB,aAAa,SAAS,gBAAgB,GAAGA,sBAAAA,iBAAiB;;AAGzH,UAAS,UAAUD,oBAAAA,cAAc,SAAS,QAAQ;AAElD,KAAI,CAAC,SAAS,iBAAiB;AAC7B,qBAAmB,SAAS,IAAI;AAChC,WAAS,kBAAkB,SAAS,MAChCE,oBAAAA,eAAe,SAAS,QAAQ,GAChC,MAAM,YAAY,oBAAoBA,oBAAAA,eAAe,SAAS,QAAQ,CAAC;;AAG7E,KAAI,CAAC,SAAS,gBAAgB;AAC5B,qBAAmB,SAAS,IAAI;EAChC,MAAM,WAAWC,sBAAAA,sBAAsB,SAAS,QAAQ;AACxD,WAAS,iBAAiB,SAAS,MAC/B,WACA,MAAM,YAAY,mBAAmB,SAAS;;AAGpD,KAAI,CAAC,SAAS,UAAU;AACtB,qBAAmB,SAAS,IAAI;AAChC,MAAI,SAAS,IACX,UAAS,WAAW;OACf;GACL,MAAM,SAAS,MAAM,YAAY,qCAAqC,MAAM;GAC5E,MAAM,WAAW,cAAc,OAAO;AACtC,OAAI,CAAC,SACH,OAAM,IAAI,MAAM,qBAAqB,OAAO,4BAA4B;AAE1E,YAAS,WAAW;;;AAIxB,KAAI,SAAS,gBAAgB,KAAA,GAAW;AACtC,qBAAmB,SAAS,IAAI;AAChC,MAAI,SAAS,IACX,UAAS,cAAc;OAClB;GACL,MAAM,SAAS,MAAM,YAAY,qCAAqC,IAAI;AAC1E,YAAS,cAAc,YAAY,KAAK,OAAO,MAAM,CAAC;;;AAI1D,QAAO;;AAGT,SAAS,aAAa,OAAuB;AAG3C,QAFmB,MAAM,QAAQ,YAAY,GAAG,CACpB,MAAM,QAAQ,CAAC,OAAO,QAAQ,CAC1C,GAAG,GAAG,IAAA"}
1
+ {"version":3,"file":"prompts.cjs","names":["readline","toDisplayName","DEFAULT_APP_NAME","slugifyAppName","getDefaultDescription"],"sources":["../../src/prompts.ts"],"sourcesContent":["import { stdin as input, stdout as output } from \"node:process\";\r\nimport readline from \"node:readline/promises\";\r\nimport { DEFAULT_APP_NAME, getDefaultDescription } from \"./constants\";\r\nimport { slugifyAppName, toDisplayName } from \"./strings\";\r\nimport type { AppPlatform, CreateCommandOptions } from \"./types\";\r\n\r\nfunction requireInteractive(yes: boolean): void {\r\n if (!yes && !process.stdin.isTTY) {\r\n throw new Error(\r\n \"Missing required values in a non-interactive shell. Pass --yes or provide flags.\"\r\n );\r\n }\r\n}\r\n\r\nasync function promptValue(question: string, fallback?: string): Promise<string> {\r\n const rl = readline.createInterface({ input, output });\r\n\r\n try {\r\n const suffix = fallback ? ` (${fallback})` : \"\";\r\n const response = await rl.question(`${question}${suffix}: `);\r\n return response.trim() || fallback || \"\";\r\n } finally {\r\n rl.close();\r\n }\r\n}\r\n\r\nfunction parsePlatform(value: string): AppPlatform | undefined {\r\n const normalized = value.trim().toLowerCase();\r\n if (normalized === \"web\" || normalized === \"expo\" || normalized === \"both\") {\r\n return normalized;\r\n }\r\n return undefined;\r\n}\r\n\r\nexport async function resolveCreateCommandOptions(\r\n options: CreateCommandOptions\r\n): Promise<CreateCommandOptions> {\r\n const resolved = { ...options };\r\n\r\n if (!resolved.appName) {\r\n requireInteractive(resolved.yes);\r\n resolved.appName = resolved.yes\r\n ? resolved.targetDirectory\r\n ? toDisplayName(pathBaseName(resolved.targetDirectory))\r\n : DEFAULT_APP_NAME\r\n : await promptValue(\r\n \"App name\",\r\n resolved.targetDirectory ? pathBaseName(resolved.targetDirectory) : DEFAULT_APP_NAME\r\n );\r\n }\r\n\r\n resolved.appName = toDisplayName(resolved.appName);\r\n\r\n if (!resolved.targetDirectory) {\r\n requireInteractive(resolved.yes);\r\n resolved.targetDirectory = resolved.yes\r\n ? slugifyAppName(resolved.appName)\r\n : await promptValue(\"Target directory\", slugifyAppName(resolved.appName));\r\n }\r\n\r\n if (!resolved.appDescription) {\r\n requireInteractive(resolved.yes);\r\n const fallback = getDefaultDescription(resolved.appName);\r\n resolved.appDescription = resolved.yes\r\n ? fallback\r\n : await promptValue(\"App description\", fallback);\r\n }\r\n\r\n if (!resolved.platform) {\r\n requireInteractive(resolved.yes);\r\n if (resolved.yes) {\r\n resolved.platform = \"web\";\r\n } else {\r\n const answer = await promptValue(\"App platform — web, expo, or both\", \"web\");\r\n const platform = parsePlatform(answer);\r\n if (!platform) {\r\n throw new Error(`Invalid platform \"${answer}\". Use web, expo, or both.`);\r\n }\r\n resolved.platform = platform;\r\n }\r\n }\r\n\r\n if (resolved.testHarness === undefined) {\r\n requireInteractive(resolved.yes);\r\n if (resolved.yes) {\r\n resolved.testHarness = false;\r\n } else {\r\n const answer = await promptValue(\"Include the e2e test harness? y/N\", \"n\");\r\n resolved.testHarness = /^y(es)?$/i.test(answer.trim());\r\n }\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\nfunction pathBaseName(value: string): string {\r\n const normalized = value.replace(/[\\\\/]+$/g, \"\");\r\n const segments = normalized.split(/[\\\\/]/).filter(Boolean);\r\n return segments.at(-1) || DEFAULT_APP_NAME;\r\n}\r\n"],"mappings":";;;;;;;;AAMA,SAAS,mBAAmB,KAAoB;AAC9C,KAAI,CAAC,OAAO,CAAC,QAAQ,MAAM,MACzB,OAAM,IAAI,MACR,mFACD;;AAIL,eAAe,YAAY,UAAkB,UAAoC;CAC/E,MAAM,KAAKA,uBAAAA,QAAS,gBAAgB;EAAE,OAAA,aAAA;EAAO,QAAA,aAAA;EAAQ,CAAC;AAEtD,KAAI;EACF,MAAM,SAAS,WAAW,KAAK,SAAS,KAAK;AAE7C,UADiB,MAAM,GAAG,SAAS,GAAG,WAAW,OAAO,IAAI,EAC5C,MAAM,IAAI,YAAY;WAC9B;AACR,KAAG,OAAO;;;AAId,SAAS,cAAc,OAAwC;CAC7D,MAAM,aAAa,MAAM,MAAM,CAAC,aAAa;AAC7C,KAAI,eAAe,SAAS,eAAe,UAAU,eAAe,OAClE,QAAO;;AAKX,eAAsB,4BACpB,SAC+B;CAC/B,MAAM,WAAW,EAAE,GAAG,SAAS;AAE/B,KAAI,CAAC,SAAS,SAAS;AACrB,qBAAmB,SAAS,IAAI;AAChC,WAAS,UAAU,SAAS,MACxB,SAAS,kBACPC,oBAAAA,cAAc,aAAa,SAAS,gBAAgB,CAAC,GACrDC,sBAAAA,mBACF,MAAM,YACJ,YACA,SAAS,kBAAkB,aAAa,SAAS,gBAAgB,GAAGA,sBAAAA,iBACrE;;AAGP,UAAS,UAAUD,oBAAAA,cAAc,SAAS,QAAQ;AAElD,KAAI,CAAC,SAAS,iBAAiB;AAC7B,qBAAmB,SAAS,IAAI;AAChC,WAAS,kBAAkB,SAAS,MAChCE,oBAAAA,eAAe,SAAS,QAAQ,GAChC,MAAM,YAAY,oBAAoBA,oBAAAA,eAAe,SAAS,QAAQ,CAAC;;AAG7E,KAAI,CAAC,SAAS,gBAAgB;AAC5B,qBAAmB,SAAS,IAAI;EAChC,MAAM,WAAWC,sBAAAA,sBAAsB,SAAS,QAAQ;AACxD,WAAS,iBAAiB,SAAS,MAC/B,WACA,MAAM,YAAY,mBAAmB,SAAS;;AAGpD,KAAI,CAAC,SAAS,UAAU;AACtB,qBAAmB,SAAS,IAAI;AAChC,MAAI,SAAS,IACX,UAAS,WAAW;OACf;GACL,MAAM,SAAS,MAAM,YAAY,qCAAqC,MAAM;GAC5E,MAAM,WAAW,cAAc,OAAO;AACtC,OAAI,CAAC,SACH,OAAM,IAAI,MAAM,qBAAqB,OAAO,4BAA4B;AAE1E,YAAS,WAAW;;;AAIxB,KAAI,SAAS,gBAAgB,KAAA,GAAW;AACtC,qBAAmB,SAAS,IAAI;AAChC,MAAI,SAAS,IACX,UAAS,cAAc;OAClB;GACL,MAAM,SAAS,MAAM,YAAY,qCAAqC,IAAI;AAC1E,YAAS,cAAc,YAAY,KAAK,OAAO,MAAM,CAAC;;;AAI1D,QAAO;;AAGT,SAAS,aAAa,OAAuB;AAG3C,QAFmB,MAAM,QAAQ,YAAY,GAAG,CACpB,MAAM,QAAQ,CAAC,OAAO,QAAQ,CAC1C,GAAG,GAAG,IAAA"}
@@ -0,0 +1,361 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
3
+ const require_src_catalog = require("./catalog.cjs");
4
+ const require_src_changes = require("./changes.cjs");
5
+ const require_src_fs = require("./fs.cjs");
6
+ const require_src_template = require("./template.cjs");
7
+ const require_src_state = require("./state.cjs");
8
+ let node_path = require("node:path");
9
+ node_path = require_runtime.__toESM(node_path);
10
+ let node_fs_promises = require("node:fs/promises");
11
+ node_fs_promises = require_runtime.__toESM(node_fs_promises);
12
+ let node_child_process = require("node:child_process");
13
+ let node_util = require("node:util");
14
+ let node_os = require("node:os");
15
+ node_os = require_runtime.__toESM(node_os);
16
+ //#region src/reconcile.ts
17
+ const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
18
+ const npmBaseTemplateProvider = { async getTemplateRoot(version) {
19
+ const directory = await node_fs_promises.default.mkdtemp(node_path.default.join(node_os.default.tmpdir(), "m5kdev-template-"));
20
+ try {
21
+ const { default: pacote } = await import("pacote");
22
+ await pacote.extract(`create-m5kdev@${version}`, directory);
23
+ const root = node_path.default.join(directory, "templates", "minimal-app");
24
+ await node_fs_promises.default.access(node_path.default.join(root, "template.manifest.json"));
25
+ return {
26
+ root,
27
+ cleanup: () => node_fs_promises.default.rm(directory, {
28
+ recursive: true,
29
+ force: true
30
+ })
31
+ };
32
+ } catch (error) {
33
+ await node_fs_promises.default.rm(directory, {
34
+ recursive: true,
35
+ force: true
36
+ });
37
+ throw error;
38
+ }
39
+ } };
40
+ function toTemplateContext(state) {
41
+ return {
42
+ ...state.template.context,
43
+ betterAuthSecret: "ignored-by-managed-state"
44
+ };
45
+ }
46
+ async function renderForState(templateRoot, state) {
47
+ const manifest = require_src_template.loadTemplateManifest(templateRoot);
48
+ const enabledFeatures = new Set(state.template.features);
49
+ return {
50
+ manifest,
51
+ files: await require_src_fs.collectTemplateFiles(templateRoot, toTemplateContext(state), {
52
+ enabledFeatures,
53
+ excludePrefixes: require_src_template.getExcludedFeaturePaths(manifest, enabledFeatures),
54
+ ignoreContentFor: (relativePath) => require_src_template.getTemplateFilePolicy(manifest, relativePath) === "ignore"
55
+ })
56
+ };
57
+ }
58
+ async function mergeText(base, local, target) {
59
+ const directory = await node_fs_promises.default.mkdtemp(node_path.default.join(node_os.default.tmpdir(), "m5kdev-merge-"));
60
+ const localPath = node_path.default.join(directory, "local");
61
+ const basePath = node_path.default.join(directory, "base");
62
+ const targetPath = node_path.default.join(directory, "target");
63
+ try {
64
+ await Promise.all([
65
+ node_fs_promises.default.writeFile(localPath, local),
66
+ node_fs_promises.default.writeFile(basePath, base),
67
+ node_fs_promises.default.writeFile(targetPath, target)
68
+ ]);
69
+ try {
70
+ return (await execFileAsync("git", [
71
+ "merge-file",
72
+ "-p",
73
+ "--diff3",
74
+ localPath,
75
+ basePath,
76
+ targetPath
77
+ ], { encoding: "utf8" })).stdout;
78
+ } catch (error) {
79
+ const mergeError = error;
80
+ if (typeof mergeError.code === "number" && mergeError.code >= 1 && mergeError.code <= 127) return;
81
+ throw error;
82
+ }
83
+ } finally {
84
+ await node_fs_promises.default.rm(directory, {
85
+ recursive: true,
86
+ force: true
87
+ });
88
+ }
89
+ }
90
+ async function reconcileTemplates(options) {
91
+ const changes = new require_src_changes.ChangeSet(options.repoRoot);
92
+ const target = await renderForState(options.targetTemplateRoot, options.state);
93
+ const enabledFeatures = new Set(options.state.template.features);
94
+ const targetState = require_src_state.createManagedState({
95
+ templateVersion: options.targetVersion,
96
+ enabledFeatures,
97
+ context: toTemplateContext(options.state),
98
+ renderedFiles: target.files,
99
+ manifest: target.manifest,
100
+ appliedMigrations: options.state.appliedMigrations
101
+ });
102
+ const targetFiles = new Map(target.files.map((file) => [file.relativePath, file]));
103
+ const mergeCandidates = [];
104
+ let dependenciesChanged = false;
105
+ const workspacePath = "pnpm-workspace.yaml";
106
+ const workspaceSymlink = await require_src_changes.findRepositorySymlink(options.repoRoot, workspacePath);
107
+ if (workspaceSymlink) changes.addConflict({
108
+ path: workspacePath,
109
+ reason: "The managed workspace file is a symbolic link."
110
+ });
111
+ const workspace = workspaceSymlink ? void 0 : await changes.read(workspacePath);
112
+ if (workspace) {
113
+ const catalogMerge = require_src_catalog.mergeManagedCatalog({
114
+ source: workspace.toString("utf8"),
115
+ base: options.state.catalog,
116
+ target: targetState.catalog
117
+ });
118
+ for (const conflict of catalogMerge.conflicts) changes.addConflict({
119
+ path: workspacePath,
120
+ reason: `Catalog entry ${conflict.name} changed both locally and in the target template.`
121
+ });
122
+ if (catalogMerge.changed) {
123
+ changes.addChange({
124
+ kind: "modify",
125
+ path: workspacePath,
126
+ content: Buffer.from(catalogMerge.source),
127
+ reason: "Reconcile managed consumer catalog"
128
+ });
129
+ dependenciesChanged = true;
130
+ }
131
+ }
132
+ const paths = new Set([...Object.keys(options.state.files), ...targetFiles.keys()]);
133
+ paths.delete(workspacePath);
134
+ for (const rename of target.manifest.sync.renames ?? []) {
135
+ const baseline = options.state.files[rename.from];
136
+ const targetFile = targetFiles.get(rename.to);
137
+ if (!baseline || !targetFile) {
138
+ changes.addConflict({
139
+ path: rename.from,
140
+ reason: `Invalid template rename ${rename.from} -> ${rename.to}.`
141
+ });
142
+ continue;
143
+ }
144
+ paths.delete(rename.from);
145
+ paths.delete(rename.to);
146
+ const sourcePath = node_path.default.join(options.repoRoot, rename.from);
147
+ const destinationPath = node_path.default.join(options.repoRoot, rename.to);
148
+ const sourceSymlink = await require_src_changes.findRepositorySymlink(options.repoRoot, rename.from);
149
+ const destinationSymlink = await require_src_changes.findRepositorySymlink(options.repoRoot, rename.to);
150
+ if (sourceSymlink || destinationSymlink) {
151
+ changes.addConflict({
152
+ path: rename.from,
153
+ reason: "A renamed managed path contains a symbolic link."
154
+ });
155
+ continue;
156
+ }
157
+ const sourceStat = await node_fs_promises.default.lstat(sourcePath).catch(() => null);
158
+ if (await node_fs_promises.default.lstat(destinationPath).catch(() => null) && rename.from !== rename.to) {
159
+ changes.addConflict({
160
+ path: rename.to,
161
+ reason: "A template rename targets an existing app-owned path."
162
+ });
163
+ continue;
164
+ }
165
+ const local = sourceStat?.isFile() ? await node_fs_promises.default.readFile(sourcePath) : void 0;
166
+ if (!local) {
167
+ if (targetState.files[rename.to]?.policy === "ensure") changes.addChange({
168
+ kind: "add",
169
+ path: rename.to,
170
+ content: targetFile.content,
171
+ reason: `Restore renamed ensured path from ${rename.from}`
172
+ });
173
+ continue;
174
+ }
175
+ const localHash = require_src_state.sha256(local);
176
+ const targetHash = require_src_state.sha256(targetFile.content);
177
+ if (localHash === baseline.sha256) {
178
+ changes.addChange({
179
+ kind: "delete",
180
+ path: rename.from,
181
+ reason: `Rename template path to ${rename.to}`
182
+ });
183
+ changes.addChange({
184
+ kind: "add",
185
+ path: rename.to,
186
+ content: targetFile.content,
187
+ reason: `Rename template path from ${rename.from}`
188
+ });
189
+ } else if (localHash === targetHash) {
190
+ changes.addChange({
191
+ kind: "delete",
192
+ path: rename.from,
193
+ reason: `Rename template path to ${rename.to}`
194
+ });
195
+ changes.addChange({
196
+ kind: "add",
197
+ path: rename.to,
198
+ content: local,
199
+ reason: `Rename template path from ${rename.from}`
200
+ });
201
+ } else if (baseline.kind === "binary" || targetFile.kind === "binary") changes.addConflict({
202
+ path: rename.from,
203
+ reason: "Renamed binary content changed locally and in the target template."
204
+ });
205
+ else mergeCandidates.push({
206
+ path: rename.to,
207
+ basePath: rename.from,
208
+ local,
209
+ target: targetFile,
210
+ deletePath: rename.from
211
+ });
212
+ }
213
+ for (const relativePath of [...paths].sort((left, right) => left.localeCompare(right))) {
214
+ const baseline = options.state.files[relativePath];
215
+ const targetFile = targetFiles.get(relativePath);
216
+ const policy = targetState.files[relativePath]?.policy ?? baseline?.policy ?? "merge";
217
+ if (policy === "ignore") continue;
218
+ const localPath = node_path.default.join(options.repoRoot, relativePath);
219
+ const symlink = await require_src_changes.findRepositorySymlink(options.repoRoot, relativePath);
220
+ if (symlink) {
221
+ changes.addConflict({
222
+ path: relativePath,
223
+ reason: `Managed path contains symbolic link ${symlink}.`
224
+ });
225
+ continue;
226
+ }
227
+ const local = (await node_fs_promises.default.lstat(localPath).catch(() => null))?.isFile() ? await node_fs_promises.default.readFile(localPath) : void 0;
228
+ const localHash = local ? require_src_state.sha256(local) : void 0;
229
+ const baseHash = baseline?.sha256;
230
+ const targetHash = targetFile ? require_src_state.sha256(targetFile.content) : void 0;
231
+ if (!baseline && targetFile) {
232
+ if (!local) changes.addChange({
233
+ kind: "add",
234
+ path: relativePath,
235
+ content: targetFile.content,
236
+ reason: "Add template path"
237
+ });
238
+ else if (localHash !== targetHash && policy !== "ensure") changes.addConflict({
239
+ path: relativePath,
240
+ reason: "The target template adds a path already owned by the app."
241
+ });
242
+ continue;
243
+ }
244
+ if (baseline && !targetFile) {
245
+ if (local && localHash === baseHash) changes.addChange({
246
+ kind: "delete",
247
+ path: relativePath,
248
+ reason: "Remove obsolete template path"
249
+ });
250
+ else if (local) changes.addConflict({
251
+ path: relativePath,
252
+ reason: "The template removed a locally customized path."
253
+ });
254
+ continue;
255
+ }
256
+ if (!baseline || !targetFile) continue;
257
+ if (!local) {
258
+ if (policy === "ensure") changes.addChange({
259
+ kind: "add",
260
+ path: relativePath,
261
+ content: targetFile.content,
262
+ reason: "Restore ensured path"
263
+ });
264
+ else if (targetHash !== baseHash) changes.addConflict({
265
+ path: relativePath,
266
+ reason: "A locally deleted path changed in the target template."
267
+ });
268
+ continue;
269
+ }
270
+ if (localHash === targetHash || localHash !== baseHash && targetHash === baseHash) continue;
271
+ if (localHash === baseHash) {
272
+ changes.addChange({
273
+ kind: "modify",
274
+ path: relativePath,
275
+ content: targetFile.content,
276
+ reason: "Update unchanged template path"
277
+ });
278
+ if (relativePath === "package.json" || relativePath.endsWith("/package.json")) dependenciesChanged = true;
279
+ continue;
280
+ }
281
+ if (baseline.kind === "binary" || targetFile.kind === "binary") {
282
+ changes.addConflict({
283
+ path: relativePath,
284
+ reason: "Binary content changed locally and in the target template."
285
+ });
286
+ continue;
287
+ }
288
+ mergeCandidates.push({
289
+ path: relativePath,
290
+ basePath: relativePath,
291
+ local,
292
+ target: targetFile
293
+ });
294
+ }
295
+ if (mergeCandidates.length > 0) {
296
+ let baseRoot;
297
+ try {
298
+ baseRoot = await (options.baseProvider ?? npmBaseTemplateProvider).getTemplateRoot(options.state.template.version);
299
+ const base = await renderForState(baseRoot.root, options.state);
300
+ const baseFiles = new Map(base.files.map((file) => [file.relativePath, file]));
301
+ for (const candidate of mergeCandidates) {
302
+ const baseFile = baseFiles.get(candidate.basePath);
303
+ if (!baseFile || require_src_state.sha256(baseFile.content) !== options.state.files[candidate.basePath]?.sha256) {
304
+ changes.addConflict({
305
+ path: candidate.path,
306
+ reason: "Historical template content does not match the stored baseline hash."
307
+ });
308
+ continue;
309
+ }
310
+ const merged = await mergeText(baseFile.content, candidate.local, candidate.target.content);
311
+ if (merged === void 0) changes.addConflict({
312
+ path: candidate.path,
313
+ reason: "Three-way merge produced conflicts."
314
+ });
315
+ else if (merged !== candidate.local.toString("utf8")) {
316
+ if (candidate.deletePath) changes.addChange({
317
+ kind: "delete",
318
+ path: candidate.deletePath,
319
+ reason: `Rename template path to ${candidate.path}`
320
+ });
321
+ changes.addChange({
322
+ kind: candidate.deletePath ? "add" : "modify",
323
+ path: candidate.path,
324
+ content: Buffer.from(merged),
325
+ reason: "Three-way merge local and template changes"
326
+ });
327
+ if (candidate.path === "package.json" || candidate.path.endsWith("/package.json")) dependenciesChanged = true;
328
+ } else if (candidate.deletePath) {
329
+ changes.addChange({
330
+ kind: "delete",
331
+ path: candidate.deletePath,
332
+ reason: `Rename template path to ${candidate.path}`
333
+ });
334
+ changes.addChange({
335
+ kind: "add",
336
+ path: candidate.path,
337
+ content: candidate.local,
338
+ reason: `Rename template path from ${candidate.deletePath}`
339
+ });
340
+ }
341
+ }
342
+ } catch (error) {
343
+ for (const candidate of mergeCandidates) changes.addConflict({
344
+ path: candidate.path,
345
+ reason: `Unable to load historical template ${options.state.template.version}: ${error instanceof Error ? error.message : String(error)}`
346
+ });
347
+ } finally {
348
+ await baseRoot?.cleanup();
349
+ }
350
+ }
351
+ return {
352
+ changes,
353
+ targetState,
354
+ dependenciesChanged
355
+ };
356
+ }
357
+ //#endregion
358
+ exports.npmBaseTemplateProvider = npmBaseTemplateProvider;
359
+ exports.reconcileTemplates = reconcileTemplates;
360
+
361
+ //# sourceMappingURL=reconcile.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.cjs","names":["execFile","fs","path","os","loadTemplateManifest","collectTemplateFiles","getExcludedFeaturePaths","getTemplateFilePolicy","ChangeSet","createManagedState","findRepositorySymlink","mergeManagedCatalog","sha256"],"sources":["../../src/reconcile.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport fs from \"node:fs/promises\";\r\nimport os from \"node:os\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport { mergeManagedCatalog } from \"./catalog\";\r\nimport { ChangeSet, findRepositorySymlink } from \"./changes\";\r\nimport { collectTemplateFiles } from \"./fs\";\r\nimport { createManagedState, type ManagedState, sha256 } from \"./state\";\r\nimport { getExcludedFeaturePaths, getTemplateFilePolicy, loadTemplateManifest } from \"./template\";\r\nimport type { RenderedTemplateFile, TemplateContext } from \"./types\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\n\r\nexport interface BaseTemplateProvider {\r\n getTemplateRoot(version: string): Promise<{ root: string; cleanup: () => Promise<void> }>;\r\n}\r\n\r\nexport const npmBaseTemplateProvider: BaseTemplateProvider = {\r\n async getTemplateRoot(version) {\r\n const directory = await fs.mkdtemp(path.join(os.tmpdir(), \"m5kdev-template-\"));\r\n try {\r\n const { default: pacote } = await import(\"pacote\");\r\n await pacote.extract(`create-m5kdev@${version}`, directory);\r\n const root = path.join(directory, \"templates\", \"minimal-app\");\r\n await fs.access(path.join(root, \"template.manifest.json\"));\r\n return { root, cleanup: () => fs.rm(directory, { recursive: true, force: true }) };\r\n } catch (error) {\r\n await fs.rm(directory, { recursive: true, force: true });\r\n throw error;\r\n }\r\n },\r\n};\r\n\r\nfunction toTemplateContext(state: ManagedState): TemplateContext {\r\n return { ...state.template.context, betterAuthSecret: \"ignored-by-managed-state\" };\r\n}\r\n\r\nasync function renderForState(\r\n templateRoot: string,\r\n state: ManagedState\r\n): Promise<{\r\n files: RenderedTemplateFile[];\r\n manifest: ReturnType<typeof loadTemplateManifest>;\r\n}> {\r\n const manifest = loadTemplateManifest(templateRoot);\r\n const enabledFeatures = new Set(state.template.features);\r\n return {\r\n manifest,\r\n files: await collectTemplateFiles(templateRoot, toTemplateContext(state), {\r\n enabledFeatures,\r\n excludePrefixes: getExcludedFeaturePaths(manifest, enabledFeatures),\r\n ignoreContentFor: (relativePath) =>\r\n getTemplateFilePolicy(manifest, relativePath) === \"ignore\",\r\n }),\r\n };\r\n}\r\n\r\nasync function mergeText(base: Buffer, local: Buffer, target: Buffer): Promise<string | undefined> {\r\n const directory = await fs.mkdtemp(path.join(os.tmpdir(), \"m5kdev-merge-\"));\r\n const localPath = path.join(directory, \"local\");\r\n const basePath = path.join(directory, \"base\");\r\n const targetPath = path.join(directory, \"target\");\r\n try {\r\n await Promise.all([\r\n fs.writeFile(localPath, local),\r\n fs.writeFile(basePath, base),\r\n fs.writeFile(targetPath, target),\r\n ]);\r\n try {\r\n const result = await execFileAsync(\r\n \"git\",\r\n [\"merge-file\", \"-p\", \"--diff3\", localPath, basePath, targetPath],\r\n {\r\n encoding: \"utf8\",\r\n }\r\n );\r\n return result.stdout;\r\n } catch (error) {\r\n const mergeError = error as Error & { code?: number };\r\n if (typeof mergeError.code === \"number\" && mergeError.code >= 1 && mergeError.code <= 127) {\r\n return undefined;\r\n }\r\n throw error;\r\n }\r\n } finally {\r\n await fs.rm(directory, { recursive: true, force: true });\r\n }\r\n}\r\n\r\nexport interface ReconcileResult {\r\n changes: ChangeSet;\r\n targetState: ManagedState;\r\n dependenciesChanged: boolean;\r\n}\r\n\r\nexport async function reconcileTemplates(options: {\r\n repoRoot: string;\r\n state: ManagedState;\r\n targetTemplateRoot: string;\r\n targetVersion: string;\r\n baseProvider?: BaseTemplateProvider;\r\n}): Promise<ReconcileResult> {\r\n const changes = new ChangeSet(options.repoRoot);\r\n const target = await renderForState(options.targetTemplateRoot, options.state);\r\n const enabledFeatures = new Set(options.state.template.features);\r\n const targetState = createManagedState({\r\n templateVersion: options.targetVersion,\r\n enabledFeatures,\r\n context: toTemplateContext(options.state),\r\n renderedFiles: target.files,\r\n manifest: target.manifest,\r\n appliedMigrations: options.state.appliedMigrations,\r\n });\r\n const targetFiles = new Map(target.files.map((file) => [file.relativePath, file]));\r\n const mergeCandidates: Array<{\r\n path: string;\r\n basePath: string;\r\n local: Buffer;\r\n target: RenderedTemplateFile;\r\n deletePath?: string;\r\n }> = [];\r\n let dependenciesChanged = false;\r\n\r\n const workspacePath = \"pnpm-workspace.yaml\";\r\n const workspaceSymlink = await findRepositorySymlink(options.repoRoot, workspacePath);\r\n if (workspaceSymlink) {\r\n changes.addConflict({\r\n path: workspacePath,\r\n reason: \"The managed workspace file is a symbolic link.\",\r\n });\r\n }\r\n const workspace = workspaceSymlink ? undefined : await changes.read(workspacePath);\r\n if (workspace) {\r\n const catalogMerge = mergeManagedCatalog({\r\n source: workspace.toString(\"utf8\"),\r\n base: options.state.catalog,\r\n target: targetState.catalog,\r\n });\r\n for (const conflict of catalogMerge.conflicts) {\r\n changes.addConflict({\r\n path: workspacePath,\r\n reason: `Catalog entry ${conflict.name} changed both locally and in the target template.`,\r\n });\r\n }\r\n if (catalogMerge.changed) {\r\n changes.addChange({\r\n kind: \"modify\",\r\n path: workspacePath,\r\n content: Buffer.from(catalogMerge.source),\r\n reason: \"Reconcile managed consumer catalog\",\r\n });\r\n dependenciesChanged = true;\r\n }\r\n }\r\n\r\n const paths = new Set([...Object.keys(options.state.files), ...targetFiles.keys()]);\r\n paths.delete(workspacePath);\r\n\r\n for (const rename of target.manifest.sync.renames ?? []) {\r\n const baseline = options.state.files[rename.from];\r\n const targetFile = targetFiles.get(rename.to);\r\n if (!baseline || !targetFile) {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: `Invalid template rename ${rename.from} -> ${rename.to}.`,\r\n });\r\n continue;\r\n }\r\n paths.delete(rename.from);\r\n paths.delete(rename.to);\r\n const sourcePath = path.join(options.repoRoot, rename.from);\r\n const destinationPath = path.join(options.repoRoot, rename.to);\r\n const sourceSymlink = await findRepositorySymlink(options.repoRoot, rename.from);\r\n const destinationSymlink = await findRepositorySymlink(options.repoRoot, rename.to);\r\n if (sourceSymlink || destinationSymlink) {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: \"A renamed managed path contains a symbolic link.\",\r\n });\r\n continue;\r\n }\r\n const sourceStat = await fs.lstat(sourcePath).catch(() => null);\r\n const destinationStat = await fs.lstat(destinationPath).catch(() => null);\r\n if (destinationStat && rename.from !== rename.to) {\r\n changes.addConflict({\r\n path: rename.to,\r\n reason: \"A template rename targets an existing app-owned path.\",\r\n });\r\n continue;\r\n }\r\n const local = sourceStat?.isFile() ? await fs.readFile(sourcePath) : undefined;\r\n if (!local) {\r\n if (targetState.files[rename.to]?.policy === \"ensure\") {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: targetFile.content,\r\n reason: `Restore renamed ensured path from ${rename.from}`,\r\n });\r\n }\r\n continue;\r\n }\r\n const localHash = sha256(local);\r\n const targetHash = sha256(targetFile.content);\r\n if (localHash === baseline.sha256) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: rename.from,\r\n reason: `Rename template path to ${rename.to}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: targetFile.content,\r\n reason: `Rename template path from ${rename.from}`,\r\n });\r\n } else if (localHash === targetHash) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: rename.from,\r\n reason: `Rename template path to ${rename.to}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: local,\r\n reason: `Rename template path from ${rename.from}`,\r\n });\r\n } else if (baseline.kind === \"binary\" || targetFile.kind === \"binary\") {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: \"Renamed binary content changed locally and in the target template.\",\r\n });\r\n } else {\r\n mergeCandidates.push({\r\n path: rename.to,\r\n basePath: rename.from,\r\n local,\r\n target: targetFile,\r\n deletePath: rename.from,\r\n });\r\n }\r\n }\r\n\r\n for (const relativePath of [...paths].sort((left, right) => left.localeCompare(right))) {\r\n const baseline = options.state.files[relativePath];\r\n const targetFile = targetFiles.get(relativePath);\r\n const policy = targetState.files[relativePath]?.policy ?? baseline?.policy ?? \"merge\";\r\n if (policy === \"ignore\") continue;\r\n\r\n const localPath = path.join(options.repoRoot, relativePath);\r\n const symlink = await findRepositorySymlink(options.repoRoot, relativePath);\r\n if (symlink) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: `Managed path contains symbolic link ${symlink}.`,\r\n });\r\n continue;\r\n }\r\n const localStat = await fs.lstat(localPath).catch(() => null);\r\n const local = localStat?.isFile() ? await fs.readFile(localPath) : undefined;\r\n const localHash = local ? sha256(local) : undefined;\r\n const baseHash = baseline?.sha256;\r\n const targetHash = targetFile ? sha256(targetFile.content) : undefined;\r\n\r\n if (!baseline && targetFile) {\r\n if (!local) {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Add template path\",\r\n });\r\n } else if (localHash !== targetHash && policy !== \"ensure\") {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"The target template adds a path already owned by the app.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (baseline && !targetFile) {\r\n if (local && localHash === baseHash) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: relativePath,\r\n reason: \"Remove obsolete template path\",\r\n });\r\n } else if (local) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"The template removed a locally customized path.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (!baseline || !targetFile) continue;\r\n\r\n if (!local) {\r\n if (policy === \"ensure\") {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Restore ensured path\",\r\n });\r\n } else if (targetHash !== baseHash) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"A locally deleted path changed in the target template.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (localHash === targetHash || (localHash !== baseHash && targetHash === baseHash)) continue;\r\n if (localHash === baseHash) {\r\n changes.addChange({\r\n kind: \"modify\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Update unchanged template path\",\r\n });\r\n if (relativePath === \"package.json\" || relativePath.endsWith(\"/package.json\"))\r\n dependenciesChanged = true;\r\n continue;\r\n }\r\n if (baseline.kind === \"binary\" || targetFile.kind === \"binary\") {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"Binary content changed locally and in the target template.\",\r\n });\r\n continue;\r\n }\r\n mergeCandidates.push({ path: relativePath, basePath: relativePath, local, target: targetFile });\r\n }\r\n\r\n if (mergeCandidates.length > 0) {\r\n let baseRoot: Awaited<ReturnType<BaseTemplateProvider[\"getTemplateRoot\"]>> | undefined;\r\n try {\r\n baseRoot = await (options.baseProvider ?? npmBaseTemplateProvider).getTemplateRoot(\r\n options.state.template.version\r\n );\r\n const base = await renderForState(baseRoot.root, options.state);\r\n const baseFiles = new Map(base.files.map((file) => [file.relativePath, file]));\r\n for (const candidate of mergeCandidates) {\r\n const baseFile = baseFiles.get(candidate.basePath);\r\n if (\r\n !baseFile ||\r\n sha256(baseFile.content) !== options.state.files[candidate.basePath]?.sha256\r\n ) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: \"Historical template content does not match the stored baseline hash.\",\r\n });\r\n continue;\r\n }\r\n const merged = await mergeText(baseFile.content, candidate.local, candidate.target.content);\r\n if (merged === undefined) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: \"Three-way merge produced conflicts.\",\r\n });\r\n } else if (merged !== candidate.local.toString(\"utf8\")) {\r\n if (candidate.deletePath) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: candidate.deletePath,\r\n reason: `Rename template path to ${candidate.path}`,\r\n });\r\n }\r\n changes.addChange({\r\n kind: candidate.deletePath ? \"add\" : \"modify\",\r\n path: candidate.path,\r\n content: Buffer.from(merged),\r\n reason: \"Three-way merge local and template changes\",\r\n });\r\n if (candidate.path === \"package.json\" || candidate.path.endsWith(\"/package.json\")) {\r\n dependenciesChanged = true;\r\n }\r\n } else if (candidate.deletePath) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: candidate.deletePath,\r\n reason: `Rename template path to ${candidate.path}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: candidate.path,\r\n content: candidate.local,\r\n reason: `Rename template path from ${candidate.deletePath}`,\r\n });\r\n }\r\n }\r\n } catch (error) {\r\n for (const candidate of mergeCandidates) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: `Unable to load historical template ${options.state.template.version}: ${error instanceof Error ? error.message : String(error)}`,\r\n });\r\n }\r\n } finally {\r\n await baseRoot?.cleanup();\r\n }\r\n }\r\n\r\n return { changes, targetState, dependenciesChanged };\r\n}\r\n"],"mappings":"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"}