kizlo 0.13.0 → 0.14.0

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 (34) hide show
  1. package/dist/cli/commands/_setup.js +91 -55
  2. package/dist/cli/commands/_tasks.js +152 -0
  3. package/dist/cli/commands/_wiring.js +11 -10
  4. package/dist/cli/commands/create.js +270 -87
  5. package/dist/cli/commands/init.js +122 -61
  6. package/dist/cli/daemon/config.js +1 -1
  7. package/dist/cli/presets/source.js +13 -8
  8. package/dist/cli/presets/template.js +255 -50
  9. package/dist/cli/utils.js +149 -80
  10. package/dist/integrations/tanstack-start/api.d.ts +15 -0
  11. package/dist/integrations/tanstack-start/api.js +21 -0
  12. package/dist/integrations/tanstack-start/kizlo.client.d.ts +20 -0
  13. package/dist/integrations/tanstack-start/kizlo.client.js +18 -0
  14. package/dist/integrations/tanstack-start/kizlo.server.d.ts +14 -0
  15. package/dist/integrations/tanstack-start/kizlo.server.js +15 -0
  16. package/dist/integrations/tanstack-start/manifest.d.ts +9 -0
  17. package/dist/integrations/tanstack-start/manifest.js +11 -0
  18. package/dist/integrations/tanstack-start/metadata.d.ts +49 -0
  19. package/dist/integrations/tanstack-start/metadata.js +112 -0
  20. package/dist/integrations/tanstack-start/robots.d.ts +8 -0
  21. package/dist/integrations/tanstack-start/robots.js +11 -0
  22. package/dist/integrations/tanstack-start/root-head.d.ts +15 -0
  23. package/dist/integrations/tanstack-start/root-head.js +54 -0
  24. package/dist/integrations/tanstack-start/sitemap.d.ts +16 -0
  25. package/dist/integrations/tanstack-start/sitemap.js +20 -0
  26. package/dist/integrations/tanstack-start/utils.d.ts +20 -0
  27. package/dist/integrations/tanstack-start/utils.js +17 -0
  28. package/dist/package.js +1 -1
  29. package/dist/router.d.ts +5 -5
  30. package/dist/tanstack-start/server.d.ts +11 -0
  31. package/dist/tanstack-start/server.js +12 -0
  32. package/dist/tanstack-start.d.ts +4 -0
  33. package/dist/tanstack-start.js +5 -0
  34. package/package.json +22 -1
@@ -38,10 +38,18 @@ import fs from "node:fs";
38
38
  * patch may instead set `mode: "note"` to always print its instructions rather than edit the file —
