vite-plus 0.1.23 → 0.2.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 (174) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +8 -6
  3. package/bin/oxfmt +5 -2
  4. package/bin/oxlint +12 -2
  5. package/binding/index.cjs +84 -67
  6. package/binding/index.d.cts +155 -4
  7. package/dist/{agent-aSGY0osq.js → agent-BD31CsvU.js} +969 -89
  8. package/dist/bin.js +29 -33
  9. package/dist/{compat-DXZgnEyq.js → compat-Cql3K40m.js} +1 -1
  10. package/dist/config/bin.js +30 -14
  11. package/dist/constants-CrfJQIUX.js +66 -0
  12. package/dist/create/bin.d.ts +7 -1
  13. package/dist/create/bin.js +578 -266
  14. package/dist/define-config-2tfJoXr1.d.ts +305 -0
  15. package/dist/define-config-BGSjF6Xp.cjs +488 -0
  16. package/dist/define-config-DJUehepE.js +445 -0
  17. package/dist/define-config.cjs +8 -1
  18. package/dist/define-config.d.ts +2 -2
  19. package/dist/define-config.js +2 -2
  20. package/dist/dist-DRJUd9bL.js +3 -0
  21. package/dist/{dist-BgQuvbtq.js → dist-Oxo16Y0q.js} +4 -4
  22. package/dist/index.cjs +9 -4
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +3 -3
  25. package/dist/{main-DpJl3LoU.js → json-Dn87fvjk.js} +137 -1
  26. package/dist/migration/bin.js +301 -84
  27. package/dist/{oxlint-plugin-config-B89iKTKN.js → oxlint-plugin-config-q8a5PFch.js} +1 -1
  28. package/dist/oxlint-plugin.js +11 -3
  29. package/dist/pack-bin.js +44 -15
  30. package/dist/{package-PmBUZ-ve.js → package-BHirM1_v.js} +3 -138
  31. package/dist/{report-DgSBQUdz.js → report-BHSkWqRR.js} +2 -0
  32. package/dist/{resolve-vite-config-TTvhycU1.js → resolve-vite-config-CmdsfQzS.js} +13 -4
  33. package/dist/staged/bin.js +150 -417
  34. package/dist/test/_at-vitest-browser/context.d.ts +2 -0
  35. package/dist/test/_at-vitest-browser.d.ts +2 -0
  36. package/dist/test/browser/context.d.ts +2 -2
  37. package/dist/test/browser/context.js +1 -1
  38. package/dist/test/browser/providers/playwright/context.d.ts +1 -0
  39. package/dist/test/browser/providers/playwright/context.js +1 -0
  40. package/dist/test/browser/providers/playwright.d.ts +124 -2
  41. package/dist/test/browser/providers/playwright.js +1 -1
  42. package/dist/test/browser/providers/preview/context.d.ts +1 -0
  43. package/dist/test/browser/providers/preview/context.js +1 -0
  44. package/dist/test/browser/providers/preview.d.ts +32 -2
  45. package/dist/test/browser/providers/preview.js +1 -1
  46. package/dist/test/browser/providers/webdriverio/context.d.ts +1 -0
  47. package/dist/test/browser/providers/webdriverio/context.js +1 -0
  48. package/dist/test/browser/providers/webdriverio.d.ts +77 -2
  49. package/dist/test/browser/providers/webdriverio.js +1 -1
  50. package/dist/test/browser-compat.d.ts +2 -0
  51. package/dist/test/browser-compat.js +1 -1
  52. package/dist/test/browser-playwright/context.d.ts +1 -0
  53. package/dist/test/browser-playwright/context.js +1 -0
  54. package/dist/test/browser-playwright.d.ts +124 -2
  55. package/dist/test/browser-playwright.js +1 -1
  56. package/dist/test/browser-preview/context.d.ts +1 -0
  57. package/dist/test/browser-preview/context.js +1 -0
  58. package/dist/test/browser-preview.d.ts +32 -2
  59. package/dist/test/browser-preview.js +1 -1
  60. package/dist/test/browser-webdriverio/context.d.ts +1 -0
  61. package/dist/test/browser-webdriverio/context.js +1 -0
  62. package/dist/test/browser-webdriverio.d.ts +77 -2
  63. package/dist/test/browser-webdriverio.js +1 -1
  64. package/dist/test/browser.d.ts +2 -2
  65. package/dist/test/browser.js +1 -1
  66. package/dist/test/client.js +1 -1
  67. package/dist/test/config.cjs +1 -1
  68. package/dist/test/config.d.ts +2 -2
  69. package/dist/test/config.js +1 -1
  70. package/dist/test/context.d.ts +942 -2
  71. package/dist/test/context.js +1 -1
  72. package/dist/test/coverage.d.ts +2 -2
  73. package/dist/test/coverage.js +1 -1
  74. package/dist/test/environments.d.ts +2 -2
  75. package/dist/test/environments.js +1 -1
  76. package/dist/test/globals.d.ts +2 -2
  77. package/dist/test/import-meta.d.ts +2 -2
  78. package/dist/test/importMeta.d.ts +2 -2
  79. package/dist/test/index.cjs +1 -1
  80. package/dist/test/index.d.cts +2 -2
  81. package/dist/test/index.d.ts +2 -2
  82. package/dist/test/index.js +1 -1
  83. package/dist/test/internal/browser.d.ts +2 -2
  84. package/dist/test/internal/browser.js +1 -1
  85. package/dist/test/jsdom.d.ts +2 -2
  86. package/dist/test/locators.d.ts +294 -0
  87. package/dist/test/locators.js +1 -1
  88. package/dist/test/matchers.d.ts +29 -0
  89. package/dist/test/matchers.js +1 -1
  90. package/dist/test/node.d.ts +2 -2
  91. package/dist/test/node.js +1 -1
  92. package/dist/test/optional-runtime-types.js.d.ts +2 -2
  93. package/dist/test/optional-types.js.d.ts +2 -2
  94. package/dist/test/plugins/browser-client.js +1 -1
  95. package/dist/test/plugins/browser-context.js +1 -1
  96. package/dist/test/plugins/browser-locators.js +1 -1
  97. package/dist/test/plugins/browser-playwright.js +1 -1
  98. package/dist/test/plugins/browser-preview.js +1 -1
  99. package/dist/test/plugins/browser-webdriverio.js +1 -1
  100. package/dist/test/plugins/browser.js +1 -1
  101. package/dist/test/plugins/expect.js +1 -1
  102. package/dist/test/plugins/mocker-automock.js +1 -1
  103. package/dist/test/plugins/mocker-browser.js +1 -1
  104. package/dist/test/plugins/mocker-node.js +1 -1
  105. package/dist/test/plugins/mocker-redirect.js +1 -1
  106. package/dist/test/plugins/mocker-register.js +1 -1
  107. package/dist/test/plugins/mocker-transforms.js +1 -1
  108. package/dist/test/plugins/mocker.js +1 -1
  109. package/dist/test/plugins/pretty-format.js +1 -1
  110. package/dist/test/plugins/runner-types.js +1 -1
  111. package/dist/test/plugins/runner-utils.js +1 -1
  112. package/dist/test/plugins/runner.js +1 -1
  113. package/dist/test/plugins/snapshot-environment.js +1 -1
  114. package/dist/test/plugins/snapshot-manager.js +1 -1
  115. package/dist/test/plugins/snapshot.js +1 -1
  116. package/dist/test/plugins/spy.js +1 -1
  117. package/dist/test/plugins/utils-constants.js +1 -1
  118. package/dist/test/plugins/utils-diff.js +1 -1
  119. package/dist/test/plugins/utils-display.js +1 -1
  120. package/dist/test/plugins/utils-error.js +1 -1
  121. package/dist/test/plugins/utils-helpers.js +1 -1
  122. package/dist/test/plugins/utils-offset.js +1 -1
  123. package/dist/test/plugins/utils-resolver.js +1 -1
  124. package/dist/test/plugins/utils-serialize.js +1 -1
  125. package/dist/test/plugins/utils-source-map-node.js +1 -1
  126. package/dist/test/plugins/utils-source-map.js +1 -1
  127. package/dist/test/plugins/utils-timers.js +1 -1
  128. package/dist/test/plugins/utils.js +1 -1
  129. package/dist/test/reporters.d.ts +2 -2
  130. package/dist/test/reporters.js +1 -1
  131. package/dist/test/runners.d.ts +2 -2
  132. package/dist/test/runners.js +1 -1
  133. package/dist/test/runtime.d.ts +2 -2
  134. package/dist/test/runtime.js +1 -1
  135. package/dist/test/snapshot.d.ts +2 -2
  136. package/dist/test/snapshot.js +1 -1
  137. package/dist/test/suite.d.ts +2 -2
  138. package/dist/test/suite.js +1 -1
  139. package/dist/test/utils.js +1 -1
  140. package/dist/test/worker.d.ts +2 -2
  141. package/dist/test/worker.js +1 -1
  142. package/dist/{tsconfig-DlUVXT3J.js → tsconfig-CJ_StdFc.js} +605 -263
  143. package/dist/tsgolint-path-B-yOos8p.js +32 -0
  144. package/dist/tsgolint-path.d.ts +8 -0
  145. package/dist/tsgolint-path.js +2 -0
  146. package/dist/version.js +3 -3
  147. package/dist/versions.d.ts +1 -1
  148. package/dist/versions.js +7 -7
  149. package/dist/{workspace-DElv730L.js → workspace-Cjoc1c_A.js} +20 -18
  150. package/docs/_data/team.ts +5 -4
  151. package/docs/config/create.md +36 -1
  152. package/docs/config/index.md +7 -5
  153. package/docs/guide/commit-hooks.md +9 -0
  154. package/docs/guide/create.md +106 -2
  155. package/docs/guide/env.md +33 -5
  156. package/docs/guide/index.md +9 -3
  157. package/docs/guide/install.md +46 -10
  158. package/docs/guide/migrate.md +13 -3
  159. package/docs/guide/troubleshooting.md +3 -29
  160. package/docs/guide/upgrade.md +36 -6
  161. package/docs/package.json +3 -3
  162. package/docs/pnpm-lock.yaml +298 -395
  163. package/package.json +104 -56
  164. package/templates/generator/bin/index.ts +6 -3
  165. package/templates/generator/package.json +2 -3
  166. package/templates/generator/src/template.ts +0 -2
  167. package/templates/monorepo/package.json +1 -1
  168. package/dist/constants-DCBWlNrn.js +0 -33
  169. package/dist/define-config-BR1Y88zz.cjs +0 -84
  170. package/dist/define-config-BRC7qPNE.js +0 -21
  171. package/dist/define-config-COdn-tsn.d.ts +0 -177
  172. package/dist/dist-Bapm49IR.js +0 -3
  173. package/dist/test/plugins/utils-highlight.js +0 -1
  174. /package/dist/{chunk-DnnnRqeS.js → rolldown-runtime-DnnnRqeS.js} +0 -0
