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,26 @@
1
+ import { ChangeSet } from "./changes.cjs";
2
+ import { ManagedState } from "./state.cjs";
3
+
4
+ //#region src/reconcile.d.ts
5
+ interface BaseTemplateProvider {
6
+ getTemplateRoot(version: string): Promise<{
7
+ root: string;
8
+ cleanup: () => Promise<void>;
9
+ }>;
10
+ }
11
+ declare const npmBaseTemplateProvider: BaseTemplateProvider;
12
+ interface ReconcileResult {
13
+ changes: ChangeSet;
14
+ targetState: ManagedState;
15
+ dependenciesChanged: boolean;
16
+ }
17
+ declare function reconcileTemplates(options: {
18
+ repoRoot: string;
19
+ state: ManagedState;
20
+ targetTemplateRoot: string;
21
+ targetVersion: string;
22
+ baseProvider?: BaseTemplateProvider;
23
+ }): Promise<ReconcileResult>;
24
+ //#endregion
25
+ export { BaseTemplateProvider, ReconcileResult, npmBaseTemplateProvider, reconcileTemplates };
26
+ //# sourceMappingURL=reconcile.d.cts.map
@@ -1,13 +1,68 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_src_create = require("./create.cjs");
3
+ const require_src_diagnostics = require("./diagnostics.cjs");
4
+ const require_src_doctor = require("./doctor.cjs");
5
+ const require_src_init = require("./init.cjs");
6
+ const require_src_update = require("./update.cjs");
3
7
  //#region src/runCli.ts