39
39
  * for a change too shaped-unlike an import/export to auto-merge (Astro's `defineConfig` output/adapter).
40
40
  */
41
- const conventionsSchema = z.object({
42
- kizloDir: z.string(),
43
- appDir: z.string(),
44
- alias: z.string()
41
+ /**
42
+ * The template's wiring config — how Kizlo addresses the files it lays down, rewritten to the project's
43
+ * own layout on apply (used by both `create` and `init`, unlike the init-only preconditions). The
44
+ * template writes its files against its own {@link configSchema.shape.kizloPath} and
45
+ * {@link configSchema.shape.alias}; on apply the Kizlo dir is retargeted to the project's chosen location,
46
+ * every alias import to the project's import style, and every other `src/`-rooted path to the project's
47
+ * `src/` convention (kept when the project uses `src/`, stripped when it doesn't).
48
+ */
49
+ const configSchema = z.object({
50
+ apiPath: z.string().optional(),
51
+ alias: z.string(),
52
+ kizloPath: z.string()
45
53
  });
46
54
  const fileSchema = z.object({
47
55
  kind: z.literal("file"),
@@ -65,12 +73,131 @@ const patchSchema = z.object({
65
73
  })).default([]),
66
74
  note: z.string().optional()
67
75
  });
68
- const bootstrapSchema = z.object({
69
- initializer: z.string(),
70
- flags: z.array(z.string()).default([])
76
+ /**
77
+ * The `command` half of {@link createSchema}: the full invocation that scaffolds the base app with the
78
+ * framework's own CLI, written exactly as it would be typed — e.g. `{{pm}} create next-app@latest {{name}}
79
+ * --ts …`. The CLI imposes no shape: it tokenizes the string, substitutes the tokens below, and runs the
80
+ * argv verbatim, so a template can use any invocation (a `create-*` initializer, `{{pm}} dlx <cli> create`,
81
+ * a subcommand after the package spec) without the CLI knowing anything framework-specific. The template is
82
+ * fully authoritative, which means it also owns any package-manager quirks (npm's `--` separator, yarn's
83
+ * `@latest` handling).
84
+ *
85
+ * The core tokens substituted before the command runs:
86
+ * - `{{pm}}` — the chosen package manager (`pnpm`/`npm`/`yarn`/`bun`).
87
+ * - `{{name}}` — the project name (or path) the user gave.
88
+ * - `{{dlx}}` — the manager's run-a-package command (`pnpm dlx`/`npx`/`yarn dlx`/`bunx`), for a CLI that
89
+ * isn't a `create-*` package (e.g. `{{dlx}} @tanstack/cli@latest create {{name}}`); a `create-*`
90
+ * package invokes more simply as `{{pm}} create <initializer> {{name}}`.
91
+ *
92
+ * Beyond those, a `{{<token>}}` for every prompt in the same `create` block (see {@link promptSchema}) is
93
+ * substituted with the CLI fragment the user's answer maps to — the mechanism that lets a template
94
+ * surface a real framework choice (which linter, turbopack on/off) instead of hard-coding the flag.
95
+ * A fragment may be empty (contributes nothing) or several flags; each is spliced in as plain text and
96
+ * re-tokenized, so the CLI still learns nothing framework-specific.
97
+ *
98
+ * The command must produce a project whose shape matches {@link configSchema} (directory layout,
99
+ * import alias) so the wiring lands where the manifest expects. Simple single/double quotes group a
100
+ * token that contains spaces; there is no shell expansion.
101
+ */
102
+ const commandSchema = z.string();
103
+ /**
104
+ * A prompt the template asks during `create`, its answer mapped to a fragment of the {@link commandSchema}
105
+ * command via the prompt's `{{token}}`. This is how a template exposes a framework CLI choice it would
106
+ * otherwise have to hard-code (which linter, turbopack on/off, a custom import alias) without the Kizlo
107
+ * CLI knowing anything framework-specific: the manifest owns both the question and the flag it produces.
108
+ *
109
+ * Three shapes, discriminated on `kind`:
110
+ * - `select` — a single choice from {@link promptOptionSchema} options; the chosen option's `arg` is the
111
+ * fragment. `default` names the option value pre-selected (and used non-interactively); absent → the
112
+ * first option.
113
+ * - `confirm` — a yes/no; `arg` is the fragment when yes, `argFalse` when no. `default` is the answer used
114
+ * non-interactively.
115
+ * - `text` — free input spliced into `arg` at its `{{value}}` placeholder; the value is shell-quoted so it
116
+ * stays a single argv token. Empty input quotes to nothing, so with the default `{{value}}` arg it
117
+ * contributes nothing (a wrapping arg like `--flag {{value}}` still emits the flag). `default` is the
118
+ * value used non-interactively.
119
+ *
120
+ * Every prompt owns a unique `token` that must not shadow a core token (`pm`/`name`/`dlx`); the bootstrap
121
+ * schema rejects collisions and duplicates.
122
+ */
123
+ const promptOptionSchema = z.object({
124
+ label: z.string(),
125
+ value: z.string(),
126
+ arg: z.string().default("")
127
+ });
128
+ const selectPromptSchema = z.object({
129
+ kind: z.literal("select"),
130
+ token: z.string(),
131
+ message: z.string(),
132
+ default: z.string().optional(),
133
+ options: z.array(promptOptionSchema).min(1)
134
+ });
135
+ const confirmPromptSchema = z.object({
136
+ kind: z.literal("confirm"),
137
+ token: z.string(),
138
+ message: z.string(),
139
+ default: z.boolean().default(true),
140
+ arg: z.string().default(""),
141
+ argFalse: z.string().default("")
142
+ });
143
+ const textPromptSchema = z.object({
144
+ kind: z.literal("text"),
145
+ token: z.string(),
146
+ message: z.string(),
147
+ placeholder: z.string().optional(),
148
+ default: z.string().optional(),
149
+ arg: z.string().default("{{value}}")
150
+ });
151
+ const promptSchema = z.discriminatedUnion("kind", [
152
+ selectPromptSchema,
153
+ confirmPromptSchema,
154
+ textPromptSchema
155
+ ]);
156
+ /** The core bootstrap tokens a prompt token must never shadow. */
157
+ const RESERVED_TOKENS = new Set([
158
+ "pm",
159
+ "name",
160
+ "dlx"
161
+ ]);
162
+ /**
163
+ * How `create` scaffolds the base app: the framework CLI `command` to run, plus the `prompts` whose answers
164
+ * fill its `{{token}}` placeholders — kept together because a prompt is meaningless without the command it
165
+ * customizes. Absent on templates that only `init` supports; `create` refuses a template whose manifest
166
+ * declares no `create` block. A `superRefine` rejects a prompt `token` that shadows a core token
167
+ * (`pm`/`name`/`dlx`) or repeats another prompt's, so a mis-authored manifest fails loudly at parse time.
168
+ */
169
+ const createSchema = z.object({
170
+ command: commandSchema,
171
+ prompts: z.array(promptSchema).default([])
172
+ }).superRefine((create, ctx) => {
173
+ const seen = /* @__PURE__ */ new Set();
174
+ for (const prompt of create.prompts) {
175
+ if (RESERVED_TOKENS.has(prompt.token)) ctx.addIssue({
176
+ code: "custom",
177
+ message: `Prompt token "${prompt.token}" is reserved (pm/name/dlx).`,
178
+ path: ["prompts"]
179
+ });
180
+ if (seen.has(prompt.token)) ctx.addIssue({
181
+ code: "custom",
182
+ message: `Duplicate prompt token "${prompt.token}".`,
183
+ path: ["prompts"]
184
+ });
185
+ seen.add(prompt.token);
186
+ }
71
187
  });
72
188
  const changeSchema = z.discriminatedUnion("kind", [fileSchema, patchSchema]);
73
189
  /**
190
+ * The changes a template lays down, split by the one question that decides whether a command may safely
191
+ * apply them — *where is this safe to land?* No command reads another's section: `base` is additive and
192
+ * safe on any project (both commands apply it), `create` overwrites framework-scaffolded files (only
193
+ * `create`), `init` patches files the user owns (only `init`). See the section doc above and {@link changesFor}.
194
+ */
195
+ const changesSchema = z.object({
196
+ base: z.array(changeSchema).default([]),
197
+ create: z.array(changeSchema).default([]),
198
+ init: z.array(changeSchema).default([])
199
+ });
200
+ /**
74
201
  * The `.env` key names the scaffold writes and the pinned runtime later reads — the one part of the
75
202
  * wiring contract that can drift when the scaffold-time CLI and the pinned runtime disagree. Declared
76
203
  * here as data so the scaffold writes whatever names the pinned runtime expects. Semantic slots only;
@@ -95,21 +222,6 @@ const envSchema = z.object({
95
222
  /** A package name → version-range map, package.json's own vocabulary. */
96
223
  const depsSchema = z.record(z.string(), z.string());
97
224
  /**
98
- * The package names whose presence in a project's dependencies identify this framework — how `init`
99
- * recognizes which template to apply to an existing project (e.g. `["next"]`, `["astro"]`). Detection is
100
- * data, so a community template declares its own signal and needs no CLI change. See {@link detectTemplate}.
101
- */
102
- const detectSchema = z.object({ dependencies: z.array(z.string()).default([]) });
103
- /**
104
- * An `init` precondition on the project's shape: at least one of `anyDir` must exist in the project,
105
- * else `init` stops with `message`. Replaces framework-specific `if` branches — e.g. Next.js needs an
106
- * `app` or `src/app` directory (the App Router), which the Pages Router lacks. Dormant when absent.
107
- */
108
- const requiresSchema = z.object({
109
- anyDir: z.array(z.string()),
110
- message: z.string()
111
- });
112
- /**
113
225
  * A post-setup manual step `init` prints after wiring — the piece Kizlo can't auto-wire into a file the
114
226
  * user owns (e.g. Astro's SEO tags render through a `.astro` component, not a patchable export). `create`
115
227
  * owns files whole, so it never prints these. `body` may carry one token, `{{importPrefix}}`, substituted
@@ -119,24 +231,95 @@ const noteSchema = z.object({
119
231
  title: z.string(),
120
232
  body: z.string()
121
233
  });
234
+ /**
235
+ * A single precondition `init` checks against the project before applying — data in place of the old
236
+ * framework-specific `if` branches, so a community template declares its own without a CLI change.
237
+ * Discriminated on `kind`:
238
+ *
239
+ * - `dep` — package(s) in the project's dependencies. Doubles as the framework *detection* signal (see
240
+ * {@link detectTemplates}): a template's match score is how many of its `dep` `values` the project has,
241
+ * read across every candidate template before one is chosen.
242
+ * - `dir` — director(ies) in the project tree, e.g. Next's `app`/`src/app` App Router (the Pages Router
243
+ * lacks it).
244
+ *
245
+ * `match` decides whether `any` (default) or `all` of `values` must be present; `message` is the abort
246
+ * text shown when the requirement is unmet, defaulting to `""` → a generated fallback. See {@link unmetRequirement}.
247
+ */
248
+ const requiresFields = {
249
+ values: z.array(z.string()).min(1),
250
+ match: z.enum(["any", "all"]).default("any"),
251
+ message: z.string().default("")
252
+ };
253
+ const requiresDepSchema = z.object({
254
+ kind: z.literal("dep"),
255
+ ...requiresFields
256
+ });
257
+ const requiresDirSchema = z.object({
258
+ kind: z.literal("dir"),
259
+ ...requiresFields
260
+ });
261
+ const requirementSchema = z.discriminatedUnion("kind", [requiresDepSchema, requiresDirSchema]);
262
+ /** Everything the `init` command needs of the project: the preconditions it checks and the steps it prints. */
263
+ const initSchema = z.object({
264
+ requires: z.array(requirementSchema).default([]),
265
+ notes: z.array(noteSchema).default([])
266
+ });
122
267
  const manifestSchema = z.object({
123
- framework: z.string(),
268
+ id: z.string(),
124
269
  name: z.string().optional(),
125
270
  env: envSchema.optional(),
126
271
  dependencies: depsSchema.optional(),
127
272
  devDependencies: depsSchema.optional(),
128
273
  minCli: z.string().optional(),
129
- apiPath: z.string().optional(),
130
- detect: detectSchema.optional(),
131
- requires: requiresSchema.optional(),
132
- notes: z.array(noteSchema).default([]),
133
- bootstrap: bootstrapSchema.optional(),
134
- conventions: conventionsSchema,
135
- base: z.array(changeSchema).default([]),
136
- create: z.array(changeSchema).default([]),
137
- init: z.array(changeSchema).default([])
274
+ config: configSchema,
275
+ init: initSchema.default({
276
+ requires: [],
277
+ notes: []
278
+ }),
279
+ create: createSchema.optional(),
280
+ changes: changesSchema.default({
281
+ base: [],
282
+ create: [],
283
+ init: []
284
+ })
138
285
  });
139
286
  /**
287
+ * Wrap a text prompt's answer so it survives {@link tokenizeCommand} as exactly one argv token: an empty
288
+ * value stays empty (contributes nothing), otherwise it's double-quoted with any inner `"` escaped. This is
289
+ * what lets a free-text value with spaces (`--import-alias "my alias"`) splice cleanly into the bootstrap
290
+ * command without the tokenizer splitting it.
291
+ */
292
+ function quoteValue(value) {
293
+ if (value === "") return "";
294
+ return `"${value.replaceAll("\"", "\\\"")}"`;
295
+ }
296
+ /**
297
+ * The CLI fragment a prompt answer maps to — the single place answer→flag translation lives, shared by the
298
+ * interactive collector and the non-interactive default path. `answer` is the chosen option `value`
299
+ * (select), the boolean (confirm), or the raw input (text). Returns the fragment spliced into the bootstrap
300
+ * command for this prompt's `{{token}}`; a select answer with no matching option (a stale `default`) yields
301
+ * the first option's fragment.
302
+ */
303
+ function promptFragment(prompt, answer) {
304
+ switch (prompt.kind) {
305
+ case "select": return (prompt.options.find((opt) => opt.value === answer) ?? prompt.options[0])?.arg ?? "";
306
+ case "confirm": return answer ? prompt.arg : prompt.argFalse;
307
+ case "text": return prompt.arg.replaceAll("{{value}}", quoteValue(String(answer)));
308
+ }
309
+ }
310
+ /**
311
+ * The answer used non-interactively (`--yes`, non-TTY) — the prompt's declared default, or the first
312
+ * option / empty string when none is set. Returns both the answer (for the choices summary) and the
313
+ * {@link promptFragment} it maps to, so the caller shows a value and splices a flag from one call.
314
+ */
315
+ function resolvePromptDefault(prompt) {
316
+ const answer = prompt.kind === "confirm" ? prompt.default : prompt.kind === "select" ? prompt.default ?? prompt.options[0]?.value ?? "" : prompt.default ?? "";
317
+ return {
318
+ answer,
319
+ arg: promptFragment(prompt, answer)
320
+ };
321
+ }
322
+ /**
140
323
  * Render a manual-step note for display, substituting the `{{importPrefix}}` token with the project's
141
324
  * source-root import prefix — the configured alias (normalized to a trailing slash, e.g. `@/`) when one
142
325
  * is set, else the relative `../`. Reproduces the old `alias ? "@/…" : "../…"` behavior exactly, so a
@@ -227,9 +410,26 @@ function minCliError(manifest) {
227
410
  * section: init's opt-in examples are the additive ones in `base`, never create's homepage overwrite.
228
411
  */
229
412
  function changesFor(manifest, command, opts = {}) {
230
- const changes = [...manifest.base, ...manifest[command]];
413
+ const changes = [...manifest.changes.base, ...manifest.changes[command]];
231
414
  return opts.includeExamples ? changes : changes.filter((change) => !isExample(change));
232
415
  }
416
+ /**
417
+ * The first `init` precondition the project fails, as a ready-to-print abort message, or `undefined` when
418
+ * every requirement passes. `deps` is the project's merged dependencies; `dirExists` tests a
419
+ * project-relative directory. A `dep` requirement checks membership in `deps`, a `dir` requirement checks
420
+ * the tree, each honoring its `match` (`any`/`all`). An unmet requirement yields its own `message`, or a
421
+ * generated fallback when it declares none (the common case for `dep` entries, which detection filters
422
+ * out first). See {@link requirementSchema}.
423
+ */
424
+ function unmetRequirement(requires, deps, dirExists) {
425
+ for (const req of requires) {
426
+ const has = req.kind === "dep" ? (value) => value in deps : dirExists;
427
+ if (req.match === "all" ? req.values.every(has) : req.values.some(has)) continue;
428
+ if (req.message) return req.message;
429
+ const noun = req.kind === "dep" ? "package" : "directory";
430
+ return `Kizlo needs ${req.match === "all" ? "all of" : "one of"} these ${noun}s: ${req.values.join(", ")}.`;
431
+ }
432
+ }
233
433
  /** The whole-file changes in a resolved change list. */
234
434
  function fileEntries(changes) {
235
435
  return changes.filter((change) => change.kind === "file");
@@ -253,11 +453,11 @@ function escapeRegExp(value) {
253
453
  * `src`/root heuristic when the template declares no matching alias, staying consistent with the rest of
254
454
  * the alias handling.
255
455
  */
256
- function templateAliasBase(templateDir, conventions) {
257
- const alias = conventions.alias.replace(/\/+$/, "");
456
+ function templateAliasBase(templateDir, config) {
457
+ const alias = config.alias.replace(/\/+$/, "");
258
458
  const mapping = readTsconfigPaths(templateDir)?.[`${alias}/*`]?.[0];
259
459
  if (mapping) return mapping.replace(/^\.\//, "").replace(/\/\*$/, "").replace(/\/+$/, "");
260
- return conventions.kizloDir.startsWith("src/") ? "src" : "";
460
+ return config.kizloPath.startsWith("src/") ? "src" : "";
261
461
  }
262
462
  /**
263
463
  * Rewrite every template-alias import in a file to the project's chosen import style — the one place that
@@ -269,23 +469,28 @@ function templateAliasBase(templateDir, conventions) {
269
469
  * Only quoted specifiers that begin with the template alias are touched, so an unrelated string can't be
270
470
  * corrupted. Subsumes the server-entry import: it is simply the alias import that lands in the Kizlo dir.
271
471
  */
272
- function rewriteAliasImports(body, fromDir, base, conventions, ctx) {
273
- const alias = conventions.alias.replace(/\/+$/, "");
472
+ function rewriteAliasImports(body, fromDir, base, config, ctx) {
473
+ const alias = config.alias.replace(/\/+$/, "");
274
474
  if (!alias) return body;
275
475
  const pattern = new RegExp(`(["'])${escapeRegExp(alias)}/([^"']+)\\1`, "g");
276
476
  return body.replace(pattern, (_match, quote, rest) => {
277
- const targetRel = adaptTemplatePath(base ? `${base}/${rest}` : rest, conventions, ctx);
477
+ const targetRel = adaptTemplatePath(base ? `${base}/${rest}` : rest, config, ctx);
278
478
  return `${quote}${ctx.importFrom(targetRel, fromDir)}${quote}`;
279
479
  });
280
480
  }
281
481
  /**
282
- * Adapt a template path prefix (`src/app/...`, `src/lib/kizlo/...`) to the project's real directories.
283
- * Only the leading convention dir is swapped; the rest of the path is preserved verbatim.
482
+ * Adapt a template path to the project's real layout. The Kizlo home dir is retargeted to wherever the
483
+ * user put it (`config.kizloPath` → `ctx.kizloPath`). Every other path is `src/`-rooted as authored, so it
484
+ * only needs to match the project's `src/` convention: a project that keeps source under `src/` gets it
485
+ * verbatim, one that doesn't gets the leading `src/` stripped (so `src/app/...` lands at `app/...`, config
486
+ * files at the root are untouched). This makes the one project-wide decision — `src/` or not — instead of
487
+ * tracking a per-framework route directory.
284
488
  */
285
- function adaptTemplatePath(relPath, conventions, ctx) {
489
+ function adaptTemplatePath(relPath, config, ctx) {
286
490
  const normalized = relPath.split(path.sep).join("/");
287
- if (normalized === conventions.appDir || normalized.startsWith(`${conventions.appDir}/`)) return `${ctx.appDir}${normalized.slice(conventions.appDir.length)}`;
288
- if (normalized === conventions.kizloDir || normalized.startsWith(`${conventions.kizloDir}/`)) return `${ctx.kizloDir}${normalized.slice(conventions.kizloDir.length)}`;
491
+ const { kizloPath } = config;
492
+ if (normalized === kizloPath || normalized.startsWith(`${kizloPath}/`)) return `${ctx.kizloPath}${normalized.slice(kizloPath.length)}`;
493
+ if (!ctx.hasSrcDir && normalized.startsWith("src/")) return normalized.slice(4);
289
494
  return normalized;
290
495
  }
291
496
  /**
@@ -296,12 +501,12 @@ function adaptTemplatePath(relPath, conventions, ctx) {
296
501
  * the template uses are rewritten, not a loose directory substring, so an unrelated occurrence can't be
297
502
  * corrupted. This is the job the old build-time extractor did; it moves to runtime and stays as precise.
298
503
  */
299
- function adaptFile(templateDir, entry, conventions, ctx) {
504
+ function adaptFile(templateDir, entry, config, ctx) {
300
505
  const abs = path.join(templateDir, entry.path);
301
506
  if (!fs.existsSync(abs)) throw new Error(`Template file "${entry.path}" is listed in the manifest but does not exist in the template.`);
302
507
  const body = fs.readFileSync(abs, "utf8");
303
- const relPath = adaptTemplatePath(entry.path, conventions, ctx);
304
- const contents = rewriteAliasImports(body, path.posix.dirname(relPath), templateAliasBase(templateDir, conventions), conventions, ctx);
508
+ const relPath = adaptTemplatePath(entry.path, config, ctx);
509
+ const contents = rewriteAliasImports(body, path.posix.dirname(relPath), templateAliasBase(templateDir, config), config, ctx);
305
510
  return {
306
511
  label: ROLE_LABELS[entry.role] ?? entry.role,
307
512
  relPath,
@@ -309,8 +514,8 @@ function adaptFile(templateDir, entry, conventions, ctx) {
309
514
  };
310
515
  }
311
516
  /** Adapt a patch's path prefix and server-import specifier to the project's real directories. */
312
- function resolvePatch(entry, conventions, ctx) {
313
- const relPath = adaptTemplatePath(entry.path, conventions, ctx);
517
+ function resolvePatch(entry, config, ctx) {
518
+ const relPath = adaptTemplatePath(entry.path, config, ctx);
314
519
  const fromDir = path.posix.dirname(relPath);
315
520
  const imports = entry.imports.map((imp) => ({
316
521
  module: imp.module.replaceAll("{{serverImport}}", ctx.serverImport(fromDir)),
@@ -327,4 +532,4 @@ function resolvePatch(entry, conventions, ctx) {
327
532
  }
328
533
 
329
534
  //#endregion
330
- export { adaptFile, changesFor, fileEntries, isOlderVersion, minCliError, patchEntries, readManifest, renderNote, resolveDependencies, resolvePatch };
535
+ export { adaptFile, changesFor, fileEntries, isOlderVersion, minCliError, patchEntries, promptFragment, readManifest, renderNote, resolveDependencies, resolvePatch, resolvePromptDefault, unmetRequirement };