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.
- package/LICENSE +30 -0
- package/README.md +8 -6
- package/bin/oxfmt +5 -2
- package/bin/oxlint +12 -2
- package/binding/index.cjs +84 -67
- package/binding/index.d.cts +155 -4
- package/dist/{agent-aSGY0osq.js → agent-BD31CsvU.js} +969 -89
- package/dist/bin.js +29 -33
- package/dist/{compat-DXZgnEyq.js → compat-Cql3K40m.js} +1 -1
- package/dist/config/bin.js +30 -14
- package/dist/constants-CrfJQIUX.js +66 -0
- package/dist/create/bin.d.ts +7 -1
- package/dist/create/bin.js +578 -266
- package/dist/define-config-2tfJoXr1.d.ts +305 -0
- package/dist/define-config-BGSjF6Xp.cjs +488 -0
- package/dist/define-config-DJUehepE.js +445 -0
- package/dist/define-config.cjs +8 -1
- package/dist/define-config.d.ts +2 -2
- package/dist/define-config.js +2 -2
- package/dist/dist-DRJUd9bL.js +3 -0
- package/dist/{dist-BgQuvbtq.js → dist-Oxo16Y0q.js} +4 -4
- package/dist/index.cjs +9 -4
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/{main-DpJl3LoU.js → json-Dn87fvjk.js} +137 -1
- package/dist/migration/bin.js +301 -84
- package/dist/{oxlint-plugin-config-B89iKTKN.js → oxlint-plugin-config-q8a5PFch.js} +1 -1
- package/dist/oxlint-plugin.js +11 -3
- package/dist/pack-bin.js +44 -15
- package/dist/{package-PmBUZ-ve.js → package-BHirM1_v.js} +3 -138
- package/dist/{report-DgSBQUdz.js → report-BHSkWqRR.js} +2 -0
- package/dist/{resolve-vite-config-TTvhycU1.js → resolve-vite-config-CmdsfQzS.js} +13 -4
- package/dist/staged/bin.js +150 -417
- package/dist/test/_at-vitest-browser/context.d.ts +2 -0
- package/dist/test/_at-vitest-browser.d.ts +2 -0
- package/dist/test/browser/context.d.ts +2 -2
- package/dist/test/browser/context.js +1 -1
- package/dist/test/browser/providers/playwright/context.d.ts +1 -0
- package/dist/test/browser/providers/playwright/context.js +1 -0
- package/dist/test/browser/providers/playwright.d.ts +124 -2
- package/dist/test/browser/providers/playwright.js +1 -1
- package/dist/test/browser/providers/preview/context.d.ts +1 -0
- package/dist/test/browser/providers/preview/context.js +1 -0
- package/dist/test/browser/providers/preview.d.ts +32 -2
- package/dist/test/browser/providers/preview.js +1 -1
- package/dist/test/browser/providers/webdriverio/context.d.ts +1 -0
- package/dist/test/browser/providers/webdriverio/context.js +1 -0
- package/dist/test/browser/providers/webdriverio.d.ts +77 -2
- package/dist/test/browser/providers/webdriverio.js +1 -1
- package/dist/test/browser-compat.d.ts +2 -0
- package/dist/test/browser-compat.js +1 -1
- package/dist/test/browser-playwright/context.d.ts +1 -0
- package/dist/test/browser-playwright/context.js +1 -0
- package/dist/test/browser-playwright.d.ts +124 -2
- package/dist/test/browser-playwright.js +1 -1
- package/dist/test/browser-preview/context.d.ts +1 -0
- package/dist/test/browser-preview/context.js +1 -0
- package/dist/test/browser-preview.d.ts +32 -2
- package/dist/test/browser-preview.js +1 -1
- package/dist/test/browser-webdriverio/context.d.ts +1 -0
- package/dist/test/browser-webdriverio/context.js +1 -0
- package/dist/test/browser-webdriverio.d.ts +77 -2
- package/dist/test/browser-webdriverio.js +1 -1
- package/dist/test/browser.d.ts +2 -2
- package/dist/test/browser.js +1 -1
- package/dist/test/client.js +1 -1
- package/dist/test/config.cjs +1 -1
- package/dist/test/config.d.ts +2 -2
- package/dist/test/config.js +1 -1
- package/dist/test/context.d.ts +942 -2
- package/dist/test/context.js +1 -1
- package/dist/test/coverage.d.ts +2 -2
- package/dist/test/coverage.js +1 -1
- package/dist/test/environments.d.ts +2 -2
- package/dist/test/environments.js +1 -1
- package/dist/test/globals.d.ts +2 -2
- package/dist/test/import-meta.d.ts +2 -2
- package/dist/test/importMeta.d.ts +2 -2
- package/dist/test/index.cjs +1 -1
- package/dist/test/index.d.cts +2 -2
- package/dist/test/index.d.ts +2 -2
- package/dist/test/index.js +1 -1
- package/dist/test/internal/browser.d.ts +2 -2
- package/dist/test/internal/browser.js +1 -1
- package/dist/test/jsdom.d.ts +2 -2
- package/dist/test/locators.d.ts +294 -0
- package/dist/test/locators.js +1 -1
- package/dist/test/matchers.d.ts +29 -0
- package/dist/test/matchers.js +1 -1
- package/dist/test/node.d.ts +2 -2
- package/dist/test/node.js +1 -1
- package/dist/test/optional-runtime-types.js.d.ts +2 -2
- package/dist/test/optional-types.js.d.ts +2 -2
- package/dist/test/plugins/browser-client.js +1 -1
- package/dist/test/plugins/browser-context.js +1 -1
- package/dist/test/plugins/browser-locators.js +1 -1
- package/dist/test/plugins/browser-playwright.js +1 -1
- package/dist/test/plugins/browser-preview.js +1 -1
- package/dist/test/plugins/browser-webdriverio.js +1 -1
- package/dist/test/plugins/browser.js +1 -1
- package/dist/test/plugins/expect.js +1 -1
- package/dist/test/plugins/mocker-automock.js +1 -1
- package/dist/test/plugins/mocker-browser.js +1 -1
- package/dist/test/plugins/mocker-node.js +1 -1
- package/dist/test/plugins/mocker-redirect.js +1 -1
- package/dist/test/plugins/mocker-register.js +1 -1
- package/dist/test/plugins/mocker-transforms.js +1 -1
- package/dist/test/plugins/mocker.js +1 -1
- package/dist/test/plugins/pretty-format.js +1 -1
- package/dist/test/plugins/runner-types.js +1 -1
- package/dist/test/plugins/runner-utils.js +1 -1
- package/dist/test/plugins/runner.js +1 -1
- package/dist/test/plugins/snapshot-environment.js +1 -1
- package/dist/test/plugins/snapshot-manager.js +1 -1
- package/dist/test/plugins/snapshot.js +1 -1
- package/dist/test/plugins/spy.js +1 -1
- package/dist/test/plugins/utils-constants.js +1 -1
- package/dist/test/plugins/utils-diff.js +1 -1
- package/dist/test/plugins/utils-display.js +1 -1
- package/dist/test/plugins/utils-error.js +1 -1
- package/dist/test/plugins/utils-helpers.js +1 -1
- package/dist/test/plugins/utils-offset.js +1 -1
- package/dist/test/plugins/utils-resolver.js +1 -1
- package/dist/test/plugins/utils-serialize.js +1 -1
- package/dist/test/plugins/utils-source-map-node.js +1 -1
- package/dist/test/plugins/utils-source-map.js +1 -1
- package/dist/test/plugins/utils-timers.js +1 -1
- package/dist/test/plugins/utils.js +1 -1
- package/dist/test/reporters.d.ts +2 -2
- package/dist/test/reporters.js +1 -1
- package/dist/test/runners.d.ts +2 -2
- package/dist/test/runners.js +1 -1
- package/dist/test/runtime.d.ts +2 -2
- package/dist/test/runtime.js +1 -1
- package/dist/test/snapshot.d.ts +2 -2
- package/dist/test/snapshot.js +1 -1
- package/dist/test/suite.d.ts +2 -2
- package/dist/test/suite.js +1 -1
- package/dist/test/utils.js +1 -1
- package/dist/test/worker.d.ts +2 -2
- package/dist/test/worker.js +1 -1
- package/dist/{tsconfig-DlUVXT3J.js → tsconfig-CJ_StdFc.js} +605 -263
- package/dist/tsgolint-path-B-yOos8p.js +32 -0
- package/dist/tsgolint-path.d.ts +8 -0
- package/dist/tsgolint-path.js +2 -0
- package/dist/version.js +3 -3
- package/dist/versions.d.ts +1 -1
- package/dist/versions.js +7 -7
- package/dist/{workspace-DElv730L.js → workspace-Cjoc1c_A.js} +20 -18
- package/docs/_data/team.ts +5 -4
- package/docs/config/create.md +36 -1
- package/docs/config/index.md +7 -5
- package/docs/guide/commit-hooks.md +9 -0
- package/docs/guide/create.md +106 -2
- package/docs/guide/env.md +33 -5
- package/docs/guide/index.md +9 -3
- package/docs/guide/install.md +46 -10
- package/docs/guide/migrate.md +13 -3
- package/docs/guide/troubleshooting.md +3 -29
- package/docs/guide/upgrade.md +36 -6
- package/docs/package.json +3 -3
- package/docs/pnpm-lock.yaml +298 -395
- package/package.json +104 -56
- package/templates/generator/bin/index.ts +6 -3
- package/templates/generator/package.json +2 -3
- package/templates/generator/src/template.ts +0 -2
- package/templates/monorepo/package.json +1 -1
- package/dist/constants-DCBWlNrn.js +0 -33
- package/dist/define-config-BR1Y88zz.cjs +0 -84
- package/dist/define-config-BRC7qPNE.js +0 -21
- package/dist/define-config-COdn-tsn.d.ts +0 -177
- package/dist/dist-Bapm49IR.js +0 -3
- package/dist/test/plugins/utils-highlight.js +0 -1
- /package/dist/{chunk-DnnnRqeS.js → rolldown-runtime-DnnnRqeS.js} +0 -0
package/dist/create/bin.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { r as __toESM, t as __commonJSMin } from "../
|
|
2
|
-
import {
|
|
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 {
|
|
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 {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
-
|
|
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
|
-
})
|
|
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
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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 (
|
|
431
|
+
if (isBingoTemplate(pkg)) {
|
|
198
432
|
type = TemplateType.bingo;
|
|
199
433
|
args.push("--skip-requests");
|
|
200
434
|
}
|
|
201
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
-
*
|
|
4129
|
-
*
|
|
4130
|
-
*
|
|
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
|
-
*
|
|
4135
|
-
*
|
|
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
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
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 (!
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
4745
|
-
|
|
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
|
-
|
|
4758
|
-
else if (resolved.kind === "
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
5184
|
-
|
|
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
|
-
|
|
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();
|