8
+ const COMMANDS = new Set([
9
+ "create",
10
+ "init",
11
+ "doctor",
12
+ "update"
13
+ ]);
14
+ const STRING_FLAGS = new Set([
15
+ "name",
16
+ "description",
17
+ "platform"
18
+ ]);
19
+ const BOOLEAN_FLAGS = new Set([
20
+ "yes",
21
+ "force",
22
+ "skip-install",
23
+ "skip-git",
24
+ "with-test-harness",
25
+ "dry-run",
26
+ "json",
27
+ "full"
28
+ ]);
4
29
  async function runCli(argv) {
5
30
  const parsed = parseCli(argv);
6
- if (!parsed.command || parsed.help || parsed.command === "--help" || parsed.command === "-h" || parsed.command === "help") {
31
+ if (!parsed.command || parsed.command === "--help" || parsed.command === "-h" || parsed.command === "help") {
7
32
  printHelp();
8
33
  return;
9
34
  }
10
- if (parsed.command !== "create") throw new Error(`Unknown command: ${parsed.command}`);
35
+ if (parsed.help) {
36
+ printHelp(parsed.command);
37
+ return;
38
+ }
39
+ switch (parsed.command) {
40
+ case "create":
41
+ await runCreate(parsed);
42
+ return;
43
+ case "init":
44
+ await runInit(parsed);
45
+ return;
46
+ case "doctor":
47
+ await runDoctor(parsed);
48
+ return;
49
+ case "update":
50
+ await runUpdate(parsed);
51
+ return;
52
+ default: throw new Error(`Unknown command: ${parsed.command}`);
53
+ }
54
+ }
55
+ async function runCreate(parsed) {
56
+ validateOptions(parsed, [
57
+ "name",
58
+ "description",
59
+ "platform",
60
+ "with-test-harness",
61
+ "yes",
62
+ "force",
63
+ "skip-install",
64
+ "skip-git"
65
+ ]);
11
66
  const options = toCreateCommandOptions(parsed);
12
67
  const result = await require_src_create.scaffoldProject(options);
13
68
  console.log("");
@@ -19,6 +74,58 @@ async function runCli(argv) {
19
74
  console.log(" pnpm --filter ./apps/server drizzle:migrate");
20
75
  console.log(" pnpm dev");
21
76
  }
77
+ async function runInit(parsed) {
78
+ validateRepositoryCommand(parsed, [
79
+ "yes",
80
+ "force",
81
+ "json"
82
+ ]);
83
+ const json = Boolean(parsed.options.json);
84
+ const result = await require_src_init.initializeManagedRepo({
85
+ repoRoot: process.cwd(),
86
+ yes: Boolean(parsed.options.yes),
87
+ force: Boolean(parsed.options.force)
88
+ });
89
+ if (json) console.log(JSON.stringify(result, null, 2));
90
+ else {
91
+ require_src_diagnostics.printDiagnosticReport(result.report);
92
+ console.log(result.initialized ? `Initialized .m5kdev.json at template ${result.state.template.version}.` : "Initialization was not applied.");
93
+ }
94
+ if (!result.initialized) process.exitCode = 1;
95
+ }
96
+ async function runDoctor(parsed) {
97
+ validateRepositoryCommand(parsed, ["full", "json"]);
98
+ const report = await require_src_doctor.diagnoseManagedRepo({
99
+ repoRoot: process.cwd(),
100
+ full: Boolean(parsed.options.full)
101
+ });
102
+ if (parsed.options.json) console.log(JSON.stringify(report, null, 2));
103
+ else require_src_diagnostics.printDiagnosticReport(report);
104
+ if (!report.ok) process.exitCode = 1;
105
+ }
106
+ async function runUpdate(parsed) {
107
+ validateRepositoryCommand(parsed, [
108
+ "dry-run",
109
+ "skip-install",
110
+ "json"
111
+ ]);
112
+ const result = await require_src_update.updateManagedRepo({
113
+ repoRoot: process.cwd(),
114
+ dryRun: Boolean(parsed.options["dry-run"]),
115
+ skipInstall: Boolean(parsed.options["skip-install"])
116
+ });
117
+ if (parsed.options.json) console.log(JSON.stringify(result, null, 2));
118
+ else {
119
+ const action = result.dryRun ? "Planned" : result.applied ? "Applied" : "Blocked";
120
+ console.log(`${action} update ${result.fromVersion} -> ${result.targetVersion}.`);
121
+ for (const change of result.changes) console.log(` ${change.kind.padEnd(6)} ${change.path} — ${change.reason}`);
122
+ for (const conflict of result.conflicts) console.log(` conflict ${conflict.path} — ${conflict.reason}`);
123
+ if (result.migrations.length > 0) console.log(`Migrations: ${result.migrations.join(", ")}`);
124
+ if (result.installRequired) console.log("Dependencies changed. Run: pnpm install");
125
+ if (result.changes.length === 0 && result.conflicts.length === 0) console.log("Already up to date.");
126
+ }
127
+ if (result.conflicts.length > 0) process.exitCode = 1;
128
+ }
22
129
  function parseCli(argv) {
23
130
  const [command, ...rest] = normalizeCliArgv(argv);
24
131
  const options = {};
@@ -31,14 +138,17 @@ function parseCli(argv) {
31
138
  continue;
32
139
  }
33
140
  if (!token.startsWith("--")) {
34
- directory ??= token;
141
+ if (directory) throw new Error(`Unexpected positional argument: ${token}`);
142
+ directory = token;
35
143
  continue;
36
144
  }
37
145
  const [rawKey, inlineValue] = token.slice(2).split("=", 2);
38
- if (isBooleanFlag(rawKey)) {
146
+ if (BOOLEAN_FLAGS.has(rawKey)) {
147
+ if (inlineValue !== void 0) throw new Error(`--${rawKey} does not accept a value.`);
39
148
  options[rawKey] = true;
40
149
  continue;
41
150
  }
151
+ if (!STRING_FLAGS.has(rawKey)) throw new Error(`Unknown option: --${rawKey}`);
42
152
  const nextValue = inlineValue ?? rest[index + 1];
43
153
  if (!nextValue || nextValue.startsWith("--")) throw new Error(`Missing value for --${rawKey}`);
44
154
  options[rawKey] = nextValue;
@@ -53,18 +163,17 @@ function parseCli(argv) {
53
163
  }
54
164
  function normalizeCliArgv(argv) {
55
165
  const [firstToken] = argv;
56
- if (!firstToken || firstToken === "create" || firstToken === "--help" || firstToken === "-h") return argv;
166
+ if (!firstToken || COMMANDS.has(firstToken) || firstToken === "--help" || firstToken === "-h") return argv;
57
167
  if (firstToken === "help") return ["--help"];
58
168
  return ["create", ...argv];
59
169
  }
60
- function isBooleanFlag(value) {
61
- return [
62
- "yes",
63
- "force",
64
- "skip-install",
65
- "skip-git",
66
- "with-test-harness"
67
- ].includes(value);
170
+ function validateRepositoryCommand(parsed, allowedOptions) {
171
+ if (parsed.directory) throw new Error(`${parsed.command} operates on the current directory and accepts no directory argument.`);
172
+ validateOptions(parsed, allowedOptions);
173
+ }
174
+ function validateOptions(parsed, allowedOptions) {
175
+ const allowed = new Set(allowedOptions);
176
+ for (const option of Object.keys(parsed.options)) if (!allowed.has(option)) throw new Error(`--${option} is not valid for m5kdev ${parsed.command}.`);
68
177
  }
69
178
  function toCreateCommandOptions(parsed) {
70
179
  const platform = getStringOption(parsed.options, "platform");
@@ -89,23 +198,42 @@ function getStringOption(options, key) {
89
198
  const value = options[key];
90
199
  return typeof value === "string" ? value : void 0;
91
200
  }
92
- function printHelp() {
201
+ function printHelp(command) {
93
202
  console.log("m5kdev");
94
203
  console.log("");
95
- console.log("Usage:");
96
- console.log(" pnpm create m5kdev [directory] [options]");
97
- console.log(" m5kdev [directory] [options]");
98
- console.log(" m5kdev create [directory] [options]");
204
+ if (!command) {
205
+ console.log("Usage:");
206
+ console.log(" pnpm dlx create-m5kdev@<version> [directory] [create options]");
207
+ console.log(" pnpm dlx create-m5kdev@<version> init [--yes] [--force] [--json]");
208
+ console.log(" pnpm dlx create-m5kdev@<version> doctor [--full] [--json]");
209
+ console.log(" pnpm dlx create-m5kdev@<version> update [--dry-run] [--skip-install] [--json]");
210
+ console.log("");
211
+ console.log("Run m5kdev <command> --help for command options.");
212
+ return;
213
+ }
214
+ console.log(`Usage: m5kdev ${command}${command === "create" ? " [directory]" : ""} [options]`);
99
215
  console.log("");
100
- console.log("Options:");
101
- console.log(" --name <value> Set the app name");
102
- console.log(" --description <value> Set the app description");
103
- console.log(" --platform <value> App platform: web (default), expo, or both");
104
- console.log(" --with-test-harness Include the e2e test harness (default: no)");
105
- console.log(" --yes Accept defaults for missing prompts");
106
- console.log(" --force Allow writing into a non-empty directory");
107
- console.log(" --skip-install Skip pnpm install");
108
- console.log(" --skip-git Skip git init");
216
+ if (command === "create") {
217
+ console.log(" --name <value> Set the app name");
218
+ console.log(" --description <value> Set the app description");
219
+ console.log(" --platform <value> web (default), expo, or both");
220
+ console.log(" --with-test-harness Include the e2e test harness");
221
+ console.log(" --yes Accept defaults for missing prompts");
222
+ console.log(" --force Allow a non-empty directory");
223
+ console.log(" --skip-install Skip pnpm install");
224
+ console.log(" --skip-git Skip git init");
225
+ } else if (command === "init") {
226
+ console.log(" --yes Confirm non-interactively");
227
+ console.log(" --force Replace existing managed state");
228
+ console.log(" --json Emit machine-readable output");
229
+ } else if (command === "doctor") {
230
+ console.log(" --full Also run check-types, lint, and build scripts");
231
+ console.log(" --json Emit machine-readable output");
232
+ } else if (command === "update") {
233
+ console.log(" --dry-run Build the complete plan without writes");
234
+ console.log(" --skip-install Apply sources without pnpm install");
235
+ console.log(" --json Emit machine-readable output");
236
+ }
109
237
  }
110
238
  //#endregion
111
239
  exports.parseCli = parseCli;
@@ -1 +1 @@
1
- {"version":3,"file":"runCli.cjs","names":["scaffoldProject"],"sources":["../../src/runCli.ts"],"sourcesContent":["import { scaffoldProject } from \"./create\";\r\nimport type { CreateCommandOptions } from \"./types\";\r\n\r\ninterface ParsedCli {\r\n command?: string;\r\n directory?: string;\r\n help?: boolean;\r\n options: Record<string, string | boolean>;\r\n}\r\n\r\nexport async function runCli(argv: readonly string[]): Promise<void> {\r\n const parsed = parseCli(argv);\r\n\r\n if (\r\n !parsed.command ||\r\n parsed.help ||\r\n parsed.command === \"--help\" ||\r\n parsed.command === \"-h\" ||\r\n parsed.command === \"help\"\r\n ) {\r\n printHelp();\r\n return;\r\n }\r\n\r\n if (parsed.command !== \"create\") {\r\n throw new Error(`Unknown command: ${parsed.command}`);\r\n }\r\n\r\n const options = toCreateCommandOptions(parsed);\r\n const result = await scaffoldProject(options);\r\n\r\n console.log(\"\");\r\n console.log(`Scaffolded ${result.context.appName} in ${result.targetDirectory}`);\r\n console.log(\"\");\r\n console.log(\"Next steps:\");\r\n console.log(` cd ${result.targetDirectory}`);\r\n if (options.skipInstall) {\r\n console.log(\" pnpm install\");\r\n }\r\n console.log(\" pnpm --filter ./apps/server drizzle:migrate\");\r\n console.log(\" pnpm dev\");\r\n}\r\n\r\nexport function parseCli(argv: readonly string[]): ParsedCli {\r\n const normalizedArgv = normalizeCliArgv(argv);\r\n const [command, ...rest] = normalizedArgv;\r\n const options: Record<string, string | boolean> = {};\r\n let directory: string | undefined;\r\n let help = false;\r\n\r\n for (let index = 0; index < rest.length; index += 1) {\r\n const token = rest[index];\r\n\r\n if (token === \"--help\" || token === \"-h\") {\r\n help = true;\r\n continue;\r\n }\r\n\r\n if (!token.startsWith(\"--\")) {\r\n directory ??= token;\r\n continue;\r\n }\r\n\r\n const [rawKey, inlineValue] = token.slice(2).split(\"=\", 2);\r\n\r\n if (isBooleanFlag(rawKey)) {\r\n options[rawKey] = true;\r\n continue;\r\n }\r\n\r\n const nextValue = inlineValue ?? rest[index + 1];\r\n if (!nextValue || nextValue.startsWith(\"--\")) {\r\n throw new Error(`Missing value for --${rawKey}`);\r\n }\r\n\r\n options[rawKey] = nextValue;\r\n if (inlineValue === undefined) {\r\n index += 1;\r\n }\r\n }\r\n\r\n return { command, directory, help, options };\r\n}\r\n\r\nfunction normalizeCliArgv(argv: readonly string[]): readonly string[] {\r\n const [firstToken] = argv;\r\n\r\n if (!firstToken || firstToken === \"create\" || firstToken === \"--help\" || firstToken === \"-h\") {\r\n return argv;\r\n }\r\n\r\n if (firstToken === \"help\") {\r\n return [\"--help\"];\r\n }\r\n\r\n return [\"create\", ...argv];\r\n}\r\n\r\nfunction isBooleanFlag(value: string): boolean {\r\n return [\"yes\", \"force\", \"skip-install\", \"skip-git\", \"with-test-harness\"].includes(value);\r\n}\r\n\r\nfunction toCreateCommandOptions(parsed: ParsedCli): CreateCommandOptions {\r\n const platform = getStringOption(parsed.options, \"platform\");\r\n if (platform && ![\"web\", \"expo\", \"both\"].includes(platform)) {\r\n throw new Error(`Invalid --platform \"${platform}\". Use web, expo, or both.`);\r\n }\r\n\r\n return {\r\n targetDirectory: parsed.directory,\r\n appName: getStringOption(parsed.options, \"name\"),\r\n appDescription: getStringOption(parsed.options, \"description\"),\r\n platform: platform as CreateCommandOptions[\"platform\"],\r\n testHarness: parsed.options[\"with-test-harness\"] ? true : undefined,\r\n yes: Boolean(parsed.options.yes),\r\n force: Boolean(parsed.options.force),\r\n skipInstall: Boolean(parsed.options[\"skip-install\"]),\r\n skipGit: Boolean(parsed.options[\"skip-git\"]),\r\n };\r\n}\r\n\r\nfunction getStringOption(\r\n options: Record<string, string | boolean>,\r\n key: string\r\n): string | undefined {\r\n const value = options[key];\r\n return typeof value === \"string\" ? value : undefined;\r\n}\r\n\r\nfunction printHelp(): void {\r\n console.log(\"m5kdev\");\r\n console.log(\"\");\r\n console.log(\"Usage:\");\r\n console.log(\" pnpm create m5kdev [directory] [options]\");\r\n console.log(\" m5kdev [directory] [options]\");\r\n console.log(\" m5kdev create [directory] [options]\");\r\n console.log(\"\");\r\n console.log(\"Options:\");\r\n console.log(\" --name <value> Set the app name\");\r\n console.log(\" --description <value> Set the app description\");\r\n console.log(\" --platform <value> App platform: web (default), expo, or both\");\r\n console.log(\" --with-test-harness Include the e2e test harness (default: no)\");\r\n console.log(\" --yes Accept defaults for missing prompts\");\r\n console.log(\" --force Allow writing into a non-empty directory\");\r\n console.log(\" --skip-install Skip pnpm install\");\r\n console.log(\" --skip-git Skip git init\");\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"runCli.cjs","names":["scaffoldProject","initializeManagedRepo","diagnoseManagedRepo","updateManagedRepo"],"sources":["../../src/runCli.ts"],"sourcesContent":["import { scaffoldProject } from \"./create\";\r\nimport { printDiagnosticReport } from \"./diagnostics\";\r\nimport { diagnoseManagedRepo } from \"./doctor\";\r\nimport { initializeManagedRepo } from \"./init\";\r\nimport type { CreateCommandOptions } from \"./types\";\r\nimport { updateManagedRepo } from \"./update\";\r\n\r\ninterface ParsedCli {\r\n command?: string;\r\n directory?: string;\r\n help?: boolean;\r\n options: Record<string, string | boolean>;\r\n}\r\n\r\nconst COMMANDS = new Set([\"create\", \"init\", \"doctor\", \"update\"]);\r\nconst STRING_FLAGS = new Set([\"name\", \"description\", \"platform\"]);\r\nconst BOOLEAN_FLAGS = new Set([\r\n \"yes\",\r\n \"force\",\r\n \"skip-install\",\r\n \"skip-git\",\r\n \"with-test-harness\",\r\n \"dry-run\",\r\n \"json\",\r\n \"full\",\r\n]);\r\n\r\nexport async function runCli(argv: readonly string[]): Promise<void> {\r\n const parsed = parseCli(argv);\r\n\r\n if (\r\n !parsed.command ||\r\n parsed.command === \"--help\" ||\r\n parsed.command === \"-h\" ||\r\n parsed.command === \"help\"\r\n ) {\r\n printHelp();\r\n return;\r\n }\r\n if (parsed.help) {\r\n printHelp(parsed.command);\r\n return;\r\n }\r\n\r\n switch (parsed.command) {\r\n case \"create\":\r\n await runCreate(parsed);\r\n return;\r\n case \"init\":\r\n await runInit(parsed);\r\n return;\r\n case \"doctor\":\r\n await runDoctor(parsed);\r\n return;\r\n case \"update\":\r\n await runUpdate(parsed);\r\n return;\r\n default:\r\n throw new Error(`Unknown command: ${parsed.command}`);\r\n }\r\n}\r\n\r\nasync function runCreate(parsed: ParsedCli): Promise<void> {\r\n validateOptions(parsed, [\r\n \"name\",\r\n \"description\",\r\n \"platform\",\r\n \"with-test-harness\",\r\n \"yes\",\r\n \"force\",\r\n \"skip-install\",\r\n \"skip-git\",\r\n ]);\r\n const options = toCreateCommandOptions(parsed);\r\n const result = await scaffoldProject(options);\r\n\r\n console.log(\"\");\r\n console.log(`Scaffolded ${result.context.appName} in ${result.targetDirectory}`);\r\n console.log(\"\");\r\n console.log(\"Next steps:\");\r\n console.log(` cd ${result.targetDirectory}`);\r\n if (options.skipInstall) console.log(\" pnpm install\");\r\n console.log(\" pnpm --filter ./apps/server drizzle:migrate\");\r\n console.log(\" pnpm dev\");\r\n}\r\n\r\nasync function runInit(parsed: ParsedCli): Promise<void> {\r\n validateRepositoryCommand(parsed, [\"yes\", \"force\", \"json\"]);\r\n const json = Boolean(parsed.options.json);\r\n const result = await initializeManagedRepo({\r\n repoRoot: process.cwd(),\r\n yes: Boolean(parsed.options.yes),\r\n force: Boolean(parsed.options.force),\r\n });\r\n if (json) {\r\n console.log(JSON.stringify(result, null, 2));\r\n } else {\r\n printDiagnosticReport(result.report);\r\n console.log(\r\n result.initialized\r\n ? `Initialized .m5kdev.json at template ${result.state.template.version}.`\r\n : \"Initialization was not applied.\"\r\n );\r\n }\r\n if (!result.initialized) process.exitCode = 1;\r\n}\r\n\r\nasync function runDoctor(parsed: ParsedCli): Promise<void> {\r\n validateRepositoryCommand(parsed, [\"full\", \"json\"]);\r\n const report = await diagnoseManagedRepo({\r\n repoRoot: process.cwd(),\r\n full: Boolean(parsed.options.full),\r\n });\r\n if (parsed.options.json) console.log(JSON.stringify(report, null, 2));\r\n else printDiagnosticReport(report);\r\n if (!report.ok) process.exitCode = 1;\r\n}\r\n\r\nasync function runUpdate(parsed: ParsedCli): Promise<void> {\r\n validateRepositoryCommand(parsed, [\"dry-run\", \"skip-install\", \"json\"]);\r\n const result = await updateManagedRepo({\r\n repoRoot: process.cwd(),\r\n dryRun: Boolean(parsed.options[\"dry-run\"]),\r\n skipInstall: Boolean(parsed.options[\"skip-install\"]),\r\n });\r\n if (parsed.options.json) {\r\n console.log(JSON.stringify(result, null, 2));\r\n } else {\r\n const action = result.dryRun ? \"Planned\" : result.applied ? \"Applied\" : \"Blocked\";\r\n console.log(`${action} update ${result.fromVersion} -> ${result.targetVersion}.`);\r\n for (const change of result.changes) {\r\n console.log(` ${change.kind.padEnd(6)} ${change.path} — ${change.reason}`);\r\n }\r\n for (const conflict of result.conflicts) {\r\n console.log(` conflict ${conflict.path} — ${conflict.reason}`);\r\n }\r\n if (result.migrations.length > 0) console.log(`Migrations: ${result.migrations.join(\", \")}`);\r\n if (result.installRequired) console.log(\"Dependencies changed. Run: pnpm install\");\r\n if (result.changes.length === 0 && result.conflicts.length === 0)\r\n console.log(\"Already up to date.\");\r\n }\r\n if (result.conflicts.length > 0) process.exitCode = 1;\r\n}\r\n\r\nexport function parseCli(argv: readonly string[]): ParsedCli {\r\n const normalizedArgv = normalizeCliArgv(argv);\r\n const [command, ...rest] = normalizedArgv;\r\n const options: Record<string, string | boolean> = {};\r\n let directory: string | undefined;\r\n let help = false;\r\n\r\n for (let index = 0; index < rest.length; index += 1) {\r\n const token = rest[index];\r\n if (token === \"--help\" || token === \"-h\") {\r\n help = true;\r\n continue;\r\n }\r\n if (!token.startsWith(\"--\")) {\r\n if (directory) throw new Error(`Unexpected positional argument: ${token}`);\r\n directory = token;\r\n continue;\r\n }\r\n\r\n const [rawKey, inlineValue] = token.slice(2).split(\"=\", 2);\r\n if (BOOLEAN_FLAGS.has(rawKey)) {\r\n if (inlineValue !== undefined) throw new Error(`--${rawKey} does not accept a value.`);\r\n options[rawKey] = true;\r\n continue;\r\n }\r\n if (!STRING_FLAGS.has(rawKey)) throw new Error(`Unknown option: --${rawKey}`);\r\n\r\n const nextValue = inlineValue ?? rest[index + 1];\r\n if (!nextValue || nextValue.startsWith(\"--\")) throw new Error(`Missing value for --${rawKey}`);\r\n options[rawKey] = nextValue;\r\n if (inlineValue === undefined) index += 1;\r\n }\r\n return { command, directory, help, options };\r\n}\r\n\r\nfunction normalizeCliArgv(argv: readonly string[]): readonly string[] {\r\n const [firstToken] = argv;\r\n if (!firstToken || COMMANDS.has(firstToken) || firstToken === \"--help\" || firstToken === \"-h\")\r\n return argv;\r\n if (firstToken === \"help\") return [\"--help\"];\r\n return [\"create\", ...argv];\r\n}\r\n\r\nfunction validateRepositoryCommand(parsed: ParsedCli, allowedOptions: readonly string[]): void {\r\n if (parsed.directory)\r\n throw new Error(\r\n `${parsed.command} operates on the current directory and accepts no directory argument.`\r\n );\r\n validateOptions(parsed, allowedOptions);\r\n}\r\n\r\nfunction validateOptions(parsed: ParsedCli, allowedOptions: readonly string[]): void {\r\n const allowed = new Set(allowedOptions);\r\n for (const option of Object.keys(parsed.options)) {\r\n if (!allowed.has(option))\r\n throw new Error(`--${option} is not valid for m5kdev ${parsed.command}.`);\r\n }\r\n}\r\n\r\nfunction toCreateCommandOptions(parsed: ParsedCli): CreateCommandOptions {\r\n const platform = getStringOption(parsed.options, \"platform\");\r\n if (platform && ![\"web\", \"expo\", \"both\"].includes(platform)) {\r\n throw new Error(`Invalid --platform \"${platform}\". Use web, expo, or both.`);\r\n }\r\n return {\r\n targetDirectory: parsed.directory,\r\n appName: getStringOption(parsed.options, \"name\"),\r\n appDescription: getStringOption(parsed.options, \"description\"),\r\n platform: platform as CreateCommandOptions[\"platform\"],\r\n testHarness: parsed.options[\"with-test-harness\"] ? true : undefined,\r\n yes: Boolean(parsed.options.yes),\r\n force: Boolean(parsed.options.force),\r\n skipInstall: Boolean(parsed.options[\"skip-install\"]),\r\n skipGit: Boolean(parsed.options[\"skip-git\"]),\r\n };\r\n}\r\n\r\nfunction getStringOption(\r\n options: Record<string, string | boolean>,\r\n key: string\r\n): string | undefined {\r\n const value = options[key];\r\n return typeof value === \"string\" ? value : undefined;\r\n}\r\n\r\nfunction printHelp(command?: string): void {\r\n console.log(\"m5kdev\");\r\n console.log(\"\");\r\n if (!command) {\r\n console.log(\"Usage:\");\r\n console.log(\" pnpm dlx create-m5kdev@<version> [directory] [create options]\");\r\n console.log(\" pnpm dlx create-m5kdev@<version> init [--yes] [--force] [--json]\");\r\n console.log(\" pnpm dlx create-m5kdev@<version> doctor [--full] [--json]\");\r\n console.log(\" pnpm dlx create-m5kdev@<version> update [--dry-run] [--skip-install] [--json]\");\r\n console.log(\"\");\r\n console.log(\"Run m5kdev <command> --help for command options.\");\r\n return;\r\n }\r\n console.log(`Usage: m5kdev ${command}${command === \"create\" ? \" [directory]\" : \"\"} [options]`);\r\n console.log(\"\");\r\n if (command === \"create\") {\r\n console.log(\" --name <value> Set the app name\");\r\n console.log(\" --description <value> Set the app description\");\r\n console.log(\" --platform <value> web (default), expo, or both\");\r\n console.log(\" --with-test-harness Include the e2e test harness\");\r\n console.log(\" --yes Accept defaults for missing prompts\");\r\n console.log(\" --force Allow a non-empty directory\");\r\n console.log(\" --skip-install Skip pnpm install\");\r\n console.log(\" --skip-git Skip git init\");\r\n } else if (command === \"init\") {\r\n console.log(\" --yes Confirm non-interactively\");\r\n console.log(\" --force Replace existing managed state\");\r\n console.log(\" --json Emit machine-readable output\");\r\n } else if (command === \"doctor\") {\r\n console.log(\" --full Also run check-types, lint, and build scripts\");\r\n console.log(\" --json Emit machine-readable output\");\r\n } else if (command === \"update\") {\r\n console.log(\" --dry-run Build the complete plan without writes\");\r\n console.log(\" --skip-install Apply sources without pnpm install\");\r\n console.log(\" --json Emit machine-readable output\");\r\n }\r\n}\r\n"],"mappings":";;;;;;;AAcA,MAAM,WAAW,IAAI,IAAI;CAAC;CAAU;CAAQ;CAAU;CAAS,CAAC;AAChE,MAAM,eAAe,IAAI,IAAI;CAAC;CAAQ;CAAe;CAAW,CAAC;AACjE,MAAM,gBAAgB,IAAI,IAAI;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,eAAsB,OAAO,MAAwC;CACnE,MAAM,SAAS,SAAS,KAAK;AAE7B,KACE,CAAC,OAAO,WACR,OAAO,YAAY,YACnB,OAAO,YAAY,QACnB,OAAO,YAAY,QACnB;AACA,aAAW;AACX;;AAEF,KAAI,OAAO,MAAM;AACf,YAAU,OAAO,QAAQ;AACzB;;AAGF,SAAQ,OAAO,SAAf;EACE,KAAK;AACH,SAAM,UAAU,OAAO;AACvB;EACF,KAAK;AACH,SAAM,QAAQ,OAAO;AACrB;EACF,KAAK;AACH,SAAM,UAAU,OAAO;AACvB;EACF,KAAK;AACH,SAAM,UAAU,OAAO;AACvB;EACF,QACE,OAAM,IAAI,MAAM,oBAAoB,OAAO,UAAU;;;AAI3D,eAAe,UAAU,QAAkC;AACzD,iBAAgB,QAAQ;EACtB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACF,MAAM,UAAU,uBAAuB,OAAO;CAC9C,MAAM,SAAS,MAAMA,mBAAAA,gBAAgB,QAAQ;AAE7C,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,cAAc,OAAO,QAAQ,QAAQ,MAAM,OAAO,kBAAkB;AAChF,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,cAAc;AAC1B,SAAQ,IAAI,QAAQ,OAAO,kBAAkB;AAC7C,KAAI,QAAQ,YAAa,SAAQ,IAAI,iBAAiB;AACtD,SAAQ,IAAI,gDAAgD;AAC5D,SAAQ,IAAI,aAAa;;AAG3B,eAAe,QAAQ,QAAkC;AACvD,2BAA0B,QAAQ;EAAC;EAAO;EAAS;EAAO,CAAC;CAC3D,MAAM,OAAO,QAAQ,OAAO,QAAQ,KAAK;CACzC,MAAM,SAAS,MAAMC,iBAAAA,sBAAsB;EACzC,UAAU,QAAQ,KAAK;EACvB,KAAK,QAAQ,OAAO,QAAQ,IAAI;EAChC,OAAO,QAAQ,OAAO,QAAQ,MAAM;EACrC,CAAC;AACF,KAAI,KACF,SAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;MACvC;AACL,0BAAA,sBAAsB,OAAO,OAAO;AACpC,UAAQ,IACN,OAAO,cACH,wCAAwC,OAAO,MAAM,SAAS,QAAQ,KACtE,kCACL;;AAEH,KAAI,CAAC,OAAO,YAAa,SAAQ,WAAW;;AAG9C,eAAe,UAAU,QAAkC;AACzD,2BAA0B,QAAQ,CAAC,QAAQ,OAAO,CAAC;CACnD,MAAM,SAAS,MAAMC,mBAAAA,oBAAoB;EACvC,UAAU,QAAQ,KAAK;EACvB,MAAM,QAAQ,OAAO,QAAQ,KAAK;EACnC,CAAC;AACF,KAAI,OAAO,QAAQ,KAAM,SAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;KAChE,yBAAA,sBAAsB,OAAO;AAClC,KAAI,CAAC,OAAO,GAAI,SAAQ,WAAW;;AAGrC,eAAe,UAAU,QAAkC;AACzD,2BAA0B,QAAQ;EAAC;EAAW;EAAgB;EAAO,CAAC;CACtE,MAAM,SAAS,MAAMC,mBAAAA,kBAAkB;EACrC,UAAU,QAAQ,KAAK;EACvB,QAAQ,QAAQ,OAAO,QAAQ,WAAW;EAC1C,aAAa,QAAQ,OAAO,QAAQ,gBAAgB;EACrD,CAAC;AACF,KAAI,OAAO,QAAQ,KACjB,SAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;MACvC;EACL,MAAM,SAAS,OAAO,SAAS,YAAY,OAAO,UAAU,YAAY;AACxE,UAAQ,IAAI,GAAG,OAAO,UAAU,OAAO,YAAY,MAAM,OAAO,cAAc,GAAG;AACjF,OAAK,MAAM,UAAU,OAAO,QAC1B,SAAQ,IAAI,KAAK,OAAO,KAAK,OAAO,EAAE,CAAC,GAAG,OAAO,KAAK,KAAK,OAAO,SAAS;AAE7E,OAAK,MAAM,YAAY,OAAO,UAC5B,SAAQ,IAAI,cAAc,SAAS,KAAK,KAAK,SAAS,SAAS;AAEjE,MAAI,OAAO,WAAW,SAAS,EAAG,SAAQ,IAAI,eAAe,OAAO,WAAW,KAAK,KAAK,GAAG;AAC5F,MAAI,OAAO,gBAAiB,SAAQ,IAAI,0CAA0C;AAClF,MAAI,OAAO,QAAQ,WAAW,KAAK,OAAO,UAAU,WAAW,EAC7D,SAAQ,IAAI,sBAAsB;;AAEtC,KAAI,OAAO,UAAU,SAAS,EAAG,SAAQ,WAAW;;AAGtD,SAAgB,SAAS,MAAoC;CAE3D,MAAM,CAAC,SAAS,GAAG,QADI,iBAAiB,KAAK;CAE7C,MAAM,UAA4C,EAAE;CACpD,IAAI;CACJ,IAAI,OAAO;AAEX,MAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACnD,MAAM,QAAQ,KAAK;AACnB,MAAI,UAAU,YAAY,UAAU,MAAM;AACxC,UAAO;AACP;;AAEF,MAAI,CAAC,MAAM,WAAW,KAAK,EAAE;AAC3B,OAAI,UAAW,OAAM,IAAI,MAAM,mCAAmC,QAAQ;AAC1E,eAAY;AACZ;;EAGF,MAAM,CAAC,QAAQ,eAAe,MAAM,MAAM,EAAE,CAAC,MAAM,KAAK,EAAE;AAC1D,MAAI,cAAc,IAAI,OAAO,EAAE;AAC7B,OAAI,gBAAgB,KAAA,EAAW,OAAM,IAAI,MAAM,KAAK,OAAO,2BAA2B;AACtF,WAAQ,UAAU;AAClB;;AAEF,MAAI,CAAC,aAAa,IAAI,OAAO,CAAE,OAAM,IAAI,MAAM,qBAAqB,SAAS;EAE7E,MAAM,YAAY,eAAe,KAAK,QAAQ;AAC9C,MAAI,CAAC,aAAa,UAAU,WAAW,KAAK,CAAE,OAAM,IAAI,MAAM,uBAAuB,SAAS;AAC9F,UAAQ,UAAU;AAClB,MAAI,gBAAgB,KAAA,EAAW,UAAS;;AAE1C,QAAO;EAAE;EAAS;EAAW;EAAM;EAAS;;AAG9C,SAAS,iBAAiB,MAA4C;CACpE,MAAM,CAAC,cAAc;AACrB,KAAI,CAAC,cAAc,SAAS,IAAI,WAAW,IAAI,eAAe,YAAY,eAAe,KACvF,QAAO;AACT,KAAI,eAAe,OAAQ,QAAO,CAAC,SAAS;AAC5C,QAAO,CAAC,UAAU,GAAG,KAAK;;AAG5B,SAAS,0BAA0B,QAAmB,gBAAyC;AAC7F,KAAI,OAAO,UACT,OAAM,IAAI,MACR,GAAG,OAAO,QAAQ,uEACnB;AACH,iBAAgB,QAAQ,eAAe;;AAGzC,SAAS,gBAAgB,QAAmB,gBAAyC;CACnF,MAAM,UAAU,IAAI,IAAI,eAAe;AACvC,MAAK,MAAM,UAAU,OAAO,KAAK,OAAO,QAAQ,CAC9C,KAAI,CAAC,QAAQ,IAAI,OAAO,CACtB,OAAM,IAAI,MAAM,KAAK,OAAO,2BAA2B,OAAO,QAAQ,GAAG;;AAI/E,SAAS,uBAAuB,QAAyC;CACvE,MAAM,WAAW,gBAAgB,OAAO,SAAS,WAAW;AAC5D,KAAI,YAAY,CAAC;EAAC;EAAO;EAAQ;EAAO,CAAC,SAAS,SAAS,CACzD,OAAM,IAAI,MAAM,uBAAuB,SAAS,4BAA4B;AAE9E,QAAO;EACL,iBAAiB,OAAO;EACxB,SAAS,gBAAgB,OAAO,SAAS,OAAO;EAChD,gBAAgB,gBAAgB,OAAO,SAAS,cAAc;EACpD;EACV,aAAa,OAAO,QAAQ,uBAAuB,OAAO,KAAA;EAC1D,KAAK,QAAQ,OAAO,QAAQ,IAAI;EAChC,OAAO,QAAQ,OAAO,QAAQ,MAAM;EACpC,aAAa,QAAQ,OAAO,QAAQ,gBAAgB;EACpD,SAAS,QAAQ,OAAO,QAAQ,YAAY;EAC7C;;AAGH,SAAS,gBACP,SACA,KACoB;CACpB,MAAM,QAAQ,QAAQ;AACtB,QAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;;AAG7C,SAAS,UAAU,SAAwB;AACzC,SAAQ,IAAI,SAAS;AACrB,SAAQ,IAAI,GAAG;AACf,KAAI,CAAC,SAAS;AACZ,UAAQ,IAAI,SAAS;AACrB,UAAQ,IAAI,kEAAkE;AAC9E,UAAQ,IAAI,qEAAqE;AACjF,UAAQ,IAAI,8DAA8D;AAC1E,UAAQ,IAAI,kFAAkF;AAC9F,UAAQ,IAAI,GAAG;AACf,UAAQ,IAAI,mDAAmD;AAC/D;;AAEF,SAAQ,IAAI,iBAAiB,UAAU,YAAY,WAAW,iBAAiB,GAAG,YAAY;AAC9F,SAAQ,IAAI,GAAG;AACf,KAAI,YAAY,UAAU;AACxB,UAAQ,IAAI,8CAA8C;AAC1D,UAAQ,IAAI,qDAAqD;AACjE,UAAQ,IAAI,0DAA0D;AACtE,UAAQ,IAAI,0DAA0D;AACtE,UAAQ,IAAI,iEAAiE;AAC7E,UAAQ,IAAI,yDAAyD;AACrE,UAAQ,IAAI,+CAA+C;AAC3D,UAAQ,IAAI,2CAA2C;YAC9C,YAAY,QAAQ;AAC7B,UAAQ,IAAI,uDAAuD;AACnE,UAAQ,IAAI,4DAA4D;AACxE,UAAQ,IAAI,0DAA0D;YAC7D,YAAY,UAAU;AAC/B,UAAQ,IAAI,2EAA2E;AACvF,UAAQ,IAAI,0DAA0D;YAC7D,YAAY,UAAU;AAC/B,UAAQ,IAAI,oEAAoE;AAChF,UAAQ,IAAI,gEAAgE;AAC5E,UAAQ,IAAI,0DAA0D"}
@@ -0,0 +1,107 @@
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_template = require("./template.cjs");
5
+ let node_fs = require("node:fs");
6
+ node_fs = require_runtime.__toESM(node_fs);
7
+ let node_path = require("node:path");
8
+ node_path = require_runtime.__toESM(node_path);
9
+ let node_fs_promises = require("node:fs/promises");
10
+ node_fs_promises = require_runtime.__toESM(node_fs_promises);
11
+ let node_crypto = require("node:crypto");
12
+ let node_url = require("node:url");
13
+ let zod = require("zod");
14
+ //#region src/state.ts
15
+ const STATE_FILE_NAME = ".m5kdev.json";
16
+ const STATE_SCHEMA_VERSION = 1;
17
+ const stateFileSchema = zod.z.object({
18
+ kind: zod.z.enum(["text", "binary"]),
19
+ policy: zod.z.enum([
20
+ "merge",
21
+ "ensure",
22
+ "ignore"
23
+ ]),
24
+ sha256: zod.z.string().regex(/^[a-f0-9]{64}$/).optional()
25
+ });
26
+ const managedStateSchema = zod.z.object({
27
+ schemaVersion: zod.z.literal(1),
28
+ template: zod.z.object({
29
+ name: zod.z.string().min(1),
30
+ version: zod.z.string().min(1),
31
+ features: zod.z.array(zod.z.string()),
32
+ context: zod.z.object({
33
+ appName: zod.z.string().min(1),
34
+ appDescription: zod.z.string(),
35
+ appSlug: zod.z.string().min(1),
36
+ packageScope: zod.z.string().min(1)
37
+ })
38
+ }),
39
+ catalog: zod.z.record(zod.z.string(), zod.z.string()),
40
+ files: zod.z.record(zod.z.string(), stateFileSchema),
41
+ appliedMigrations: zod.z.array(zod.z.string())
42
+ });
43
+ function sha256(content) {
44
+ return (0, node_crypto.createHash)("sha256").update(content).digest("hex");
45
+ }
46
+ function getCliVersion() {
47
+ const moduleDirectory = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
48
+ const candidates = [node_path.default.resolve(moduleDirectory, "../package.json"), node_path.default.resolve(moduleDirectory, "../../package.json")];
49
+ for (const candidate of candidates) {
50
+ if (!node_fs.default.existsSync(candidate)) continue;
51
+ const manifest = JSON.parse(node_fs.default.readFileSync(candidate, "utf8"));
52
+ if (manifest.version) return manifest.version;
53
+ }
54
+ throw new Error("Unable to resolve the create-m5kdev package version.");
55
+ }
56
+ function createManagedState(options) {
57
+ const files = {};
58
+ let catalog = {};
59
+ for (const file of options.renderedFiles) {
60
+ const policy = require_src_template.getTemplateFilePolicy(options.manifest, file.relativePath);
61
+ files[file.relativePath] = {
62
+ kind: file.kind,
63
+ policy,
64
+ ...policy === "ignore" ? {} : { sha256: sha256(file.content) }
65
+ };
66
+ if (file.relativePath === "pnpm-workspace.yaml") catalog = require_src_catalog.readCatalog(file.content.toString("utf8"));
67
+ }
68
+ const renderedPackageManifests = options.renderedFiles.filter((file) => file.relativePath === "package.json" || file.relativePath.endsWith("/package.json")).map((file) => JSON.parse(file.content.toString("utf8")));
69
+ const referencedCatalogNames = new Set(require_src_catalog.collectConsumerDependencyNamesFromManifests(renderedPackageManifests));
70
+ catalog = Object.fromEntries(Object.entries(catalog).filter(([name]) => referencedCatalogNames.has(name)));
71
+ return {
72
+ schemaVersion: 1,
73
+ template: {
74
+ name: "minimal-app",
75
+ version: options.templateVersion,
76
+ features: [...options.enabledFeatures].sort(),
77
+ context: {
78
+ appName: options.context.appName,
79
+ appDescription: options.context.appDescription,
80
+ appSlug: options.context.appSlug,
81
+ packageScope: options.context.packageScope
82
+ }
83
+ },
84
+ catalog,
85
+ files,
86
+ appliedMigrations: [...options.appliedMigrations ?? []]
87
+ };
88
+ }
89
+ async function writeManagedState(repoRoot, state) {
90
+ await node_fs_promises.default.writeFile(node_path.default.join(repoRoot, STATE_FILE_NAME), `${JSON.stringify(state, null, 2)}\n`, "utf8");
91
+ }
92
+ async function readManagedState(repoRoot) {
93
+ const statePath = node_path.default.join(repoRoot, STATE_FILE_NAME);
94
+ const parsed = JSON.parse(await node_fs_promises.default.readFile(statePath, "utf8"));
95
+ return managedStateSchema.parse(parsed);
96
+ }
97
+ //#endregion
98
+ exports.STATE_FILE_NAME = STATE_FILE_NAME;
99
+ exports.STATE_SCHEMA_VERSION = STATE_SCHEMA_VERSION;
100
+ exports.createManagedState = createManagedState;
101
+ exports.getCliVersion = getCliVersion;
102
+ exports.managedStateSchema = managedStateSchema;
103
+ exports.readManagedState = readManagedState;
104
+ exports.sha256 = sha256;
105
+ exports.writeManagedState = writeManagedState;
106
+
107
+ //# sourceMappingURL=state.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.cjs","names":["z","path","fs","getTemplateFilePolicy","readCatalog","collectConsumerDependencyNamesFromManifests","fsp"],"sources":["../../src/state.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\r\nimport fs from \"node:fs\";\r\nimport fsp from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport { z } from \"zod\";\r\nimport {\r\n type ConsumerCatalog,\r\n collectConsumerDependencyNamesFromManifests,\r\n readCatalog,\r\n} from \"./catalog\";\r\nimport { getTemplateFilePolicy } from \"./template\";\r\nimport type {\r\n RenderedTemplateFile,\r\n TemplateContext,\r\n TemplateFeatureManifest,\r\n TemplateFilePolicy,\r\n} from \"./types\";\r\n\r\nexport const STATE_FILE_NAME = \".m5kdev.json\";\r\nexport const STATE_SCHEMA_VERSION = 1;\r\n\r\nconst stateFileSchema = z.object({\r\n kind: z.enum([\"text\", \"binary\"]),\r\n policy: z.enum([\"merge\", \"ensure\", \"ignore\"]),\r\n sha256: z\r\n .string()\r\n .regex(/^[a-f0-9]{64}$/)\r\n .optional(),\r\n});\r\n\r\nexport const managedStateSchema = z.object({\r\n schemaVersion: z.literal(STATE_SCHEMA_VERSION),\r\n template: z.object({\r\n name: z.string().min(1),\r\n version: z.string().min(1),\r\n features: z.array(z.string()),\r\n context: z.object({\r\n appName: z.string().min(1),\r\n appDescription: z.string(),\r\n appSlug: z.string().min(1),\r\n packageScope: z.string().min(1),\r\n }),\r\n }),\r\n catalog: z.record(z.string(), z.string()),\r\n files: z.record(z.string(), stateFileSchema),\r\n appliedMigrations: z.array(z.string()),\r\n});\r\n\r\nexport type ManagedState = z.infer<typeof managedStateSchema>;\r\n\r\nexport function sha256(content: Uint8Array | string): string {\r\n return createHash(\"sha256\").update(content).digest(\"hex\");\r\n}\r\n\r\nexport function getCliVersion(): string {\r\n const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));\r\n const candidates = [\r\n path.resolve(moduleDirectory, \"../package.json\"),\r\n path.resolve(moduleDirectory, \"../../package.json\"),\r\n ];\r\n for (const candidate of candidates) {\r\n if (!fs.existsSync(candidate)) continue;\r\n const manifest = JSON.parse(fs.readFileSync(candidate, \"utf8\")) as { version?: string };\r\n if (manifest.version) return manifest.version;\r\n }\r\n throw new Error(\"Unable to resolve the create-m5kdev package version.\");\r\n}\r\n\r\nexport function createManagedState(options: {\r\n templateVersion: string;\r\n enabledFeatures: ReadonlySet<string>;\r\n context: TemplateContext;\r\n renderedFiles: readonly RenderedTemplateFile[];\r\n manifest: TemplateFeatureManifest;\r\n appliedMigrations?: readonly string[];\r\n}): ManagedState {\r\n const files: ManagedState[\"files\"] = {};\r\n let catalog: ConsumerCatalog = {};\r\n\r\n for (const file of options.renderedFiles) {\r\n const policy: TemplateFilePolicy = getTemplateFilePolicy(options.manifest, file.relativePath);\r\n files[file.relativePath] = {\r\n kind: file.kind,\r\n policy,\r\n ...(policy === \"ignore\" ? {} : { sha256: sha256(file.content) }),\r\n };\r\n if (file.relativePath === \"pnpm-workspace.yaml\") {\r\n catalog = readCatalog(file.content.toString(\"utf8\"));\r\n }\r\n }\r\n\r\n const renderedPackageManifests = options.renderedFiles\r\n .filter(\r\n (file) => file.relativePath === \"package.json\" || file.relativePath.endsWith(\"/package.json\")\r\n )\r\n .map((file) => JSON.parse(file.content.toString(\"utf8\")) as Record<string, unknown>);\r\n const referencedCatalogNames = new Set(\r\n collectConsumerDependencyNamesFromManifests(renderedPackageManifests)\r\n );\r\n catalog = Object.fromEntries(\r\n Object.entries(catalog).filter(([name]) => referencedCatalogNames.has(name))\r\n );\r\n\r\n return {\r\n schemaVersion: STATE_SCHEMA_VERSION,\r\n template: {\r\n name: \"minimal-app\",\r\n version: options.templateVersion,\r\n features: [...options.enabledFeatures].sort(),\r\n context: {\r\n appName: options.context.appName,\r\n appDescription: options.context.appDescription,\r\n appSlug: options.context.appSlug,\r\n packageScope: options.context.packageScope,\r\n },\r\n },\r\n catalog,\r\n files,\r\n appliedMigrations: [...(options.appliedMigrations ?? [])],\r\n };\r\n}\r\n\r\nexport async function writeManagedState(repoRoot: string, state: ManagedState): Promise<void> {\r\n await fsp.writeFile(\r\n path.join(repoRoot, STATE_FILE_NAME),\r\n `${JSON.stringify(state, null, 2)}\\n`,\r\n \"utf8\"\r\n );\r\n}\r\n\r\nexport async function readManagedState(repoRoot: string): Promise<ManagedState> {\r\n const statePath = path.join(repoRoot, STATE_FILE_NAME);\r\n const parsed = JSON.parse(await fsp.readFile(statePath, \"utf8\")) as unknown;\r\n return managedStateSchema.parse(parsed);\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { RenderedTemplateFile, TemplateContext, TemplateFeatureManifest } from "./types.cjs";
2
+ import { z } from "zod";
3
+
4
+ //#region src/state.d.ts
5
+ declare const STATE_FILE_NAME = ".m5kdev.json";
6
+ declare const STATE_SCHEMA_VERSION = 1;
7
+ declare const managedStateSchema: z.ZodObject<{
8
+ schemaVersion: z.ZodLiteral<1>;
9
+ template: z.ZodObject<{
10
+ name: z.ZodString;
11
+ version: z.ZodString;
12
+ features: z.ZodArray<z.ZodString>;
13
+ context: z.ZodObject<{
14
+ appName: z.ZodString;
15
+ appDescription: z.ZodString;
16
+ appSlug: z.ZodString;
17
+ packageScope: z.ZodString;
18
+ }, z.core.$strip>;
19
+ }, z.core.$strip>;
20
+ catalog: z.ZodRecord<z.ZodString, z.ZodString>;
21
+ files: z.ZodRecord<z.ZodString, z.ZodObject<{
22
+ kind: z.ZodEnum<{
23
+ text: "text";
24
+ binary: "binary";
25
+ }>;
26
+ policy: z.ZodEnum<{
27
+ merge: "merge";
28
+ ensure: "ensure";
29
+ ignore: "ignore";
30
+ }>;
31
+ sha256: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$strip>>;
33
+ appliedMigrations: z.ZodArray<z.ZodString>;
34
+ }, z.core.$strip>;
35
+ type ManagedState = z.infer<typeof managedStateSchema>;
36
+ declare function sha256(content: Uint8Array | string): string;
37
+ declare function getCliVersion(): string;
38
+ declare function createManagedState(options: {
39
+ templateVersion: string;
40
+ enabledFeatures: ReadonlySet<string>;
41
+ context: TemplateContext;
42
+ renderedFiles: readonly RenderedTemplateFile[];
43
+ manifest: TemplateFeatureManifest;
44
+ appliedMigrations?: readonly string[];
45
+ }): ManagedState;
46
+ declare function writeManagedState(repoRoot: string, state: ManagedState): Promise<void>;
47
+ declare function readManagedState(repoRoot: string): Promise<ManagedState>;
48
+ //#endregion
49
+ export { ManagedState, STATE_FILE_NAME, STATE_SCHEMA_VERSION, createManagedState, getCliVersion, managedStateSchema, readManagedState, sha256, writeManagedState };
50
+ //# sourceMappingURL=state.d.cts.map
@@ -0,0 +1,45 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
3
+ let node_fs = require("node:fs");
4
+ node_fs = require_runtime.__toESM(node_fs);
5
+ let node_path = require("node:path");
6
+ node_path = require_runtime.__toESM(node_path);
7
+ //#region src/template.ts
8
+ function loadTemplateManifest(templateDirectory) {
9
+ const manifestPath = node_path.default.join(templateDirectory, "template.manifest.json");
10
+ if (!node_fs.default.existsSync(manifestPath)) throw new Error(`Template manifest is missing: ${manifestPath}`);
11
+ return JSON.parse(node_fs.default.readFileSync(manifestPath, "utf8"));
12
+ }
13
+ function globToRegExp(pattern) {
14
+ let source = "^";
15
+ for (let index = 0; index < pattern.length; index += 1) {
16
+ const character = pattern[index];
17
+ if (character === "*" && pattern[index + 1] === "*") {
18
+ source += ".*";
19
+ index += 1;
20
+ } else if (character === "*") source += "[^/]*";
21
+ else source += character.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22
+ }
23
+ return new RegExp(`${source}$`);
24
+ }
25
+ function getTemplateFilePolicy(manifest, relativePath) {
26
+ for (const rule of manifest.sync.rules) if (globToRegExp(rule.pattern).test(relativePath)) return rule.policy;
27
+ return manifest.sync.defaultPolicy;
28
+ }
29
+ function getEnabledFeatures(platform, testHarness) {
30
+ const enabled = /* @__PURE__ */ new Set();
31
+ if (platform !== "expo") enabled.add("webapp");
32
+ if (platform !== "web") enabled.add("expo");
33
+ if (testHarness) enabled.add("test-harness");
34
+ return enabled;
35
+ }
36
+ function getExcludedFeaturePaths(manifest, enabledFeatures) {
37
+ return Object.entries(manifest.features).filter(([feature]) => !enabledFeatures.has(feature)).flatMap(([, config]) => [...config.paths]);
38
+ }
39
+ //#endregion
40
+ exports.getEnabledFeatures = getEnabledFeatures;
41
+ exports.getExcludedFeaturePaths = getExcludedFeaturePaths;
42
+ exports.getTemplateFilePolicy = getTemplateFilePolicy;
43
+ exports.loadTemplateManifest = loadTemplateManifest;
44
+
45
+ //# sourceMappingURL=template.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.cjs","names":["path","fs"],"sources":["../../src/template.ts"],"sourcesContent":["import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport type { TemplateFeatureManifest, TemplateFilePolicy } from \"./types\";\r\n\r\nexport function loadTemplateManifest(templateDirectory: string): TemplateFeatureManifest {\r\n const manifestPath = path.join(templateDirectory, \"template.manifest.json\");\r\n if (!fs.existsSync(manifestPath)) {\r\n throw new Error(`Template manifest is missing: ${manifestPath}`);\r\n }\r\n return JSON.parse(fs.readFileSync(manifestPath, \"utf8\")) as TemplateFeatureManifest;\r\n}\r\n\r\nfunction globToRegExp(pattern: string): RegExp {\r\n let source = \"^\";\r\n for (let index = 0; index < pattern.length; index += 1) {\r\n const character = pattern[index];\r\n if (character === \"*\" && pattern[index + 1] === \"*\") {\r\n source += \".*\";\r\n index += 1;\r\n } else if (character === \"*\") {\r\n source += \"[^/]*\";\r\n } else {\r\n source += character.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n }\r\n }\r\n return new RegExp(`${source}$`);\r\n}\r\n\r\nexport function getTemplateFilePolicy(\r\n manifest: TemplateFeatureManifest,\r\n relativePath: string\r\n): TemplateFilePolicy {\r\n for (const rule of manifest.sync.rules) {\r\n if (globToRegExp(rule.pattern).test(relativePath)) return rule.policy;\r\n }\r\n return manifest.sync.defaultPolicy;\r\n}\r\n\r\nexport function getEnabledFeatures(\r\n platform: \"web\" | \"expo\" | \"both\",\r\n testHarness: boolean\r\n): Set<string> {\r\n const enabled = new Set<string>();\r\n if (platform !== \"expo\") enabled.add(\"webapp\");\r\n if (platform !== \"web\") enabled.add(\"expo\");\r\n if (testHarness) enabled.add(\"test-harness\");\r\n return enabled;\r\n}\r\n\r\nexport function getExcludedFeaturePaths(\r\n manifest: TemplateFeatureManifest,\r\n enabledFeatures: ReadonlySet<string>\r\n): string[] {\r\n return Object.entries(manifest.features)\r\n .filter(([feature]) => !enabledFeatures.has(feature))\r\n .flatMap(([, config]) => [...config.paths]);\r\n}\r\n"],"mappings":";;;;;;;AAIA,SAAgB,qBAAqB,mBAAoD;CACvF,MAAM,eAAeA,UAAAA,QAAK,KAAK,mBAAmB,yBAAyB;AAC3E,KAAI,CAACC,QAAAA,QAAG,WAAW,aAAa,CAC9B,OAAM,IAAI,MAAM,iCAAiC,eAAe;AAElE,QAAO,KAAK,MAAMA,QAAAA,QAAG,aAAa,cAAc,OAAO,CAAC;;AAG1D,SAAS,aAAa,SAAyB;CAC7C,IAAI,SAAS;AACb,MAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,GAAG;EACtD,MAAM,YAAY,QAAQ;AAC1B,MAAI,cAAc,OAAO,QAAQ,QAAQ,OAAO,KAAK;AACnD,aAAU;AACV,YAAS;aACA,cAAc,IACvB,WAAU;MAEV,WAAU,UAAU,QAAQ,uBAAuB,OAAO;;AAG9D,QAAO,IAAI,OAAO,GAAG,OAAO,GAAG;;AAGjC,SAAgB,sBACd,UACA,cACoB;AACpB,MAAK,MAAM,QAAQ,SAAS,KAAK,MAC/B,KAAI,aAAa,KAAK,QAAQ,CAAC,KAAK,aAAa,CAAE,QAAO,KAAK;AAEjE,QAAO,SAAS,KAAK;;AAGvB,SAAgB,mBACd,UACA,aACa;CACb,MAAM,0BAAU,IAAI,KAAa;AACjC,KAAI,aAAa,OAAQ,SAAQ,IAAI,SAAS;AAC9C,KAAI,aAAa,MAAO,SAAQ,IAAI,OAAO;AAC3C,KAAI,YAAa,SAAQ,IAAI,eAAe;AAC5C,QAAO;;AAGT,SAAgB,wBACd,UACA,iBACU;AACV,QAAO,OAAO,QAAQ,SAAS,SAAS,CACrC,QAAQ,CAAC,aAAa,CAAC,gBAAgB,IAAI,QAAQ,CAAC,CACpD,SAAS,GAAG,YAAY,CAAC,GAAG,OAAO,MAAM,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { TemplateFeatureManifest, TemplateFilePolicy } from "./types.cjs";
2
+
3
+ //#region src/template.d.ts
4
+ declare function loadTemplateManifest(templateDirectory: string): TemplateFeatureManifest;
5
+ declare function getTemplateFilePolicy(manifest: TemplateFeatureManifest, relativePath: string): TemplateFilePolicy;
6
+ declare function getEnabledFeatures(platform: "web" | "expo" | "both", testHarness: boolean): Set<string>;
7
+ declare function getExcludedFeaturePaths(manifest: TemplateFeatureManifest, enabledFeatures: ReadonlySet<string>): string[];
8
+ //#endregion
9
+ export { getEnabledFeatures, getExcludedFeaturePaths, getTemplateFilePolicy, loadTemplateManifest };
10
+ //# sourceMappingURL=template.d.cts.map
@@ -21,10 +21,29 @@ interface TemplateContext {
21
21
  betterAuthSecret: string;
22
22
  }
23
23
  interface TemplateFeatureManifest {
24
+ schemaVersion: number;
24
25
  features: Record<string, {
25
26
  paths: readonly string[];
26
27
  }>;
28
+ requiredPaths: readonly string[];
29
+ sync: {
30
+ defaultPolicy: TemplateFilePolicy;
31
+ rules: readonly {
32
+ pattern: string;
33
+ policy: TemplateFilePolicy;
34
+ }[];
35
+ renames?: readonly {
36
+ from: string;
37
+ to: string;
38
+ }[];
39
+ };
40
+ }
41
+ type TemplateFilePolicy = "merge" | "ensure" | "ignore";
42
+ interface RenderedTemplateFile {
43
+ content: Buffer;
44
+ kind: "text" | "binary";
45
+ relativePath: string;
27
46
  }
28
47
  //#endregion
29
- export { AppPlatform, CreateCommandOptions, PromptValues, TemplateContext, TemplateFeatureManifest };
48
+ export { AppPlatform, CreateCommandOptions, PromptValues, RenderedTemplateFile, TemplateContext, TemplateFeatureManifest, TemplateFilePolicy };
30
49
  //# sourceMappingURL=types.d.cts.map