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 ({
|
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 |
|
59 | npm_config_color: 'always',
|
60 | npm_config_progress: true
|
61 |
|
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 |
|
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 |
|
114 | module.exports.getNpmClient = getNpmClient
|
115 | module.exports.setNpmClient = setNpmClient
|