@@ -1,14 +1,16 @@
1
- import { r as __toESM, t as __commonJSMin } from "../chunk-DnnnRqeS.js";
2
- import { A as q, C as log, D as spinner, E as select, M as runCommandSilently, N as require_cross_spawn, O as text, S as intro, _ as DependencyType, b as cancel, c as defaultInteractive, d as promptGitHooks, f as promptGitInit, h as selectPackageManager, j as runCommand$1, k as require_picocolors, l as downloadPackageManager$1, m as runViteInstall, p as runViteFmt, v as PackageManager, w as multiselect, x as confirm } from "../tsconfig-DlUVXT3J.js";
1
+ import { r as __toESM, t as __commonJSMin } from "../rolldown-runtime-DnnnRqeS.js";
2
+ import { o as VITE_PLUS_NAME } from "../constants-CrfJQIUX.js";
3
+ import { A as select, D as log, E as intro, F as runCommand$1, I as runCommandSilently, L as require_cross_spawn, M as text, N as require_picocolors, O as multiselect, P as R, S as PackageManager, T as confirm, _ as approveBuilds, d as promptGitHooks, f as resolveGitInit, h as selectPackageManager, j as spinner, l as defaultInteractive, m as runViteInstall, p as runViteFmt, u as downloadPackageManager$1, v as detectGatedBuilds, w as cancel, x as DependencyType, y as resolveApproveBuildTargets } from "../tsconfig-CJ_StdFc.js";
3
4
  import { a as printHeader, i as muted, o as success, r as log$1, t as accent } from "../terminal-uTv0ZaMr.js";
4
- import { i as resolveViteConfig, n as hasViteConfig, t as findWorkspaceRoot } from "../resolve-vite-config-TTvhycU1.js";
5
+ import { r as readJsonFile, t as editJsonFile } from "../json-Dn87fvjk.js";
6
+ import { a as resolveViteConfig, n as findWorkspaceRoot, r as hasViteConfig, t as findViteConfig } from "../resolve-vite-config-CmdsfQzS.js";
5
7
  import { t as lib_default } from "../lib-L3DWSRQp.js";
6
- import { c as editJsonFile, o as fetchNpmResource, s as getNpmRegistry, t as checkNpmPackageExists, u as readJsonFile } from "../package-PmBUZ-ve.js";
7
- import { A as promptEslintMigration, F as setPackageManager, H as displayRelative, M as rewriteMonorepo, N as rewriteMonorepoProject, P as rewriteStandaloneProject, S as injectCreateDefaultTemplate, U as templatesDir, a as selectAgentTargets, b as hasFrameworkShim, c as writeCopilotSetupWorkflow, h as detectFramework, j as promptPrettierMigration, l as addFrameworkShim, m as detectEslintProject, r as detectExistingAgentTargetPaths, s as writeAgentInstructions, t as COPILOT_AGENT_ID, v as detectPrettierProject, w as installGitHooks } from "../agent-aSGY0osq.js";
8
- import { a as detectExistingEditors, c as writeEditorConfigs, n as updatePackageJsonWithDeps, r as updateWorkspaceConfig, s as selectEditors, t as detectWorkspace$1 } from "../workspace-DElv730L.js";
8
+ import { o as fetchNpmResource, s as getNpmRegistry, t as checkNpmPackageExists } from "../package-BHirM1_v.js";
9
+ import { B as setPackageManager, D as injectCreateDefaultTemplate, F as promptEslintMigration, I as promptPrettierMigration, J as templatesDir, L as rewriteMonorepo, R as rewriteMonorepoProject, T as hasFrameworkShim, a as selectAgentTargets, b as detectPrettierProject, c as writeCopilotSetupWorkflow, h as detectFramework, k as installGitHooks, l as addFrameworkShim, m as detectEslintProject, q as displayRelative, r as detectExistingAgentTargetPaths, s as writeAgentInstructions, t as COPILOT_AGENT_ID, z as rewriteStandaloneProject } from "../agent-BD31CsvU.js";
10
+ import { c as selectEditors, i as updateWorkspaceConfig, l as writeEditorConfigs, n as isBingoTemplate, o as detectExistingEditors, r as updatePackageJsonWithDeps, t as detectWorkspace$1 } from "../workspace-Cjoc1c_A.js";
9
11
  import { t as renderCliDoc } from "../help-YP84FSEz.js";
10
12
  import path from "node:path";
11
- import { runCommand, vitePlusHeader } from "../../binding/index.js";
13
+ import { runCommand, upsertJsonConfig, vitePlusHeader } from "../../binding/index.js";
12
14
  import fs from "node:fs";
13
15
  import { styleText } from "node:util";
14
16
  import os from "node:os";
@@ -32,7 +34,7 @@ async function createInitialCommit(cwd) {
32
34
  cwd,
33
35
  envs: process.env
34
36
  });
35
- return (await runCommandSilently({
37
+ const result = await runCommandSilently({
36
38
  command: "git",
37
39
  args: [
38
40
  "commit",
@@ -41,7 +43,11 @@ async function createInitialCommit(cwd) {
41
43
  ],
42
44
  cwd,
43
45
  envs: process.env
44
- })).exitCode === 0;
46
+ });
47
+ return {
48
+ success: result.exitCode === 0,
49
+ output: `${result.stdout.toString()}${result.stderr.toString()}`.trim()
50
+ };
45
51
  }
46
52
  //#endregion
47
53
  //#region src/create/command.ts
@@ -121,6 +127,220 @@ function prependToPathToEnvs(extraPath, envs) {
121
127
  return envs;
122
128
  }
123
129
  //#endregion
