feat: support PNPM as a package manager (#1531)

Enables vue-cli to use PNPM (https://pnpm.js.org/) as package manager
This commit is contained in:
robertkruis
2019-04-08 18:43:47 +02:00
committed by Haoqun Jiang
parent a88203d3c2
commit a02ef3988f
13 changed files with 82 additions and 31 deletions
@@ -1,6 +1,7 @@
const {
info,
hasProjectYarn,
hasPnpm,
openBrowser,
IpcMessenger
} = require('@vue/cli-shared-utils')
@@ -234,7 +235,7 @@ module.exports = (api, options) => {
isFirstCompile = false
if (!isProduction) {
const buildCommand = hasProjectYarn(api.getCwd()) ? `yarn build` : `npm run build`
const buildCommand = hasProjectYarn(api.getCwd()) ? `yarn build` : hasPnpm() ? `pnpm run build` : `npm run build`
console.log(` Note that the development build is not optimized.`)
console.log(` To create a production build, run ${chalk.cyan(buildCommand)}.`)
} else {
+21
View File
@@ -2,6 +2,7 @@ const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')
const LRU = require('lru-cache')
const semver = require('semver')
let _hasYarn
const _yarnProjects = new LRU({
@@ -13,6 +14,7 @@ const _gitProjects = new LRU({
max: 10,
maxAge: 1000
})
let _hasPnpm
// env detection
exports.hasYarn = () => {
@@ -77,6 +79,25 @@ exports.hasProjectGit = (cwd) => {
return result
}
exports.hasPnpm = () => {
if (process.env.VUE_CLI_TEST) {
return true
}
if (_hasPnpm != null) {
return _hasPnpm
}
try {
const pnpmVersion = execSync('pnpm --version').toString()
// there's a critical bug in pnpm 2
// https://github.com/pnpm/pnpm/issues/1678#issuecomment-469981972
// so we only support pnpm >= 3.0.0
_hasPnpm = semver.gte(pnpmVersion, '3.0.0')
return _hasPnpm
} catch (e) {
return (_hasPnpm = false)
}
}
// OS
exports.isWindows = process.platform === 'win32'
exports.isMacintosh = process.platform === 'darwin'
@@ -8,6 +8,7 @@ scalar JSON
enum PackageManager {
npm
yarn
pnpm
}
interface DescribedEntity {
@@ -1,12 +1,13 @@
const {
hasYarn,
hasProjectYarn
hasProjectYarn,
hasPnpm
} = require('@vue/cli-shared-utils')
const { loadOptions } = require('@vue/cli/lib/options')
exports.getCommand = function (cwd = undefined) {
if (!cwd) {
return loadOptions().packageManager || (hasYarn() ? 'yarn' : 'npm')
return loadOptions().packageManager || (hasYarn() ? 'yarn' : hasPnpm() ? 'pnpm' : 'npm')
}
return hasProjectYarn(cwd) ? 'yarn' : 'npm'
return hasProjectYarn(cwd) ? 'yarn' : hasPnpm() ? 'pnpm' : 'npm'
}
@@ -96,6 +96,10 @@
value="yarn"
label="yarn"
/>
<VueSelectButton
value="pnpm"
label="pnpm"
/>
</VueSelect>
</VueFormField>
+1 -1
View File
@@ -16,7 +16,7 @@ test('default', async () => {
},
{
message: 'package manager',
choices: ['Yarn', 'NPM'],
choices: ['Yarn', 'PNPM', 'NPM'],
choose: 0
}
]
+30 -15
View File
@@ -33,6 +33,7 @@ const {
hasGit,
hasProjectGit,
hasYarn,
hasPnpm,
logWithSpinner,
stopSpinner,
exit,
@@ -97,7 +98,8 @@ module.exports = class Creator extends EventEmitter {
const packageManager = (
cliOptions.packageManager ||
loadOptions().packageManager ||
(hasYarn() ? 'yarn' : 'npm')
(hasYarn() ? 'yarn' : null) ||
(hasPnpm() ? 'pnpm' : 'npm')
)
await clearConsole()
@@ -214,7 +216,7 @@ module.exports = class Creator extends EventEmitter {
log(
`👉 Get started with the following commands:\n\n` +
(this.context === process.cwd() ? `` : chalk.cyan(` ${chalk.gray('$')} cd ${name}\n`)) +
chalk.cyan(` ${chalk.gray('$')} ${packageManager === 'yarn' ? 'yarn serve' : 'npm run serve'}`)
chalk.cyan(` ${chalk.gray('$')} ${packageManager === 'yarn' ? 'yarn serve' : packageManager === 'pnpm' ? 'pnpm run serve' : 'npm run serve'}`)
)
}
log()
@@ -410,23 +412,36 @@ module.exports = class Creator extends EventEmitter {
// ask for packageManager once
const savedOptions = loadOptions()
if (!savedOptions.packageManager && hasYarn()) {
if (!savedOptions.packageManager && (hasYarn() || hasPnpm())) {
const packageManagerChoices = []
if (hasYarn()) {
packageManagerChoices.push({
name: 'Use Yarn',
value: 'yarn',
short: 'Yarn'
})
}
if (hasPnpm()) {
packageManagerChoices.push({
name: 'Use PNPM',
value: 'pnpm',
short: 'PNPM'
})
}
packageManagerChoices.push({
name: 'Use NPM',
value: 'npm',
short: 'NPM'
})
outroPrompts.push({
name: 'packageManager',
type: 'list',
message: 'Pick the package manager to use when installing dependencies:',
choices: [
{
name: 'Use Yarn',
value: 'yarn',
short: 'Yarn'
},
{
name: 'Use NPM',
value: 'npm',
short: 'NPM'
}
]
choices: packageManagerChoices
})
}
+2 -1
View File
@@ -6,6 +6,7 @@ const {
log,
error,
hasProjectYarn,
hasPnpm,
resolvePluginId,
resolveModule,
loadModule
@@ -26,7 +27,7 @@ async function add (pluginName, options = {}, context = process.cwd()) {
log(`📦 Installing ${chalk.cyan(packageName)}...`)
log()
const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : 'npm')
const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : hasPnpm() ? 'pnpm' : 'npm')
await installPackage(context, packageManager, options.registry, packageName)
log(`${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}`)
+2 -1
View File
@@ -14,6 +14,7 @@ const {
error,
hasProjectYarn,
hasProjectGit,
hasPnpm,
logWithSpinner,
stopSpinner,
resolvePluginId,
@@ -138,7 +139,7 @@ async function runGenerator (context, plugin, pkg = getPkg(context)) {
log(`📦 Installing additional dependencies...`)
log()
const packageManager =
loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : 'npm')
loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : hasPnpm() ? 'pnpm' : 'npm')
await installDeps(context, packageManager, plugin.options && plugin.options.registry)
}
+1 -1
View File
@@ -22,7 +22,7 @@ const presetSchema = createSchema(joi => joi.object().keys({
const schema = createSchema(joi => joi.object().keys({
latestVersion: joi.string().regex(/^\d+\.\d+\.\d+$/),
lastChecked: joi.date().timestamp(),
packageManager: joi.string().only(['yarn', 'npm']),
packageManager: joi.string().only(['yarn', 'npm', 'pnpm']),
useTaobaoRegistry: joi.boolean(),
presets: joi.object().pattern(/^/, presetSchema)
}))
+5 -5
View File
@@ -9,7 +9,7 @@ const debug = require('debug')('vue-cli:install')
const taobaoDistURL = 'https://npm.taobao.org/dist'
const supportPackageManagerList = ['npm', 'yarn']
const supportPackageManagerList = ['npm', 'yarn', 'pnpm']
class InstallProgress extends EventEmitter {
constructor () {
@@ -176,7 +176,7 @@ exports.installDeps = async function installDeps (targetDir, command, cliRegistr
const args = []
if (command === 'npm') {
if (command === 'npm' || command === 'pnpm') {
args.push('install', '--loglevel', 'error')
} else if (command === 'yarn') {
// do nothing
@@ -195,7 +195,7 @@ exports.installPackage = async function (targetDir, command, cliRegistry, packag
const args = []
if (command === 'npm') {
if (command === 'npm' || command === 'pnpm') {
args.push('install', '--loglevel', 'error')
} else if (command === 'yarn') {
args.push('add')
@@ -218,7 +218,7 @@ exports.uninstallPackage = async function (targetDir, command, cliRegistry, pack
const args = []
if (command === 'npm') {
if (command === 'npm' || command === 'pnpm') {
args.push('uninstall', '--loglevel', 'error')
} else if (command === 'yarn') {
args.push('remove')
@@ -239,7 +239,7 @@ exports.updatePackage = async function (targetDir, command, cliRegistry, package
const args = []
if (command === 'npm') {
if (command === 'npm' || command === 'pnpm') {
args.push('update', '--loglevel', 'error')
} else if (command === 'yarn') {
args.push('upgrade')
+7 -2
View File
@@ -11,8 +11,13 @@ module.exports = function loadCommand (commandName, moduleName) {
} catch (err2) {
if (isNotFoundError(err2)) {
const chalk = require('chalk')
const { hasYarn } = require('@vue/cli-shared-utils')
const installCommand = hasYarn() ? `yarn global add` : `npm install -g`
const { hasYarn, hasPnpm } = require('@vue/cli-shared-utils')
let installCommand = `npm install -g`
if (hasYarn()) {
installCommand = `yarn global add`
} else if (hasPnpm()) {
installCommand = `pnpm install -g`
}
console.log()
console.log(
` Command ${chalk.cyan(`vue ${commandName}`)} requires a global addon to be installed.\n` +
+2 -1
View File
@@ -1,7 +1,8 @@
const registries = {
npm: 'https://registry.npmjs.org',
yarn: 'https://registry.yarnpkg.com',
taobao: 'https://registry.npm.taobao.org'
taobao: 'https://registry.npm.taobao.org',
pnpm: 'https://registry.npmjs.org'
}
module.exports = registries