UNPKG

2.89 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 if (installArgs) {
47 args.push(...installArgs)
48 }
49
50 logger.debug(npmClient, args.join(' '))
51 spinner.start(`Installing ${packageName} with ${npmClient}`)
52 const ps = spawn(npmClient, args, {
53 stdio: [0, 'pipe', 'pipe'],
54 cwd,
55 env: Object.assign(
56 {
57 FORCE_COLOR: true,
58 /* eslint-disable camelcase */
59 npm_config_color: 'always',
60 npm_config_progress: true
61 /* eslint-enable camelcase */
62 },
63 process.env
64 )
65 })
66
67 let output = ''
68 const stream = process.stderr
69
70 ps.stdout &&
71 ps.stdout.on('data', data => {
72 output += data
73 spinner.stop()
74 stream.write(data)
75 spinner.start()
76 })
77
78 ps.stderr &&
79 ps.stderr.on('data', data => {
80 output += data
81 spinner.stop()
82 stream.write(data)
83 spinner.start()
84 })
85
86 ps.on('close', code => {
87 spinner.stop()
88 // Clear output when succeeded
89 if (code === 0) {
90 const columns = stream.columns || 80
91 const lineCount = stripAnsi(output)
92 .split('\n')
93 .reduce((count, line) => {
94 return count + Math.max(1, Math.ceil(wcwidth(line) / columns))
95 }, 0)
96 for (let i = 0; i < lineCount; i++) {
97 if (i > 0) {
98 readline.moveCursor(stream, 0, -1)
99 }
100 readline.clearLine(stream, 0)
101 readline.cursorTo(stream, 0)
102 }
103 logger.success(`Installed ${packageName}`)
104 } else {
105 throw new SAOError(`Failed to install ${packageName} in ${cwd}`)
106 }
107 resolve({ code, npmClient })
108 })
109
110 ps.on('error', reject)
111 })
112}
113
114module.exports.getNpmClient = getNpmClient
115module.exports.setNpmClient = setNpmClient