130
+ //#region src/create/org-manifest.ts
131
+ /**
132
+ * Parse the org picker specifier: `@scope` (scope only → picker) or
133
+ * `@scope:name` (direct manifest-entry selection). Colon mirrors the
134
+ * existing `vite:monorepo` / `vite:library` builtin-template syntax and
135
+ * keeps manifest entries syntactically distinct from real
136
+ * `@scope/package-name` npm specifiers.
137
+ *
138
+ * Returns `null` for anything else — including the plain `@scope/name`
139
+ * form, which routes to the existing `@scope/create-name` shorthand as
140
+ * it did before the org-manifest feature.
141
+ *
142
+ * The optional `version` suffix (`@scope@1.2.3`, `@scope:name@1.2.3`)
143
+ * pins `@scope/create` to a specific release rather than `dist-tags.latest`.
144
+ */
145
+ function parseOrgScopedSpec(spec) {
146
+ if (!spec.startsWith("@")) return null;
147
+ if (spec.includes("/")) return null;
148
+ const colonIndex = spec.indexOf(":");
149
+ if (colonIndex === -1) {
150
+ const atIndex = spec.indexOf("@", 1);
151
+ if (atIndex === -1) return { scope: spec };
152
+ const version = spec.slice(atIndex + 1);
153
+ return version ? {
154
+ scope: spec.slice(0, atIndex),
155
+ version
156
+ } : { scope: spec.slice(0, atIndex) };
157
+ }
158
+ const scope = spec.slice(0, colonIndex);
159
+ const rest = spec.slice(colonIndex + 1);
160
+ const atIndex = rest.indexOf("@");
161
+ const name = atIndex === -1 ? rest : rest.slice(0, atIndex);
162
+ const version = atIndex === -1 ? "" : rest.slice(atIndex + 1);
163
+ if (!name) return version ? {
164
+ scope,
165
+ version
166
+ } : { scope };
167
+ return version ? {
168
+ scope,
169
+ name,
170
+ version
171
+ } : {
172
+ scope,
173
+ name
174
+ };
175
+ }
176
+ /**
177
+ * Schema-level failure. Never falls through silently — a maintainer who
178
+ * shipped an invalid manifest should see the offending field.
179
+ */
180
+ var OrgManifestSchemaError = class extends Error {
181
+ packageName;
182
+ constructor(message, packageName) {
183
+ super(`${packageName}: ${message}`);
184
+ this.packageName = packageName;
185
+ this.name = "OrgManifestSchemaError";
186
+ }
187
+ };
188
+ function isRelativePath(spec) {
189
+ return spec.startsWith("./") || spec.startsWith("../");
190
+ }
191
+ /**
192
+ * Validate the `{ name, description, template }` fields shared by org manifest
193
+ * entries and local `create.templates` entries. `label` is the config path
194
+ * used in error messages (e.g. `createConfig.templates` or `create.templates`)
195
+ * and `makeError` builds the thrown error so each source uses its own type.
196
+ */
197
+ function validateTemplateEntry(entry, index, label, makeError) {
198
+ if (!entry || typeof entry !== "object") throw makeError(`${label}[${index}] must be an object`);
199
+ const raw = entry;
200
+ const requireString = (field) => {
201
+ const value = raw[field];
202
+ if (typeof value !== "string" || value.length === 0) throw makeError(`${label}[${index}].${field} must be a non-empty string`);
203
+ return value;
204
+ };
205
+ const name = requireString("name");
206
+ if (name.startsWith("__vp_")) throw makeError(`${label}[${index}].name uses the reserved \`__vp_\` prefix`);
207
+ const description = requireString("description");
208
+ const template = requireString("template");
209
+ if (isRelativePath(template)) {
210
+ const resolved = path.posix.resolve("/root", template.replaceAll("\\", "/"));
211
+ if (resolved !== "/root" && !resolved.startsWith("/root/")) throw makeError(`${label}[${index}].template escapes the package root: ${template}`);
212
+ }
213
+ return {
214
+ name,
215
+ description,
216
+ template
217
+ };
218
+ }
219
+ /**
220
+ * Validate a list of entries, rejecting duplicate `name`s. Shared by org
221
+ * manifests and local `create.templates`.
222
+ */
223
+ function validateTemplateEntries(templates, label, makeError, validateOne) {
224
+ const entries = [];
225
+ const seen = /* @__PURE__ */ new Set();
226
+ for (let index = 0; index < templates.length; index += 1) {
227
+ const entry = validateOne(templates[index], index);
228
+ if (seen.has(entry.name)) throw makeError(`${label}[${index}].name duplicates an earlier entry: "${entry.name}"`);
229
+ seen.add(entry.name);
230
+ entries.push(entry);
231
+ }
232
+ return entries;
233
+ }
234
+ function validateEntry(entry, index, packageName) {
235
+ const makeError = (message) => new OrgManifestSchemaError(message, packageName);
236
+ const base = validateTemplateEntry(entry, index, "createConfig.templates", makeError);
237
+ let monorepo;
238
+ const raw = entry;
239
+ if (raw.monorepo !== void 0) {
240
+ if (typeof raw.monorepo !== "boolean") throw makeError(`createConfig.templates[${index}].monorepo must be a boolean`);
241
+ monorepo = raw.monorepo;
242
+ }
243
+ return {
244
+ ...base,
245
+ ...monorepo !== void 0 ? { monorepo } : {}
246
+ };
247
+ }
248
+ function validateManifest(raw, packageName) {
249
+ if (!raw || typeof raw !== "object") return null;
250
+ const createConfig = raw.createConfig;
251
+ if (!createConfig || typeof createConfig !== "object") return null;
252
+ const templates = createConfig.templates;
253
+ if (templates === void 0) return null;
254
+ if (!Array.isArray(templates)) throw new OrgManifestSchemaError("createConfig.templates must be an array", packageName);
255
+ if (templates.length === 0) return null;
256
+ return validateTemplateEntries(templates, "createConfig.templates", (message) => new OrgManifestSchemaError(message, packageName), (entry, index) => validateEntry(entry, index, packageName));
257
+ }
258
+ /**
259
+ * Schema-level failure for `create.templates` in `vite.config.ts`. A misconfigured
260
+ * local template should surface clearly rather than silently disappear.
261
+ */
262
+ var CreateConfigSchemaError = class extends Error {
263
+ constructor(message) {
264
+ super(message);
265
+ this.name = "CreateConfigSchemaError";
266
+ }
267
+ };
268
+ /**
269
+ * Validate `create.templates` from `vite.config.ts`. Returns `[]` when the field
270
+ * is absent or an empty array; throws {@link CreateConfigSchemaError} when present
271
+ * but malformed.
272
+ */
273
+ function validateCreateTemplates(templates) {
274
+ if (templates === void 0) return [];
275
+ if (!Array.isArray(templates)) throw new CreateConfigSchemaError("create.templates must be an array");
276
+ const makeError = (message) => new CreateConfigSchemaError(message);
277
+ return validateTemplateEntries(templates, "create.templates", makeError, (entry, index) => {
278
+ const validated = validateTemplateEntry(entry, index, "create.templates", makeError);
279
+ if (validated.name.startsWith("vite:")) throw makeError(`create.templates[${index}].name uses the reserved \`vite:\` prefix`);
280
+ return validated;
281
+ });
282
+ }
283
+ async function fetchPackument(scope, packageName) {
284
+ const response = await fetchNpmResource(`${getNpmRegistry(scope)}/${packageName}`, {
285
+ headers: { accept: "application/json" },
286
+ timeoutMs: 5e3
287
+ });
288
+ if (response.status === 404) return null;
289
+ if (!response.ok) throw new Error(`npm registry responded with ${response.status} for ${packageName}`);
290
+ return await response.json();
291
+ }
292
+ /**
293
+ * Fetch `@scope/create` from the npm registry and parse its `createConfig.templates`
294
+ * manifest.
295
+ *
296
+ * Returns `null` when:
297
+ * - the package does not exist on the registry (404), or
298
+ * - the package exists but has no `createConfig.templates` field
299
+ *
300
+ * Throws when:
301
+ * - the `createConfig.templates` field is present but malformed (`OrgManifestSchemaError`), or
302
+ * - the registry request fails for any non-404 reason
303
+ *
304
+ * `requestedVersion` pins the lookup to a specific `versions[...]` entry
305
+ * (equivalent to `vp create @scope@1.2.3`); omit it to resolve `dist-tags.latest`.
306
+ */
307
+ async function readOrgManifest(scope, requestedVersion) {
308
+ if (!scope.startsWith("@")) return null;
309
+ const packageName = `${scope}/create`;
310
+ const packument = await fetchPackument(scope, packageName);
311
+ if (!packument) return null;
312
+ let resolvedVersion;
313
+ if (requestedVersion) {
314
+ resolvedVersion = packument["dist-tags"]?.[requestedVersion] ?? (packument.versions?.[requestedVersion] ? requestedVersion : void 0);
315
+ if (!resolvedVersion) throw new OrgManifestSchemaError(`version "${requestedVersion}" not found (known tags: ${Object.keys(packument["dist-tags"] ?? {}).join(", ") || "none"})`, packageName);
316
+ } else {
317
+ resolvedVersion = packument["dist-tags"]?.latest;
318
+ if (!resolvedVersion) return null;
319
+ }
320
+ const meta = packument.versions?.[resolvedVersion];
321
+ if (!meta) return null;
322
+ const templates = validateManifest(meta, packageName);
323
+ if (!templates) return null;
324
+ if (!meta.dist?.tarball) throw new OrgManifestSchemaError(`missing dist.tarball for ${resolvedVersion}`, packageName);
325
+ return {
326
+ scope,
327
+ packageName,
328
+ version: resolvedVersion,
329
+ tarballUrl: meta.dist.tarball,
330
+ integrity: meta.dist.integrity,
331
+ templates
332
+ };
333
+ }
334
+ /**
335
+ * Apply the in-monorepo filter rule from the RFC: entries with
336
+ * `monorepo: true` are hidden when the command is invoked inside an
337
+ * existing monorepo, mirroring `initial-template-options.ts:9-31`.
338
+ */
339
+ function filterManifestForContext(templates, isMonorepo) {
340
+ if (!isMonorepo) return [...templates];
341
+ return templates.filter((entry) => !entry.monorepo);
342
+ }
343
+ //#endregion
124
344
  //#region src/create/templates/types.ts
125
345
  const LibraryTemplateRepo = "github:sxzz/tsdown-templates/vite-plus";
