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