vite-plus 0.1.20-alpha.3 → 0.1.20
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 +0 -28
- package/README.md +0 -10
- package/dist/{agent-C8Me6C5u.js → agent-D_WSpD0r.js} +2 -14
- package/dist/bin.js +1 -1
- package/dist/config/bin.js +1 -1
- package/dist/create/bin.js +57 -847
- package/dist/{define-config-bKSulJaG.d.ts → define-config-hLuWEqIf.d.ts} +0 -14
- package/dist/define-config.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/migration/bin.js +3 -3
- package/dist/pack-bin.js +1 -1
- package/dist/{package-CrKanQYM.js → package-D_LD1iiI.js} +4 -135
- package/dist/{resolve-vite-config-C1KX9CZU.js → resolve-vite-config-B_w1u1j4.js} +1 -1
- package/dist/staged/bin.js +17 -17
- package/dist/version.js +1 -1
- package/dist/{workspace-Du9O0xar.js → workspace-DVOyShUK.js} +6 -2
- package/docs/guide/create.md +1 -148
- package/docs/guide/ide-integration.md +5 -1
- package/package.json +14 -15
- package/docs/config/create.md +0 -35
package/dist/create/bin.js
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "../chunk-q7NCDQ7-.js";
|
|
2
2
|
import { a as require_cross_spawn } from "../tsconfig-DQTf06oN.js";
|
|
3
3
|
import { a as printHeader, i as muted, o as success, r as log, t as accent } from "../terminal-CxTMfsxZ.js";
|
|
4
|
-
import {
|
|
5
|
-
import { $ as multiselect, A as setPackageManager, B as runViteFmt, D as rewriteMonorepo, E as promptPrettierMigration, G as templatesDir, H as selectPackageManager, K as DependencyType, L as defaultInteractive, O as rewriteMonorepoProject, Q as log$1, R as downloadPackageManager$1, T as promptEslintMigration, V as runViteInstall, W as displayRelative, X as confirm, Y as cancel, Z as intro, a as writeAgentInstructions, at as q, d as detectEslintProject, f as detectFramework, g as hasFrameworkShim, it as require_picocolors, k as rewriteStandaloneProject, m as detectPrettierProject, n as detectExistingAgentTargetPaths, nt as spinner, o as addFrameworkShim, q as PackageManager, r as selectAgentTargetPaths, rt as text, tt as select, v as injectCreateDefaultTemplate, y as installGitHooks, z as promptGitHooks } from "../agent-C8Me6C5u.js";
|
|
4
|
+
import { B as runViteInstall, D as rewriteMonorepoProject, E as rewriteMonorepo, G as DependencyType, I as defaultInteractive, J as cancel, K as PackageManager, L as downloadPackageManager$1, O as rewriteStandaloneProject, Q as multiselect, R as promptGitHooks, T as promptPrettierMigration, U as displayRelative, V as selectPackageManager, W as templatesDir, X as intro, Y as confirm, Z as log$1, a as writeAgentInstructions, d as detectEslintProject, et as select, f as detectFramework, g as hasFrameworkShim, it as q, k as setPackageManager, m as detectPrettierProject, n as detectExistingAgentTargetPaths, nt as text, o as addFrameworkShim, r as selectAgentTargetPaths, rt as require_picocolors, tt as spinner, v as installGitHooks, w as promptEslintMigration, z as runViteFmt } from "../agent-D_WSpD0r.js";
|
|
6
5
|
import { t as lib_default } from "../lib-BamM40b7.js";
|
|
7
|
-
import { c as
|
|
8
|
-
import { a as detectExistingEditors, c as writeEditorConfigs, n as updatePackageJsonWithDeps, r as updateWorkspaceConfig, s as selectEditors, t as detectWorkspace$1 } from "../workspace-
|
|
6
|
+
import { c as readJsonFile, o as editJsonFile, t as checkNpmPackageExists } from "../package-D_LD1iiI.js";
|
|
7
|
+
import { a as detectExistingEditors, c as writeEditorConfigs, n as updatePackageJsonWithDeps, r as updateWorkspaceConfig, s as selectEditors, t as detectWorkspace$1 } from "../workspace-DVOyShUK.js";
|
|
9
8
|
import { t as renderCliDoc } from "../help-BtkjXtRM.js";
|
|
10
9
|
import path from "node:path";
|
|
11
10
|
import { runCommand, vitePlusHeader } from "../../binding/index.js";
|
|
12
11
|
import fs from "node:fs";
|
|
13
12
|
import { styleText } from "node:util";
|
|
14
|
-
import os from "node:os";
|
|
15
13
|
import fsPromises from "node:fs/promises";
|
|
16
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
17
14
|
import assert from "node:assert";
|
|
18
15
|
//#region src/create/command.ts
|
|
19
16
|
var import_cross_spawn = /* @__PURE__ */ __toESM(require_cross_spawn(), 1);
|
|
@@ -141,8 +138,7 @@ const BuiltinTemplate = {
|
|
|
141
138
|
const TemplateType = {
|
|
142
139
|
builtin: "builtin",
|
|
143
140
|
bingo: "bingo",
|
|
144
|
-
remote: "remote"
|
|
145
|
-
bundled: "bundled"
|
|
141
|
+
remote: "remote"
|
|
146
142
|
};
|
|
147
143
|
//#endregion
|
|
148
144
|
//#region src/create/discovery.ts
|
|
@@ -160,18 +156,9 @@ function inferGitHubRepoName(templateName) {
|
|
|
160
156
|
if (!degitPath) return null;
|
|
161
157
|
return degitPath.split("/").pop() || null;
|
|
162
158
|
}
|
|
163
|
-
function discoverTemplate(templateName, templateArgs, workspaceInfo, interactive
|
|
159
|
+
function discoverTemplate(templateName, templateArgs, workspaceInfo, interactive) {
|
|
164
160
|
const envs = prependToPathToEnvs(workspaceInfo.downloadPackageManager.binPrefix, { ...process.env });
|
|
165
161
|
const parentDir = inferParentDir(templateName, workspaceInfo);
|
|
166
|
-
if (bundledLocalPath) return {
|
|
167
|
-
command: "",
|
|
168
|
-
args: [...templateArgs],
|
|
169
|
-
envs,
|
|
170
|
-
type: TemplateType.bundled,
|
|
171
|
-
parentDir,
|
|
172
|
-
interactive,
|
|
173
|
-
localPath: bundledLocalPath
|
|
174
|
-
};
|
|
175
162
|
if (templateName.startsWith("vite:")) return {
|
|
176
163
|
command: templateName,
|
|
177
164
|
args: [...templateArgs],
|
|
@@ -217,7 +204,7 @@ function discoverTemplate(templateName, templateArgs, workspaceInfo, interactive
|
|
|
217
204
|
};
|
|
218
205
|
}
|
|
219
206
|
return {
|
|
220
|
-
command:
|
|
207
|
+
command: expandCreateShorthand(templateName),
|
|
221
208
|
args: [...templateArgs],
|
|
222
209
|
envs,
|
|
223
210
|
type: TemplateType.remote,
|
|
@@ -299,591 +286,6 @@ function getInitialTemplateOptions(isMonorepo) {
|
|
|
299
286
|
];
|
|
300
287
|
}
|
|
301
288
|
//#endregion
|
|
302
|
-
//#region src/create/org-manifest.ts
|
|
303
|
-
/**
|
|
304
|
-
* Parse the org picker specifier: `@scope` (scope only → picker) or
|
|
305
|
-
* `@scope:name` (direct manifest-entry selection). Colon mirrors the
|
|
306
|
-
* existing `vite:monorepo` / `vite:library` builtin-template syntax and
|
|
307
|
-
* keeps manifest entries syntactically distinct from real
|
|
308
|
-
* `@scope/package-name` npm specifiers.
|
|
309
|
-
*
|
|
310
|
-
* Returns `null` for anything else — including the plain `@scope/name`
|
|
311
|
-
* form, which routes to the existing `@scope/create-name` shorthand as
|
|
312
|
-
* it did before the org-manifest feature.
|
|
313
|
-
*
|
|
314
|
-
* The optional `version` suffix (`@scope@1.2.3`, `@scope:name@1.2.3`)
|
|
315
|
-
* pins `@scope/create` to a specific release rather than `dist-tags.latest`.
|
|
316
|
-
*/
|
|
317
|
-
function parseOrgScopedSpec(spec) {
|
|
318
|
-
if (!spec.startsWith("@")) return null;
|
|
319
|
-
if (spec.includes("/")) return null;
|
|
320
|
-
const colonIndex = spec.indexOf(":");
|
|
321
|
-
if (colonIndex === -1) {
|
|
322
|
-
const atIndex = spec.indexOf("@", 1);
|
|
323
|
-
if (atIndex === -1) return { scope: spec };
|
|
324
|
-
const version = spec.slice(atIndex + 1);
|
|
325
|
-
return version ? {
|
|
326
|
-
scope: spec.slice(0, atIndex),
|
|
327
|
-
version
|
|
328
|
-
} : { scope: spec.slice(0, atIndex) };
|
|
329
|
-
}
|
|
330
|
-
const scope = spec.slice(0, colonIndex);
|
|
331
|
-
const rest = spec.slice(colonIndex + 1);
|
|
332
|
-
const atIndex = rest.indexOf("@");
|
|
333
|
-
const name = atIndex === -1 ? rest : rest.slice(0, atIndex);
|
|
334
|
-
const version = atIndex === -1 ? "" : rest.slice(atIndex + 1);
|
|
335
|
-
if (!name) return version ? {
|
|
336
|
-
scope,
|
|
337
|
-
version
|
|
338
|
-
} : { scope };
|
|
339
|
-
return version ? {
|
|
340
|
-
scope,
|
|
341
|
-
name,
|
|
342
|
-
version
|
|
343
|
-
} : {
|
|
344
|
-
scope,
|
|
345
|
-
name
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Schema-level failure. Never falls through silently — a maintainer who
|
|
350
|
-
* shipped an invalid manifest should see the offending field.
|
|
351
|
-
*/
|
|
352
|
-
var OrgManifestSchemaError = class extends Error {
|
|
353
|
-
constructor(message, packageName) {
|
|
354
|
-
super(`${packageName}: ${message}`);
|
|
355
|
-
this.packageName = packageName;
|
|
356
|
-
this.name = "OrgManifestSchemaError";
|
|
357
|
-
}
|
|
358
|
-
};
|
|
359
|
-
function isRelativePath(spec) {
|
|
360
|
-
return spec.startsWith("./") || spec.startsWith("../");
|
|
361
|
-
}
|
|
362
|
-
function validateEntry(entry, index, packageName) {
|
|
363
|
-
if (!entry || typeof entry !== "object") throw new OrgManifestSchemaError(`createConfig.templates[${index}] must be an object`, packageName);
|
|
364
|
-
const raw = entry;
|
|
365
|
-
const requireString = (field) => {
|
|
366
|
-
const value = raw[field];
|
|
367
|
-
if (typeof value !== "string" || value.length === 0) throw new OrgManifestSchemaError(`createConfig.templates[${index}].${field} must be a non-empty string`, packageName);
|
|
368
|
-
return value;
|
|
369
|
-
};
|
|
370
|
-
const name = requireString("name");
|
|
371
|
-
if (name.startsWith("__vp_")) throw new OrgManifestSchemaError(`createConfig.templates[${index}].name uses the reserved \`__vp_\` prefix`, packageName);
|
|
372
|
-
const description = requireString("description");
|
|
373
|
-
const template = requireString("template");
|
|
374
|
-
let monorepo;
|
|
375
|
-
if (raw.monorepo !== void 0) {
|
|
376
|
-
if (typeof raw.monorepo !== "boolean") throw new OrgManifestSchemaError(`createConfig.templates[${index}].monorepo must be a boolean`, packageName);
|
|
377
|
-
monorepo = raw.monorepo;
|
|
378
|
-
}
|
|
379
|
-
if (isRelativePath(template)) {
|
|
380
|
-
const resolved = path.posix.resolve("/root", template.replaceAll("\\", "/"));
|
|
381
|
-
if (resolved !== "/root" && !resolved.startsWith("/root/")) throw new OrgManifestSchemaError(`createConfig.templates[${index}].template escapes the package root: ${template}`, packageName);
|
|
382
|
-
}
|
|
383
|
-
return {
|
|
384
|
-
name,
|
|
385
|
-
description,
|
|
386
|
-
template,
|
|
387
|
-
...monorepo !== void 0 ? { monorepo } : {}
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
function validateManifest(raw, packageName) {
|
|
391
|
-
if (!raw || typeof raw !== "object") return null;
|
|
392
|
-
const createConfig = raw.createConfig;
|
|
393
|
-
if (!createConfig || typeof createConfig !== "object") return null;
|
|
394
|
-
const templates = createConfig.templates;
|
|
395
|
-
if (templates === void 0) return null;
|
|
396
|
-
if (!Array.isArray(templates)) throw new OrgManifestSchemaError("createConfig.templates must be an array", packageName);
|
|
397
|
-
if (templates.length === 0) return null;
|
|
398
|
-
const entries = [];
|
|
399
|
-
const seen = /* @__PURE__ */ new Set();
|
|
400
|
-
for (let index = 0; index < templates.length; index += 1) {
|
|
401
|
-
const entry = validateEntry(templates[index], index, packageName);
|
|
402
|
-
if (seen.has(entry.name)) throw new OrgManifestSchemaError(`createConfig.templates[${index}].name duplicates an earlier entry: "${entry.name}"`, packageName);
|
|
403
|
-
seen.add(entry.name);
|
|
404
|
-
entries.push(entry);
|
|
405
|
-
}
|
|
406
|
-
return entries;
|
|
407
|
-
}
|
|
408
|
-
async function fetchPackument(scope, packageName) {
|
|
409
|
-
const response = await fetchNpmResource(`${getNpmRegistry(scope)}/${packageName}`, {
|
|
410
|
-
headers: { accept: "application/json" },
|
|
411
|
-
timeoutMs: 5e3
|
|
412
|
-
});
|
|
413
|
-
if (response.status === 404) return null;
|
|
414
|
-
if (!response.ok) throw new Error(`npm registry responded with ${response.status} for ${packageName}`);
|
|
415
|
-
return await response.json();
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Fetch `@scope/create` from the npm registry and parse its `createConfig.templates`
|
|
419
|
-
* manifest.
|
|
420
|
-
*
|
|
421
|
-
* Returns `null` when:
|
|
422
|
-
* - the package does not exist on the registry (404), or
|
|
423
|
-
* - the package exists but has no `createConfig.templates` field
|
|
424
|
-
*
|
|
425
|
-
* Throws when:
|
|
426
|
-
* - the `createConfig.templates` field is present but malformed (`OrgManifestSchemaError`), or
|
|
427
|
-
* - the registry request fails for any non-404 reason
|
|
428
|
-
*
|
|
429
|
-
* `requestedVersion` pins the lookup to a specific `versions[...]` entry
|
|
430
|
-
* (equivalent to `vp create @scope@1.2.3`); omit it to resolve `dist-tags.latest`.
|
|
431
|
-
*/
|
|
432
|
-
async function readOrgManifest(scope, requestedVersion) {
|
|
433
|
-
if (!scope.startsWith("@")) return null;
|
|
434
|
-
const packageName = `${scope}/create`;
|
|
435
|
-
const packument = await fetchPackument(scope, packageName);
|
|
436
|
-
if (!packument) return null;
|
|
437
|
-
let resolvedVersion;
|
|
438
|
-
if (requestedVersion) {
|
|
439
|
-
resolvedVersion = packument["dist-tags"]?.[requestedVersion] ?? (packument.versions?.[requestedVersion] ? requestedVersion : void 0);
|
|
440
|
-
if (!resolvedVersion) throw new OrgManifestSchemaError(`version "${requestedVersion}" not found (known tags: ${Object.keys(packument["dist-tags"] ?? {}).join(", ") || "none"})`, packageName);
|
|
441
|
-
} else {
|
|
442
|
-
resolvedVersion = packument["dist-tags"]?.latest;
|
|
443
|
-
if (!resolvedVersion) return null;
|
|
444
|
-
}
|
|
445
|
-
const meta = packument.versions?.[resolvedVersion];
|
|
446
|
-
if (!meta) return null;
|
|
447
|
-
const templates = validateManifest(meta, packageName);
|
|
448
|
-
if (!templates) return null;
|
|
449
|
-
if (!meta.dist?.tarball) throw new OrgManifestSchemaError(`missing dist.tarball for ${resolvedVersion}`, packageName);
|
|
450
|
-
return {
|
|
451
|
-
scope,
|
|
452
|
-
packageName,
|
|
453
|
-
version: resolvedVersion,
|
|
454
|
-
tarballUrl: meta.dist.tarball,
|
|
455
|
-
integrity: meta.dist.integrity,
|
|
456
|
-
templates
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Apply the in-monorepo filter rule from the RFC: entries with
|
|
461
|
-
* `monorepo: true` are hidden when the command is invoked inside an
|
|
462
|
-
* existing monorepo, mirroring `initial-template-options.ts:9-31`.
|
|
463
|
-
*/
|
|
464
|
-
function filterManifestForContext(templates, isMonorepo) {
|
|
465
|
-
if (!isMonorepo) return [...templates];
|
|
466
|
-
return templates.filter((entry) => !entry.monorepo);
|
|
467
|
-
}
|
|
468
|
-
//#endregion
|
|
469
|
-
//#region src/create/org-picker.ts
|
|
470
|
-
const ORG_PICKER_CANCEL = Symbol("org-picker-cancel");
|
|
471
|
-
const ORG_PICKER_BUILTIN_ESCAPE = Symbol("org-picker-builtin-escape");
|
|
472
|
-
const ESCAPE_HATCH = Symbol("builtin-escape");
|
|
473
|
-
/**
|
|
474
|
-
* Render the interactive picker for an org manifest. Always appends a
|
|
475
|
-
* trailing "Vite+ built-in templates" escape-hatch entry.
|
|
476
|
-
*
|
|
477
|
-
* Context-filters entries with `monorepo: true` when running inside an
|
|
478
|
-
* existing monorepo, mirroring `initial-template-options.ts:9-31`.
|
|
479
|
-
*
|
|
480
|
-
* Returns `ORG_PICKER_BUILTIN_ESCAPE` when the escape hatch is selected,
|
|
481
|
-
* or `ORG_PICKER_CANCEL` when the user hits Ctrl-C.
|
|
482
|
-
*/
|
|
483
|
-
async function pickOrgTemplate(manifest, opts) {
|
|
484
|
-
const filtered = filterManifestForContext(manifest.templates, opts.isMonorepo);
|
|
485
|
-
if (filtered.length === 0) return ORG_PICKER_BUILTIN_ESCAPE;
|
|
486
|
-
const escapeValue = `__vp_builtin_escape__::${randomUUID()}`;
|
|
487
|
-
const lookup = /* @__PURE__ */ new Map();
|
|
488
|
-
const options = filtered.map((entry) => {
|
|
489
|
-
lookup.set(entry.name, entry);
|
|
490
|
-
return {
|
|
491
|
-
value: entry.name,
|
|
492
|
-
label: entry.name,
|
|
493
|
-
hint: entry.description
|
|
494
|
-
};
|
|
495
|
-
});
|
|
496
|
-
lookup.set(escapeValue, ESCAPE_HATCH);
|
|
497
|
-
const builtinHint = opts.isMonorepo ? "Use defaults (application / library)" : "Use defaults (monorepo / application / library)";
|
|
498
|
-
options.push({
|
|
499
|
-
value: escapeValue,
|
|
500
|
-
label: "Vite+ built-in templates",
|
|
501
|
-
hint: builtinHint
|
|
502
|
-
});
|
|
503
|
-
const picked = await select({
|
|
504
|
-
message: `Pick a template from ${manifest.scope}`,
|
|
505
|
-
options
|
|
506
|
-
});
|
|
507
|
-
if (q(picked)) return ORG_PICKER_CANCEL;
|
|
508
|
-
const found = lookup.get(picked);
|
|
509
|
-
if (found === ESCAPE_HATCH) return ORG_PICKER_BUILTIN_ESCAPE;
|
|
510
|
-
if (!found) throw new Error(`org-picker: prompts.select returned an unregistered value: ${picked}`);
|
|
511
|
-
return {
|
|
512
|
-
kind: "entry",
|
|
513
|
-
entry: found
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Render the manifest as a plain-text table for the `--no-interactive`
|
|
518
|
-
* error output. Fixed column order so AI agents and scripts can recover
|
|
519
|
-
* available template names without a `--json` flag.
|
|
520
|
-
*/
|
|
521
|
-
function formatManifestTable(manifest, isMonorepo) {
|
|
522
|
-
const visible = filterManifestForContext(manifest.templates, isMonorepo);
|
|
523
|
-
const filteredCount = manifest.templates.length - visible.length;
|
|
524
|
-
const nameWidth = Math.max(4, ...visible.map((entry) => entry.name.length));
|
|
525
|
-
const descWidth = Math.max(11, ...visible.map((entry) => entry.description.length));
|
|
526
|
-
const lines = [];
|
|
527
|
-
lines.push(` ${"NAME".padEnd(nameWidth)} ${"DESCRIPTION".padEnd(descWidth)} TEMPLATE`);
|
|
528
|
-
for (const entry of visible) lines.push(` ${entry.name.padEnd(nameWidth)} ${entry.description.padEnd(descWidth)} ${entry.template}`);
|
|
529
|
-
return {
|
|
530
|
-
lines,
|
|
531
|
-
filteredCount
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
//#endregion
|
|
535
|
-
//#region ../../node_modules/.pnpm/nanotar@0.3.0/node_modules/nanotar/dist/index.mjs
|
|
536
|
-
const tarItemTypeMap = {
|
|
537
|
-
"0": "file",
|
|
538
|
-
"1": "hardLink",
|
|
539
|
-
"2": "symbolicLink",
|
|
540
|
-
"3": "characterDevice",
|
|
541
|
-
"4": "blockDevice",
|
|
542
|
-
"5": "directory",
|
|
543
|
-
"6": "fifo",
|
|
544
|
-
"7": "contiguousFile",
|
|
545
|
-
"g": "globalExtendedHeader",
|
|
546
|
-
"x": "extendedHeader",
|
|
547
|
-
"D": "gnuDirectory",
|
|
548
|
-
"I": "gnuInodeMetadata",
|
|
549
|
-
"K": "gnuLongLinkName",
|
|
550
|
-
"L": "gnuLongFileName",
|
|
551
|
-
"N": "gnuOldLongFileName",
|
|
552
|
-
"M": "gnuMultiVolume",
|
|
553
|
-
"S": "gnuSparseFile",
|
|
554
|
-
"E": "gnuExtendedSparse",
|
|
555
|
-
"A": "solarisAcl",
|
|
556
|
-
"V": "solarisVolumeLabel",
|
|
557
|
-
"X": "solarisOldExtendedHeader"
|
|
558
|
-
};
|
|
559
|
-
function parseTar(data, opts) {
|
|
560
|
-
const buffer = data.buffer || data;
|
|
561
|
-
const files = [];
|
|
562
|
-
let offset = 0;
|
|
563
|
-
let nextExtendedHeader;
|
|
564
|
-
let globalExtendedHeader;
|
|
565
|
-
while (offset < buffer.byteLength - 512) {
|
|
566
|
-
let name = _readString(buffer, offset, 100);
|
|
567
|
-
if (name.length === 0) break;
|
|
568
|
-
if (nextExtendedHeader) {
|
|
569
|
-
const longName = nextExtendedHeader.path || nextExtendedHeader.linkpath;
|
|
570
|
-
if (longName) name = longName;
|
|
571
|
-
}
|
|
572
|
-
const mode = _readString(buffer, offset + 100, 8).trim();
|
|
573
|
-
const uid = Number.parseInt(_readString(buffer, offset + 108, 8));
|
|
574
|
-
const gid = Number.parseInt(_readString(buffer, offset + 116, 8));
|
|
575
|
-
const size = _readNumber(buffer, offset + 124, 12);
|
|
576
|
-
const seek = 512 + 512 * Math.trunc(size / 512) + (size % 512 ? 512 : 0);
|
|
577
|
-
const mtime = _readNumber(buffer, offset + 136, 12);
|
|
578
|
-
const _type = _readString(buffer, offset + 156, 1) || "0";
|
|
579
|
-
const type = tarItemTypeMap[_type] || _type;
|
|
580
|
-
switch (type) {
|
|
581
|
-
case "extendedHeader":
|
|
582
|
-
case "globalExtendedHeader": {
|
|
583
|
-
const headers = _parseExtendedHeaders(new Uint8Array(buffer, offset + 512, size));
|
|
584
|
-
if (type === "extendedHeader") nextExtendedHeader = headers;
|
|
585
|
-
else {
|
|
586
|
-
nextExtendedHeader = void 0;
|
|
587
|
-
globalExtendedHeader = {
|
|
588
|
-
...globalExtendedHeader,
|
|
589
|
-
...headers
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
offset += seek;
|
|
593
|
-
continue;
|
|
594
|
-
}
|
|
595
|
-
case "gnuLongFileName":
|
|
596
|
-
case "gnuOldLongFileName":
|
|
597
|
-
case "gnuLongLinkName":
|
|
598
|
-
nextExtendedHeader = { path: _readString(buffer, offset + 512, size) };
|
|
599
|
-
offset += seek;
|
|
600
|
-
continue;
|
|
601
|
-
}
|
|
602
|
-
const user = _readString(buffer, offset + 265, 32);
|
|
603
|
-
const group = _readString(buffer, offset + 297, 32);
|
|
604
|
-
name = _sanitizePath(name);
|
|
605
|
-
const meta = {
|
|
606
|
-
name,
|
|
607
|
-
type,
|
|
608
|
-
size,
|
|
609
|
-
attrs: {
|
|
610
|
-
...globalExtendedHeader,
|
|
611
|
-
...nextExtendedHeader,
|
|
612
|
-
mode,
|
|
613
|
-
uid,
|
|
614
|
-
gid,
|
|
615
|
-
mtime,
|
|
616
|
-
user,
|
|
617
|
-
group
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
nextExtendedHeader = void 0;
|
|
621
|
-
if (opts?.filter && !opts.filter(meta)) {
|
|
622
|
-
offset += seek;
|
|
623
|
-
continue;
|
|
624
|
-
}
|
|
625
|
-
if (opts?.metaOnly) {
|
|
626
|
-
files.push(meta);
|
|
627
|
-
offset += seek;
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
|
-
const data2 = size === 0 ? void 0 : new Uint8Array(buffer, offset + 512, size);
|
|
631
|
-
files.push({
|
|
632
|
-
...meta,
|
|
633
|
-
data: data2,
|
|
634
|
-
get text() {
|
|
635
|
-
return new TextDecoder().decode(this.data);
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
offset += seek;
|
|
639
|
-
}
|
|
640
|
-
return files;
|
|
641
|
-
}
|
|
642
|
-
async function parseTarGzip(data, opts = {}) {
|
|
643
|
-
const stream = new ReadableStream({ start(controller) {
|
|
644
|
-
controller.enqueue(new Uint8Array(data));
|
|
645
|
-
controller.close();
|
|
646
|
-
} }).pipeThrough(new DecompressionStream(opts.compression ?? "gzip"));
|
|
647
|
-
return parseTar(await new Response(stream).arrayBuffer(), opts);
|
|
648
|
-
}
|
|
649
|
-
function _sanitizePath(path) {
|
|
650
|
-
let normalized = path.replace(/\\/g, "/");
|
|
651
|
-
normalized = normalized.replace(/^[a-zA-Z]:\//, "");
|
|
652
|
-
normalized = normalized.replace(/^\/+/, "");
|
|
653
|
-
const hasLeadingDotSlash = normalized.startsWith("./");
|
|
654
|
-
const parts = normalized.split("/");
|
|
655
|
-
const resolved = [];
|
|
656
|
-
for (const part of parts) if (part === "..") resolved.pop();
|
|
657
|
-
else if (part !== "." && part !== "") resolved.push(part);
|
|
658
|
-
let result = resolved.join("/");
|
|
659
|
-
if (hasLeadingDotSlash && !result.startsWith("./")) result = "./" + result;
|
|
660
|
-
if (path.endsWith("/") && !result.endsWith("/")) result += "/";
|
|
661
|
-
return result;
|
|
662
|
-
}
|
|
663
|
-
function _readString(buffer, offset, size) {
|
|
664
|
-
const view = new Uint8Array(buffer, offset, size);
|
|
665
|
-
const i = view.indexOf(0);
|
|
666
|
-
return new TextDecoder().decode(i === -1 ? view : view.slice(0, i));
|
|
667
|
-
}
|
|
668
|
-
function _readNumber(buffer, offset, size) {
|
|
669
|
-
const view = new Uint8Array(buffer, offset, size);
|
|
670
|
-
let str = "";
|
|
671
|
-
for (let i = 0; i < size; i++) str += String.fromCodePoint(view[i]);
|
|
672
|
-
return Number.parseInt(str, 8);
|
|
673
|
-
}
|
|
674
|
-
function _parseExtendedHeaders(data) {
|
|
675
|
-
const dataStr = new TextDecoder().decode(data);
|
|
676
|
-
const headers = {};
|
|
677
|
-
for (const line of dataStr.split("\n")) {
|
|
678
|
-
const s = line.split(" ")[1]?.split("=");
|
|
679
|
-
if (s) headers[s[0]] = s[1];
|
|
680
|
-
}
|
|
681
|
-
return headers;
|
|
682
|
-
}
|
|
683
|
-
//#endregion
|
|
684
|
-
//#region src/create/org-tarball.ts
|
|
685
|
-
function getCacheRoot() {
|
|
686
|
-
const home = process.env.VP_HOME || path.join(os.homedir(), ".vite-plus");
|
|
687
|
-
return path.join(home, "tmp", "create-org");
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Replace characters that are illegal in Windows path segments
|
|
691
|
-
* (`\ / : * ? " < > |` plus the IPv6 bracket pair `[ ]`). The host
|
|
692
|
-
* comes from `new URL(...).host` which can carry a port (`:4873`) or
|
|
693
|
-
* IPv6 literal (`[::1]`); both end up in the cache path otherwise.
|
|
694
|
-
*/
|
|
695
|
-
function sanitizeHostForPath(host) {
|
|
696
|
-
return host.replaceAll(/[\\/:*?"<>|[\]]/g, "_");
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Cache extracted tarballs under `<host>/<scope>/create/<version>` so two
|
|
700
|
-
* repos resolving the same `<scope>@<version>` through different registries
|
|
701
|
-
* (via `.npmrc` scope mappings) don't share a cache slot. The registry
|
|
702
|
-
* guarantees `manifest.tarballUrl` is a valid URL, so any parse failure
|
|
703
|
-
* here is a real bug worth surfacing.
|
|
704
|
-
*/
|
|
705
|
-
function getExtractionDir(manifest) {
|
|
706
|
-
const { host } = new URL(manifest.tarballUrl);
|
|
707
|
-
return path.join(getCacheRoot(), sanitizeHostForPath(host), manifest.scope, "create", manifest.version);
|
|
708
|
-
}
|
|
709
|
-
function parseIntegrity(integrity) {
|
|
710
|
-
const match = integrity.split(/\s+/)[0].match(/^(sha\d+)-(.+)$/);
|
|
711
|
-
if (!match) return null;
|
|
712
|
-
return {
|
|
713
|
-
algorithm: match[1],
|
|
714
|
-
expected: match[2]
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
function verifyIntegrity(bytes, integrity) {
|
|
718
|
-
if (!integrity) return;
|
|
719
|
-
const parsed = parseIntegrity(integrity);
|
|
720
|
-
if (!parsed) return;
|
|
721
|
-
const hash = createHash(parsed.algorithm);
|
|
722
|
-
hash.update(bytes);
|
|
723
|
-
const actual = hash.digest("base64");
|
|
724
|
-
if (actual !== parsed.expected) throw new Error(`integrity check failed: expected ${integrity}, got ${parsed.algorithm}-${actual}`);
|
|
725
|
-
}
|
|
726
|
-
const MAX_TARBALL_BYTES = 50 * 1024 * 1024;
|
|
727
|
-
async function downloadTarball(url) {
|
|
728
|
-
const response = await fetchNpmResource(url, { timeoutMs: 3e4 });
|
|
729
|
-
if (!response.ok) throw new Error(`failed to download tarball (${response.status}): ${url}`);
|
|
730
|
-
const contentLength = Number(response.headers.get("content-length"));
|
|
731
|
-
if (Number.isFinite(contentLength) && contentLength > MAX_TARBALL_BYTES) throw new Error(`tarball exceeds ${MAX_TARBALL_BYTES} byte size limit: ${url}`);
|
|
732
|
-
const reader = response.body?.getReader();
|
|
733
|
-
if (!reader) throw new Error(`tarball response has no body: ${url}`);
|
|
734
|
-
const chunks = [];
|
|
735
|
-
let total = 0;
|
|
736
|
-
while (true) {
|
|
737
|
-
const { done, value } = await reader.read();
|
|
738
|
-
if (done) break;
|
|
739
|
-
total += value.byteLength;
|
|
740
|
-
if (total > MAX_TARBALL_BYTES) {
|
|
741
|
-
await reader.cancel();
|
|
742
|
-
throw new Error(`tarball exceeds ${MAX_TARBALL_BYTES} byte size limit: ${url}`);
|
|
743
|
-
}
|
|
744
|
-
chunks.push(value);
|
|
745
|
-
}
|
|
746
|
-
const bytes = new Uint8Array(total);
|
|
747
|
-
let offset = 0;
|
|
748
|
-
for (const chunk of chunks) {
|
|
749
|
-
bytes.set(chunk, offset);
|
|
750
|
-
offset += chunk.byteLength;
|
|
751
|
-
}
|
|
752
|
-
return bytes;
|
|
753
|
-
}
|
|
754
|
-
const STAGING_SUFFIX_PREFIX = ".tmp-";
|
|
755
|
-
/**
|
|
756
|
-
* Parse a tar entry's stored mode (always octal) into the numeric
|
|
757
|
-
* permission bits (low 9 bits — `rwxrwxrwx`). Returns `undefined` when
|
|
758
|
-
* the mode is missing or unparsable so the caller leaves the file with
|
|
759
|
-
* its default (umask-derived) permissions instead of downgrading.
|
|
760
|
-
*/
|
|
761
|
-
function parseEntryMode(raw) {
|
|
762
|
-
if (!raw) return;
|
|
763
|
-
const parsed = Number.parseInt(raw, 8);
|
|
764
|
-
if (!Number.isFinite(parsed)) return;
|
|
765
|
-
return parsed & 511;
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Strip the `package/` prefix from an `npm pack` tarball entry. Returns
|
|
769
|
-
* `null` for entries to skip (root dir, PaxHeader, anything outside
|
|
770
|
-
* `package/`).
|
|
771
|
-
*/
|
|
772
|
-
function normalizeEntryName(rawName) {
|
|
773
|
-
const name = rawName.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
774
|
-
if (!name || name === "package" || name === "package/") return null;
|
|
775
|
-
if (name.startsWith("PaxHeader/") || name.includes("/PaxHeader/")) return null;
|
|
776
|
-
if (!name.startsWith("package/")) return null;
|
|
777
|
-
return name.slice(8);
|
|
778
|
-
}
|
|
779
|
-
async function extractTarballTo(bytes, destDir) {
|
|
780
|
-
const entries = await parseTarGzip(bytes);
|
|
781
|
-
const stagingDir = `${destDir}${STAGING_SUFFIX_PREFIX}${process.pid}-${Date.now()}`;
|
|
782
|
-
await fs.promises.mkdir(stagingDir, { recursive: true });
|
|
783
|
-
const resolvedStaging = path.resolve(stagingDir);
|
|
784
|
-
try {
|
|
785
|
-
for (const entry of entries) {
|
|
786
|
-
const relativeName = normalizeEntryName(entry.name);
|
|
787
|
-
if (relativeName === null) continue;
|
|
788
|
-
const targetPath = path.join(stagingDir, relativeName);
|
|
789
|
-
const resolvedTarget = path.resolve(targetPath);
|
|
790
|
-
if (resolvedTarget !== resolvedStaging && !resolvedTarget.startsWith(`${resolvedStaging}${path.sep}`)) throw new Error(`tarball entry escapes extraction root: ${entry.name}`);
|
|
791
|
-
if (entry.type === "directory" || relativeName.endsWith("/")) {
|
|
792
|
-
await fs.promises.mkdir(targetPath, { recursive: true });
|
|
793
|
-
continue;
|
|
794
|
-
}
|
|
795
|
-
await fs.promises.mkdir(path.dirname(targetPath), { recursive: true });
|
|
796
|
-
const data = entry.data ?? new Uint8Array(0);
|
|
797
|
-
await fs.promises.writeFile(targetPath, data);
|
|
798
|
-
const mode = parseEntryMode(entry.attrs?.mode);
|
|
799
|
-
if (mode !== void 0) await fs.promises.chmod(targetPath, mode);
|
|
800
|
-
}
|
|
801
|
-
try {
|
|
802
|
-
await fs.promises.rename(stagingDir, destDir);
|
|
803
|
-
} catch (error) {
|
|
804
|
-
const code = error.code;
|
|
805
|
-
if ((code === "ENOTEMPTY" || code === "EEXIST") && fs.existsSync(path.join(destDir, "package.json"))) {
|
|
806
|
-
await fs.promises.rm(stagingDir, {
|
|
807
|
-
recursive: true,
|
|
808
|
-
force: true
|
|
809
|
-
}).catch(() => {});
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
throw error;
|
|
813
|
-
}
|
|
814
|
-
} catch (error) {
|
|
815
|
-
await fs.promises.rm(stagingDir, {
|
|
816
|
-
recursive: true,
|
|
817
|
-
force: true
|
|
818
|
-
}).catch(() => {});
|
|
819
|
-
throw error;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
const STAGING_STALE_MS = 1440 * 60 * 1e3;
|
|
823
|
-
/**
|
|
824
|
-
* Remove `<destDir>.tmp-*` siblings left behind by a previous crash so
|
|
825
|
-
* repeated aborts don't accumulate orphaned staging trees. Only deletes
|
|
826
|
-
* entries whose mtime is older than 24 hours — a concurrent `vp create`
|
|
827
|
-
* that's still actively extracting will always be younger than that, so
|
|
828
|
-
* the age gate keeps this safe to run at the top of every extract.
|
|
829
|
-
*/
|
|
830
|
-
async function cleanupStaleStagingDirs(destDir) {
|
|
831
|
-
const parent = path.dirname(destDir);
|
|
832
|
-
const prefix = `${path.basename(destDir)}${STAGING_SUFFIX_PREFIX}`;
|
|
833
|
-
let entries;
|
|
834
|
-
try {
|
|
835
|
-
entries = await fs.promises.readdir(parent);
|
|
836
|
-
} catch {
|
|
837
|
-
return;
|
|
838
|
-
}
|
|
839
|
-
const cutoff = Date.now() - STAGING_STALE_MS;
|
|
840
|
-
await Promise.all(entries.filter((name) => name.startsWith(prefix)).map(async (name) => {
|
|
841
|
-
const fullPath = path.join(parent, name);
|
|
842
|
-
try {
|
|
843
|
-
if ((await fs.promises.stat(fullPath)).mtimeMs < cutoff) await fs.promises.rm(fullPath, {
|
|
844
|
-
recursive: true,
|
|
845
|
-
force: true
|
|
846
|
-
});
|
|
847
|
-
} catch {}
|
|
848
|
-
}));
|
|
849
|
-
}
|
|
850
|
-
/**
|
|
851
|
-
* Ensure the `@org/create` package tarball for the given manifest has been
|
|
852
|
-
* downloaded and extracted locally. Returns the absolute path to the
|
|
853
|
-
* extracted package root (i.e. the directory that contains
|
|
854
|
-
* `package.json`).
|
|
855
|
-
*
|
|
856
|
-
* Idempotent: subsequent calls for the same `<scope, version>` reuse the
|
|
857
|
-
* cached extraction. Concurrent calls race on the final rename; the loser
|
|
858
|
-
* cleans up and returns the existing directory.
|
|
859
|
-
*/
|
|
860
|
-
async function ensureOrgPackageExtracted(manifest) {
|
|
861
|
-
const extractedRoot = getExtractionDir(manifest);
|
|
862
|
-
if (fs.existsSync(path.join(extractedRoot, "package.json"))) return extractedRoot;
|
|
863
|
-
const parent = path.dirname(extractedRoot);
|
|
864
|
-
await fs.promises.mkdir(parent, { recursive: true });
|
|
865
|
-
await cleanupStaleStagingDirs(extractedRoot);
|
|
866
|
-
const bytes = await downloadTarball(manifest.tarballUrl);
|
|
867
|
-
verifyIntegrity(bytes, manifest.integrity);
|
|
868
|
-
await extractTarballTo(bytes, extractedRoot);
|
|
869
|
-
return extractedRoot;
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* Resolve a manifest entry's relative `./...` path against an already-
|
|
873
|
-
* extracted package root, rejecting any path that escapes the root (via
|
|
874
|
-
* `..` walks or an absolute specifier).
|
|
875
|
-
*
|
|
876
|
-
* Existence is NOT checked here — the subsequent `copyDir` surfaces any
|
|
877
|
-
* missing-directory error with a clearer errno.
|
|
878
|
-
*/
|
|
879
|
-
function resolveBundledPath(extractedRoot, relativePath) {
|
|
880
|
-
if (path.isAbsolute(relativePath)) throw new Error(`bundled template path must be relative, got ${relativePath}`);
|
|
881
|
-
const resolvedRoot = path.resolve(extractedRoot);
|
|
882
|
-
const resolvedTarget = path.resolve(extractedRoot, relativePath);
|
|
883
|
-
if (resolvedTarget !== resolvedRoot && !resolvedTarget.startsWith(`${resolvedRoot}${path.sep}`)) throw new Error(`bundled template path escapes the package root: ${relativePath}`);
|
|
884
|
-
return resolvedTarget;
|
|
885
|
-
}
|
|
886
|
-
//#endregion
|
|
887
289
|
//#region ../../node_modules/.pnpm/validate-npm-package-name@7.0.2/node_modules/validate-npm-package-name/lib/builtin-modules.json
|
|
888
290
|
var require_builtin_modules = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
889
291
|
module.exports = [
|
|
@@ -3848,24 +3250,6 @@ function setPackageName(projectDir, packageName) {
|
|
|
3848
3250
|
return pkg;
|
|
3849
3251
|
});
|
|
3850
3252
|
}
|
|
3851
|
-
/**
|
|
3852
|
-
* Make sure the scaffolded project's `.gitignore` excludes `node_modules`.
|
|
3853
|
-
*
|
|
3854
|
-
* Called right after `git init` so even bundled `@org` templates (which
|
|
3855
|
-
* may ship without a `.gitignore`) don't end up tracking installed
|
|
3856
|
-
* dependencies on the user's first commit. No-op when an existing
|
|
3857
|
-
* `.gitignore` already lists `node_modules`.
|
|
3858
|
-
*/
|
|
3859
|
-
function ensureGitignoreNodeModules(projectDir) {
|
|
3860
|
-
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
3861
|
-
let content = "";
|
|
3862
|
-
try {
|
|
3863
|
-
content = fs.readFileSync(gitignorePath, "utf-8");
|
|
3864
|
-
} catch {}
|
|
3865
|
-
if (/^\s*node_modules\/?\s*$/m.test(content)) return;
|
|
3866
|
-
const prefix = content === "" || content.endsWith("\n") ? "" : "\n";
|
|
3867
|
-
fs.appendFileSync(gitignorePath, `${prefix}node_modules\n`);
|
|
3868
|
-
}
|
|
3869
3253
|
function formatDisplayTargetDir(targetDir) {
|
|
3870
3254
|
const normalized = targetDir.split(path.sep).join("/");
|
|
3871
3255
|
if (normalized === "" || normalized === ".") return "./";
|
|
@@ -4007,109 +3391,6 @@ function validateTargetDir(input, cwd) {
|
|
|
4007
3391
|
return { directory: targetDir };
|
|
4008
3392
|
}
|
|
4009
3393
|
//#endregion
|
|
4010
|
-
//#region src/create/org-resolve.ts
|
|
4011
|
-
function printNonInteractiveTable(manifest, orgSpec, isMonorepo) {
|
|
4012
|
-
const { lines, filteredCount } = formatManifestTable(manifest, isMonorepo);
|
|
4013
|
-
const [firstVisible] = filterManifestForContext(manifest.templates, isMonorepo);
|
|
4014
|
-
const body = [
|
|
4015
|
-
"",
|
|
4016
|
-
`A template name is required when running \`vp create ${orgSpec.scope}\` in non-interactive mode.`,
|
|
4017
|
-
"",
|
|
4018
|
-
`Available templates in ${manifest.packageName}:`,
|
|
4019
|
-
"",
|
|
4020
|
-
...lines
|
|
4021
|
-
];
|
|
4022
|
-
if (filteredCount > 0) body.push("", `(omitted ${filteredCount} monorepo-only ${filteredCount === 1 ? "entry" : "entries"} because this workspace is already a monorepo)`);
|
|
4023
|
-
body.push("", "Examples:");
|
|
4024
|
-
if (firstVisible) body.push(" # Scaffold a specific template from the org", ` vp create ${orgSpec.scope}:${firstVisible.name} --no-interactive`, "");
|
|
4025
|
-
body.push(" # Or use a Vite+ built-in template", " vp create vite:application --no-interactive", "");
|
|
4026
|
-
process.stderr.write(body.join("\n"));
|
|
4027
|
-
}
|
|
4028
|
-
function rejectMonorepoEntryInsideMonorepo(entry, isMonorepo) {
|
|
4029
|
-
if (entry.monorepo && isMonorepo) {
|
|
4030
|
-
log$1.info("You are already in a monorepo workspace.\nUse a different template or run this command outside the monorepo");
|
|
4031
|
-
cancelAndExit("Cannot create a monorepo inside an existing monorepo", 1);
|
|
4032
|
-
}
|
|
4033
|
-
}
|
|
4034
|
-
async function resolveEntry(manifest, entry) {
|
|
4035
|
-
if (isRelativePath(entry.template)) return {
|
|
4036
|
-
kind: "bundled",
|
|
4037
|
-
bundledLocalPath: resolveBundledPath(await ensureOrgPackageExtracted(manifest), entry.template),
|
|
4038
|
-
entryName: entry.name,
|
|
4039
|
-
scope: manifest.scope,
|
|
4040
|
-
...entry.monorepo === true ? { monorepo: true } : {}
|
|
4041
|
-
};
|
|
4042
|
-
return {
|
|
4043
|
-
kind: "replaced",
|
|
4044
|
-
templateName: entry.template
|
|
4045
|
-
};
|
|
4046
|
-
}
|
|
4047
|
-
/**
|
|
4048
|
-
* If `selectedTemplateName` points at an `@scope[/name]` org whose
|
|
4049
|
-
* `@scope/create` package publishes a `createConfig.templates` manifest, apply the
|
|
4050
|
-
* manifest rules (picker / direct lookup / escape hatch / bundled
|
|
4051
|
-
* extraction) and report the outcome.
|
|
4052
|
-
*
|
|
4053
|
-
* The caller — `packages/cli/src/create/bin.ts` — decides what to do next
|
|
4054
|
-
* based on the returned variant.
|
|
4055
|
-
*/
|
|
4056
|
-
async function resolveOrgManifestForCreate(args) {
|
|
4057
|
-
const orgSpec = parseOrgScopedSpec(args.templateName);
|
|
4058
|
-
if (!orgSpec) return { kind: "passthrough" };
|
|
4059
|
-
let manifest;
|
|
4060
|
-
try {
|
|
4061
|
-
manifest = await readOrgManifest(orgSpec.scope, orgSpec.version);
|
|
4062
|
-
} catch (error) {
|
|
4063
|
-
cancelAndExit(error instanceof OrgManifestSchemaError ? error.message : `Failed to read ${orgSpec.scope}/create manifest: ${error.message}`, 1);
|
|
4064
|
-
}
|
|
4065
|
-
if (!manifest) {
|
|
4066
|
-
if (orgSpec.name !== void 0) cancelAndExit(`No \`createConfig.templates\` manifest in ${orgSpec.scope}/create — \`@org:name\` requires one.`, 1);
|
|
4067
|
-
log$1.info(`No \`createConfig.templates\` manifest in ${orgSpec.scope}/create — running it as a normal package.`);
|
|
4068
|
-
return { kind: "passthrough" };
|
|
4069
|
-
}
|
|
4070
|
-
if (orgSpec.name === void 0) {
|
|
4071
|
-
if (!args.interactive) {
|
|
4072
|
-
printNonInteractiveTable(manifest, orgSpec, args.isMonorepo);
|
|
4073
|
-
process.exit(1);
|
|
4074
|
-
}
|
|
4075
|
-
const picked = await pickOrgTemplate(manifest, { isMonorepo: args.isMonorepo });
|
|
4076
|
-
if (picked === ORG_PICKER_CANCEL) cancelAndExit();
|
|
4077
|
-
if (picked === ORG_PICKER_BUILTIN_ESCAPE) {
|
|
4078
|
-
if (args.isMonorepo && manifest.templates.every((t) => t.monorepo)) log$1.info(`No templates from ${manifest.packageName} are applicable inside a monorepo — showing Vite+ built-in templates instead.`);
|
|
4079
|
-
return { kind: "escape-hatch" };
|
|
4080
|
-
}
|
|
4081
|
-
rejectMonorepoEntryInsideMonorepo(picked.entry, args.isMonorepo);
|
|
4082
|
-
return resolveEntry(manifest, picked.entry);
|
|
4083
|
-
}
|
|
4084
|
-
const entry = manifest.templates.find((candidate) => candidate.name === orgSpec.name);
|
|
4085
|
-
if (!entry) {
|
|
4086
|
-
const available = filterManifestForContext(manifest.templates, args.isMonorepo).map((t) => t.name).join(", ");
|
|
4087
|
-
cancelAndExit(`No template named "${orgSpec.name}" in ${manifest.packageName}. Available: ${available || "(none applicable in this context)"}.`, 1);
|
|
4088
|
-
}
|
|
4089
|
-
rejectMonorepoEntryInsideMonorepo(entry, args.isMonorepo);
|
|
4090
|
-
return resolveEntry(manifest, entry);
|
|
4091
|
-
}
|
|
4092
|
-
/**
|
|
4093
|
-
* Read `create.defaultTemplate` from the workspace root's `vite.config.ts`.
|
|
4094
|
-
*
|
|
4095
|
-
* Walks up from `startDir` via `findWorkspaceRoot` (monorepo markers
|
|
4096
|
-
* only — `pnpm-workspace.yaml`, `workspaces` in `package.json`,
|
|
4097
|
-
* `lerna.json`) so monorepo invocations from any subdirectory still
|
|
4098
|
-
* pick up the root config. Standalone repos without a monorepo marker
|
|
4099
|
-
* only see a config that sits at `startDir` itself.
|
|
4100
|
-
*
|
|
4101
|
-
* Best-effort: if there's no config file or evaluation fails, return
|
|
4102
|
-
* `undefined` so the create flow behaves as if no default was set.
|
|
4103
|
-
*/
|
|
4104
|
-
async function getConfiguredDefaultTemplate(startDir) {
|
|
4105
|
-
const projectRoot = findWorkspaceRoot(startDir) ?? startDir;
|
|
4106
|
-
if (!hasViteConfig(projectRoot)) return;
|
|
4107
|
-
try {
|
|
4108
|
-
const value = (await resolveViteConfig(projectRoot)).create?.defaultTemplate;
|
|
4109
|
-
if (typeof value === "string" && value.length > 0) return value;
|
|
4110
|
-
} catch {}
|
|
4111
|
-
}
|
|
4112
|
-
//#endregion
|
|
4113
3394
|
//#region src/create/templates/generator.ts
|
|
4114
3395
|
async function executeGeneratorScaffold(workspaceInfo, templateInfo, options) {
|
|
4115
3396
|
if (!options?.silent) log$1.step("Creating generator scaffold...");
|
|
@@ -4257,38 +3538,24 @@ async function executeBuiltinTemplate(workspaceInfo, templateInfo, options) {
|
|
|
4257
3538
|
};
|
|
4258
3539
|
}
|
|
4259
3540
|
//#endregion
|
|
4260
|
-
//#region src/create/templates/bundled.ts
|
|
4261
|
-
/**
|
|
4262
|
-
* Scaffold a bundled template by copying the pre-extracted directory at
|
|
4263
|
-
* `localPath` into `workspaceInfo.rootDir/targetDir`.
|
|
4264
|
-
*/
|
|
4265
|
-
async function executeBundledTemplate(workspaceInfo, templateInfo) {
|
|
4266
|
-
assert(templateInfo.localPath, "localPath is required for bundled templates");
|
|
4267
|
-
assert(templateInfo.targetDir, "targetDir is required");
|
|
4268
|
-
assert(templateInfo.packageName, "packageName is required");
|
|
4269
|
-
const destDir = path.join(workspaceInfo.rootDir, templateInfo.targetDir);
|
|
4270
|
-
try {
|
|
4271
|
-
copyDir(templateInfo.localPath, destDir);
|
|
4272
|
-
} catch (error) {
|
|
4273
|
-
if (error.code === "ENOENT") throw new Error(`bundled template directory not found: ${templateInfo.localPath}`, { cause: error });
|
|
4274
|
-
throw error;
|
|
4275
|
-
}
|
|
4276
|
-
try {
|
|
4277
|
-
setPackageName(destDir, templateInfo.packageName);
|
|
4278
|
-
} catch {}
|
|
4279
|
-
return {
|
|
4280
|
-
exitCode: 0,
|
|
4281
|
-
projectDir: templateInfo.targetDir
|
|
4282
|
-
};
|
|
4283
|
-
}
|
|
4284
|
-
//#endregion
|
|
4285
3541
|
//#region src/create/templates/monorepo.ts
|
|
4286
3542
|
const InitialMonorepoAppDir = "apps/website";
|
|
4287
|
-
async function executeMonorepoTemplate(workspaceInfo, templateInfo, options) {
|
|
3543
|
+
async function executeMonorepoTemplate(workspaceInfo, templateInfo, interactive, options) {
|
|
4288
3544
|
assert(templateInfo.packageName, "packageName is required");
|
|
4289
3545
|
assert(templateInfo.targetDir, "targetDir is required");
|
|
4290
3546
|
workspaceInfo.monorepoScope = getScopeFromPackageName(templateInfo.packageName);
|
|
4291
3547
|
const fullPath = path.join(workspaceInfo.rootDir, templateInfo.targetDir);
|
|
3548
|
+
let initGit = true;
|
|
3549
|
+
if (interactive && !options?.silent) {
|
|
3550
|
+
const selected = await confirm({
|
|
3551
|
+
message: `Initialize git repository:`,
|
|
3552
|
+
initialValue: true
|
|
3553
|
+
});
|
|
3554
|
+
if (q(selected)) {
|
|
3555
|
+
log$1.info("Operation cancelled. Skipping git initialization");
|
|
3556
|
+
initGit = false;
|
|
3557
|
+
} else initGit = selected;
|
|
3558
|
+
} else if (!options?.silent) log$1.info(`Initializing git repository (default: yes)`);
|
|
4292
3559
|
if (!options?.silent) {
|
|
4293
3560
|
log$1.info(`Target directory: ${formatDisplayTargetDir(templateInfo.targetDir)}`);
|
|
4294
3561
|
log$1.step("Creating Vite+ monorepo...");
|
|
@@ -4325,6 +3592,18 @@ async function executeMonorepoTemplate(workspaceInfo, templateInfo, options) {
|
|
|
4325
3592
|
if (fs.existsSync(yarnrcPath)) fs.unlinkSync(yarnrcPath);
|
|
4326
3593
|
}
|
|
4327
3594
|
if (!options?.silent) log$1.success("Monorepo template created");
|
|
3595
|
+
if (initGit) {
|
|
3596
|
+
const gitResult = import_cross_spawn.default.sync("git", ["init"], {
|
|
3597
|
+
stdio: "pipe",
|
|
3598
|
+
cwd: fullPath
|
|
3599
|
+
});
|
|
3600
|
+
if (gitResult.status === 0) {
|
|
3601
|
+
if (!options?.silent) log$1.success("Git repository initialized");
|
|
3602
|
+
} else {
|
|
3603
|
+
log$1.warn("Failed to initialize git repository");
|
|
3604
|
+
if (gitResult.stderr) log$1.info(gitResult.stderr.toString());
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
4328
3607
|
if (!options?.silent) log$1.step("Creating default application in apps/website...");
|
|
4329
3608
|
const appResult = await runRemoteTemplateCommand(workspaceInfo, fullPath, discoverTemplate("create-vite@latest", [
|
|
4330
3609
|
InitialMonorepoAppDir,
|
|
@@ -4387,10 +3666,7 @@ const helpMessage = renderCliDoc({
|
|
|
4387
3666
|
`- Default: ${accent("vite:monorepo")}, ${accent("vite:application")}, ${accent("vite:library")}, ${accent("vite:generator")}`,
|
|
4388
3667
|
"- Remote: vite, @tanstack/start, create-next-app,",
|
|
4389
3668
|
" create-nuxt, github:user/repo, https://github.com/user/template-repo, etc.",
|
|
4390
|
-
"- Local: @company/generator-*, ./tools/create-ui-component"
|
|
4391
|
-
`- Org scope: ${accent("@your-org")} → picker from ${accent("@your-org/create")}'s ${accent("createConfig.templates")} manifest`,
|
|
4392
|
-
`- Org entry: ${accent("@your-org:web")} → manifest entry "web" from ${accent("@your-org/create")}`,
|
|
4393
|
-
`When omitted, uses \`create.defaultTemplate\` from vite.config.ts if set.`
|
|
3669
|
+
"- Local: @company/generator-*, ./tools/create-ui-component"
|
|
4394
3670
|
]
|
|
4395
3671
|
}]
|
|
4396
3672
|
},
|
|
@@ -4467,11 +3743,7 @@ const helpMessage = renderCliDoc({
|
|
|
4467
3743
|
"",
|
|
4468
3744
|
` ${muted("# Use templates from GitHub (via degit)")}`,
|
|
4469
3745
|
` ${accent("vp create github:user/repo")}`,
|
|
4470
|
-
` ${accent("vp create https://github.com/user/template-repo")}
|
|
4471
|
-
"",
|
|
4472
|
-
` ${muted("# Pick from an org that publishes @scope/create with createConfig.templates")}`,
|
|
4473
|
-
` ${accent("vp create @your-org")} ${muted("# interactive picker")}`,
|
|
4474
|
-
` ${accent("vp create @your-org:web")} ${muted("# direct manifest-entry selection")}`
|
|
3746
|
+
` ${accent("vp create https://github.com/user/template-repo")}`
|
|
4475
3747
|
]
|
|
4476
3748
|
}
|
|
4477
3749
|
]
|
|
@@ -4651,6 +3923,20 @@ async function main() {
|
|
|
4651
3923
|
await showAvailableTemplates();
|
|
4652
3924
|
return;
|
|
4653
3925
|
}
|
|
3926
|
+
if (!templateName && !options.interactive) {
|
|
3927
|
+
console.error(`
|
|
3928
|
+
A template name is required when running in non-interactive mode
|
|
3929
|
+
|
|
3930
|
+
Usage: vp create [TEMPLATE] [OPTIONS] [-- TEMPLATE_OPTIONS]
|
|
3931
|
+
|
|
3932
|
+
Example:
|
|
3933
|
+
${muted("# Create a new application in non-interactive mode with a custom target directory")}
|
|
3934
|
+
vp create vite:application --no-interactive --directory=apps/my-app
|
|
3935
|
+
|
|
3936
|
+
Use \`vp create --list\` to list all available templates, or run \`vp create --help\` for more information.
|
|
3937
|
+
`);
|
|
3938
|
+
process.exit(1);
|
|
3939
|
+
}
|
|
4654
3940
|
if (options.interactive) intro(vitePlusHeader());
|
|
4655
3941
|
let targetDir = "";
|
|
4656
3942
|
let packageName = "";
|
|
@@ -4678,39 +3964,7 @@ async function main() {
|
|
|
4678
3964
|
let selectedParentDir;
|
|
4679
3965
|
let remoteTargetDir;
|
|
4680
3966
|
let shouldSetupHooks = false;
|
|
4681
|
-
let bundled;
|
|
4682
|
-
let skipShorthandExpansion = false;
|
|
4683
3967
|
const installArgs = process.env.CI ? ["--no-frozen-lockfile"] : void 0;
|
|
4684
|
-
if (!selectedTemplateName) {
|
|
4685
|
-
const defaultTemplate = await getConfiguredDefaultTemplate(workspaceInfoOptional.rootDir);
|
|
4686
|
-
if (defaultTemplate) selectedTemplateName = defaultTemplate;
|
|
4687
|
-
}
|
|
4688
|
-
if (selectedTemplateName) {
|
|
4689
|
-
const resolved = await resolveOrgManifestForCreate({
|
|
4690
|
-
templateName: selectedTemplateName,
|
|
4691
|
-
isMonorepo,
|
|
4692
|
-
interactive: options.interactive
|
|
4693
|
-
});
|
|
4694
|
-
if (resolved.kind === "replaced") {
|
|
4695
|
-
selectedTemplateName = resolved.templateName;
|
|
4696
|
-
skipShorthandExpansion = true;
|
|
4697
|
-
} else if (resolved.kind === "bundled") bundled = resolved;
|
|
4698
|
-
else if (resolved.kind === "escape-hatch") selectedTemplateName = "";
|
|
4699
|
-
}
|
|
4700
|
-
if (!selectedTemplateName && !options.interactive) {
|
|
4701
|
-
console.error(`
|
|
4702
|
-
A template name is required when running in non-interactive mode
|
|
4703
|
-
|
|
4704
|
-
Usage: vp create [TEMPLATE] [OPTIONS] [-- TEMPLATE_OPTIONS]
|
|
4705
|
-
|
|
4706
|
-
Example:
|
|
4707
|
-
${muted("# Create a new application in non-interactive mode with a custom target directory")}
|
|
4708
|
-
vp create vite:application --no-interactive --directory=apps/my-app
|
|
4709
|
-
|
|
4710
|
-
Use \`vp create --list\` to list all available templates, or run \`vp create --help\` for more information.
|
|
4711
|
-
`);
|
|
4712
|
-
process.exit(1);
|
|
4713
|
-
}
|
|
4714
3968
|
if (!selectedTemplateName) {
|
|
4715
3969
|
const template = await select({
|
|
4716
3970
|
message: "",
|
|
@@ -4720,11 +3974,8 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
|
|
|
4720
3974
|
selectedTemplateName = template;
|
|
4721
3975
|
}
|
|
4722
3976
|
const isBuiltinTemplate = selectedTemplateName.startsWith("vite:");
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
const isDirectScaffoldTemplate = isBuiltinTemplate || isBundledTemplate;
|
|
4726
|
-
if (!isDirectScaffoldTemplate) compactOutput = false;
|
|
4727
|
-
if (targetDir && !isDirectScaffoldTemplate) cancelAndExit("The --directory option is only available for builtin and bundled @org templates", 1);
|
|
3977
|
+
if (!isBuiltinTemplate) compactOutput = false;
|
|
3978
|
+
if (targetDir && !isBuiltinTemplate) cancelAndExit("The --directory option is only available for builtin templates", 1);
|
|
4728
3979
|
if (selectedTemplateName === BuiltinTemplate.monorepo && isMonorepo) {
|
|
4729
3980
|
log$1.info("You are already in a monorepo workspace.\nUse a different template or run this command outside the monorepo");
|
|
4730
3981
|
cancelAndExit("Cannot create a monorepo inside an existing monorepo", 1);
|
|
@@ -4786,9 +4037,9 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
|
|
|
4786
4037
|
remoteTargetDir = await promptTargetDir(defaultTargetDir, options.interactive, { cwd: remoteTargetBaseDir });
|
|
4787
4038
|
selectedTemplateArgs = [remoteTargetDir, ...selectedTemplateArgs];
|
|
4788
4039
|
}
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
packageName = deriveDefaultPackageName(cwd, workspaceInfoOptional.monorepoScope,
|
|
4040
|
+
if (isBuiltinTemplate && (!targetDir || targetDir === ".")) if (targetDir === ".") {
|
|
4041
|
+
const fallbackName = selectedTemplateName === BuiltinTemplate.monorepo ? "vite-plus-monorepo" : `vite-plus-${selectedTemplateName.split(":")[1]}`;
|
|
4042
|
+
packageName = deriveDefaultPackageName(cwd, workspaceInfoOptional.monorepoScope, fallbackName);
|
|
4792
4043
|
if (isMonorepo) {
|
|
4793
4044
|
if (!cwdRelativeToRoot) cancelAndExit("Cannot scaffold into the monorepo root directory. Use --directory to specify a target directory", 1);
|
|
4794
4045
|
const enclosingPackage = workspaceInfoOptional.packages.find((pkg) => cwdRelativeToRoot === pkg.path || cwdRelativeToRoot.startsWith(`${pkg.path}/`));
|
|
@@ -4803,7 +4054,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
|
|
|
4803
4054
|
} else {
|
|
4804
4055
|
const selected = await promptPackageNameAndTargetDir(getRandomProjectName({
|
|
4805
4056
|
scope: workspaceInfoOptional.monorepoScope,
|
|
4806
|
-
fallbackName:
|
|
4057
|
+
fallbackName: `vite-plus-${selectedTemplateName.split(":")[1]}`
|
|
4807
4058
|
}), options.interactive);
|
|
4808
4059
|
packageName = selected.packageName;
|
|
4809
4060
|
targetDir = selectedParentDir ? path.join(selectedParentDir, selected.targetDir).split(path.sep).join("/") : selected.targetDir;
|
|
@@ -4871,7 +4122,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
|
|
|
4871
4122
|
}
|
|
4872
4123
|
};
|
|
4873
4124
|
updateCreateProgress("Scaffolding project");
|
|
4874
|
-
const templateInfo = discoverTemplate(selectedTemplateName, selectedTemplateArgs, workspaceInfo, options.interactive
|
|
4125
|
+
const templateInfo = discoverTemplate(selectedTemplateName, selectedTemplateArgs, workspaceInfo, options.interactive);
|
|
4875
4126
|
if (selectedParentDir) templateInfo.parentDir = selectedParentDir;
|
|
4876
4127
|
if (targetDir) templateInfo.parentDir = void 0;
|
|
4877
4128
|
if (remoteTargetDir) {
|
|
@@ -4880,50 +4131,20 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
|
|
|
4880
4131
|
await checkProjectDirExists(path.join(workspaceInfo.rootDir, projectDir), options.interactive);
|
|
4881
4132
|
resumeCreateProgress();
|
|
4882
4133
|
}
|
|
4883
|
-
if (templateInfo.command === BuiltinTemplate.monorepo
|
|
4884
|
-
let shouldInitGit = true;
|
|
4885
|
-
if (options.interactive && !compactOutput) {
|
|
4886
|
-
pauseCreateProgress();
|
|
4887
|
-
const selected = await confirm({
|
|
4888
|
-
message: "Initialize git repository:",
|
|
4889
|
-
initialValue: true
|
|
4890
|
-
});
|
|
4891
|
-
resumeCreateProgress();
|
|
4892
|
-
if (q(selected)) {
|
|
4893
|
-
log$1.info("Operation cancelled. Skipping git initialization");
|
|
4894
|
-
shouldInitGit = false;
|
|
4895
|
-
} else shouldInitGit = selected;
|
|
4896
|
-
} else if (!compactOutput) log$1.info("Initializing git repository (default: yes)");
|
|
4134
|
+
if (templateInfo.command === BuiltinTemplate.monorepo) {
|
|
4897
4135
|
updateCreateProgress("Creating monorepo");
|
|
4898
4136
|
await checkProjectDirExists(path.join(workspaceInfo.rootDir, targetDir), options.interactive);
|
|
4899
|
-
const result =
|
|
4137
|
+
const result = await executeMonorepoTemplate(workspaceInfo, {
|
|
4900
4138
|
...templateInfo,
|
|
4901
4139
|
packageName,
|
|
4902
4140
|
targetDir
|
|
4903
|
-
}
|
|
4904
|
-
...templateInfo,
|
|
4905
|
-
packageName,
|
|
4906
|
-
targetDir
|
|
4907
|
-
}, { silent: compactOutput });
|
|
4141
|
+
}, options.interactive, { silent: compactOutput });
|
|
4908
4142
|
const { projectDir } = result;
|
|
4909
4143
|
if (result.exitCode !== 0 || !projectDir) {
|
|
4910
4144
|
failCreateProgress("Scaffolding failed");
|
|
4911
4145
|
cancelAndExit(`Failed to create monorepo, exit code: ${result.exitCode}`, result.exitCode);
|
|
4912
4146
|
}
|
|
4913
4147
|
const fullPath = path.join(workspaceInfo.rootDir, projectDir);
|
|
4914
|
-
if (shouldInitGit) {
|
|
4915
|
-
const gitResult = import_cross_spawn.default.sync("git", ["init"], {
|
|
4916
|
-
stdio: "pipe",
|
|
4917
|
-
cwd: fullPath
|
|
4918
|
-
});
|
|
4919
|
-
if (gitResult.status === 0) {
|
|
4920
|
-
if (!compactOutput) log$1.success("Git repository initialized");
|
|
4921
|
-
ensureGitignoreNodeModules(fullPath);
|
|
4922
|
-
} else {
|
|
4923
|
-
log$1.warn("Failed to initialize git repository");
|
|
4924
|
-
if (gitResult.stderr) log$1.info(gitResult.stderr.toString());
|
|
4925
|
-
}
|
|
4926
|
-
}
|
|
4927
4148
|
updateCreateProgress("Writing agent instructions");
|
|
4928
4149
|
pauseCreateProgress();
|
|
4929
4150
|
await writeAgentInstructions({
|
|
@@ -4946,7 +4167,6 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
|
|
|
4946
4167
|
workspaceInfo.rootDir = fullPath;
|
|
4947
4168
|
updateCreateProgress("Integrating monorepo");
|
|
4948
4169
|
rewriteMonorepo(workspaceInfo, void 0, compactOutput);
|
|
4949
|
-
if (bundled?.monorepo) injectCreateDefaultTemplate(fullPath, bundled.scope, compactOutput);
|
|
4950
4170
|
if (shouldSetupHooks) installGitHooks(fullPath, compactOutput);
|
|
4951
4171
|
updateCreateProgress("Installing dependencies");
|
|
4952
4172
|
const installSummary = await runViteInstall(fullPath, options.interactive, installArgs, { silent: compactOutput });
|
|
@@ -4964,17 +4184,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
|
|
|
4964
4184
|
return;
|
|
4965
4185
|
}
|
|
4966
4186
|
let result;
|
|
4967
|
-
if (templateInfo.type === TemplateType.
|
|
4968
|
-
pauseCreateProgress();
|
|
4969
|
-
await checkProjectDirExists(path.join(workspaceInfo.rootDir, targetDir), options.interactive);
|
|
4970
|
-
resumeCreateProgress();
|
|
4971
|
-
updateCreateProgress("Copying template files");
|
|
4972
|
-
result = await executeBundledTemplate(workspaceInfo, {
|
|
4973
|
-
...templateInfo,
|
|
4974
|
-
packageName,
|
|
4975
|
-
targetDir
|
|
4976
|
-
});
|
|
4977
|
-
} else if (templateInfo.type === TemplateType.builtin) {
|
|
4187
|
+
if (templateInfo.type === TemplateType.builtin) {
|
|
4978
4188
|
if (!targetDir) {
|
|
4979
4189
|
const selected = await promptPackageNameAndTargetDir(getRandomProjectName({
|
|
4980
4190
|
scope: workspaceInfo.monorepoScope,
|