Skip to content

Commit a02ef39

Browse files
robertkruishaoqunjiang
authored andcommitted
feat: support PNPM as a package manager (#1531)
Enables vue-cli to use PNPM (https://pnpm.js.org/) as package manager
1 parent a88203d commit a02ef39

File tree

13 files changed

+82
-31
lines changed

13 files changed

+82
-31
lines changed

packages/@vue/cli-service/lib/commands/serve.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const {
22
info,
33
hasProjectYarn,
4+
hasPnpm,
45
openBrowser,
56
IpcMessenger
67
} = require('@vue/cli-shared-utils')
@@ -234,7 +235,7 @@ module.exports = (api, options) => {
234235
isFirstCompile = false
235236

236237
if (!isProduction) {
237-
const buildCommand = hasProjectYarn(api.getCwd()) ? `yarn build` : `npm run build`
238+
const buildCommand = hasProjectYarn(api.getCwd()) ? `yarn build` : hasPnpm() ? `pnpm run build` : `npm run build`
238239
console.log(` Note that the development build is not optimized.`)
239240
console.log(` To create a production build, run ${chalk.cyan(buildCommand)}.`)
240241
} else {

packages/@vue/cli-shared-utils/lib/env.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const { execSync } = require('child_process')
22
const fs = require('fs')
33
const path = require('path')
44
const LRU = require('lru-cache')
5+
const semver = require('semver')
56

67
let _hasYarn
78
const _yarnProjects = new LRU({
@@ -13,6 +14,7 @@ const _gitProjects = new LRU({
1314
max: 10,
1415
maxAge: 1000
1516
})
17+
let _hasPnpm
1618

1719
// env detection
1820
exports.hasYarn = () => {
@@ -77,6 +79,25 @@ exports.hasProjectGit = (cwd) => {
7779
return result
7880
}
7981

82+
exports.hasPnpm = () => {
83+
if (process.env.VUE_CLI_TEST) {
84+
return true
85+
}
86+
if (_hasPnpm != null) {
87+
return _hasPnpm
88+
}
89+
try {
90+
const pnpmVersion = execSync('pnpm --version').toString()
91+
// there's a critical bug in pnpm 2
92+
// https://github.com/pnpm/pnpm/issues/1678#issuecomment-469981972
93+
// so we only support pnpm >= 3.0.0
94+
_hasPnpm = semver.gte(pnpmVersion, '3.0.0')
95+
return _hasPnpm
96+
} catch (e) {
97+
return (_hasPnpm = false)
98+
}
99+
}
100+
80101
// OS
81102
exports.isWindows = process.platform === 'win32'
82103
exports.isMacintosh = process.platform === 'darwin'

packages/@vue/cli-ui/apollo-server/type-defs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ scalar JSON
88
enum PackageManager {
99
npm
1010
yarn
11+
pnpm
1112
}
1213
1314
interface DescribedEntity {
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
const {
22
hasYarn,
3-
hasProjectYarn
3+
hasProjectYarn,
4+
hasPnpm
45
} = require('@vue/cli-shared-utils')
56
const { loadOptions } = require('@vue/cli/lib/options')
67

78
exports.getCommand = function (cwd = undefined) {
89
if (!cwd) {
9-
return loadOptions().packageManager || (hasYarn() ? 'yarn' : 'npm')
10+
return loadOptions().packageManager || (hasYarn() ? 'yarn' : hasPnpm() ? 'pnpm' : 'npm')
1011
}
11-
return hasProjectYarn(cwd) ? 'yarn' : 'npm'
12+
return hasProjectYarn(cwd) ? 'yarn' : hasPnpm() ? 'pnpm' : 'npm'
1213
}

packages/@vue/cli-ui/src/components/project-create/ProjectCreate.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@
9696
value="yarn"
9797
label="yarn"
9898
/>
99+
<VueSelectButton
100+
value="pnpm"
101+
label="pnpm"
102+
/>
99103
</VueSelect>
100104
</VueFormField>
101105

packages/@vue/cli/__tests__/Creator.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ test('default', async () => {
1616
},
1717
{
1818
message: 'package manager',
19-
choices: ['Yarn', 'NPM'],
19+
choices: ['Yarn', 'PNPM', 'NPM'],
2020
choose: 0
2121
}
2222
]

packages/@vue/cli/lib/Creator.js

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const {
3333
hasGit,
3434
hasProjectGit,
3535
hasYarn,
36+
hasPnpm,
3637
logWithSpinner,
3738
stopSpinner,
3839
exit,
@@ -97,7 +98,8 @@ module.exports = class Creator extends EventEmitter {
9798
const packageManager = (
9899
cliOptions.packageManager ||
99100
loadOptions().packageManager ||
100-
(hasYarn() ? 'yarn' : 'npm')
101+
(hasYarn() ? 'yarn' : null) ||
102+
(hasPnpm() ? 'pnpm' : 'npm')
101103
)
102104

103105
await clearConsole()
@@ -214,7 +216,7 @@ module.exports = class Creator extends EventEmitter {
214216
log(
215217
`👉 Get started with the following commands:\n\n` +
216218
(this.context === process.cwd() ? `` : chalk.cyan(` ${chalk.gray('$')} cd ${name}\n`)) +
217-
chalk.cyan(` ${chalk.gray('$')} ${packageManager === 'yarn' ? 'yarn serve' : 'npm run serve'}`)
219+
chalk.cyan(` ${chalk.gray('$')} ${packageManager === 'yarn' ? 'yarn serve' : packageManager === 'pnpm' ? 'pnpm run serve' : 'npm run serve'}`)
218220
)
219221
}
220222
log()
@@ -410,23 +412,36 @@ module.exports = class Creator extends EventEmitter {
410412

411413
// ask for packageManager once
412414
const savedOptions = loadOptions()
413-
if (!savedOptions.packageManager && hasYarn()) {
415+
if (!savedOptions.packageManager && (hasYarn() || hasPnpm())) {
416+
const packageManagerChoices = []
417+
418+
if (hasYarn()) {
419+
packageManagerChoices.push({
420+
name: 'Use Yarn',
421+
value: 'yarn',
422+
short: 'Yarn'
423+
})
424+
}
425+
426+
if (hasPnpm()) {
427+
packageManagerChoices.push({
428+
name: 'Use PNPM',
429+
value: 'pnpm',
430+
short: 'PNPM'
431+
})
432+
}
433+
434+
packageManagerChoices.push({
435+
name: 'Use NPM',
436+
value: 'npm',
437+
short: 'NPM'
438+
})
439+
414440
outroPrompts.push({
415441
name: 'packageManager',
416442
type: 'list',
417443
message: 'Pick the package manager to use when installing dependencies:',
418-
choices: [
419-
{
420-
name: 'Use Yarn',
421-
value: 'yarn',
422-
short: 'Yarn'
423-
},
424-
{
425-
name: 'Use NPM',
426-
value: 'npm',
427-
short: 'NPM'
428-
}
429-
]
444+
choices: packageManagerChoices
430445
})
431446
}
432447

packages/@vue/cli/lib/add.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
log,
77
error,
88
hasProjectYarn,
9+
hasPnpm,
910
resolvePluginId,
1011
resolveModule,
1112
loadModule
@@ -26,7 +27,7 @@ async function add (pluginName, options = {}, context = process.cwd()) {
2627
log(`📦 Installing ${chalk.cyan(packageName)}...`)
2728
log()
2829

29-
const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : 'npm')
30+
const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : hasPnpm() ? 'pnpm' : 'npm')
3031
await installPackage(context, packageManager, options.registry, packageName)
3132

3233
log(`${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}`)

packages/@vue/cli/lib/invoke.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
error,
1515
hasProjectYarn,
1616
hasProjectGit,
17+
hasPnpm,
1718
logWithSpinner,
1819
stopSpinner,
1920
resolvePluginId,
@@ -138,7 +139,7 @@ async function runGenerator (context, plugin, pkg = getPkg(context)) {
138139
log(`📦 Installing additional dependencies...`)
139140
log()
140141
const packageManager =
141-
loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : 'npm')
142+
loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : hasPnpm() ? 'pnpm' : 'npm')
142143
await installDeps(context, packageManager, plugin.options && plugin.options.registry)
143144
}
144145

packages/@vue/cli/lib/options.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const presetSchema = createSchema(joi => joi.object().keys({
2222
const schema = createSchema(joi => joi.object().keys({
2323
latestVersion: joi.string().regex(/^\d+\.\d+\.\d+$/),
2424
lastChecked: joi.date().timestamp(),
25-
packageManager: joi.string().only(['yarn', 'npm']),
25+
packageManager: joi.string().only(['yarn', 'npm', 'pnpm']),
2626
useTaobaoRegistry: joi.boolean(),
2727
presets: joi.object().pattern(/^/, presetSchema)
2828
}))

packages/@vue/cli/lib/util/installDeps.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const debug = require('debug')('vue-cli:install')
99

1010
const taobaoDistURL = 'https://npm.taobao.org/dist'
1111

12-
const supportPackageManagerList = ['npm', 'yarn']
12+
const supportPackageManagerList = ['npm', 'yarn', 'pnpm']
1313

1414
class InstallProgress extends EventEmitter {
1515
constructor () {
@@ -176,7 +176,7 @@ exports.installDeps = async function installDeps (targetDir, command, cliRegistr
176176

177177
const args = []
178178

179-
if (command === 'npm') {
179+
if (command === 'npm' || command === 'pnpm') {
180180
args.push('install', '--loglevel', 'error')
181181
} else if (command === 'yarn') {
182182
// do nothing
@@ -195,7 +195,7 @@ exports.installPackage = async function (targetDir, command, cliRegistry, packag
195195

196196
const args = []
197197

198-
if (command === 'npm') {
198+
if (command === 'npm' || command === 'pnpm') {
199199
args.push('install', '--loglevel', 'error')
200200
} else if (command === 'yarn') {
201201
args.push('add')
@@ -218,7 +218,7 @@ exports.uninstallPackage = async function (targetDir, command, cliRegistry, pack
218218

219219
const args = []
220220

221-
if (command === 'npm') {
221+
if (command === 'npm' || command === 'pnpm') {
222222
args.push('uninstall', '--loglevel', 'error')
223223
} else if (command === 'yarn') {
224224
args.push('remove')
@@ -239,7 +239,7 @@ exports.updatePackage = async function (targetDir, command, cliRegistry, package
239239

240240
const args = []
241241

242-
if (command === 'npm') {
242+
if (command === 'npm' || command === 'pnpm') {
243243
args.push('update', '--loglevel', 'error')
244244
} else if (command === 'yarn') {
245245
args.push('upgrade')

packages/@vue/cli/lib/util/loadCommand.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@ module.exports = function loadCommand (commandName, moduleName) {
1111
} catch (err2) {
1212
if (isNotFoundError(err2)) {
1313
const chalk = require('chalk')
14-
const { hasYarn } = require('@vue/cli-shared-utils')
15-
const installCommand = hasYarn() ? `yarn global add` : `npm install -g`
14+
const { hasYarn, hasPnpm } = require('@vue/cli-shared-utils')
15+
let installCommand = `npm install -g`
16+
if (hasYarn()) {
17+
installCommand = `yarn global add`
18+
} else if (hasPnpm()) {
19+
installCommand = `pnpm install -g`
20+
}
1621
console.log()
1722
console.log(
1823
` Command ${chalk.cyan(`vue ${commandName}`)} requires a global addon to be installed.\n` +
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
const registries = {
22
npm: 'https://registry.npmjs.org',
33
yarn: 'https://registry.yarnpkg.com',
4-
taobao: 'https://registry.npm.taobao.org'
4+
taobao: 'https://registry.npm.taobao.org',
5+
pnpm: 'https://registry.npmjs.org'
56
}
67

78
module.exports = registries

0 commit comments

Comments
 (0)