UNPKG

4.73 kBJavaScriptView Raw
1const chalk = require('chalk');
2const decamel = require('decamelize');
3const loadUtils = require('loader-utils');
4const meant = require('meant');
5const merge = require('merge-options');
6const resolve = require('enhanced-resolve');
7const strip = require('strip-ansi');
8const table = require('text-table');
9const weblog = require('webpack-log');
10
11module.exports = {
12 bind(value, enforce, options) {
13 if (!value) {
14 return options;
15 }
16
17 let [extension, loader] = value.split('=');
18
19 // this is logic copied from webpack-cli/convert-arg. not entirely sure why
20 // this is done, perhaps for something like `--module-bind js`?
21 if (extension && !loader) {
22 loader = `${extension}-loader`;
23 }
24
25 // eslint-disable-next-line no-useless-escape
26 extension = extension.replace(
27 // eslint-disable-next-line no-useless-escape
28 /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
29 '\\$&'
30 );
31
32 const test = new RegExp(`\\.${extension}$`);
33 const rule = { enforce, loader, test };
34
35 // eslint-disable-next-line no-param-reassign
36 const result = merge({ module: { rules: [] } }, options);
37 result.module.rules.push(rule);
38
39 return result;
40 },
41
42 loadPlugin(name) {
43 const log = weblog({ name: 'webpack', id: 'webpack-command' });
44 const queryPos = name && name.indexOf('?');
45 let args;
46 let pluginPath;
47
48 try {
49 if (queryPos > -1) {
50 args = loadUtils.parseQuery(name.substring(queryPos));
51 // eslint-disable-next-line no-param-reassign
52 name = name.substring(0, queryPos);
53 }
54 } catch (e) {
55 /* istanbul ignore next */
56 log.error(`Invalid plugin arguments ${name} (${e}).`);
57 /* istanbul ignore next */
58 throw e;
59 }
60
61 try {
62 pluginPath = resolve.sync(process.cwd(), name);
63 // eslint-disable-next-line global-require, import/no-dynamic-require
64 const PluginClass = require(pluginPath);
65 return new PluginClass(args);
66 } catch (e) {
67 log.error(chalk`Cannot load plugin ${name} {dim from ${pluginPath}}`);
68 throw e;
69 }
70 },
71
72 // eslint-disable-next-line consistent-return
73 validate(flag, value) {
74 const { type: types } = flag;
75 let result = false;
76
77 if (!value || !types) {
78 return true;
79 }
80
81 for (const type of [].concat(types)) {
82 if (result !== true) {
83 if (type === 'array') {
84 result = Array.isArray(value);
85 } else {
86 // eslint-disable-next-line valid-typeof
87 result = typeof value === type;
88 }
89 }
90 }
91
92 return result;
93 },
94
95 validateFlags(flags, argv, options = { stdout: true }) {
96 const errors = [];
97 const log = weblog({ name: 'webpack', id: 'webpack-command' });
98 const names = Object.keys(flags);
99 const tableOptions = {
100 align: ['', 'l', 'l'],
101 stringLength(str) {
102 return strip(str).length;
103 },
104 };
105 const uniqueArgs = new Set(Object.keys(argv).map((n) => decamel(n, '-')));
106 const unknown = [];
107 const { validate } = module.exports;
108
109 for (const unique of uniqueArgs) {
110 if (!names.includes(unique)) {
111 const [suggestion] = meant(unique, names);
112 let help = 'Not sure what you mean there';
113
114 if (suggestion) {
115 help = chalk`Did you mean {bold --${suggestion}}?`;
116 }
117
118 unknown.push(['', chalk.blue(`--${unique}`), help]);
119 }
120 }
121
122 for (const name of names) {
123 const flag = flags[name];
124 const value = argv[name];
125
126 // eslint-disable-next-line valid-typeof
127 if (!validate(flag, value)) {
128 errors.push([
129 '',
130 chalk.blue(`--${name}`),
131 chalk`must be set to a {bold ${flag.type}}`,
132 ]);
133 }
134 }
135
136 if (errors.length) {
137 const pre = 'Flags were specified with invalid values:';
138 const post = 'Please check the command executed.';
139 const out = `${pre}\n\n${table(errors, tableOptions)}\n\n${post}`;
140
141 /* istanbul ignore else */
142 if (!options.stdout) {
143 throw new Error(out);
144 } else {
145 log.error(out);
146 }
147 }
148
149 if (unknown.length) {
150 /* istanbul ignore if */
151 if (errors.length && options.stdout) {
152 console.log(''); // eslint-disable-line no-console
153 }
154
155 const pre = `Flags were specified that were not recognized:`;
156 const post = 'Please check the command executed.';
157 const out = `${pre}\n\n${table(unknown, tableOptions)}\n\n${post}`;
158
159 /* istanbul ignore if */
160 if (options.stdout) {
161 log.error(out);
162 } else {
163 throw new Error(out);
164 }
165 }
166
167 /* istanbul ignore if */
168 if (errors.length || unknown.length) {
169 return false;
170 }
171
172 return true;
173 },
174};