UNPKG

2.91 kBJavaScriptView Raw
1const readline = require('readline')
2const stripAnsi = require('strip-ansi')
3const spawn = require('cross-spawn')
4const wcwidth = require('wcwidth')
5const spinner = require('./spinner')
6const logger = require('./logger')
7const SAOError = require('./SAOError')
8
9let cachedNpmClient = null
10
11function setNpmClient(npmClient) {
12 if (npmClient) {
13 cachedNpmClient = npmClient
14 return npmClient
15 }
16
17 const { stdout, status } = spawn.sync('yarn', ['--version'])
18 cachedNpmClient = status === 0 && stdout ? 'yarn' : 'npm'
19 return cachedNpmClient
20}
21
22function getNpmClient() {
23 return cachedNpmClient || setNpmClient()
24}
25
26module.exports = async ({
27 cwd,
28 npmClient,
29 installArgs,
30 packages,
31 saveDev,
32 registry
33}) => {
34 npmClient = npmClient || getNpmClient()
35 const packageName = packages ? packages.join(', ') : 'packages'
36
37 return new Promise((resolve, reject) => {
38 const args = [packages ? 'add' : 'install'].concat(packages ? packages : [])
39 if (saveDev) {
40 args.push(npmClient === 'npm' ? '-D' : '--dev')
41 }
42 if (registry) {
43 args.push('--registry', registry)
44 }
45
46 installArgs = args.concat(installArgs || [])
47 logger.debug(npmClient, installArgs.join(' '))
48 spinner.start(`Installing ${packageName} with ${npmClient}`)
49 const ps = spawn(npmClient, args.concat(installArgs || []), {
50 stdio: [0, 'pipe', 'pipe'],
51 cwd,
52 env: Object.assign(
53 {
54 FORCE_COLOR: true,
55 /* eslint-disable camelcase */
56 npm_config_color: 'always',
57 npm_config_progress: true
58 /* eslint-enable camelcase */
59 },
60 process.env
61 )
62 })
63
64 let output = ''
65 const stream = process.stderr
66
67 ps.stdout &&
68 ps.stdout.on('data', data => {
69 output += data
70 spinner.stop()
71 stream.write(data)
72 spinner.start()
73 })
74
75 ps.stderr &&
76 ps.stderr.on('data', data => {
77 output += data
78 spinner.stop()
79 stream.write(data)
80 spinner.start()
81 })
82
83 ps.on('close', code => {
84 spinner.stop()
85 // Clear output when succeeded
86 if (code === 0) {
87 const columns = stream.columns || 80
88 const lineCount = stripAnsi(output)
89 .split('\n')
90 .reduce((count, line) => {
91 return count + Math.max(1, Math.ceil(wcwidth(line) / columns))
92 }, 0)
93 for (let i = 0; i < lineCount; i++) {
94 if (i > 0) {
95 readline.moveCursor(stream, 0, -1)
96 }
97 readline.clearLine(stream, 0)
98 readline.cursorTo(stream, 0)
99 }
100 logger.success(`Installed ${packageName}`)
101 } else {
102 throw new SAOError(`Failed to install ${packageName} in ${cwd}`)
103 }
104 resolve({ code, npmClient })
105 })
106
107 ps.on('error', reject)
108 })
109}
110
111module.exports.getNpmClient = getNpmClient
112module.exports.setNpmClient = setNpmClient