Skip to content

Commit 6df2a4a

Browse files
committed
feat!: migrate to the same codebase as v3
1 parent 77790f7 commit 6df2a4a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+3539
-589
lines changed

index.js renamed to index.ts

Lines changed: 164 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
#!/usr/bin/env node
2-
// @ts-check
32

4-
import fs from 'fs'
5-
import path from 'path'
3+
import * as fs from 'fs'
4+
import * as path from 'path'
65

76
import minimist from 'minimist'
87
import prompts from 'prompts'
98
import { red, green, bold } from 'kolorist'
109

11-
import renderTemplate from './utils/renderTemplate.js'
12-
import { postOrderDirectoryTraverse, preOrderDirectoryTraverse } from './utils/directoryTraverse.js'
13-
import generateReadme from './utils/generateReadme.js'
14-
import getCommand from './utils/getCommand.js'
10+
import renderTemplate from './utils/renderTemplate'
11+
import { postOrderDirectoryTraverse, preOrderDirectoryTraverse } from './utils/directoryTraverse'
12+
import generateReadme from './utils/generateReadme'
13+
import getCommand from './utils/getCommand'
14+
import renderEslint from './utils/renderEslint'
15+
import banner from './utils/banner'
1516

1617
function isValidPackageName(projectName) {
1718
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(projectName)
@@ -31,6 +32,10 @@ function canSafelyOverwrite(dir) {
3132
}
3233

3334
function emptyDir(dir) {
35+
if (!fs.existsSync(dir)) {
36+
return
37+
}
38+
3439
postOrderDirectoryTraverse(
3540
dir,
3641
(dir) => fs.rmdirSync(dir),
@@ -39,45 +44,76 @@ function emptyDir(dir) {
3944
}
4045

4146
async function init() {
47+
console.log(`\n${banner}\n`)
48+
4249
const cwd = process.cwd()
4350
// possible options:
4451
// --default
4552
// --typescript / --ts
53+
// --jsx
4654
// --router / --vue-router
4755
// --pinia
48-
// --with-tests / --tests / --cypress
56+
// --with-tests / --tests (equals to `--vitest --cypress`)
57+
// --vitest
58+
// --cypress
59+
// --eslint
60+
// --eslint-with-prettier (only support prettier through eslint for simplicity)
4961
// --force (for force overwriting)
5062
const argv = minimist(process.argv.slice(2), {
5163
alias: {
5264
typescript: ['ts'],
53-
'with-tests': ['tests', 'cypress'],
65+
'with-tests': ['tests'],
5466
router: ['vue-router']
5567
},
5668
// all arguments are treated as booleans
5769
boolean: true
5870
})
5971

6072
// if any of the feature flags is set, we would skip the feature prompts
61-
// use `??` instead of `||` once we drop Node.js 12 support
6273
const isFeatureFlagsUsed =
63-
typeof (argv.default || argv.ts || argv.router || argv.pinia || argv.tests) === 'boolean'
74+
typeof (
75+
argv.default ??
76+
argv.ts ??
77+
argv.jsx ??
78+
argv.router ??
79+
argv.pinia ??
80+
argv.tests ??
81+
argv.vitest ??
82+
argv.cypress ??
83+
argv.eslint
84+
) === 'boolean'
6485

6586
let targetDir = argv._[0]
6687
const defaultProjectName = !targetDir ? 'vue-project' : targetDir
6788

6889
const forceOverwrite = argv.force
6990

70-
let result = {}
91+
let result: {
92+
projectName?: string
93+
shouldOverwrite?: boolean
94+
packageName?: string
95+
needsTypeScript?: boolean
96+
needsJsx?: boolean
97+
needsRouter?: boolean
98+
needsPinia?: boolean
99+
needsVitest?: boolean
100+
needsCypress?: boolean
101+
needsEslint?: boolean
102+
needsPrettier?: boolean
103+
} = {}
71104

72105
try {
73106
// Prompts:
74107
// - Project name:
75108
// - whether to overwrite the existing directory or not?
76109
// - enter a valid package name for package.json
77110
// - Project language: JavaScript / TypeScript
111+
// - Add JSX Support?
78112
// - Install Vue Router for SPA development?
79113
// - Install Pinia for state management?
80114
// - Add Cypress for testing?
115+
// - Add ESLint for code quality?
116+
// - Add Prettier for code formatting?
81117
result = await prompts(
82118
[
83119
{
@@ -99,7 +135,7 @@ async function init() {
99135
},
100136
{
101137
name: 'overwriteChecker',
102-
type: (prev, values = {}) => {
138+
type: (prev, values) => {
103139
if (values.shouldOverwrite === false) {
104140
throw new Error(red('✖') + ' Operation cancelled')
105141
}
@@ -121,6 +157,14 @@ async function init() {
121157
active: 'Yes',
122158
inactive: 'No'
123159
},
160+
{
161+
name: 'needsJsx',
162+
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
163+
message: 'Add JSX Support?',
164+
initial: false,
165+
active: 'Yes',
166+
inactive: 'No'
167+
},
124168
{
125169
name: 'needsRouter',
126170
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
@@ -138,9 +182,41 @@ async function init() {
138182
inactive: 'No'
139183
},
140184
{
141-
name: 'needsTests',
185+
name: 'needsVitest',
186+
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
187+
message: 'Add Vitest for Unit Testing?',
188+
initial: false,
189+
active: 'Yes',
190+
inactive: 'No'
191+
},
192+
{
193+
name: 'needsCypress',
142194
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
143-
message: 'Add Cypress for testing?',
195+
message: (prev, answers) =>
196+
answers.needsVitest
197+
? 'Add Cypress for End-to-End testing?'
198+
: 'Add Cypress for both Unit and End-to-End testing?',
199+
initial: false,
200+
active: 'Yes',
201+
inactive: 'No'
202+
},
203+
{
204+
name: 'needsEslint',
205+
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
206+
message: 'Add ESLint for code quality?',
207+
initial: false,
208+
active: 'Yes',
209+
inactive: 'No'
210+
},
211+
{
212+
name: 'needsPrettier',
213+
type: (prev, values) => {
214+
if (isFeatureFlagsUsed || !values.needsEslint) {
215+
return null
216+
}
217+
return 'toggle'
218+
},
219+
message: 'Add Prettier for code formatting?',
144220
initial: false,
145221
active: 'Yes',
146222
inactive: 'No'
@@ -160,16 +236,22 @@ async function init() {
160236
// `initial` won't take effect if the prompt type is null
161237
// so we still have to assign the default values here
162238
const {
163-
packageName = toValidPackageName(defaultProjectName),
164-
shouldOverwrite,
239+
projectName,
240+
packageName = projectName ?? defaultProjectName,
241+
shouldOverwrite = argv.force,
242+
needsJsx = argv.jsx,
165243
needsTypeScript = argv.typescript,
166244
needsRouter = argv.router,
167245
needsPinia = argv.pinia,
168-
needsTests = argv.tests
246+
needsCypress = argv.cypress || argv.tests,
247+
needsVitest = argv.vitest || argv.tests,
248+
needsEslint = argv.eslint || argv['eslint-with-prettier'],
249+
needsPrettier = argv['eslint-with-prettier']
169250
} = result
251+
const needsCypressCT = needsCypress && !needsVitest
170252
const root = path.join(cwd, targetDir)
171253

172-
if (shouldOverwrite) {
254+
if (fs.existsSync(root) && shouldOverwrite) {
173255
emptyDir(root)
174256
} else if (!fs.existsSync(root)) {
175257
fs.mkdirSync(root)
@@ -194,17 +276,43 @@ async function init() {
194276
render('base')
195277

196278
// Add configs.
279+
if (needsJsx) {
280+
render('config/jsx')
281+
}
197282
if (needsRouter) {
198283
render('config/router')
199284
}
200285
if (needsPinia) {
201286
render('config/pinia')
202287
}
203-
if (needsTests) {
288+
if (needsVitest) {
289+
render('config/vitest')
290+
}
291+
if (needsCypress) {
204292
render('config/cypress')
205293
}
294+
if (needsCypressCT) {
295+
render('config/cypress-ct')
296+
}
206297
if (needsTypeScript) {
207298
render('config/typescript')
299+
300+
// Render tsconfigs
301+
render('tsconfig/base')
302+
if (needsCypress) {
303+
render('tsconfig/cypress')
304+
}
305+
if (needsCypressCT) {
306+
render('tsconfig/cypress-ct')
307+
}
308+
if (needsVitest) {
309+
render('tsconfig/vitest')
310+
}
311+
}
312+
313+
// Render ESLint config
314+
if (needsEslint) {
315+
renderEslint(root, { needsTypeScript, needsCypress, needsCypressCT, needsPrettier })
208316
}
209317

210318
// Render code template.
@@ -227,17 +335,32 @@ async function init() {
227335

228336
// Cleanup.
229337

338+
// We try to share as many files between TypeScript and JavaScript as possible.
339+
// If that's not possible, we put `.ts` version alongside the `.js` one in the templates.
340+
// So after all the templates are rendered, we need to clean up the redundant files.
341+
// (Currently it's only `cypress/plugin/index.ts`, but we might add more in the future.)
342+
// (Or, we might completely get rid of the plugins folder as Cypress 10 supports `cypress.config.ts`)
343+
230344
if (needsTypeScript) {
231-
// rename all `.js` files to `.ts`
232-
// rename jsconfig.json to tsconfig.json
345+
// Convert the JavaScript template to the TypeScript
346+
// Check all the remaining `.js` files:
347+
// - If the corresponding TypeScript version already exists, remove the `.js` version.
348+
// - Otherwise, rename the `.js` file to `.ts`
349+
// Remove `jsconfig.json`, because we already have tsconfig.json
350+
// `jsconfig.json` is not reused, because we use solution-style `tsconfig`s, which are much more complicated.
233351
preOrderDirectoryTraverse(
234352
root,
235353
() => {},
236354
(filepath) => {
237355
if (filepath.endsWith('.js')) {
238-
fs.renameSync(filepath, filepath.replace(/\.js$/, '.ts'))
356+
const tsFilePath = filepath.replace(/\.js$/, '.ts')
357+
if (fs.existsSync(tsFilePath)) {
358+
fs.unlinkSync(filepath)
359+
} else {
360+
fs.renameSync(filepath, tsFilePath)
361+
}
239362
} else if (path.basename(filepath) === 'jsconfig.json') {
240-
fs.renameSync(filepath, filepath.replace(/jsconfig\.json$/, 'tsconfig.json'))
363+
fs.unlinkSync(filepath)
241364
}
242365
}
243366
)
@@ -246,44 +369,37 @@ async function init() {
246369
const indexHtmlPath = path.resolve(root, 'index.html')
247370
const indexHtmlContent = fs.readFileSync(indexHtmlPath, 'utf8')
248371
fs.writeFileSync(indexHtmlPath, indexHtmlContent.replace('src/main.js', 'src/main.ts'))
249-
}
250-
251-
if (!needsTests) {
252-
// All templates assumes the need of tests.
253-
// If the user doesn't need it:
254-
// rm -rf cypress **/__tests__/
372+
} else {
373+
// Remove all the remaining `.ts` files
255374
preOrderDirectoryTraverse(
256375
root,
257-
(dirpath) => {
258-
const dirname = path.basename(dirpath)
259-
260-
if (dirname === 'cypress' || dirname === '__tests__') {
261-
emptyDir(dirpath)
262-
fs.rmdirSync(dirpath)
376+
() => {},
377+
(filepath) => {
378+
if (filepath.endsWith('.ts')) {
379+
fs.unlinkSync(filepath)
263380
}
264-
},
265-
() => {}
381+
}
266382
)
267383
}
268384

269385
// Instructions:
270386
// Supported package managers: pnpm > yarn > npm
271387
// Note: until <https://github.com/pnpm/pnpm/issues/3505> is resolved,
272388
// it is not possible to tell if the command is called by `pnpm init`.
273-
const packageManager = /pnpm/.test(process.env.npm_execpath)
274-
? 'pnpm'
275-
: /yarn/.test(process.env.npm_execpath)
276-
? 'yarn'
277-
: 'npm'
389+
const userAgent = process.env.npm_config_user_agent ?? ''
390+
const packageManager = /pnpm/.test(userAgent) ? 'pnpm' : /yarn/.test(userAgent) ? 'yarn' : 'npm'
278391

279392
// README generation
280393
fs.writeFileSync(
281394
path.resolve(root, 'README.md'),
282395
generateReadme({
283-
projectName: result.projectName || defaultProjectName,
396+
projectName: result.projectName ?? defaultProjectName,
284397
packageManager,
285398
needsTypeScript,
286-
needsTests
399+
needsVitest,
400+
needsCypress,
401+
needsCypressCT,
402+
needsEslint
287403
})
288404
)
289405

@@ -292,6 +408,9 @@ async function init() {
292408
console.log(` ${bold(green(`cd ${path.relative(cwd, root)}`))}`)
293409
}
294410
console.log(` ${bold(green(getCommand(packageManager, 'install')))}`)
411+
if (needsPrettier) {
412+
console.log(` ${bold(green(getCommand(packageManager, 'lint')))}`)
413+
}
295414
console.log(` ${bold(green(getCommand(packageManager, 'dev')))}`)
296415
console.log()
297416
}

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@
3434
},
3535
"homepage": "https://github.com/vuejs/create-vue#readme",
3636
"devDependencies": {
37+
"@types/eslint": "^8.4.5",
38+
"@types/prompts": "^2.0.14",
39+
"@vue/tsconfig": "^0.1.3",
3740
"esbuild": "^0.14.49",
3841
"esbuild-plugin-license": "^1.2.2",
3942
"husky": "^8.0.1",
4043
"kolorist": "^1.5.1",
41-
"lint-staged": "^12.5.0",
44+
"lint-staged": "^13.0.3",
4245
"minimist": "^1.2.6",
4346
"npm-run-all": "^4.1.5",
4447
"prettier": "^2.7.1",

0 commit comments

Comments
 (0)