mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-03-13 12:40:18 -05:00
feat: implement vue upgrade (#2428)
* feat: add vue upgrade command * feat: implement vue upgrade
This commit is contained in:
3
packages/@vue/cli-upgrade/bin/vue-cli-upgrade.js
Normal file
3
packages/@vue/cli-upgrade/bin/vue-cli-upgrade.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const vueCliUpgrade = require('../index')
|
||||
|
||||
vueCliUpgrade()
|
||||
14
packages/@vue/cli-upgrade/get-installed-version.js
Normal file
14
packages/@vue/cli-upgrade/get-installed-version.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const path = require('path')
|
||||
const getPackageJson = require('./get-package-json')
|
||||
|
||||
module.exports = function getInstalledVersion (packageName) {
|
||||
// for first level deps, read package.json directly is way faster than `npm list`
|
||||
try {
|
||||
const packageJson = getPackageJson(
|
||||
path.resolve(process.cwd(), 'node_modules', packageName)
|
||||
)
|
||||
return packageJson.version
|
||||
} catch (e) {
|
||||
return 'N/A'
|
||||
}
|
||||
}
|
||||
21
packages/@vue/cli-upgrade/get-package-json.js
Normal file
21
packages/@vue/cli-upgrade/get-package-json.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = function getPackageJson (projectPath) {
|
||||
const packagePath = path.join(projectPath, 'package.json')
|
||||
|
||||
let packageJson
|
||||
try {
|
||||
packageJson = fs.readFileSync(packagePath, 'utf-8')
|
||||
} catch (err) {
|
||||
throw new Error(`${packagePath} not exist`)
|
||||
}
|
||||
|
||||
try {
|
||||
packageJson = JSON.parse(packageJson)
|
||||
} catch (err) {
|
||||
throw new Error('The package.json is malformed')
|
||||
}
|
||||
|
||||
return packageJson
|
||||
}
|
||||
38
packages/@vue/cli-upgrade/get-upgradable-version.js
Normal file
38
packages/@vue/cli-upgrade/get-upgradable-version.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const execa = require('execa')
|
||||
|
||||
function getMaxSatisfying (packageName, range) {
|
||||
let version = JSON.parse(
|
||||
execa.shellSync(`npm view ${packageName}@${range} version --json`).stdout
|
||||
)
|
||||
|
||||
if (typeof version !== 'string') {
|
||||
version = version[0]
|
||||
}
|
||||
|
||||
return version
|
||||
}
|
||||
|
||||
module.exports = function getUpgradableVersion (
|
||||
packageName,
|
||||
currRange,
|
||||
semverLevel
|
||||
) {
|
||||
let newRange
|
||||
if (semverLevel === 'patch') {
|
||||
const currMaxVersion = getMaxSatisfying(packageName, currRange)
|
||||
newRange = `~${currMaxVersion}`
|
||||
const newMaxVersion = getMaxSatisfying(packageName, newRange)
|
||||
newRange = `~${newMaxVersion}`
|
||||
} else if (semverLevel === 'minor') {
|
||||
const currMaxVersion = getMaxSatisfying(packageName, currRange)
|
||||
newRange = `^${currMaxVersion}`
|
||||
const newMaxVersion = getMaxSatisfying(packageName, newRange)
|
||||
newRange = `^${newMaxVersion}`
|
||||
} else if (semverLevel === 'major') {
|
||||
newRange = `^${getMaxSatisfying(packageName, 'latest')}`
|
||||
} else {
|
||||
throw new Error('Release type must be one of patch | minor | major!')
|
||||
}
|
||||
|
||||
return newRange
|
||||
}
|
||||
153
packages/@vue/cli-upgrade/index.js
Normal file
153
packages/@vue/cli-upgrade/index.js
Normal file
@@ -0,0 +1,153 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const chalk = require('chalk')
|
||||
const Table = require('cli-table')
|
||||
const inquirer = require('inquirer')
|
||||
|
||||
/* eslint-disable node/no-extraneous-require */
|
||||
const {
|
||||
hasYarn,
|
||||
logWithSpinner,
|
||||
stopSpinner
|
||||
} = require('@vue/cli-shared-utils')
|
||||
const { loadOptions } = require('@vue/cli/lib/options')
|
||||
const { installDeps } = require('@vue/cli/lib/util/installDeps')
|
||||
/* eslint-enable node/no-extraneous-require */
|
||||
|
||||
const getPackageJson = require('./get-package-json')
|
||||
const getInstalledVersion = require('./get-installed-version')
|
||||
const getUpgradableVersion = require('./get-upgradable-version')
|
||||
|
||||
const projectPath = process.cwd()
|
||||
|
||||
// - Resolve the version to upgrade to.
|
||||
// - `vue upgrade [patch|minor|major]`: defaults to minor
|
||||
// - If already latest, print message and exit
|
||||
// - Otherwise, confirm via prompt
|
||||
|
||||
function isCorePackage (packageName) {
|
||||
return (
|
||||
packageName === '@vue/cli-service' ||
|
||||
packageName.startsWith('@vue/cli-plugin-')
|
||||
)
|
||||
}
|
||||
|
||||
function shouldUseYarn () {
|
||||
// infer from lockfiles first
|
||||
if (fs.existsSync(path.resolve(projectPath, 'package-lock.json'))) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (fs.existsSync(path.resolve(projectPath, 'yarn.lock')) && hasYarn()) {
|
||||
return true
|
||||
}
|
||||
|
||||
// fallback to packageManager field in ~/.vuerc
|
||||
const { packageManager } = loadOptions()
|
||||
if (packageManager) {
|
||||
return packageManager === 'yarn'
|
||||
}
|
||||
|
||||
return hasYarn()
|
||||
}
|
||||
|
||||
module.exports = async function vueCliUpgrade (semverLevel = 'minor') {
|
||||
// get current deps
|
||||
// filter @vue/cli-service & @vue/cli-plugin-*
|
||||
const pkg = getPackageJson(projectPath)
|
||||
const upgradableDepMaps = new Map([
|
||||
['dependencies', new Map()],
|
||||
['devDependencies', new Map()],
|
||||
['optionalDependencies', new Map()]
|
||||
])
|
||||
|
||||
logWithSpinner('Gathering update information...')
|
||||
for (const depType of upgradableDepMaps.keys()) {
|
||||
for (const [packageName, currRange] of Object.entries(pkg[depType] || {})) {
|
||||
if (!isCorePackage(packageName)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const upgradable = getUpgradableVersion(
|
||||
packageName,
|
||||
currRange,
|
||||
semverLevel
|
||||
)
|
||||
if (upgradable !== currRange) {
|
||||
upgradableDepMaps.get(depType).set(packageName, upgradable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const table = new Table({
|
||||
head: ['package', 'installed', '', 'upgraded'],
|
||||
colAligns: ['left', 'right', 'right', 'right'],
|
||||
chars: {
|
||||
top: '',
|
||||
'top-mid': '',
|
||||
'top-left': '',
|
||||
'top-right': '',
|
||||
bottom: '',
|
||||
'bottom-mid': '',
|
||||
'bottom-left': '',
|
||||
'bottom-right': '',
|
||||
left: '',
|
||||
'left-mid': '',
|
||||
mid: '',
|
||||
'mid-mid': '',
|
||||
right: '',
|
||||
'right-mid': '',
|
||||
middle: ''
|
||||
}
|
||||
})
|
||||
|
||||
for (const [depType, depMap] of upgradableDepMaps.entries()) {
|
||||
for (const packageName of depMap.keys()) {
|
||||
const installedVersion = getInstalledVersion(packageName)
|
||||
const upgradedVersion = depMap.get(packageName)
|
||||
table.push([packageName, installedVersion, '→', upgradedVersion])
|
||||
|
||||
pkg[depType][packageName] = upgradedVersion
|
||||
}
|
||||
}
|
||||
|
||||
stopSpinner()
|
||||
|
||||
if ([...upgradableDepMaps.values()].every(depMap => depMap.size === 0)) {
|
||||
console.log('Already up-to-date.')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('These packages can be upgraded:\n')
|
||||
console.log(table.toString())
|
||||
console.log(
|
||||
`\nView complete changelog at ${chalk.blue(
|
||||
'https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md'
|
||||
)}\n`
|
||||
)
|
||||
|
||||
const useYarn = shouldUseYarn()
|
||||
const { confirmed } = await inquirer.prompt([
|
||||
{
|
||||
name: 'confirmed',
|
||||
type: 'confirm',
|
||||
message: `Upgrade ${chalk.yellow('package.json')} and run ${chalk.blue(
|
||||
useYarn ? 'yarn install' : 'npm install'
|
||||
)}?`
|
||||
}
|
||||
])
|
||||
|
||||
if (!confirmed) {
|
||||
return
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.resolve(projectPath, 'package.json'), JSON.stringify(pkg, null, 2))
|
||||
console.log()
|
||||
console.log(`${chalk.yellow('package.json')} saved`)
|
||||
if (useYarn) {
|
||||
await installDeps(projectPath, 'yarn')
|
||||
} else {
|
||||
await installDeps(projectPath, 'npm')
|
||||
}
|
||||
}
|
||||
28
packages/@vue/cli-upgrade/package.json
Normal file
28
packages/@vue/cli-upgrade/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@vue/cli-upgrade",
|
||||
"version": "3.0.1",
|
||||
"description": "utility to upgrade vue cli service / plugins in vue apps",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/vue-cli.git"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"cli",
|
||||
"upgrade",
|
||||
"update"
|
||||
],
|
||||
"author": "Haoqun Jiang <haoqunjiang+npm@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/vue-cli/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/cli-upgrade#readme",
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.1",
|
||||
"cli-table": "^0.3.1",
|
||||
"execa": "^0.10.0",
|
||||
"inquirer": "^6.0.0"
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,13 @@ program
|
||||
require('../lib/config')(value, cleanArgs(cmd))
|
||||
})
|
||||
|
||||
program
|
||||
.command('upgrade [semverLevel]')
|
||||
.description('upgrade vue cli service / plugins (default semverLevel: minor)')
|
||||
.action((semverLevel, cmd) => {
|
||||
loadCommand('upgrade', '@vue/cli-upgrade')(semverLevel, cleanArgs(cmd))
|
||||
})
|
||||
|
||||
// output help information on unknown commands
|
||||
program
|
||||
.arguments('<command>')
|
||||
@@ -189,12 +196,16 @@ if (!process.argv.slice(2).length) {
|
||||
program.outputHelp()
|
||||
}
|
||||
|
||||
function camelize (str) {
|
||||
return str.replace(/-(\w)/g, (_, c) => c ? c.toUpperCase() : '')
|
||||
}
|
||||
|
||||
// commander passes the Command object itself as options,
|
||||
// extract only actual options into a fresh object.
|
||||
function cleanArgs (cmd) {
|
||||
const args = {}
|
||||
cmd.options.forEach(o => {
|
||||
const key = o.long.replace(/^--/, '')
|
||||
const key = camelize(o.long.replace(/^--/, ''))
|
||||
// if an option is not present and Command has a method with the same name
|
||||
// it should not be copied
|
||||
if (typeof cmd[key] !== 'function' && typeof cmd[key] !== 'undefined') {
|
||||
|
||||
@@ -3601,6 +3601,12 @@ cli-spinners@^1.0.1, cli-spinners@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a"
|
||||
integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==
|
||||
|
||||
cli-table@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "http://registry.npm.taobao.org/cli-table/download/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
|
||||
dependencies:
|
||||
colors "1.0.3"
|
||||
|
||||
cli-truncate@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
|
||||
|
||||
Reference in New Issue
Block a user