126
346
  const BuiltinTemplate = {
@@ -151,9 +371,24 @@ function inferGitHubRepoName(templateName) {
151
371
  if (!degitPath) return null;
152
372
  return degitPath.split("/").pop() || null;
153
373
  }
154
- function discoverTemplate(templateName, templateArgs, workspaceInfo, interactive, bundledLocalPath, skipShorthand) {
374
+ function localTemplateDir(workspaceInfo, templateName) {
375
+ if (isRelativePath(templateName)) return templateName.replace(/^\.\//, "");
376
+ return workspaceInfo.packages.find((pkg) => pkg.name === templateName)?.path;
377
+ }
378
+ function resolveLocalBinPath(localPackagePath, packageName, bin) {
379
+ if (!bin) return;
380
+ if (typeof bin === "string") return path.join(localPackagePath, bin);
381
+ const entries = Object.entries(bin);
382
+ if (entries.length === 0) return;
383
+ if (entries.length === 1) return path.join(localPackagePath, entries[0][1]);
384
+ const unscopedName = packageName.slice(packageName.lastIndexOf("/") + 1);
385
+ const preferred = bin[packageName] ?? bin[unscopedName];
386
+ if (preferred) return path.join(localPackagePath, preferred);
387
+ throw new Error(`Local template package "${packageName}" defines multiple "bin" entries (${entries.map(([name]) => name).join(", ")}); add a "bin" entry named "${packageName}" so the template entry is unambiguous`);
388
+ }
389
+ function discoverTemplate(templateName, templateArgs, workspaceInfo, interactive, bundledLocalPath, skipShorthand, localTemplate) {
155
390
  const envs = prependToPathToEnvs(workspaceInfo.downloadPackageManager.binPrefix, { ...process.env });
156
- const parentDir = inferParentDir(templateName, workspaceInfo);
391
+ const parentDir = inferParentDir(templateName, workspaceInfo, localTemplate);
157
392
  if (bundledLocalPath) return {
158
393
  command: "",
159
394
  args: [...templateArgs],
@@ -182,23 +417,22 @@ function discoverTemplate(templateName, templateArgs, workspaceInfo, interactive
182
417
  interactive
183
418
  };
184
419
  }
185
- const localPackage = workspaceInfo.packages.find((pkg) => pkg.name === templateName);
186
- if (localPackage) {
187
- const localPackagePath = path.join(workspaceInfo.rootDir, localPackage.path);
188
- const pkg = readJsonFile(path.join(localPackagePath, "package.json"));
189
- let binPath = "";
190
- if (pkg.bin) if (typeof pkg.bin === "string") binPath = path.join(localPackagePath, pkg.bin);
191
- else {
192
- const binName = Object.keys(pkg.bin)[0];
193
- binPath = path.join(localPackagePath, pkg.bin[binName]);
194
- }
420
+ if (localTemplate) {
421
+ const localDir = localTemplateDir(workspaceInfo, templateName);
422
+ if (!localDir) throw new Error(`Local template "${templateName}" does not match any workspace package; update the \`create.templates\` entry in vite.config.ts`);
423
+ const localPackagePath = path.join(workspaceInfo.rootDir, localDir);
424
+ const packageJsonPath = path.join(localPackagePath, "package.json");
425
+ if (!fs.existsSync(packageJsonPath)) throw new Error(`Local template "${templateName}" has no package.json, so it cannot be run as a template`);
426
+ const pkg = readJsonFile(packageJsonPath);
427
+ const binPath = resolveLocalBinPath(localPackagePath, pkg.name ?? templateName, pkg.bin);
428
+ if (!binPath) throw new Error(`Local template "${templateName}" has no "bin" entry in its package.json, so it cannot be run as a template`);
195
429
  const args = [binPath, ...templateArgs];
196
430
  let type = TemplateType.remote;
197
- if (pkg.keywords?.includes("bingo-template") || !!pkg.dependencies?.bingo) {
431
+ if (isBingoTemplate(pkg)) {
198
432
  type = TemplateType.bingo;
199
433
  args.push("--skip-requests");
200
434
  }
201
- if (binPath) return {
435
+ return {
202
436
  command: "node",
203
437
  args,
204
438
  envs,
@@ -261,8 +495,13 @@ function expandCreateShorthand(templateName) {
261
495
  if (name === "svelte") return `sv${version}`;
262
496
  return `create-${name}${version}`;
263
497
  }
264
- function inferParentDir(templateName, workspaceInfo) {
498
+ function inferParentDir(templateName, workspaceInfo, localTemplate = false) {
265
499
  if (workspaceInfo.parentDirs.length === 0) return;
500
+ const localDir = localTemplate ? localTemplateDir(workspaceInfo, templateName) : void 0;
501
+ if (localDir) {
502
+ const ownParentDir = path.dirname(localDir);
503
+ if (workspaceInfo.parentDirs.includes(ownParentDir)) return ownParentDir;
504
+ }
266
505
  let rule = /app/i;
267
506
  if (templateName === BuiltinTemplate.library) rule = /lib|component|package/i;
268
507
  else if (templateName === BuiltinTemplate.generator) rule = /generator|tool/i;
@@ -270,7 +509,7 @@ function inferParentDir(templateName, workspaceInfo) {
270
509
  }
271
510
  //#endregion
272
511
  //#region src/create/initial-template-options.ts
273
- function getInitialTemplateOptions(isMonorepo) {
512
+ function getInitialTemplateOptions(isMonorepo, templates = []) {
274
513
  return [
275
514
  ...!isMonorepo ? [{
276
515
  label: "Vite+ Monorepo",
@@ -286,178 +525,15 @@ function getInitialTemplateOptions(isMonorepo) {
286
525
  label: "Vite+ Library",
287
526
  value: BuiltinTemplate.library,
288
527
  hint: "Create vite libraries"
289
- }
528
+ },
529
+ ...isMonorepo ? templates.map((entry) => ({
530
+ label: entry.name,
531
+ value: entry.name,
532
+ hint: entry.description
533
+ })) : []
290
534
  ];
291
535
  }
292
536
  //#endregion
293
- //#region src/create/org-manifest.ts
294
- /**
295
- * Parse the org picker specifier: `@scope` (scope only → picker) or
296
- * `@scope:name` (direct manifest-entry selection). Colon mirrors the
297
- * existing `vite:monorepo` / `vite:library` builtin-template syntax and
298
- * keeps manifest entries syntactically distinct from real
299
- * `@scope/package-name` npm specifiers.
300
- *
301
- * Returns `null` for anything else — including the plain `@scope/name`
302
- * form, which routes to the existing `@scope/create-name` shorthand as
303
- * it did before the org-manifest feature.
304
- *
305
- * The optional `version` suffix (`@scope@1.2.3`, `@scope:name@1.2.3`)
306
- * pins `@scope/create` to a specific release rather than `dist-tags.latest`.
307
- */
308
- function parseOrgScopedSpec(spec) {
309
- if (!spec.startsWith("@")) return null;
310
- if (spec.includes("/")) return null;
311
- const colonIndex = spec.indexOf(":");
312
- if (colonIndex === -1) {
313
- const atIndex = spec.indexOf("@", 1);
314
- if (atIndex === -1) return { scope: spec };
315
- const version = spec.slice(atIndex + 1);
316
- return version ? {
317
- scope: spec.slice(0, atIndex),
318
- version
319
- } : { scope: spec.slice(0, atIndex) };
320
- }
321
- const scope = spec.slice(0, colonIndex);
322
- const rest = spec.slice(colonIndex + 1);
323
- const atIndex = rest.indexOf("@");
324
- const name = atIndex === -1 ? rest : rest.slice(0, atIndex);
325
- const version = atIndex === -1 ? "" : rest.slice(atIndex + 1);
326
- if (!name) return version ? {
327
- scope,
328
- version
329
- } : { scope };
330
- return version ? {
331
- scope,
332
- name,
333
- version
334
- } : {
335
- scope,
336
- name
337
- };
338
- }
339
- /**
340
- * Schema-level failure. Never falls through silently — a maintainer who
341
- * shipped an invalid manifest should see the offending field.
342
- */
343
- var OrgManifestSchemaError = class extends Error {
344
- packageName;
345
- constructor(message, packageName) {
346
- super(`${packageName}: ${message}`);
347
- this.packageName = packageName;
348
- this.name = "OrgManifestSchemaError";
349
- }
350
- };
351
- function isRelativePath(spec) {
352
- return spec.startsWith("./") || spec.startsWith("../");
353
- }
354
- function validateEntry(entry, index, packageName) {
355
- if (!entry || typeof entry !== "object") throw new OrgManifestSchemaError(`createConfig.templates[${index}] must be an object`, packageName);
356
- const raw = entry;
357
- const requireString = (field) => {
358
- const value = raw[field];
359
- if (typeof value !== "string" || value.length === 0) throw new OrgManifestSchemaError(`createConfig.templates[${index}].${field} must be a non-empty string`, packageName);
360
- return value;
361
- };
362
- const name = requireString("name");
363
- if (name.startsWith("__vp_")) throw new OrgManifestSchemaError(`createConfig.templates[${index}].name uses the reserved \`__vp_\` prefix`, packageName);
364
- const description = requireString("description");
365
- const template = requireString("template");
366
- let monorepo;
367
- if (raw.monorepo !== void 0) {
368
- if (typeof raw.monorepo !== "boolean") throw new OrgManifestSchemaError(`createConfig.templates[${index}].monorepo must be a boolean`, packageName);
369
- monorepo = raw.monorepo;
370
- }
371
- if (isRelativePath(template)) {
372
- const resolved = path.posix.resolve("/root", template.replaceAll("\\", "/"));
373
- if (resolved !== "/root" && !resolved.startsWith("/root/")) throw new OrgManifestSchemaError(`createConfig.templates[${index}].template escapes the package root: ${template}`, packageName);
374
- }
375
- return {
376
- name,
377
- description,
378
- template,
379
- ...monorepo !== void 0 ? { monorepo } : {}
380
- };
381
- }
382
- function validateManifest(raw, packageName) {
383
- if (!raw || typeof raw !== "object") return null;
384
- const createConfig = raw.createConfig;
385
- if (!createConfig || typeof createConfig !== "object") return null;
386
- const templates = createConfig.templates;
387
- if (templates === void 0) return null;
388
- if (!Array.isArray(templates)) throw new OrgManifestSchemaError("createConfig.templates must be an array", packageName);
389
- if (templates.length === 0) return null;
390
- const entries = [];
391
- const seen = /* @__PURE__ */ new Set();
392
- for (let index = 0; index < templates.length; index += 1) {
393
- const entry = validateEntry(templates[index], index, packageName);
394
- if (seen.has(entry.name)) throw new OrgManifestSchemaError(`createConfig.templates[${index}].name duplicates an earlier entry: "${entry.name}"`, packageName);
395
- seen.add(entry.name);
396
- entries.push(entry);
397
- }
398
- return entries;
399
- }
400
- async function fetchPackument(scope, packageName) {
401
- const response = await fetchNpmResource(`${getNpmRegistry(scope)}/${packageName}`, {
402
- headers: { accept: "application/json" },
403
- timeoutMs: 5e3
404
- });
405
- if (response.status === 404) return null;
406
- if (!response.ok) throw new Error(`npm registry responded with ${response.status} for ${packageName}`);
407
- return await response.json();
408
- }
409
- /**
410
- * Fetch `@scope/create` from the npm registry and parse its `createConfig.templates`
411
- * manifest.
412
- *
413
- * Returns `null` when:
414
- * - the package does not exist on the registry (404), or
415
- * - the package exists but has no `createConfig.templates` field
416
- *
417
- * Throws when:
418
- * - the `createConfig.templates` field is present but malformed (`OrgManifestSchemaError`), or
419
- * - the registry request fails for any non-404 reason
420
- *
421
- * `requestedVersion` pins the lookup to a specific `versions[...]` entry
422
- * (equivalent to `vp create @scope@1.2.3`); omit it to resolve `dist-tags.latest`.
423
- */
424
- async function readOrgManifest(scope, requestedVersion) {
425
- if (!scope.startsWith("@")) return null;
426
- const packageName = `${scope}/create`;
427
- const packument = await fetchPackument(scope, packageName);
428
- if (!packument) return null;
429
- let resolvedVersion;
430
- if (requestedVersion) {
431
- resolvedVersion = packument["dist-tags"]?.[requestedVersion] ?? (packument.versions?.[requestedVersion] ? requestedVersion : void 0);
432
- if (!resolvedVersion) throw new OrgManifestSchemaError(`version "${requestedVersion}" not found (known tags: ${Object.keys(packument["dist-tags"] ?? {}).join(", ") || "none"})`, packageName);
433
- } else {
434
- resolvedVersion = packument["dist-tags"]?.latest;
435
- if (!resolvedVersion) return null;
436
- }
437
- const meta = packument.versions?.[resolvedVersion];
438
- if (!meta) return null;
439
- const templates = validateManifest(meta, packageName);
440
- if (!templates) return null;
441
- if (!meta.dist?.tarball) throw new OrgManifestSchemaError(`missing dist.tarball for ${resolvedVersion}`, packageName);
442
- return {
443
- scope,
444
- packageName,
445
- version: resolvedVersion,
446
- tarballUrl: meta.dist.tarball,
447
- integrity: meta.dist.integrity,
448
- templates
449
- };
450
- }
451
- /**
452
- * Apply the in-monorepo filter rule from the RFC: entries with
453
- * `monorepo: true` are hidden when the command is invoked inside an
454
- * existing monorepo, mirroring `initial-template-options.ts:9-31`.
455
- */
456
- function filterManifestForContext(templates, isMonorepo) {
457
- if (!isMonorepo) return [...templates];
458
- return templates.filter((entry) => !entry.monorepo);
459
- }
460
- //#endregion
461
537
  //#region src/create/org-picker.ts
462
538
  const ORG_PICKER_CANCEL = Symbol("org-picker-cancel");
463
539
  const ORG_PICKER_BUILTIN_ESCAPE = Symbol("org-picker-builtin-escape");
@@ -496,7 +572,7 @@ async function pickOrgTemplate(manifest, opts) {
496
572
  message: `Pick a template from ${manifest.scope}`,
497
573
  options
498
574
  });
499
- if (q(picked)) return ORG_PICKER_CANCEL;
575
+ if (R(picked)) return ORG_PICKER_CANCEL;
500
576
  const found = lookup.get(picked);
501
577
  if (found === ESCAPE_HATCH) return ORG_PICKER_BUILTIN_ESCAPE;
502
578
  if (!found) throw new Error(`org-picker: prompts.select returned an unregistered value: ${picked}`);
@@ -3763,6 +3839,19 @@ function getRandomProjectName(options = {}) {
3763
3839
  }
3764
3840
  //#endregion
3765
3841
  //#region src/create/utils.ts
3842
+ function hasExplicitEditorOptIn(editor) {
3843
+ return typeof editor === "string" && editor.trim() !== "";
3844
+ }
3845
+ function normalizeEditorOption(editor) {
3846
+ if (!Array.isArray(editor)) return editor;
3847
+ if (editor.includes(false)) return false;
3848
+ return editor.findLast((value) => typeof value === "string");
3849
+ }
3850
+ function shouldConfigureEditorsForCreate({ editor, isMonorepo }) {
3851
+ if (editor === false) return false;
3852
+ if (!isMonorepo) return true;
3853
+ return hasExplicitEditorOptIn(editor);
3854
+ }
3766
3855
  function copy(src, dest) {
3767
3856
  if (fs.statSync(src).isDirectory()) copyDir(src, dest);
3768
3857
  else fs.copyFileSync(src, dest);
@@ -3930,7 +4019,7 @@ async function promptPackageNameAndTargetDir(defaultPackageName, interactive) {
3930
4019
  return result?.errors?.[0] ?? result?.warnings?.[0] ?? "Invalid package name";
3931
4020
  }
3932
4021
  });
3933
- if (q(selected)) cancelAndExit();
4022
+ if (R(selected)) cancelAndExit();
3934
4023
  packageName = selected;
3935
4024
  targetDir = getProjectDirFromPackageName(packageName);
3936
4025
  } else {
@@ -3952,7 +4041,7 @@ async function promptTargetDir(defaultTargetDir, interactive, options) {
3952
4041
  defaultValue: defaultTargetDir,
3953
4042
  validate: (value) => validateTargetDir(value ?? defaultTargetDir, options?.cwd).error
3954
4043
  });
3955
- if (q(selected)) cancelAndExit();
4044
+ if (R(selected)) cancelAndExit();
3956
4045
  targetDir = validateTargetDir(selected ?? defaultTargetDir, options?.cwd).directory;
3957
4046
  } else {
3958
4047
  targetDir = validateTargetDir(defaultTargetDir, options?.cwd).directory;
@@ -3985,7 +4074,7 @@ async function checkProjectDirExists(projectDirFullPath, interactive) {
3985
4074
  value: "yes"
3986
4075
  }]
3987
4076
  });
3988
- if (q(overwrite)) cancelAndExit();
4077
+ if (R(overwrite)) cancelAndExit();
3989
4078
  switch (overwrite) {
3990
4079
  case "yes":
3991
4080
  emptyDir(projectDirFullPath);
@@ -4123,24 +4212,133 @@ async function resolveOrgManifestForCreate(args) {
4123
4212
  return resolveEntry(manifest, entry);
4124
4213
  }
4125
4214
  /**
4126
- * Read `create.defaultTemplate` from the workspace root's `vite.config.ts`.
4215
+ * Read the `create` config (`defaultTemplate` + validated `templates`) from
4216
+ * a workspace's `vite.config.ts` in a single config evaluation.
4217
+ *
4218
+ * By default, walks up from `startDir` via `findWorkspaceRoot` (monorepo
4219
+ * markers only — `pnpm-workspace.yaml`, `workspaces` in `package.json`,
4220
+ * `lerna.json`) so monorepo invocations from any subdirectory still pick up
4221
+ * the root config. Pass `walkUp: false` to read `startDir` directly when the
4222
+ * caller already holds the exact workspace root.
4127
4223
  *
4128
- * Walks up from `startDir` via `findWorkspaceRoot` (monorepo markers
4129
- * only `pnpm-workspace.yaml`, `workspaces` in `package.json`,
4130
- * `lerna.json`) so monorepo invocations from any subdirectory still
4131
- * pick up the root config. Standalone repos without a monorepo marker
4132
- * only see a config that sits at `startDir` itself.
4224
+ * Best-effort for resolution: a missing or unresolvable config reads as
4225
+ * empty. A present-but-malformed `create.templates` still throws a
4226
+ * {@link CreateConfigSchemaError} so the misconfiguration surfaces.
4133
4227
  *
4134
- * Best-effort: if there's no config file or evaluation fails, return
4135
- * `undefined` so the create flow behaves as if no default was set.
4228
+ * Pass `throwOnReadError: true` for read-modify-write callers (registration):
4229
+ * if a config file exists but cannot be evaluated, an empty read would let a
4230
+ * later write clobber the real `create` block, so the eval error is rethrown
4231
+ * instead of swallowed.
4232
+ */
4233
+ async function getConfiguredCreate(startDir, options) {
4234
+ const projectRoot = options?.walkUp === false ? startDir : findWorkspaceRoot(startDir) ?? startDir;
4235
+ if (!hasViteConfig(projectRoot)) return { templates: [] };
4236
+ let create;
4237
+ try {
4238
+ create = (await resolveViteConfig(projectRoot)).create;
4239
+ } catch (error) {
4240
+ if (options?.throwOnReadError) throw error;
4241
+ return { templates: [] };
4242
+ }
4243
+ const defaultTemplate = typeof create?.defaultTemplate === "string" && create.defaultTemplate.length > 0 ? create.defaultTemplate : void 0;
4244
+ const templates = validateCreateTemplates(create?.templates);
4245
+ return {
4246
+ ...defaultTemplate !== void 0 ? { defaultTemplate } : {},
4247
+ templates
4248
+ };
4249
+ }
4250
+ /**
4251
+ * Read `create.defaultTemplate` only. Best-effort for missing or unresolvable
4252
+ * configs (returns `undefined`), but a malformed `create.templates` still
4253
+ * rethrows its {@link CreateConfigSchemaError}: swallowing it here would
4254
+ * silently drop a valid `defaultTemplate` along with the diagnostic.
4136
4255
  */
4137
4256
  async function getConfiguredDefaultTemplate(startDir) {
4138
- const projectRoot = findWorkspaceRoot(startDir) ?? startDir;
4139
- if (!hasViteConfig(projectRoot)) return;
4140
4257
  try {
4141
- const value = (await resolveViteConfig(projectRoot)).create?.defaultTemplate;
4142
- if (typeof value === "string" && value.length > 0) return value;
4143
- } catch {}
4258
+ return (await getConfiguredCreate(startDir)).defaultTemplate;
4259
+ } catch (error) {
4260
+ if (error instanceof CreateConfigSchemaError) throw error;
4261
+ return;
4262
+ }
4263
+ }
4264
+ //#endregion
4265
+ //#region src/create/register-template.ts
4266
+ /**
4267
+ * Register a local template into `create.templates` in a monorepo's root
4268
+ * `vite.config.ts`. Used after `vp create vite:generator` scaffolds a
4269
+ * generator so the generated template shows up in this workspace's
4270
+ * `vp create` picker.
4271
+ *
4272
+ * Behavior:
4273
+ * - Reads the existing `create` config from the workspace root's `vite.config.*`.
4274
+ * - If an entry with the same `name` already exists → no-op (idempotent),
4275
+ * warning when it points at a different `template` so a stale entry does
4276
+ * not silently shadow the new generator.
4277
+ * - Otherwise appends `entry` to `create.templates`, preserving any sibling
4278
+ * `create.defaultTemplate` and any existing entries, and writes back.
4279
+ * - If there is no `vite.config.*` yet, or no `create` block, it is created.
4280
+ *
4281
+ * Read-modify-write: the existing `create` object is read in full first and
4282
+ * the complete, recomputed object is written back via `upsertJsonConfig`
4283
+ * (replace the existing `create` value, or insert the key), so
4284
+ * `defaultTemplate` and prior `templates` are kept. Throws when the config
4285
+ * shape is not supported by the upsert, rather than writing nothing or a key
4286
+ * that is dead at runtime.
4287
+ *
4288
+ * Returns the absolute path of the config file written, so the caller can fold
4289
+ * it into its own formatting pass (the upsert writes a JSON-style block that
4290
+ * needs reformatting). Returns `undefined` for the idempotent no-op.
4291
+ */
4292
+ async function registerLocalTemplate(workspaceRoot, entry, silent = false) {
4293
+ const configPath = findViteConfig(workspaceRoot);
4294
+ const existing = await getConfiguredCreate(workspaceRoot, {
4295
+ walkUp: false,
4296
+ throwOnReadError: true
4297
+ });
4298
+ const existingEntry = existing.templates.find((t) => t.name === entry.name);
4299
+ if (existingEntry) {
4300
+ if (existingEntry.template !== entry.template) log.warn(`create.templates already has an entry named '${entry.name}' pointing at '${existingEntry.template}'; left unchanged.\nUpdate it by hand if it should run '${entry.template}' instead.`);
4301
+ return;
4302
+ }
4303
+ const nextCreate = {
4304
+ ...existing.defaultTemplate !== void 0 ? { defaultTemplate: existing.defaultTemplate } : {},
4305
+ templates: [...existing.templates, entry]
4306
+ };
4307
+ const targetPath = configPath ?? ensureViteConfig(workspaceRoot, silent);
4308
+ writeCreateBlock(targetPath, nextCreate);
4309
+ return targetPath;
4310
+ }
4311
+ /**
4312
+ * Create a minimal `vite.config.ts` (matching the migrator's
4313
+ * `ensureViteConfig` shape) and return its absolute path.
4314
+ */
4315
+ function ensureViteConfig(workspaceRoot, silent) {
4316
+ const configPath = path.join(workspaceRoot, "vite.config.ts");
4317
+ fs.writeFileSync(configPath, `import { defineConfig } from '${VITE_PLUS_NAME}';\n\nexport default defineConfig({});\n`);
4318
+ if (!silent) log.success(`✔ Created vite.config.ts in ${displayRelative(configPath)}`);
4319
+ return configPath;
4320
+ }
4321
+ /**
4322
+ * Write the full `create` object into vite.config.ts via the shared config
4323
+ * upsert: replace the existing `create:` value in place, or insert the key
4324
+ * when absent. The caller reformats the file afterward, so the JSON-style
4325
+ * block written here is normalized to the surrounding style.
4326
+ *
4327
+ * Throws when the config shape is not supported (`updated: false`, e.g.
4328
+ * `module.exports` or `export default someVar`), so the caller can warn and
4329
+ * point at a manual edit instead of reporting a registration that never
4330
+ * happened.
4331
+ */
4332
+ function writeCreateBlock(configPath, create) {
4333
+ const tempPath = path.join(os.tmpdir(), `vite-plus-create-register-${randomUUID()}.json`);
4334
+ fs.writeFileSync(tempPath, JSON.stringify(create));
4335
+ try {
4336
+ const result = upsertJsonConfig(configPath, tempPath, "create");
4337
+ if (!result.updated) throw new Error(`could not find a supported config object in ${displayRelative(configPath)}`);
4338
+ fs.writeFileSync(configPath, result.content);
4339
+ } finally {
4340
+ fs.rmSync(tempPath, { force: true });
4341
+ }
4144
4342
  }
4145
4343
  //#endregion
4146
4344
  //#region src/create/templates/generator.ts
@@ -4154,7 +4352,7 @@ async function executeGeneratorScaffold(workspaceInfo, templateInfo, options) {
4154
4352
  placeholder: defaultDescription,
4155
4353
  defaultValue: defaultDescription
4156
4354
  });
4157
- if (!q(descPrompt)) description = descPrompt;
4355
+ if (!R(descPrompt)) description = descPrompt;
4158
4356
  }
4159
4357
  const fullPath = path.join(workspaceInfo.rootDir, templateInfo.targetDir);
4160
4358
  copyDir(path.join(templatesDir, "generator"), fullPath);
@@ -4374,14 +4572,7 @@ async function executeMonorepoTemplate(workspaceInfo, templateInfo, options) {
4374
4572
  const appProjectPath = path.join(fullPath, InitialMonorepoAppDir);
4375
4573
  setPackageName(appProjectPath, appPackageName);
4376
4574
  rewriteMonorepoProject(appProjectPath, workspaceInfo.packageManager, void 0, options?.silent ?? false);
4377
- editJsonFile(path.join(appProjectPath, "package.json"), (pkg) => {
4378
- let changed = false;
4379
- for (const name of ["vite", "vitest"]) if (pkg.devDependencies?.[name]) {
4380
- delete pkg.devDependencies[name];
4381
- changed = true;
4382
- }
4383
- return changed ? pkg : void 0;
4384
- });
4575
+ dropAliasedRuntimeDevDeps(appProjectPath, workspaceInfo.packageManager);
4385
4576
  if (!options?.silent) log.step("Creating default library in packages/utils...");
4386
4577
  const libraryDir = "packages/utils";
4387
4578
  const libraryResult = await runRemoteTemplateCommand(workspaceInfo, fullPath, discoverTemplate(LibraryTemplateRepo, [libraryDir], workspaceInfo), false, options?.silent ?? false);
@@ -4398,6 +4589,30 @@ async function executeMonorepoTemplate(workspaceInfo, templateInfo, options) {
4398
4589
  projectDir: templateInfo.targetDir
4399
4590
  };
4400
4591
  }
4592
+ /**
4593
+ * Drop the aliased `vite` / `vitest` devDeps that `create-vite` leaves on a
4594
+ * scaffolded sub-package. After migration its scripts already use `vp ...` and
4595
+ * nothing imports `'vite'` directly, so `vite-plus` provides them transitively.
4596
+ *
4597
+ * pnpm is the exception and keeps them: pnpm only surfaces the
4598
+ * pnpm-workspace.yaml `overrides.vite: catalog:` entry through a package that
4599
+ * directly depends on `vite`, so keeping the aliased devDep lets `vp why vite`
4600
+ * reflect the override (resolving to @voidzero-dev/vite-plus-core). npm, yarn,
4601
+ * and bun redirect the transitive/peer vite via their root
4602
+ * overrides/resolutions regardless of a direct dep, so the aliased keys are
4603
+ * dead weight and are dropped.
4604
+ */
4605
+ function dropAliasedRuntimeDevDeps(appProjectPath, packageManager) {
4606
+ if (packageManager === PackageManager.pnpm) return;
4607
+ editJsonFile(path.join(appProjectPath, "package.json"), (pkg) => {
4608
+ let changed = false;
4609
+ for (const name of ["vite", "vitest"]) if (pkg.devDependencies?.[name]) {
4610
+ delete pkg.devDependencies[name];
4611
+ changed = true;
4612
+ }
4613
+ return changed ? pkg : void 0;
4614
+ });
4615
+ }
4401
4616
  function getScopeFromPackageName(packageName) {
4402
4617
  if (packageName.startsWith("@")) return packageName.split("/")[0];
4403
4618
  return "";
@@ -4418,7 +4633,7 @@ const helpMessage = renderCliDoc({
4418
4633
  `- Default: ${accent("vite:monorepo")}, ${accent("vite:application")}, ${accent("vite:library")}, ${accent("vite:generator")}`,
4419
4634
  "- Remote: vite, @tanstack/start, create-next-app,",
4420
4635
  " create-nuxt, github:user/repo, https://github.com/user/template-repo, etc.",
4421
- "- Local: @company/generator-*, ./tools/create-ui-component",
4636
+ "- Local: a `create.templates` entry name from vite.config.ts (monorepo)",
4422
4637
  `- Org scope: ${accent("@your-org")} → picker from ${accent("@your-org/create")}'s ${accent("createConfig.templates")} manifest`,
4423
4638
  `- Org entry: ${accent("@your-org:web")} → manifest entry "web" from ${accent("@your-org/create")}`,
4424
4639
  `When omitted, uses \`create.defaultTemplate\` from vite.config.ts if set.`
@@ -4436,10 +4651,18 @@ const helpMessage = renderCliDoc({
4436
4651
  label: "--agent NAME",
4437
4652
  description: "Write coding agent instructions to AGENTS.md, CLAUDE.md, etc."
4438
4653
  },
4654
+ {
4655
+ label: "--no-agent",
4656
+ description: "Skip writing coding agent instructions"
4657
+ },
4439
4658
  {
4440
4659
  label: "--editor NAME",
4441
4660
  description: "Write editor config files for the specified editor."
4442
4661
  },
4662
+ {
4663
+ label: "--no-editor",
4664
+ description: "Skip writing editor config files"
4665
+ },
4443
4666
  {
4444
4667
  label: "--git",
4445
4668
  description: "Initialize a git repository with an initial commit"
@@ -4460,6 +4683,10 @@ const helpMessage = renderCliDoc({
4460
4683
  label: "--package-manager NAME",
4461
4684
  description: "Use specified package manager (pnpm, npm, yarn, bun)"
4462
4685
  },
4686
+ {
4687
+ label: "--approve-builds",
4688
+ description: "Approve and run gated dependency build scripts without prompting"
4689
+ },
4463
4690
  {
4464
4691
  label: "--verbose",
4465
4692
  description: "Show detailed scaffolding output"
@@ -4608,7 +4835,8 @@ function parseArgs() {
4608
4835
  "interactive",
4609
4836
  "hooks",
4610
4837
  "verbose",
4611
- "git"
4838
+ "git",
4839
+ "approve-builds"
4612
4840
  ],
4613
4841
  string: [
4614
4842
  "directory",
@@ -4627,10 +4855,11 @@ function parseArgs() {
4627
4855
  help: parsed.help || false,
4628
4856
  verbose: parsed.verbose || false,
4629
4857
  agent: normalizeAgentOption(parsed.agent),
4630
- editor: parsed.editor,
4858
+ editor: normalizeEditorOption(parsed.editor),
4631
4859
  git: parsed.git,
4632
4860
  hooks: parsed.hooks,
4633
- packageManager: parsed["package-manager"]
4861
+ packageManager: parsed["package-manager"],
4862
+ approveBuilds: parsed["approve-builds"] || false
4634
4863
  },
4635
4864
  templateArgs
4636
4865
  };
@@ -4740,11 +4969,28 @@ async function main() {
4740
4969
  let shouldSetupHooks = false;
4741
4970
  let bundled;
4742
4971
  let skipShorthandExpansion = false;
4972
+ let registeredConfigPath;
4743
4973
  const installArgs = process.env.CI ? ["--no-frozen-lockfile"] : void 0;
4744
- if (!selectedTemplateName) {
4745
- const defaultTemplate = await getConfiguredDefaultTemplate(workspaceInfoOptional.rootDir);
4974
+ let localTemplates = [];
4975
+ if (isMonorepo) try {
4976
+ const configuredCreate = await getConfiguredCreate(workspaceInfoOptional.rootDir, { throwOnReadError: true });
4977
+ localTemplates = configuredCreate.templates;
4978
+ if (!selectedTemplateName && configuredCreate.defaultTemplate) selectedTemplateName = configuredCreate.defaultTemplate;
4979
+ } catch (error) {
4980
+ if (error instanceof CreateConfigSchemaError) cancelAndExit(error.message, 1);
4981
+ log.warn(`Could not read \`create\` config from the workspace vite.config (${error.message}); local templates are unavailable`);
4982
+ }
4983
+ else if (!selectedTemplateName) {
4984
+ let defaultTemplate;
4985
+ try {
4986
+ defaultTemplate = await getConfiguredDefaultTemplate(workspaceInfoOptional.rootDir);
4987
+ } catch (error) {
4988
+ if (error instanceof CreateConfigSchemaError) cancelAndExit(error.message, 1);
4989
+ throw error;
4990
+ }
4746
4991
  if (defaultTemplate) selectedTemplateName = defaultTemplate;
4747
4992
  }
4993
+ let resolvedByOrg = false;
4748
4994
  if (selectedTemplateName) {
4749
4995
  const resolved = await resolveOrgManifestForCreate({
4750
4996
  templateName: selectedTemplateName,
@@ -4754,8 +5000,11 @@ async function main() {
4754
5000
  if (resolved.kind === "replaced") {
4755
5001
  selectedTemplateName = resolved.templateName;
4756
5002
  skipShorthandExpansion = true;
4757
- } else if (resolved.kind === "bundled") bundled = resolved;
4758
- else if (resolved.kind === "escape-hatch") selectedTemplateName = "";
5003
+ resolvedByOrg = true;
5004
+ } else if (resolved.kind === "bundled") {
5005
+ bundled = resolved;
5006
+ resolvedByOrg = true;
5007
+ } else if (resolved.kind === "escape-hatch") selectedTemplateName = "";
4759
5008
  }
4760
5009
  if (!selectedTemplateName && !options.interactive) {
4761
5010
  console.error(`
@@ -4774,11 +5023,17 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
4774
5023
  if (!selectedTemplateName) {
4775
5024
  const template = await select({
4776
5025
  message: "",
4777
- options: getInitialTemplateOptions(isMonorepo)
5026
+ options: getInitialTemplateOptions(isMonorepo, localTemplates)
4778
5027
  });
4779
- if (q(template)) cancelAndExit();
5028
+ if (R(template)) cancelAndExit();
4780
5029
  selectedTemplateName = template;
4781
5030
  }
5031
+ const matchedLocalTemplate = resolvedByOrg ? void 0 : localTemplates.find((entry) => entry.name === selectedTemplateName);
5032
+ if (matchedLocalTemplate) {
5033
+ selectedTemplateName = matchedLocalTemplate.template;
5034
+ skipShorthandExpansion = true;
5035
+ }
5036
+ const isLocalTemplate = matchedLocalTemplate !== void 0;
4782
5037
  const isBuiltinTemplate = selectedTemplateName.startsWith("vite:");
4783
5038
  const isBundledTemplate = bundled !== void 0;
4784
5039
  const isBundledMonorepo = bundled?.monorepo === true;
@@ -4793,6 +5048,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
4793
5048
  log.info("The vite:generator template requires a monorepo workspace.\nRun this command inside a Vite+ monorepo, or create one first with `vp create vite:monorepo`");
4794
5049
  cancelAndExit("Cannot create a generator outside a monorepo", 1);
4795
5050
  }
5051
+ if (isMonorepo && options.git !== void 0) cancelAndExit("The --git/--no-git options are not available when adding a package to an existing monorepo", 1);
4796
5052
  if (isInSubdirectory && !compactOutput) log.info(`Detected monorepo root at ${accent(workspaceInfoOptional.rootDir)}`);
4797
5053
  if (isMonorepo && options.interactive && !targetDir) {
4798
5054
  let parentDir;
@@ -4815,9 +5071,9 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
4815
5071
  const selected = await select({
4816
5072
  message: "Where should the new package be added to the monorepo:",
4817
5073
  options: dirOptions,
4818
- initialValue: shouldOfferCwdOption ? cwdRelativeToRoot : inferParentDir(selectedTemplateName, workspaceInfoOptional) ?? workspaceInfoOptional.parentDirs[0]
5074
+ initialValue: shouldOfferCwdOption ? cwdRelativeToRoot : inferParentDir(selectedTemplateName, workspaceInfoOptional, isLocalTemplate) ?? workspaceInfoOptional.parentDirs[0]
4819
5075
  });
4820
- if (q(selected)) cancelAndExit();
5076
+ if (R(selected)) cancelAndExit();
4821
5077
  if (selected !== "other") parentDir = selected;
4822
5078
  }
4823
5079
  if (!parentDir) {
@@ -4828,14 +5084,14 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
4828
5084
  return value ? formatTargetDir(value).error : "Target directory is required";
4829
5085
  }
4830
5086
  });
4831
- if (q(customTargetDir)) cancelAndExit();
5087
+ if (R(customTargetDir)) cancelAndExit();
4832
5088
  parentDir = customTargetDir;
4833
5089
  }
4834
5090
  selectedParentDir = parentDir;
4835
5091
  }
4836
5092
  if (isMonorepo && !options.interactive && !targetDir) {
4837
5093
  if (isInSubdirectory && !compactOutput) log.info(`Use ${accent("--directory")} to specify a different target location.`);
4838
- selectedParentDir = inferParentDir(selectedTemplateName, workspaceInfoOptional) ?? workspaceInfoOptional.parentDirs[0];
5094
+ selectedParentDir = inferParentDir(selectedTemplateName, workspaceInfoOptional, isLocalTemplate) ?? workspaceInfoOptional.parentDirs[0];
4839
5095
  }
4840
5096
  if (isGitHubUrl(selectedTemplateName)) if (hasExplicitTargetDir(selectedTemplateArgs)) remoteTargetDir = selectedTemplateArgs[0];
4841
5097
  else {
@@ -4892,12 +5148,16 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
4892
5148
  selectedAgentTargetPaths = agentSelection.targetPaths;
4893
5149
  shouldWriteCopilotSetupWorkflow = agentSelection.selectedAgents.some((agent) => agent.id === COPILOT_AGENT_ID);
4894
5150
  }
4895
- selectedEditors = (options.editor || !options.interactive ? void 0 : detectExistingEditors(workspaceInfoOptional.rootDir)) ?? await selectEditors({
5151
+ const shouldConfigureEditors = shouldConfigureEditorsForCreate({
5152
+ editor: options.editor,
5153
+ isMonorepo
5154
+ });
5155
+ if (shouldConfigureEditors) selectedEditors = (options.editor || !options.interactive ? void 0 : detectExistingEditors(workspaceInfoOptional.rootDir)) ?? await selectEditors({
4896
5156
  interactive: options.interactive,
4897
5157
  editor: options.editor,
4898
5158
  onCancel: () => cancelAndExit()
4899
5159
  });
4900
- const shouldSetupGit = await promptGitInit(options);
5160
+ const shouldSetupGit = await resolveGitInit(options, isMonorepo);
4901
5161
  if (!isMonorepo) shouldSetupHooks = await promptGitHooks(options);
4902
5162
  const createProgress = options.interactive && compactOutput ? spinner({ indicator: "timer" }) : void 0;
4903
5163
  let createProgressStarted = false;
@@ -4936,8 +5196,28 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
4936
5196
  createProgressStarted = true;
4937
5197
  }
4938
5198
  };
5199
+ let migratePendingBuilds = [];
5200
+ const handleIgnoredBuilds = async (projectPath, installCwd, summary) => {
5201
+ if (summary?.status !== "installed") return;
5202
+ const reportedBuilds = [...new Set([...summary.pendingBuilds ?? [], ...migratePendingBuilds])];
5203
+ const targets = resolveApproveBuildTargets(projectPath, await detectGatedBuilds(installCwd, workspaceInfo.packageManager, reportedBuilds), workspaceInfo.packageManager);
5204
+ if (targets.length === 0) return;
5205
+ pauseCreateProgress();
5206
+ const approved = await approveBuilds({
5207
+ cwd: installCwd,
5208
+ projectDir: projectPath,
5209
+ packageManager: workspaceInfo.packageManager,
5210
+ packageManagerVersion: workspaceInfo.downloadPackageManager.version,
5211
+ targets,
5212
+ interactive: options.interactive,
5213
+ autoApprove: options.approveBuilds === true,
5214
+ silent: compactOutput
5215
+ });
5216
+ resumeCreateProgress();
5217
+ if (!approved && !options.interactive && options.approveBuilds === true) process.exitCode = 1;
5218
+ };
4939
5219
  updateCreateProgress("Scaffolding project");
4940
- const templateInfo = discoverTemplate(selectedTemplateName, selectedTemplateArgs, workspaceInfo, options.interactive, bundled?.bundledLocalPath, skipShorthandExpansion);
5220
+ const templateInfo = discoverTemplate(selectedTemplateName, selectedTemplateArgs, workspaceInfo, options.interactive, bundled?.bundledLocalPath, skipShorthandExpansion, isLocalTemplate);
4941
5221
  if (selectedParentDir) templateInfo.parentDir = selectedParentDir;
4942
5222
  if (targetDir) templateInfo.parentDir = void 0;
4943
5223
  if (remoteTargetDir) {
@@ -4948,14 +5228,14 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
4948
5228
  }
4949
5229
  if (templateInfo.command === BuiltinTemplate.monorepo || isBundledMonorepo) {
4950
5230
  let shouldInitGit = shouldSetupGit;
4951
- if (options.interactive && !compactOutput) {
5231
+ if (options.interactive && !compactOutput && options.git === void 0) {
4952
5232
  pauseCreateProgress();
4953
5233
  const selected = await confirm({
4954
5234
  message: "Initialize git repository:",
4955
5235
  initialValue: true
4956
5236
  });
4957
5237
  resumeCreateProgress();
4958
- if (q(selected)) {
5238
+ if (R(selected)) {
4959
5239
  log.info("Operation cancelled. Skipping git initialization");
4960
5240
  shouldInitGit = false;
4961
5241
  } else shouldInitGit = selected;
@@ -5022,18 +5302,24 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
5022
5302
  await initGitRepository(fullPath);
5023
5303
  }
5024
5304
  if (bundled?.monorepo) injectCreateDefaultTemplate(fullPath, bundled.scope, compactOutput);
5025
- if (shouldSetupHooks) installGitHooks(fullPath, compactOutput);
5305
+ if (shouldSetupHooks) installGitHooks(fullPath, compactOutput, void 0, workspaceInfo.packageManager);
5026
5306
  updateCreateProgress("Installing dependencies");
5027
5307
  const installSummary = await runViteInstall(fullPath, options.interactive, installArgs, {
5028
5308
  silent: compactOutput,
5029
5309
  packageManager: workspaceInfo.packageManager,
5030
- packageManagerVersion: workspaceInfo.downloadPackageManager.version
5310
+ packageManagerVersion: workspaceInfo.downloadPackageManager.version,
5311
+ detectIgnoredBuilds: true
5031
5312
  });
5313
+ await handleIgnoredBuilds(fullPath, fullPath, installSummary);
5032
5314
  updateCreateProgress("Formatting code");
5033
5315
  await runViteFmt(fullPath, options.interactive, void 0, { silent: compactOutput });
5034
5316
  if (shouldSetupGit) {
5035
5317
  updateCreateProgress("Creating initial commit");
5036
- if (!await createInitialCommit(fullPath)) log.warn("Initial commit failed. Check your git user.name/user.email config");
5318
+ const commitResult = await createInitialCommit(fullPath);
5319
+ if (!commitResult.success) {
5320
+ log.warn("Initial commit failed");
5321
+ if (commitResult.output) log.info(commitResult.output);
5322
+ }
5037
5323
  }
5038
5324
  clearCreateProgress();
5039
5325
  showCreateSummary({
@@ -5089,6 +5375,24 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
5089
5375
  process.exit(0);
5090
5376
  }
5091
5377
  const fullPath = path.join(workspaceInfo.rootDir, projectDir);
5378
+ if (selectedTemplateName === BuiltinTemplate.generator && isMonorepo) {
5379
+ updateCreateProgress("Registering generator");
5380
+ pauseCreateProgress();
5381
+ const generatorTemplatePath = `./${projectDir.split(path.sep).join("/")}`;
5382
+ let generatorName = packageName;
5383
+ try {
5384
+ const generatorPkg = readJsonFile(path.join(fullPath, "package.json"));
5385
+ generatorName = generatorPkg.name ?? packageName;
5386
+ if (generatorName) registeredConfigPath = await registerLocalTemplate(workspaceInfo.rootDir, {
5387
+ name: generatorName,
5388
+ description: generatorPkg.description || `Run the ${generatorName} generator`,
5389
+ template: generatorTemplatePath
5390
+ }, compactOutput);
5391
+ } catch (error) {
5392
+ log.warn(`Could not register the generator in create.templates (${error.message}).\nAdd it by hand: { name: '${generatorName || path.basename(projectDir)}', template: '${generatorTemplatePath}' }`);
5393
+ }
5394
+ resumeCreateProgress();
5395
+ }
5092
5396
  const agentInstructionsRoot = isMonorepo ? workspaceInfo.rootDir : fullPath;
5093
5397
  updateCreateProgress("Writing agent instructions");
5094
5398
  pauseCreateProgress();
@@ -5103,17 +5407,19 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
5103
5407
  silent: compactOutput
5104
5408
  });
5105
5409
  resumeCreateProgress();
5106
- updateCreateProgress("Writing editor configs");
5107
- pauseCreateProgress();
5108
- await writeEditorConfigs({
5109
- projectRoot: fullPath,
5110
- editorId: selectedEditors,
5111
- interactive: options.interactive,
5112
- silent: compactOutput,
5113
- extraVsCodeSettings: { "npm.scriptRunner": "vp" }
5114
- });
5115
- if (selectedEditors?.includes("vscode")) ensureGitignoreVsCodeEditorConfigs(fullPath);
5116
- resumeCreateProgress();
5410
+ if (shouldConfigureEditors) {
5411
+ updateCreateProgress("Writing editor configs");
5412
+ pauseCreateProgress();
5413
+ await writeEditorConfigs({
5414
+ projectRoot: fullPath,
5415
+ editorId: selectedEditors,
5416
+ interactive: options.interactive,
5417
+ silent: compactOutput,
5418
+ extraVsCodeSettings: { "npm.scriptRunner": "vp" }
5419
+ });
5420
+ if (selectedEditors?.includes("vscode")) ensureGitignoreVsCodeEditorConfigs(fullPath);
5421
+ resumeCreateProgress();
5422
+ }
5117
5423
  const shouldMigrateLintFmtTools = detectEslintProject(fullPath).hasDependency || detectPrettierProject(fullPath).hasDependency;
5118
5424
  let installSummary;
5119
5425
  const installAndMigrate = async (installCwd) => {
@@ -5126,9 +5432,11 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
5126
5432
  installSummary = await runViteInstall(installCwd, options.interactive, installArgs, {
5127
5433
  silent: compactOutput,
5128
5434
  packageManager: workspaceInfo.packageManager,
5129
- packageManagerVersion: workspaceInfo.downloadPackageManager.version
5435
+ packageManagerVersion: workspaceInfo.downloadPackageManager.version,
5436
+ detectIgnoredBuilds: true
5130
5437
  });
5131
5438
  if (installSummary.status !== "installed") return;
5439
+ if (installSummary.pendingBuilds && installSummary.pendingBuilds.length > 0) migratePendingBuilds = installSummary.pendingBuilds;
5132
5440
  updateCreateProgress("Migrating lint and format tools");
5133
5441
  pauseCreateProgress();
5134
5442
  await promptEslintMigration(fullPath, false);
@@ -5151,7 +5459,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
5151
5459
  required: false
5152
5460
  });
5153
5461
  let selectedDepTypes = [];
5154
- if (!q(selectedDepTypeOptions)) selectedDepTypes = selectedDepTypeOptions;
5462
+ if (!R(selectedDepTypeOptions)) selectedDepTypes = selectedDepTypeOptions;
5155
5463
  for (const selectedDepType of selectedDepTypes) {
5156
5464
  const selected = await multiselect({
5157
5465
  message: `Which packages should be added as ${selectedDepType} to ${success(projectDir)}?`,
@@ -5162,7 +5470,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
5162
5470
  required: false
5163
5471
  });
5164
5472
  let selectedDeps = [];
5165
- if (!q(selected)) selectedDeps = selected;
5473
+ if (!R(selected)) selectedDeps = selected;
5166
5474
  if (selectedDeps.length > 0) updatePackageJsonWithDeps(workspaceInfo.rootDir, projectDir, selectedDeps, selectedDepType);
5167
5475
  }
5168
5476
  resumeCreateProgress();
@@ -5177,15 +5485,13 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
5177
5485
  installSummary = await runViteInstall(workspaceInfo.rootDir, options.interactive, installArgs, {
5178
5486
  silent: compactOutput,
5179
5487
  packageManager: workspaceInfo.packageManager,
5180
- packageManagerVersion: workspaceInfo.downloadPackageManager.version
5488
+ packageManagerVersion: workspaceInfo.downloadPackageManager.version,
5489
+ detectIgnoredBuilds: true
5181
5490
  });
5491
+ await handleIgnoredBuilds(fullPath, workspaceInfo.rootDir, installSummary);
5182
5492
  updateCreateProgress("Formatting code");
5183
- await runViteFmt(workspaceInfo.rootDir, options.interactive, [projectDir], { silent: compactOutput });
5184
- if (shouldSetupGit) {
5185
- updateCreateProgress("Creating initial commit");
5186
- await initGitRepository(workspaceInfo.rootDir);
5187
- await createInitialCommit(workspaceInfo.rootDir);
5188
- }
5493
+ const fmtPaths = registeredConfigPath ? [projectDir, path.relative(workspaceInfo.rootDir, registeredConfigPath)] : [projectDir];
5494
+ await runViteFmt(workspaceInfo.rootDir, options.interactive, fmtPaths, { silent: compactOutput });
5189
5495
  } else {
5190
5496
  if (shouldMigrateLintFmtTools) await installAndMigrate(fullPath);
5191
5497
  updateCreateProgress("Applying Vite+ project setup");
@@ -5195,18 +5501,24 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
5195
5501
  updateCreateProgress("Initializing git repository");
5196
5502
  await initGitRepository(fullPath);
5197
5503
  }
5198
- if (shouldSetupHooks) installGitHooks(fullPath, compactOutput);
5504
+ if (shouldSetupHooks) installGitHooks(fullPath, compactOutput, void 0, workspaceInfo.packageManager);
5199
5505
  updateCreateProgress("Installing dependencies");
5200
5506
  installSummary = await runViteInstall(fullPath, options.interactive, installArgs, {
5201
5507
  silent: compactOutput,
5202
5508
  packageManager: workspaceInfo.packageManager,
5203
- packageManagerVersion: workspaceInfo.downloadPackageManager.version
5509
+ packageManagerVersion: workspaceInfo.downloadPackageManager.version,
5510
+ detectIgnoredBuilds: true
5204
5511
  });
5512
+ await handleIgnoredBuilds(fullPath, fullPath, installSummary);
5205
5513
  updateCreateProgress("Formatting code");
5206
5514
  await runViteFmt(fullPath, options.interactive, void 0, { silent: compactOutput });
5207
5515
  if (shouldSetupGit) {
5208
5516
  updateCreateProgress("Creating initial commit");
5209
- if (!await createInitialCommit(fullPath)) log.warn("Initial commit failed. Check your git user.name/user.email config");
5517
+ const commitResult = await createInitialCommit(fullPath);
5518
+ if (!commitResult.success) {
5519
+ log.warn("Initial commit failed");
5520
+ if (commitResult.output) log.info(commitResult.output);
5521
+ }
5210
5522
  }
5211
5523
  }
5212
5524
  clearCreateProgress();