Skip to content
This repository was archived by the owner on May 16, 2025. It is now read-only.

Commit 0051d1f

Browse files
authored
feat: add prompt option (#189)
1 parent 5ccad37 commit 0051d1f

File tree

6 files changed

+100
-49
lines changed

6 files changed

+100
-49
lines changed

package-lock.json

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
},
4747
"homepage": "https://github.com/webpack-contrib/install-webpack-plugin#readme",
4848
"dependencies": {
49+
"colorette": "^1.2.2",
4950
"cross-spawn": "^7.0.3",
5051
"json5": "^2.2.0",
5152
"memfs": "^3.2.2",

src/installer.js

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
/* eslint-disable no-console, consistent-return,no-useless-escape */
22
const fs = require('fs');
33
const path = require('path');
4-
54
const util = require('util');
65

76
const resolve = require('resolve');
8-
97
const spawn = require('cross-spawn');
10-
118
const JSON5 = require('json5');
129

10+
const { green, yellow } = require('colorette');
11+
1312
// Match "react", "path", "fs", "lodash.random", etc.
1413
const EXTERNAL = /^\w[a-z\-0-9\.]+$/;
1514
const PEERS = /UNMET PEER DEPENDENCY ([a-z\-0-9\.]+)@(.+)/gm;
@@ -18,6 +17,7 @@ const defaultOptions = {
1817
dev: false,
1918
peerDependencies: true,
2019
quiet: false,
20+
prompt: true,
2121
npm: 'npm',
2222
};
2323
const erroneous = [];
@@ -34,6 +34,30 @@ function normalizeBabelPlugin(plugin, prefix) {
3434
return prefix + plugin;
3535
}
3636

37+
module.exports.prompt = ({ message, defaultResponse, stream }) => {
38+
const readline = require('readline');
39+
const rl = readline.createInterface({
40+
input: process.stdin,
41+
output: stream,
42+
});
43+
44+
return new Promise((resolve) => {
45+
rl.question(`${message} `, (answer) => {
46+
// Close the stream
47+
rl.close();
48+
49+
const response = (answer || defaultResponse).toLowerCase();
50+
51+
// Resolve with the input response
52+
if (response === 'y' || response === 'yes') {
53+
resolve(true);
54+
} else {
55+
resolve(false);
56+
}
57+
});
58+
});
59+
};
60+
3761
module.exports.packageExists = function packageExists() {
3862
const pkgPath = path.resolve('package.json');
3963
try {
@@ -72,15 +96,17 @@ module.exports.check = function check(request) {
7296
return dep;
7397
};
7498

75-
module.exports.checkBabel = function checkBabel() {
99+
module.exports.checkBabel = function checkBabel(pluginOptions) {
76100
let babelOpts;
77101
let babelrc;
78102
try {
79-
babelrc = require.resolve(path.resolve('.babelrc'));
103+
babelrc = require.resolve(path.join(process.cwd(), '.babelrc'));
80104
babelOpts = JSON5.parse(fs.readFileSync(babelrc, 'utf8'));
81105
} catch (e) {
82106
try {
83-
const babelConfigJs = require.resolve(path.resolve('babel.config.js'));
107+
const babelConfigJs = require.resolve(
108+
path.join(process.cwd(), '.babelrc')
109+
);
84110
// eslint-disable-next-line
85111
babelOpts = require(babelConfigJs);
86112
} catch (e2) {
@@ -146,12 +172,12 @@ module.exports.checkBabel = function checkBabel() {
146172
const missing = deps.filter((dep) => this.check(dep));
147173

148174
// Install missing dependencies
149-
this.install(missing);
175+
this.install(missing, pluginOptions);
150176
};
151177

152178
module.exports.defaultOptions = defaultOptions;
153179

154-
module.exports.install = function install(deps, options) {
180+
module.exports.install = async function install(deps, options) {
155181
if (!deps) {
156182
return;
157183
}
@@ -188,6 +214,27 @@ module.exports.install = function install(deps, options) {
188214
quietOptions = ['--silent', '--no-progress'];
189215
}
190216

217+
console.log({ options });
218+
219+
if (options.prompt) {
220+
const response = await this.prompt({
221+
message: `[install-webpack-plugin] Would you like to install package(s) ${green(
222+
deps.join(', ')
223+
)},
224+
)}'? (That will run '${green(`${client} ${args[0]}`)}') (${yellow(
225+
'Y/n'
226+
)})`,
227+
defaultResponse: 'Y',
228+
stream: process.stderr,
229+
});
230+
if (!response) {
231+
console.log(
232+
"[install-webpack-plugin] Missing packages won't be installed."
233+
);
234+
return;
235+
}
236+
}
237+
191238
args = args.concat(deps).filter(Boolean);
192239

193240
if (save && module.exports.packageExists()) {
@@ -198,6 +245,8 @@ module.exports.install = function install(deps, options) {
198245
args = args.concat(quietOptions);
199246
}
200247

248+
console.log({ args });
249+
201250
deps.forEach((dep) => {
202251
console.info('Installing %s...', dep);
203252
});

src/plugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class InstallPlugin {
4242
this.options = { ...installer.defaultOptions, ...options };
4343
this.resolving = new Set();
4444

45-
installer.checkBabel();
45+
installer.checkBabel(this.options);
4646
}
4747

4848
apply(compiler) {

test/installer.test.js

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const spawn = require('cross-spawn');
66
const installer = require('../src/installer');
77

88
describe('installer', () => {
9+
jest.spyOn(installer, 'prompt').mockImplementation(() => true);
910
describe('.defaultOptions', () => {
1011
it('should default dev to false', () => {
1112
expect(installer.defaultOptions.dev).toEqual(false);
@@ -160,18 +161,18 @@ describe('installer', () => {
160161
});
161162

162163
describe('given a falsey value', () => {
163-
it('should return undefined', () => {
164-
expect(installer.install()).toEqual(undefined);
165-
expect(installer.install(0)).toEqual(undefined);
166-
expect(installer.install(false)).toEqual(undefined);
167-
expect(installer.install(null)).toEqual(undefined);
168-
expect(installer.install('')).toEqual(undefined);
164+
it('should return undefined', async () => {
165+
expect(await installer.install()).toEqual(undefined);
166+
expect(await installer.install(0)).toEqual(undefined);
167+
expect(await installer.install(false)).toEqual(undefined);
168+
expect(await installer.install(null)).toEqual(undefined);
169+
expect(await installer.install('')).toEqual(undefined);
169170
});
170171
});
171172

172173
describe('given an empty array', () => {
173-
it('should return undefined', () => {
174-
expect(installer.install([])).toEqual(undefined);
174+
it('should return undefined', async () => {
175+
expect(await installer.install([])).toEqual(undefined);
175176
});
176177
});
177178

@@ -182,14 +183,14 @@ describe('installer', () => {
182183
});
183184
});
184185

185-
it('should attempt to install once', () => {
186-
installer.install('does.not.exist.jsx');
186+
it('should attempt to install once', async () => {
187+
await installer.install('does.not.exist.jsx');
187188

188189
expect(this.sync).toHaveBeenCalled();
189190
});
190191

191-
it('should not attempt to install it again', () => {
192-
installer.install('does.not.exist.jsx');
192+
it('should not attempt to install it again', async () => {
193+
await installer.install('does.not.exist.jsx');
193194

194195
expect(this.sync).not.toHaveBeenCalled();
195196
});
@@ -208,8 +209,8 @@ describe('installer', () => {
208209

209210
describe('given a dependency', () => {
210211
describe('with no options', () => {
211-
it('should install it with --save', () => {
212-
installer.install('foo', {
212+
it('should install it with --save', async () => {
213+
await installer.install('foo', {
213214
yarn: true,
214215
});
215216
expect(this.sync).toHaveBeenCalled();
@@ -220,8 +221,8 @@ describe('installer', () => {
220221
});
221222

222223
describe('with dev set to true', () => {
223-
it('should install it with --dev', () => {
224-
installer.install('foo', {
224+
it('should install it with --dev', async () => {
225+
await installer.install('foo', {
225226
dev: true,
226227
yarn: true,
227228
});
@@ -244,8 +245,8 @@ describe('installer', () => {
244245
jest.clearAllMocks();
245246
});
246247

247-
it('should install without options', () => {
248-
installer.install('foo', {
248+
it('should install without options', async () => {
249+
await installer.install('foo', {
249250
yarn: true,
250251
});
251252
expect(this.sync).toHaveBeenCalled();
@@ -256,8 +257,8 @@ describe('installer', () => {
256257
});
257258

258259
describe('with quiet set to true', () => {
259-
it('should install it with --silent --noprogress', () => {
260-
installer.install('foo', {
260+
it('should install it with --silent --noprogress', async () => {
261+
await installer.install('foo', {
261262
quiet: true,
262263
yarn: true,
263264
});
@@ -296,8 +297,8 @@ describe('installer', () => {
296297
});
297298

298299
describe('given no options', () => {
299-
it('should install peerDependencies', () => {
300-
installer.install('redbox-react', {
300+
it('should install peerDependencies', async () => {
301+
await installer.install('redbox-react', {
301302
yarn: true,
302303
});
303304

@@ -316,8 +317,8 @@ describe('installer', () => {
316317
});
317318

318319
describe('given peerDependencies set to false', () => {
319-
it('should not install peerDependencies', () => {
320-
installer.install('redbox-react', {
320+
it('should not install peerDependencies', async () => {
321+
await installer.install('redbox-react', {
321322
peerDependencies: false,
322323
yarn: true,
323324
});
@@ -347,8 +348,8 @@ describe('installer', () => {
347348

348349
describe('given a dependency', () => {
349350
describe('with no options', () => {
350-
it('should install it with --save', () => {
351-
installer.install('foo');
351+
it('should install it with --save', async () => {
352+
await installer.install('foo');
352353

353354
expect(this.sync).toHaveBeenCalled();
354355
expect(this.sync.mock.calls.length).toEqual(1);
@@ -362,8 +363,8 @@ describe('installer', () => {
362363
});
363364

364365
describe('with dev set to true', () => {
365-
it('should install it with --save-dev', () => {
366-
installer.install('foo', {
366+
it('should install it with --save-dev', async () => {
367+
await installer.install('foo', {
367368
dev: true,
368369
});
369370

@@ -389,8 +390,8 @@ describe('installer', () => {
389390
jest.clearAllMocks();
390391
});
391392

392-
it('should install without --save', () => {
393-
installer.install('foo');
393+
it('should install without --save', async () => {
394+
await installer.install('foo');
394395
expect(this.sync).toHaveBeenCalled();
395396
expect(this.sync.mock.calls.length).toEqual(1);
396397
expect(this.sync.mock.calls[0][0]).toEqual('npm');
@@ -399,8 +400,8 @@ describe('installer', () => {
399400
});
400401

401402
describe('with quiet set to true', () => {
402-
it('should install it with --silent --noprogress', () => {
403-
installer.install('foo', {
403+
it('should install it with --silent --noprogress', async () => {
404+
await installer.install('foo', {
404405
quiet: true,
405406
});
406407

@@ -440,8 +441,8 @@ describe('installer', () => {
440441
});
441442

442443
describe('given no options', () => {
443-
it('should install peerDependencies', () => {
444-
installer.install('redbox-react');
444+
it('should install peerDependencies', async () => {
445+
await installer.install('redbox-react');
445446

446447
expect(this.sync.mock.calls.length).toEqual(2);
447448
expect(this.sync.mock.calls[0][1]).toEqual([
@@ -460,8 +461,8 @@ describe('installer', () => {
460461
});
461462

462463
describe('given peerDependencies set to false', () => {
463-
it('should not install peerDependencies', () => {
464-
installer.install('redbox-react', {
464+
it('should not install peerDependencies', async () => {
465+
await installer.install('redbox-react', {
465466
peerDependencies: false,
466467
});
467468

test/plugin.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ describe('plugin', () => {
2424
dev: false,
2525
peerDependencies: true,
2626
quiet: false,
27+
prompt: true,
2728
npm: 'npm',
2829
};
2930

0 commit comments

Comments
 (0)