create-m5kdev 0.38.14 → 0.38.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/catalog.cjs +5 -1
- package/dist/src/catalog.cjs.map +1 -1
- package/dist/src/catalog.d.cts +2 -1
- package/dist/src/create.cjs +6 -3
- package/dist/src/create.cjs.map +1 -1
- package/dist/src/doctor.cjs +5 -0
- package/dist/src/doctor.cjs.map +1 -1
- package/dist/src/fs.cjs +5 -2
- package/dist/src/fs.cjs.map +1 -1
- package/dist/src/init.cjs +2 -1
- package/dist/src/init.cjs.map +1 -1
- package/dist/src/prompts.cjs +28 -4
- package/dist/src/prompts.cjs.map +1 -1
- package/dist/src/prompts.d.cts +11 -2
- package/dist/src/reconcile.cjs +2 -3
- package/dist/src/reconcile.cjs.map +1 -1
- package/dist/src/runCli.cjs +21 -11
- package/dist/src/runCli.cjs.map +1 -1
- package/dist/src/template.cjs +21 -4
- package/dist/src/template.cjs.map +1 -1
- package/dist/src/template.d.cts +6 -3
- package/dist/src/types.d.cts +1 -1
- package/dist/src/update.cjs +91 -6
- package/dist/src/update.cjs.map +1 -1
- package/dist/src/update.d.cts +5 -0
- package/package.json +1 -1
- package/templates/minimal-app/.gitignore.tpl +1 -0
- package/templates/minimal-app/AGENTS.md.tpl +14 -2
- package/templates/minimal-app/CLAUDE.md.tpl +12 -1
- package/templates/minimal-app/README.md.tpl +32 -14
- package/templates/minimal-app/apps/landing/.env.example.tpl +1 -0
- package/templates/minimal-app/apps/landing/.env.tpl +1 -0
- package/templates/minimal-app/apps/landing/src/LandingPage.tsx.tpl +3 -9
- package/templates/minimal-app/apps/landing/vite.config.ts.tpl +2 -0
- package/templates/minimal-app/package.json.tpl +2 -0
- package/templates/minimal-app/pnpm-workspace.yaml.tpl +12 -12
- package/templates/minimal-app/template.manifest.json +8 -3
|
@@ -1 +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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"}
|
|
1
|
+
{"version":3,"file":"reconcile.cjs","names":["execFile","fs","path","os","loadTemplateManifest","withImpliedFeatures","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 {\r\n getExcludedFeaturePaths,\r\n getTemplateFilePolicy,\r\n loadTemplateManifest,\r\n withImpliedFeatures,\r\n} 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 = withImpliedFeatures(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 targetState = createManagedState({\r\n templateVersion: options.targetVersion,\r\n enabledFeatures: new Set(options.state.template.features),\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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"}
|
package/dist/src/runCli.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_src_prompts = require("./prompts.cjs");
|
|
2
3
|
const require_src_create = require("./create.cjs");
|
|
3
4
|
const require_src_diagnostics = require("./diagnostics.cjs");
|
|
4
5
|
const require_src_doctor = require("./doctor.cjs");
|
|
@@ -71,8 +72,11 @@ async function runCreate(parsed) {
|
|
|
71
72
|
console.log("Next steps:");
|
|
72
73
|
console.log(` cd ${result.targetDirectory}`);
|
|
73
74
|
if (options.skipInstall) console.log(" pnpm install");
|
|
74
|
-
console.log(" pnpm
|
|
75
|
-
|
|
75
|
+
if (options.platform === "landing") console.log(" pnpm dev");
|
|
76
|
+
else {
|
|
77
|
+
console.log(" pnpm --filter ./apps/server drizzle:migrate");
|
|
78
|
+
console.log(" pnpm dev");
|
|
79
|
+
}
|
|
76
80
|
}
|
|
77
81
|
async function runInit(parsed) {
|
|
78
82
|
validateRepositoryCommand(parsed, [
|
|
@@ -107,12 +111,19 @@ async function runUpdate(parsed) {
|
|
|
107
111
|
validateRepositoryCommand(parsed, [
|
|
108
112
|
"dry-run",
|
|
109
113
|
"skip-install",
|
|
110
|
-
"json"
|
|
114
|
+
"json",
|
|
115
|
+
"platform",
|
|
116
|
+
"yes"
|
|
111
117
|
]);
|
|
118
|
+
const platformOption = getStringOption(parsed.options, "platform");
|
|
119
|
+
const platform = platformOption ? require_src_prompts.parsePlatform(platformOption) : void 0;
|
|
120
|
+
if (platformOption && !platform) throw new Error(`Invalid --platform "${platformOption}". Use web, expo, or both.`);
|
|
112
121
|
const result = await require_src_update.updateManagedRepo({
|
|
113
122
|
repoRoot: process.cwd(),
|
|
114
123
|
dryRun: Boolean(parsed.options["dry-run"]),
|
|
115
|
-
skipInstall: Boolean(parsed.options["skip-install"])
|
|
124
|
+
skipInstall: Boolean(parsed.options["skip-install"]),
|
|
125
|
+
yes: Boolean(parsed.options.yes),
|
|
126
|
+
platform
|
|
116
127
|
});
|
|
117
128
|
if (parsed.options.json) console.log(JSON.stringify(result, null, 2));
|
|
118
129
|
else {
|
|
@@ -176,12 +187,9 @@ function validateOptions(parsed, allowedOptions) {
|
|
|
176
187
|
for (const option of Object.keys(parsed.options)) if (!allowed.has(option)) throw new Error(`--${option} is not valid for m5kdev ${parsed.command}.`);
|
|
177
188
|
}
|
|
178
189
|
function toCreateCommandOptions(parsed) {
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"expo",
|
|
183
|
-
"both"
|
|
184
|
-
].includes(platform)) throw new Error(`Invalid --platform "${platform}". Use web, expo, or both.`);
|
|
190
|
+
const platformOption = getStringOption(parsed.options, "platform");
|
|
191
|
+
const platform = platformOption ? require_src_prompts.parsePlatform(platformOption) : void 0;
|
|
192
|
+
if (platformOption && !platform) throw new Error(`Invalid --platform "${platformOption}". Use web, expo, both, or landing.`);
|
|
185
193
|
return {
|
|
186
194
|
targetDirectory: parsed.directory,
|
|
187
195
|
appName: getStringOption(parsed.options, "name"),
|
|
@@ -216,7 +224,7 @@ function printHelp(command) {
|
|
|
216
224
|
if (command === "create") {
|
|
217
225
|
console.log(" --name <value> Set the app name");
|
|
218
226
|
console.log(" --description <value> Set the app description");
|
|
219
|
-
console.log(" --platform <value> web (default), expo, or
|
|
227
|
+
console.log(" --platform <value> web (default), expo, both, or landing");
|
|
220
228
|
console.log(" --with-test-harness Include the e2e test harness");
|
|
221
229
|
console.log(" --yes Accept defaults for missing prompts");
|
|
222
230
|
console.log(" --force Allow a non-empty directory");
|
|
@@ -231,6 +239,8 @@ function printHelp(command) {
|
|
|
231
239
|
console.log(" --json Emit machine-readable output");
|
|
232
240
|
} else if (command === "update") {
|
|
233
241
|
console.log(" --dry-run Build the complete plan without writes");
|
|
242
|
+
console.log(" --platform <value> landing-only escape hatch: web, expo, or both");
|
|
243
|
+
console.log(" --yes Skip module and e2e prompts for --platform");
|
|
234
244
|
console.log(" --skip-install Apply sources without pnpm install");
|
|
235
245
|
console.log(" --json Emit machine-readable output");
|
|
236
246
|
}
|
package/dist/src/runCli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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":"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"}
|
|
1
|
+
{"version":3,"file":"runCli.cjs","names":["scaffoldProject","initializeManagedRepo","diagnoseManagedRepo","parsePlatform","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 { parsePlatform } from \"./prompts\";\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 if (options.platform === \"landing\") {\r\n console.log(\" pnpm dev\");\r\n } else {\r\n console.log(\" pnpm --filter ./apps/server drizzle:migrate\");\r\n console.log(\" pnpm dev\");\r\n }\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\", \"platform\", \"yes\"]);\r\n const platformOption = getStringOption(parsed.options, \"platform\");\r\n const platform = platformOption ? parsePlatform(platformOption) : undefined;\r\n if (platformOption && !platform) {\r\n throw new Error(`Invalid --platform \"${platformOption}\". Use web, expo, or both.`);\r\n }\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 yes: Boolean(parsed.options.yes),\r\n platform,\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 platformOption = getStringOption(parsed.options, \"platform\");\r\n const platform = platformOption ? parsePlatform(platformOption) : undefined;\r\n if (platformOption && !platform) {\r\n throw new Error(`Invalid --platform \"${platformOption}\". Use web, expo, both, or landing.`);\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,\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, both, or landing\");\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(\" --platform <value> landing-only escape hatch: web, expo, or both\");\r\n console.log(\" --yes Skip module and e2e prompts for --platform\");\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":"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"}
|
package/dist/src/template.cjs
CHANGED
|
@@ -5,6 +5,16 @@ node_fs = require_runtime.__toESM(node_fs);
|
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
node_path = require_runtime.__toESM(node_path);
|
|
7
7
|
//#region src/template.ts
|
|
8
|
+
const PRODUCT_STACK_FEATURE = "server";
|
|
9
|
+
function withImpliedFeatures(enabled) {
|
|
10
|
+
const next = new Set(enabled);
|
|
11
|
+
if (next.has("webapp") || next.has("expo")) next.add(PRODUCT_STACK_FEATURE);
|
|
12
|
+
return next;
|
|
13
|
+
}
|
|
14
|
+
function hasProductStack(enabled) {
|
|
15
|
+
const features = enabled instanceof Set ? enabled : new Set(enabled);
|
|
16
|
+
return features.has("webapp") || features.has("expo") || features.has("server");
|
|
17
|
+
}
|
|
8
18
|
function loadTemplateManifest(templateDirectory) {
|
|
9
19
|
const manifestPath = node_path.default.join(templateDirectory, "template.manifest.json");
|
|
10
20
|
if (!node_fs.default.existsSync(manifestPath)) throw new Error(`Template manifest is missing: ${manifestPath}`);
|
|
@@ -28,7 +38,7 @@ function getTemplateFilePolicy(manifest, relativePath) {
|
|
|
28
38
|
}
|
|
29
39
|
function getFeatureKind(options) {
|
|
30
40
|
if (options.config.kind) return options.config.kind;
|
|
31
|
-
if (options.id === "webapp" || options.id === "expo") return "platform";
|
|
41
|
+
if (options.id === "webapp" || options.id === "expo" || options.id === "server") return "platform";
|
|
32
42
|
if (options.id === "test-harness") return "harness";
|
|
33
43
|
return "module";
|
|
34
44
|
}
|
|
@@ -44,21 +54,28 @@ function listBackendModuleChoices(manifest) {
|
|
|
44
54
|
}
|
|
45
55
|
function getEnabledFeatures(options) {
|
|
46
56
|
const enabled = /* @__PURE__ */ new Set();
|
|
47
|
-
if (options.platform !== "expo") enabled.add("webapp");
|
|
48
|
-
if (options.platform !== "web") enabled.add("expo");
|
|
57
|
+
if (options.platform !== "expo" && options.platform !== "landing") enabled.add("webapp");
|
|
58
|
+
if (options.platform !== "web" && options.platform !== "landing") enabled.add("expo");
|
|
49
59
|
if (options.testHarness) enabled.add("test-harness");
|
|
50
60
|
for (const id of options.modules ?? []) enabled.add(id);
|
|
51
61
|
return enabled;
|
|
52
62
|
}
|
|
63
|
+
const LANDING_LOCAL_ENV_PATHS = ["apps/landing/.env", "apps/landing/.env.example"];
|
|
53
64
|
function getExcludedFeaturePaths(manifest, enabledFeatures) {
|
|
54
|
-
|
|
65
|
+
const effective = withImpliedFeatures(enabledFeatures);
|
|
66
|
+
const excluded = Object.entries(manifest.features).filter(([feature]) => !effective.has(feature)).flatMap(([, config]) => [...config.paths]);
|
|
67
|
+
if (hasProductStack(effective)) excluded.push(...LANDING_LOCAL_ENV_PATHS);
|
|
68
|
+
return excluded;
|
|
55
69
|
}
|
|
56
70
|
//#endregion
|
|
71
|
+
exports.PRODUCT_STACK_FEATURE = PRODUCT_STACK_FEATURE;
|
|
57
72
|
exports.getEnabledFeatures = getEnabledFeatures;
|
|
58
73
|
exports.getExcludedFeaturePaths = getExcludedFeaturePaths;
|
|
59
74
|
exports.getFeatureKind = getFeatureKind;
|
|
60
75
|
exports.getTemplateFilePolicy = getTemplateFilePolicy;
|
|
76
|
+
exports.hasProductStack = hasProductStack;
|
|
61
77
|
exports.listBackendModuleChoices = listBackendModuleChoices;
|
|
62
78
|
exports.loadTemplateManifest = loadTemplateManifest;
|
|
79
|
+
exports.withImpliedFeatures = withImpliedFeatures;
|
|
63
80
|
|
|
64
81
|
//# sourceMappingURL=template.cjs.map
|
|
@@ -1 +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 {
|
|
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 {\r\n AppPlatform,\r\n TemplateFeatureKind,\r\n TemplateFeatureManifest,\r\n TemplateFilePolicy,\r\n} from \"./types\";\r\n\r\nexport const PRODUCT_STACK_FEATURE = \"server\";\r\n\r\nexport function withImpliedFeatures(enabled: ReadonlySet<string>): Set<string> {\r\n const next = new Set(enabled);\r\n if (next.has(\"webapp\") || next.has(\"expo\")) next.add(PRODUCT_STACK_FEATURE);\r\n return next;\r\n}\r\n\r\nexport function hasProductStack(enabled: ReadonlySet<string> | readonly string[]): boolean {\r\n const features = enabled instanceof Set ? enabled : new Set(enabled);\r\n return features.has(\"webapp\") || features.has(\"expo\") || features.has(PRODUCT_STACK_FEATURE);\r\n}\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 interface BackendModuleChoice {\r\n id: string;\r\n label: string;\r\n experimental: boolean;\r\n}\r\n\r\nexport function getFeatureKind(options: {\r\n id: string;\r\n config: TemplateFeatureManifest[\"features\"][string];\r\n}): TemplateFeatureKind {\r\n if (options.config.kind) return options.config.kind;\r\n if (options.id === \"webapp\" || options.id === \"expo\" || options.id === PRODUCT_STACK_FEATURE)\r\n return \"platform\";\r\n if (options.id === \"test-harness\") return \"harness\";\r\n return \"module\";\r\n}\r\n\r\nexport function listBackendModuleChoices(manifest: TemplateFeatureManifest): BackendModuleChoice[] {\r\n return Object.entries(manifest.features)\r\n .filter(([id, config]) => getFeatureKind({ id, config }) === \"module\")\r\n .map(([id, config]) => ({\r\n id,\r\n label: config.label ?? id,\r\n experimental: Boolean(config.experimental),\r\n }));\r\n}\r\n\r\nexport function getEnabledFeatures(options: {\r\n platform: AppPlatform;\r\n testHarness: boolean;\r\n modules?: readonly string[];\r\n}): Set<string> {\r\n const enabled = new Set<string>();\r\n if (options.platform !== \"expo\" && options.platform !== \"landing\") enabled.add(\"webapp\");\r\n if (options.platform !== \"web\" && options.platform !== \"landing\") enabled.add(\"expo\");\r\n if (options.testHarness) enabled.add(\"test-harness\");\r\n for (const id of options.modules ?? []) enabled.add(id);\r\n return enabled;\r\n}\r\n\r\nconst LANDING_LOCAL_ENV_PATHS = [\"apps/landing/.env\", \"apps/landing/.env.example\"] as const;\r\n\r\nexport function getExcludedFeaturePaths(\r\n manifest: TemplateFeatureManifest,\r\n enabledFeatures: ReadonlySet<string>\r\n): string[] {\r\n const effective = withImpliedFeatures(enabledFeatures);\r\n const excluded = Object.entries(manifest.features)\r\n .filter(([feature]) => !effective.has(feature))\r\n .flatMap(([, config]) => [...config.paths]);\r\n if (hasProductStack(effective)) excluded.push(...LANDING_LOCAL_ENV_PATHS);\r\n return excluded;\r\n}\r\n"],"mappings":";;;;;;;AASA,MAAa,wBAAwB;AAErC,SAAgB,oBAAoB,SAA2C;CAC7E,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,KAAI,KAAK,IAAI,SAAS,IAAI,KAAK,IAAI,OAAO,CAAE,MAAK,IAAI,sBAAsB;AAC3E,QAAO;;AAGT,SAAgB,gBAAgB,SAA2D;CACzF,MAAM,WAAW,mBAAmB,MAAM,UAAU,IAAI,IAAI,QAAQ;AACpE,QAAO,SAAS,IAAI,SAAS,IAAI,SAAS,IAAI,OAAO,IAAI,SAAS,IAAA,SAA0B;;AAG9F,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;;AASvB,SAAgB,eAAe,SAGP;AACtB,KAAI,QAAQ,OAAO,KAAM,QAAO,QAAQ,OAAO;AAC/C,KAAI,QAAQ,OAAO,YAAY,QAAQ,OAAO,UAAU,QAAQ,OAAA,SAC9D,QAAO;AACT,KAAI,QAAQ,OAAO,eAAgB,QAAO;AAC1C,QAAO;;AAGT,SAAgB,yBAAyB,UAA0D;AACjG,QAAO,OAAO,QAAQ,SAAS,SAAS,CACrC,QAAQ,CAAC,IAAI,YAAY,eAAe;EAAE;EAAI;EAAQ,CAAC,KAAK,SAAS,CACrE,KAAK,CAAC,IAAI,aAAa;EACtB;EACA,OAAO,OAAO,SAAS;EACvB,cAAc,QAAQ,OAAO,aAAa;EAC3C,EAAE;;AAGP,SAAgB,mBAAmB,SAInB;CACd,MAAM,0BAAU,IAAI,KAAa;AACjC,KAAI,QAAQ,aAAa,UAAU,QAAQ,aAAa,UAAW,SAAQ,IAAI,SAAS;AACxF,KAAI,QAAQ,aAAa,SAAS,QAAQ,aAAa,UAAW,SAAQ,IAAI,OAAO;AACrF,KAAI,QAAQ,YAAa,SAAQ,IAAI,eAAe;AACpD,MAAK,MAAM,MAAM,QAAQ,WAAW,EAAE,CAAE,SAAQ,IAAI,GAAG;AACvD,QAAO;;AAGT,MAAM,0BAA0B,CAAC,qBAAqB,4BAA4B;AAElF,SAAgB,wBACd,UACA,iBACU;CACV,MAAM,YAAY,oBAAoB,gBAAgB;CACtD,MAAM,WAAW,OAAO,QAAQ,SAAS,SAAS,CAC/C,QAAQ,CAAC,aAAa,CAAC,UAAU,IAAI,QAAQ,CAAC,CAC9C,SAAS,GAAG,YAAY,CAAC,GAAG,OAAO,MAAM,CAAC;AAC7C,KAAI,gBAAgB,UAAU,CAAE,UAAS,KAAK,GAAG,wBAAwB;AACzE,QAAO"}
|
package/dist/src/template.d.cts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { TemplateFeatureKind, TemplateFeatureManifest, TemplateFilePolicy } from "./types.cjs";
|
|
1
|
+
import { AppPlatform, TemplateFeatureKind, TemplateFeatureManifest, TemplateFilePolicy } from "./types.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/template.d.ts
|
|
4
|
+
declare const PRODUCT_STACK_FEATURE = "server";
|
|
5
|
+
declare function withImpliedFeatures(enabled: ReadonlySet<string>): Set<string>;
|
|
6
|
+
declare function hasProductStack(enabled: ReadonlySet<string> | readonly string[]): boolean;
|
|
4
7
|
declare function loadTemplateManifest(templateDirectory: string): TemplateFeatureManifest;
|
|
5
8
|
declare function getTemplateFilePolicy(manifest: TemplateFeatureManifest, relativePath: string): TemplateFilePolicy;
|
|
6
9
|
interface BackendModuleChoice {
|
|
@@ -14,11 +17,11 @@ declare function getFeatureKind(options: {
|
|
|
14
17
|
}): TemplateFeatureKind;
|
|
15
18
|
declare function listBackendModuleChoices(manifest: TemplateFeatureManifest): BackendModuleChoice[];
|
|
16
19
|
declare function getEnabledFeatures(options: {
|
|
17
|
-
platform:
|
|
20
|
+
platform: AppPlatform;
|
|
18
21
|
testHarness: boolean;
|
|
19
22
|
modules?: readonly string[];
|
|
20
23
|
}): Set<string>;
|
|
21
24
|
declare function getExcludedFeaturePaths(manifest: TemplateFeatureManifest, enabledFeatures: ReadonlySet<string>): string[];
|
|
22
25
|
//#endregion
|
|
23
|
-
export { BackendModuleChoice, getEnabledFeatures, getExcludedFeaturePaths, getFeatureKind, getTemplateFilePolicy, listBackendModuleChoices, loadTemplateManifest };
|
|
26
|
+
export { BackendModuleChoice, PRODUCT_STACK_FEATURE, getEnabledFeatures, getExcludedFeaturePaths, getFeatureKind, getTemplateFilePolicy, hasProductStack, listBackendModuleChoices, loadTemplateManifest, withImpliedFeatures };
|
|
24
27
|
//# sourceMappingURL=template.d.cts.map
|
package/dist/src/types.d.cts
CHANGED
package/dist/src/update.cjs
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_src_fs = require("./fs.cjs");
|
|
3
4
|
const require_src_paths = require("./paths.cjs");
|
|
5
|
+
const require_src_template = require("./template.cjs");
|
|
6
|
+
const require_src_prompts = require("./prompts.cjs");
|
|
4
7
|
const require_src_state = require("./state.cjs");
|
|
5
8
|
const require_src_migrations_registry = require("./migrations/registry.cjs");
|
|
6
9
|
const require_src_reconcile = require("./reconcile.cjs");
|
|
7
10
|
let node_path = require("node:path");
|
|
8
11
|
node_path = require_runtime.__toESM(node_path);
|
|
12
|
+
let node_fs_promises = require("node:fs/promises");
|
|
13
|
+
node_fs_promises = require_runtime.__toESM(node_fs_promises);
|
|
9
14
|
let node_child_process = require("node:child_process");
|
|
10
15
|
let node_util = require("node:util");
|
|
11
16
|
let semver = require("semver");
|
|
@@ -18,32 +23,112 @@ async function assertCleanGit(repoRoot) {
|
|
|
18
23
|
encoding: "utf8"
|
|
19
24
|
})).stdout.trim()) throw new Error("Write-mode update requires a clean Git worktree. Commit or stash changes first.");
|
|
20
25
|
}
|
|
26
|
+
async function resolveUpgradeState(options) {
|
|
27
|
+
if (!options.platform) return options.state;
|
|
28
|
+
if (options.platform === "landing") throw new Error("m5kdev update --platform cannot switch to landing.");
|
|
29
|
+
const current = new Set(options.state.template.features);
|
|
30
|
+
if (current.has("webapp") || current.has("expo")) throw new Error("m5kdev update --platform is only for landing-only apps. This repository already has a Webapp or Expo client.");
|
|
31
|
+
const extras = await require_src_prompts.resolveHarnessAndModuleOptions({
|
|
32
|
+
yes: options.yes,
|
|
33
|
+
testHarness: options.testHarness,
|
|
34
|
+
modules: options.modules
|
|
35
|
+
});
|
|
36
|
+
const enabledFeatures = require_src_template.getEnabledFeatures({
|
|
37
|
+
platform: options.platform,
|
|
38
|
+
testHarness: extras.testHarness,
|
|
39
|
+
modules: extras.modules
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
...options.state,
|
|
43
|
+
template: {
|
|
44
|
+
...options.state.template,
|
|
45
|
+
features: [...enabledFeatures].sort()
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function overlayEnv(base, overlay) {
|
|
50
|
+
if (!overlay.trim()) return base;
|
|
51
|
+
let next = base;
|
|
52
|
+
for (const line of overlay.split("\n")) {
|
|
53
|
+
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
54
|
+
if (!match) continue;
|
|
55
|
+
const [, key, value] = match;
|
|
56
|
+
const assignment = `${key}=${value}`;
|
|
57
|
+
const pattern = new RegExp(`^${key}=.*$`, "m");
|
|
58
|
+
if (pattern.test(next)) next = next.replace(pattern, assignment);
|
|
59
|
+
else next = `${next}${next.endsWith("\n") || next.length === 0 ? "" : "\n"}${assignment}\n`;
|
|
60
|
+
}
|
|
61
|
+
return next;
|
|
62
|
+
}
|
|
63
|
+
async function planLandingEnvMigration(options) {
|
|
64
|
+
const sharedEnvRelative = "apps/shared/.env";
|
|
65
|
+
const landingEnvRelative = "apps/landing/.env";
|
|
66
|
+
const landingExampleRelative = "apps/landing/.env.example";
|
|
67
|
+
const landingEnv = await node_fs_promises.default.readFile(node_path.default.join(options.repoRoot, landingEnvRelative), "utf8").catch(() => "");
|
|
68
|
+
const existing = await options.changes.read(sharedEnvRelative);
|
|
69
|
+
const sharedEnv = (await require_src_fs.collectTemplateFiles(options.templateRoot, {
|
|
70
|
+
...options.state.template.context,
|
|
71
|
+
betterAuthSecret: "ignored-by-managed-state"
|
|
72
|
+
}, { enabledFeatures: require_src_template.withImpliedFeatures(new Set(options.state.template.features)) })).find((file) => file.relativePath === sharedEnvRelative);
|
|
73
|
+
const base = existing?.toString("utf8") || sharedEnv?.content.toString("utf8") || "";
|
|
74
|
+
if (base.length > 0) {
|
|
75
|
+
const content = overlayEnv(base, landingEnv);
|
|
76
|
+
if (!existing || content !== existing.toString("utf8")) options.changes.addChange({
|
|
77
|
+
kind: existing ? "modify" : "add",
|
|
78
|
+
path: sharedEnvRelative,
|
|
79
|
+
content: Buffer.from(content),
|
|
80
|
+
reason: "Copy Landing env into Deploy home"
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
for (const relativePath of [landingEnvRelative, landingExampleRelative]) {
|
|
84
|
+
if (!await node_fs_promises.default.access(node_path.default.join(options.repoRoot, relativePath)).then(() => true).catch(() => false)) continue;
|
|
85
|
+
options.changes.addChange({
|
|
86
|
+
kind: "delete",
|
|
87
|
+
path: relativePath,
|
|
88
|
+
reason: "Remove Landing-local env after attaching Deploy home"
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
21
92
|
async function updateManagedRepo(options) {
|
|
22
93
|
const state = await require_src_state.readManagedState(options.repoRoot);
|
|
23
94
|
const targetVersion = options.targetVersion ?? require_src_state.getCliVersion();
|
|
24
95
|
if (semver.default.gt(state.template.version, targetVersion)) throw new Error(`The running CLI ${targetVersion} is older than managed state ${state.template.version}.`);
|
|
96
|
+
const upgradedState = await resolveUpgradeState({
|
|
97
|
+
state,
|
|
98
|
+
platform: options.platform,
|
|
99
|
+
yes: Boolean(options.yes),
|
|
100
|
+
testHarness: options.testHarness,
|
|
101
|
+
modules: options.modules
|
|
102
|
+
});
|
|
25
103
|
if (!options.dryRun) await (options.assertClean ?? assertCleanGit)(options.repoRoot);
|
|
104
|
+
const templateRoot = options.targetTemplateRoot ?? require_src_paths.getTemplateRoot();
|
|
26
105
|
const reconciliation = await require_src_reconcile.reconcileTemplates({
|
|
27
106
|
repoRoot: options.repoRoot,
|
|
28
|
-
state,
|
|
29
|
-
targetTemplateRoot:
|
|
107
|
+
state: upgradedState,
|
|
108
|
+
targetTemplateRoot: templateRoot,
|
|
30
109
|
targetVersion,
|
|
31
110
|
baseProvider: options.baseProvider
|
|
32
111
|
});
|
|
112
|
+
if (options.platform && options.platform !== "landing") await planLandingEnvMigration({
|
|
113
|
+
repoRoot: options.repoRoot,
|
|
114
|
+
templateRoot,
|
|
115
|
+
state: upgradedState,
|
|
116
|
+
changes: reconciliation.changes
|
|
117
|
+
});
|
|
33
118
|
const registry = options.registry ?? require_src_migrations_registry.getMigrationRegistry();
|
|
34
|
-
const pending = require_src_migrations_registry.getPendingMigrations(
|
|
119
|
+
const pending = require_src_migrations_registry.getPendingMigrations(upgradedState, targetVersion, registry);
|
|
35
120
|
const appliedMigrations = [];
|
|
36
121
|
for (const migration of pending) {
|
|
37
122
|
const context = {
|
|
38
123
|
repoRoot: options.repoRoot,
|
|
39
|
-
state,
|
|
124
|
+
state: upgradedState,
|
|
40
125
|
changes: reconciliation.changes
|
|
41
126
|
};
|
|
42
127
|
if (migration.applies && !await migration.applies(context)) continue;
|
|
43
128
|
await migration.plan(context);
|
|
44
129
|
appliedMigrations.push(migration.id);
|
|
45
130
|
}
|
|
46
|
-
reconciliation.targetState.appliedMigrations = [...new Set([...
|
|
131
|
+
reconciliation.targetState.appliedMigrations = [...new Set([...upgradedState.appliedMigrations, ...appliedMigrations])];
|
|
47
132
|
const validationDiagnostics = await require_src_migrations_registry.runMigrationValidators({
|
|
48
133
|
repoRoot: options.repoRoot,
|
|
49
134
|
state: reconciliation.targetState,
|
|
@@ -58,7 +143,7 @@ async function updateManagedRepo(options) {
|
|
|
58
143
|
}
|
|
59
144
|
const stateContent = Buffer.from(`${JSON.stringify(reconciliation.targetState, null, 2)}\n`);
|
|
60
145
|
const currentState = await reconciliation.changes.read(require_src_state.STATE_FILE_NAME);
|
|
61
|
-
if (!currentState
|
|
146
|
+
if (!currentState?.equals(stateContent)) reconciliation.changes.addChange({
|
|
62
147
|
kind: currentState ? "modify" : "add",
|
|
63
148
|
path: require_src_state.STATE_FILE_NAME,
|
|
64
149
|
content: stateContent,
|
package/dist/src/update.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.cjs","names":["execFile","readManagedState","getCliVersion","reconcileTemplates","getTemplateRoot","getMigrationRegistry","getPendingMigrations","runMigrationValidators","STATE_FILE_NAME","path"],"sources":["../../src/update.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport semver from \"semver\";\r\nimport type { PlannedChange, PlannedConflict } from \"./changes\";\r\nimport {\r\n getMigrationRegistry,\r\n getPendingMigrations,\r\n runMigrationValidators,\r\n} from \"./migrations/registry\";\r\nimport type { MigrationDefinition } from \"./migrations/types\";\r\nimport { getTemplateRoot } from \"./paths\";\r\nimport { type BaseTemplateProvider, reconcileTemplates } from \"./reconcile\";\r\nimport { getCliVersion, readManagedState, STATE_FILE_NAME } from \"./state\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\n\r\nexport interface UpdateCommandOptions {\r\n repoRoot: string;\r\n dryRun: boolean;\r\n skipInstall: boolean;\r\n baseProvider?: BaseTemplateProvider;\r\n /** Test seam; published commands always use the embedded production registry. */\r\n registry?: readonly MigrationDefinition[];\r\n /** Test seams for fixture-version updates. */\r\n targetTemplateRoot?: string;\r\n targetVersion?: string;\r\n assertClean?: (repoRoot: string) => Promise<void>;\r\n install?: (repoRoot: string) => Promise<void>;\r\n}\r\n\r\nexport interface UpdateResult {\r\n fromVersion: string;\r\n targetVersion: string;\r\n dryRun: boolean;\r\n applied: boolean;\r\n changes: Array<Omit<PlannedChange, \"content\">>;\r\n conflicts: PlannedConflict[];\r\n migrations: string[];\r\n dependenciesChanged: boolean;\r\n installRequired: boolean;\r\n}\r\n\r\nasync function assertCleanGit(repoRoot: string): Promise<void> {\r\n const result = await execFileAsync(\"git\", [\"status\", \"--porcelain\"], {\r\n cwd: repoRoot,\r\n encoding: \"utf8\",\r\n });\r\n if (result.stdout.trim()) {\r\n throw new Error(\r\n \"Write-mode update requires a clean Git worktree. Commit or stash changes first.\"\r\n );\r\n }\r\n}\r\n\r\nexport async function updateManagedRepo(options: UpdateCommandOptions): Promise<UpdateResult> {\r\n const state = await readManagedState(options.repoRoot);\r\n const targetVersion = options.targetVersion ?? getCliVersion();\r\n if (semver.gt(state.template.version, targetVersion)) {\r\n throw new Error(\r\n `The running CLI ${targetVersion} is older than managed state ${state.template.version}.`\r\n );\r\n }\r\n if (!options.dryRun) await (options.assertClean ?? assertCleanGit)(options.repoRoot);\r\n\r\n const reconciliation = await reconcileTemplates({\r\n repoRoot: options.repoRoot,\r\n state,\r\n targetTemplateRoot: options.targetTemplateRoot ?? getTemplateRoot(),\r\n targetVersion,\r\n baseProvider: options.baseProvider,\r\n });\r\n const registry = options.registry ?? getMigrationRegistry();\r\n const pending = getPendingMigrations(state, targetVersion, registry);\r\n const appliedMigrations: string[] = [];\r\n for (const migration of pending) {\r\n const context = { repoRoot: options.repoRoot, state, changes: reconciliation.changes };\r\n if (migration.applies && !(await migration.applies(context))) continue;\r\n await migration.plan(context);\r\n appliedMigrations.push(migration.id);\r\n }\r\n reconciliation.targetState.appliedMigrations = [\r\n ...new Set([...state.appliedMigrations, ...appliedMigrations]),\r\n ];\r\n\r\n const validationDiagnostics = await runMigrationValidators(\r\n {\r\n repoRoot: options.repoRoot,\r\n state: reconciliation.targetState,\r\n changes: reconciliation.changes,\r\n },\r\n registry\r\n );\r\n for (const diagnostic of validationDiagnostics) {\r\n if (diagnostic.severity !== \"error\") continue;\r\n reconciliation.changes.addConflict({\r\n path: diagnostic.path ?? \".m5kdev.json\",\r\n reason: `[${diagnostic.code}] ${diagnostic.message}`,\r\n });\r\n }\r\n\r\n const stateContent = Buffer.from(`${JSON.stringify(reconciliation.targetState, null, 2)}\\n`);\r\n const currentState = await reconciliation.changes.read(STATE_FILE_NAME);\r\n if (!currentState || !currentState.equals(stateContent)) {\r\n reconciliation.changes.addChange({\r\n kind: currentState ? \"modify\" : \"add\",\r\n path: STATE_FILE_NAME,\r\n content: stateContent,\r\n reason: \"Advance managed template state\",\r\n });\r\n }\r\n\r\n const result: UpdateResult = {\r\n fromVersion: state.template.version,\r\n targetVersion,\r\n dryRun: options.dryRun,\r\n applied: false,\r\n changes: [...reconciliation.changes.changes.values()].map(\r\n ({ content: _content, ...change }) => change\r\n ),\r\n conflicts: [...reconciliation.changes.conflicts],\r\n migrations: appliedMigrations,\r\n dependenciesChanged: reconciliation.dependenciesChanged,\r\n installRequired: reconciliation.dependenciesChanged && options.skipInstall,\r\n };\r\n if (options.dryRun || result.conflicts.length > 0) return result;\r\n\r\n await reconciliation.changes.apply();\r\n result.applied = true;\r\n if (reconciliation.dependenciesChanged && !options.skipInstall) {\r\n if (options.install) await options.install(path.resolve(options.repoRoot));\r\n else\r\n await execFileAsync(\"pnpm\", [\"install\"], {\r\n cwd: path.resolve(options.repoRoot),\r\n env: process.env,\r\n });\r\n }\r\n return result;\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"update.cjs","names":["execFile","resolveHarnessAndModuleOptions","getEnabledFeatures","fs","path","collectTemplateFiles","withImpliedFeatures","readManagedState","getCliVersion","getTemplateRoot","reconcileTemplates","getMigrationRegistry","getPendingMigrations","runMigrationValidators","STATE_FILE_NAME"],"sources":["../../src/update.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport fs from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport semver from \"semver\";\r\nimport type { ChangeSet, PlannedChange, PlannedConflict } from \"./changes\";\r\nimport { collectTemplateFiles } from \"./fs\";\r\nimport {\r\n getMigrationRegistry,\r\n getPendingMigrations,\r\n runMigrationValidators,\r\n} from \"./migrations/registry\";\r\nimport type { MigrationDefinition } from \"./migrations/types\";\r\nimport { getTemplateRoot } from \"./paths\";\r\nimport { resolveHarnessAndModuleOptions } from \"./prompts\";\r\nimport { type BaseTemplateProvider, reconcileTemplates } from \"./reconcile\";\r\nimport { getCliVersion, type ManagedState, readManagedState, STATE_FILE_NAME } from \"./state\";\r\nimport { getEnabledFeatures, withImpliedFeatures } from \"./template\";\r\nimport type { AppPlatform } from \"./types\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\n\r\nexport interface UpdateCommandOptions {\r\n repoRoot: string;\r\n dryRun: boolean;\r\n skipInstall: boolean;\r\n yes?: boolean;\r\n platform?: AppPlatform;\r\n testHarness?: boolean;\r\n modules?: string[];\r\n baseProvider?: BaseTemplateProvider;\r\n /** Test seam; published commands always use the embedded production registry. */\r\n registry?: readonly MigrationDefinition[];\r\n /** Test seams for fixture-version updates. */\r\n targetTemplateRoot?: string;\r\n targetVersion?: string;\r\n assertClean?: (repoRoot: string) => Promise<void>;\r\n install?: (repoRoot: string) => Promise<void>;\r\n}\r\n\r\nexport interface UpdateResult {\r\n fromVersion: string;\r\n targetVersion: string;\r\n dryRun: boolean;\r\n applied: boolean;\r\n changes: Array<Omit<PlannedChange, \"content\">>;\r\n conflicts: PlannedConflict[];\r\n migrations: string[];\r\n dependenciesChanged: boolean;\r\n installRequired: boolean;\r\n}\r\n\r\nasync function assertCleanGit(repoRoot: string): Promise<void> {\r\n const result = await execFileAsync(\"git\", [\"status\", \"--porcelain\"], {\r\n cwd: repoRoot,\r\n encoding: \"utf8\",\r\n });\r\n if (result.stdout.trim()) {\r\n throw new Error(\r\n \"Write-mode update requires a clean Git worktree. Commit or stash changes first.\"\r\n );\r\n }\r\n}\r\n\r\nasync function resolveUpgradeState(options: {\r\n state: ManagedState;\r\n platform?: AppPlatform;\r\n yes: boolean;\r\n testHarness?: boolean;\r\n modules?: string[];\r\n}): Promise<ManagedState> {\r\n if (!options.platform) return options.state;\r\n if (options.platform === \"landing\") {\r\n throw new Error(\"m5kdev update --platform cannot switch to landing.\");\r\n }\r\n const current = new Set(options.state.template.features);\r\n if (current.has(\"webapp\") || current.has(\"expo\")) {\r\n throw new Error(\r\n \"m5kdev update --platform is only for landing-only apps. This repository already has a Webapp or Expo client.\"\r\n );\r\n }\r\n const extras = await resolveHarnessAndModuleOptions({\r\n yes: options.yes,\r\n testHarness: options.testHarness,\r\n modules: options.modules,\r\n });\r\n const enabledFeatures = getEnabledFeatures({\r\n platform: options.platform,\r\n testHarness: extras.testHarness,\r\n modules: extras.modules,\r\n });\r\n return {\r\n ...options.state,\r\n template: {\r\n ...options.state.template,\r\n features: [...enabledFeatures].sort(),\r\n },\r\n };\r\n}\r\n\r\nfunction overlayEnv(base: string, overlay: string): string {\r\n if (!overlay.trim()) return base;\r\n let next = base;\r\n for (const line of overlay.split(\"\\n\")) {\r\n const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);\r\n if (!match) continue;\r\n const [, key, value] = match;\r\n const assignment = `${key}=${value}`;\r\n const pattern = new RegExp(`^${key}=.*$`, \"m\");\r\n if (pattern.test(next)) next = next.replace(pattern, assignment);\r\n else next = `${next}${next.endsWith(\"\\n\") || next.length === 0 ? \"\" : \"\\n\"}${assignment}\\n`;\r\n }\r\n return next;\r\n}\r\n\r\nasync function planLandingEnvMigration(options: {\r\n repoRoot: string;\r\n templateRoot: string;\r\n state: ManagedState;\r\n changes: ChangeSet;\r\n}): Promise<void> {\r\n const sharedEnvRelative = \"apps/shared/.env\";\r\n const landingEnvRelative = \"apps/landing/.env\";\r\n const landingExampleRelative = \"apps/landing/.env.example\";\r\n const landingEnv = await fs\r\n .readFile(path.join(options.repoRoot, landingEnvRelative), \"utf8\")\r\n .catch(() => \"\");\r\n const existing = await options.changes.read(sharedEnvRelative);\r\n const files = await collectTemplateFiles(\r\n options.templateRoot,\r\n {\r\n ...options.state.template.context,\r\n betterAuthSecret: \"ignored-by-managed-state\",\r\n },\r\n {\r\n enabledFeatures: withImpliedFeatures(new Set(options.state.template.features)),\r\n }\r\n );\r\n const sharedEnv = files.find((file) => file.relativePath === sharedEnvRelative);\r\n const base = existing?.toString(\"utf8\") || sharedEnv?.content.toString(\"utf8\") || \"\";\r\n if (base.length > 0) {\r\n const content = overlayEnv(base, landingEnv);\r\n if (!existing || content !== existing.toString(\"utf8\")) {\r\n options.changes.addChange({\r\n kind: existing ? \"modify\" : \"add\",\r\n path: sharedEnvRelative,\r\n content: Buffer.from(content),\r\n reason: \"Copy Landing env into Deploy home\",\r\n });\r\n }\r\n }\r\n for (const relativePath of [landingEnvRelative, landingExampleRelative]) {\r\n const present = await fs\r\n .access(path.join(options.repoRoot, relativePath))\r\n .then(() => true)\r\n .catch(() => false);\r\n if (!present) continue;\r\n options.changes.addChange({\r\n kind: \"delete\",\r\n path: relativePath,\r\n reason: \"Remove Landing-local env after attaching Deploy home\",\r\n });\r\n }\r\n}\r\n\r\nexport async function updateManagedRepo(options: UpdateCommandOptions): Promise<UpdateResult> {\r\n const state = await readManagedState(options.repoRoot);\r\n const targetVersion = options.targetVersion ?? getCliVersion();\r\n if (semver.gt(state.template.version, targetVersion)) {\r\n throw new Error(\r\n `The running CLI ${targetVersion} is older than managed state ${state.template.version}.`\r\n );\r\n }\r\n const upgradedState = await resolveUpgradeState({\r\n state,\r\n platform: options.platform,\r\n yes: Boolean(options.yes),\r\n testHarness: options.testHarness,\r\n modules: options.modules,\r\n });\r\n if (!options.dryRun) await (options.assertClean ?? assertCleanGit)(options.repoRoot);\r\n\r\n const templateRoot = options.targetTemplateRoot ?? getTemplateRoot();\r\n const reconciliation = await reconcileTemplates({\r\n repoRoot: options.repoRoot,\r\n state: upgradedState,\r\n targetTemplateRoot: templateRoot,\r\n targetVersion,\r\n baseProvider: options.baseProvider,\r\n });\r\n if (options.platform && options.platform !== \"landing\") {\r\n await planLandingEnvMigration({\r\n repoRoot: options.repoRoot,\r\n templateRoot,\r\n state: upgradedState,\r\n changes: reconciliation.changes,\r\n });\r\n }\r\n const registry = options.registry ?? getMigrationRegistry();\r\n const pending = getPendingMigrations(upgradedState, targetVersion, registry);\r\n const appliedMigrations: string[] = [];\r\n for (const migration of pending) {\r\n const context = {\r\n repoRoot: options.repoRoot,\r\n state: upgradedState,\r\n changes: reconciliation.changes,\r\n };\r\n if (migration.applies && !(await migration.applies(context))) continue;\r\n await migration.plan(context);\r\n appliedMigrations.push(migration.id);\r\n }\r\n reconciliation.targetState.appliedMigrations = [\r\n ...new Set([...upgradedState.appliedMigrations, ...appliedMigrations]),\r\n ];\r\n\r\n const validationDiagnostics = await runMigrationValidators(\r\n {\r\n repoRoot: options.repoRoot,\r\n state: reconciliation.targetState,\r\n changes: reconciliation.changes,\r\n },\r\n registry\r\n );\r\n for (const diagnostic of validationDiagnostics) {\r\n if (diagnostic.severity !== \"error\") continue;\r\n reconciliation.changes.addConflict({\r\n path: diagnostic.path ?? \".m5kdev.json\",\r\n reason: `[${diagnostic.code}] ${diagnostic.message}`,\r\n });\r\n }\r\n\r\n const stateContent = Buffer.from(`${JSON.stringify(reconciliation.targetState, null, 2)}\\n`);\r\n const currentState = await reconciliation.changes.read(STATE_FILE_NAME);\r\n if (!currentState?.equals(stateContent)) {\r\n reconciliation.changes.addChange({\r\n kind: currentState ? \"modify\" : \"add\",\r\n path: STATE_FILE_NAME,\r\n content: stateContent,\r\n reason: \"Advance managed template state\",\r\n });\r\n }\r\n\r\n const result: UpdateResult = {\r\n fromVersion: state.template.version,\r\n targetVersion,\r\n dryRun: options.dryRun,\r\n applied: false,\r\n changes: [...reconciliation.changes.changes.values()].map(\r\n ({ content: _content, ...change }) => change\r\n ),\r\n conflicts: [...reconciliation.changes.conflicts],\r\n migrations: appliedMigrations,\r\n dependenciesChanged: reconciliation.dependenciesChanged,\r\n installRequired: reconciliation.dependenciesChanged && options.skipInstall,\r\n };\r\n if (options.dryRun || result.conflicts.length > 0) return result;\r\n\r\n await reconciliation.changes.apply();\r\n result.applied = true;\r\n if (reconciliation.dependenciesChanged && !options.skipInstall) {\r\n if (options.install) await options.install(path.resolve(options.repoRoot));\r\n else\r\n await execFileAsync(\"pnpm\", [\"install\"], {\r\n cwd: path.resolve(options.repoRoot),\r\n env: process.env,\r\n });\r\n }\r\n return result;\r\n}\r\n"],"mappings":"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"}
|
package/dist/src/update.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PlannedChange, PlannedConflict } from "./changes.cjs";
|
|
2
|
+
import { AppPlatform } from "./types.cjs";
|
|
2
3
|
import { MigrationDefinition } from "./migrations/types.cjs";
|
|
3
4
|
import { BaseTemplateProvider } from "./reconcile.cjs";
|
|
4
5
|
|
|
@@ -7,6 +8,10 @@ interface UpdateCommandOptions {
|
|
|
7
8
|
repoRoot: string;
|
|
8
9
|
dryRun: boolean;
|
|
9
10
|
skipInstall: boolean;
|
|
11
|
+
yes?: boolean;
|
|
12
|
+
platform?: AppPlatform;
|
|
13
|
+
testHarness?: boolean;
|
|
14
|
+
modules?: string[];
|
|
10
15
|
baseProvider?: BaseTemplateProvider;
|
|
11
16
|
/** Test seam; published commands always use the embedded production registry. */
|
|
12
17
|
registry?: readonly MigrationDefinition[];
|