diff --git a/packages/create-vite/index.js b/packages/create-vite/index.js index 40d0aa583124d3..e19f53fe47a794 100755 --- a/packages/create-vite/index.js +++ b/packages/create-vite/index.js @@ -4,12 +4,14 @@ import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' +import spawn from 'cross-spawn' import minimist from 'minimist' import prompts from 'prompts' import { blue, cyan, green, + lightGreen, lightRed, magenta, red, @@ -25,6 +27,7 @@ const cwd = process.cwd() const FRAMEWORKS = [ { name: 'vanilla', + display: 'Vanilla', color: yellow, variants: [ { @@ -41,6 +44,7 @@ const FRAMEWORKS = [ }, { name: 'vue', + display: 'Vue', color: green, variants: [ { @@ -52,11 +56,24 @@ const FRAMEWORKS = [ name: 'vue-ts', display: 'TypeScript', color: blue + }, + { + name: 'custom-create-vue', + display: 'Customize with create-vue', + color: green, + customCommand: 'npm create vue@latest TARGET_DIR' + }, + { + name: 'custom-nuxt', + display: 'Nuxt', + color: lightGreen, + customCommand: 'npm exec nuxi init TARGET_DIR' } ] }, { name: 'react', + display: 'React', color: cyan, variants: [ { @@ -73,6 +90,7 @@ const FRAMEWORKS = [ }, { name: 'preact', + display: 'Preact', color: magenta, variants: [ { @@ -89,6 +107,7 @@ const FRAMEWORKS = [ }, { name: 'lit', + display: 'Lit', color: lightRed, variants: [ { @@ -105,6 +124,7 @@ const FRAMEWORKS = [ }, { name: 'svelte', + display: 'Svelte', color: red, variants: [ { @@ -116,6 +136,12 @@ const FRAMEWORKS = [ name: 'svelte-ts', display: 'TypeScript', color: blue + }, + { + name: 'custom-svelte-kit', + display: 'SvelteKit', + color: red, + customCommand: 'npm create svelte@latest TARGET_DIR' } ] } @@ -191,7 +217,7 @@ async function init() { choices: FRAMEWORKS.map((framework) => { const frameworkColor = framework.color return { - title: frameworkColor(framework.name), + title: frameworkColor(framework.display || framework.name), value: framework } }) @@ -206,7 +232,7 @@ async function init() { framework.variants.map((variant) => { const variantColor = variant.color return { - title: variantColor(variant.name), + title: variantColor(variant.display || variant.name), value: variant.name } }) @@ -237,6 +263,46 @@ async function init() { // determine template template = variant || framework || template + const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent) + const pkgManager = pkgInfo ? pkgInfo.name : 'npm' + const isYarn1 = pkgManager === 'yarn' && pkgInfo?.version.startsWith('1.') + + if (template.startsWith('custom-')) { + const getCustomCommand = (name) => { + for (const f of FRAMEWORKS) { + for (const v of f.variants || []) { + if (v.name === name) { + return v.customCommand + } + } + } + } + const customCommand = getCustomCommand(template) + const fullCustomCommand = customCommand + .replace('TARGET_DIR', targetDir) + .replace(/^npm create/, `${pkgManager} create`) + // Only Yarn 1.x doesn't support `@version` in the `create` command + .replace('@latest', () => (isYarn1 ? '' : '@latest')) + .replace(/^npm exec/, () => { + // Prefer `pnpm dlx` or `yarn dlx` + if (pkgManager === 'pnpm') { + return 'pnpm dlx' + } + if (pkgManager === 'yarn' && !isYarn1) { + return 'yarn dlx' + } + // Use `npm exec` in all other cases, + // including Yarn 1.x and other custom npm clients. + return 'npm exec' + }) + + const [command, ...args] = fullCustomCommand.split(' ') + const { status } = spawn.sync(command, args, { + stdio: 'inherit' + }) + process.exit(status ?? 0) + } + console.log(`\nScaffolding project in ${root}...`) const templateDir = path.resolve( @@ -269,9 +335,6 @@ async function init() { write('package.json', JSON.stringify(pkg, null, 2)) - const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent) - const pkgManager = pkgInfo ? pkgInfo.name : 'npm' - console.log(`\nDone. Now run:\n`) if (root !== cwd) { console.log(` cd ${path.relative(cwd, root)}`) diff --git a/packages/create-vite/package.json b/packages/create-vite/package.json index 77ae57a92c60fd..5ec35494d5a90a 100644 --- a/packages/create-vite/package.json +++ b/packages/create-vite/package.json @@ -26,6 +26,7 @@ }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/create-vite#readme", "dependencies": { + "cross-spawn": "^7.0.3", "kolorist": "^1.5.1", "minimist": "^1.2.6", "prompts": "^2.4.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74c0cc0cb09e17..502a334b1bff44 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,10 +128,12 @@ importers: packages/create-vite: specifiers: + cross-spawn: ^7.0.3 kolorist: ^1.5.1 minimist: ^1.2.6 prompts: ^2.4.2 dependencies: + cross-spawn: 7.0.3 kolorist: 1.5.1 minimist: 1.2.6 prompts: 2.4.2 @@ -3955,7 +3957,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /crosspath/0.0.8: resolution: {integrity: sha512-IKlS3MpP0fhJ50M6ltyLO7Q4NzwfhafpmolMH0EDKyyaY81HutF2mH4hLpCdm3fKZ/TSTW5qPIdTy62YnefEyQ==} @@ -5814,7 +5815,6 @@ packages: /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true /jiti/1.13.0: resolution: {integrity: sha512-/n9mNxZj/HDSrincJ6RP+L+yXbpnB8FybySBa+IjIaoH9FIxBbrbRT5XUbe8R7zuVM2AQqNMNDDqz0bzx3znOQ==} @@ -6796,7 +6796,6 @@ packages: /path-key/3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - dev: true /path-key/4.0.0: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} @@ -7724,7 +7723,6 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 - dev: true /shebang-regex/1.0.0: resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=} @@ -7734,7 +7732,6 @@ packages: /shebang-regex/3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - dev: true /shell-exec/1.0.2: resolution: {integrity: sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==} @@ -8735,7 +8732,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true /wide-align/1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}