Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: pass a callback to webpack from webpack-cli #1977

Merged
merged 12 commits into from Oct 26, 2020
51 changes: 51 additions & 0 deletions packages/webpack-cli/lib/plugins/WebpackCLIPlugin.js
@@ -0,0 +1,51 @@
const { packageExists } = require('../utils/package-exists');
const webpack = packageExists('webpack') ? require('webpack') : undefined;
const logger = require('../utils/logger');

const PluginName = 'webpack-cli';

class WebpackCLIPlugin {
constructor(options) {
this.options = options;
}
async apply(compiler) {
const compilers = compiler.compilers ? compiler.compilers : [compiler];
piecyk marked this conversation as resolved.
Show resolved Hide resolved

for (const compiler of compilers) {
if (this.options.progress) {
const { ProgressPlugin } = compiler.webpack || webpack;

let progressPluginExists;

if (compiler.options.plugins) {
progressPluginExists = Boolean(compiler.options.plugins.find((e) => e instanceof ProgressPlugin));
}

if (!progressPluginExists) {
new ProgressPlugin().apply(compiler);
}
}
}

compiler.hooks.watchRun.tap(PluginName, (compilation) => {
const { bail, watch } = compilation.options;
if (bail && watch) {
logger.warn('You are using "bail" with "watch". "bail" will still exit webpack when the first error is found.');
}

logger.success(`Compilation${compilation.name ? `${compilation.name}` : ''} starting...`);
});

compiler.hooks.done.tap(PluginName, (compilation) => {
logger.success(`Compilation${compilation.name ? `${compilation.name}` : ''} finished`);

process.nextTick(() => {
if (compiler.watchMode) {
logger.success('watching files for updates...');
}
});
});
}
}

module.exports = WebpackCLIPlugin;
126 changes: 41 additions & 85 deletions packages/webpack-cli/lib/webpack-cli.js
Expand Up @@ -10,6 +10,7 @@ const { toKebabCase } = require('./utils/helpers');
const assignFlagDefaults = require('./utils/flag-defaults');
const { writeFileSync } = require('fs');
const { options: coloretteOptions } = require('colorette');
const WebpackCLIPlugin = require('./plugins/WebpackCLIPlugin');

// CLI arg resolvers
const handleConfigResolution = require('./groups/ConfigGroup');
Expand Down Expand Up @@ -212,24 +213,27 @@ class WebpackCLI extends GroupHelper {
return this.runOptionGroups(args);
}

createCompiler(options) {
handleError(error) {
// https://github.com/webpack/webpack/blob/master/lib/index.js#L267
// https://github.com/webpack/webpack/blob/v4.44.2/lib/webpack.js#L90
const ValidationError = webpack.ValidationError ? webpack.ValidationError : webpack.WebpackOptionsValidationError;
piecyk marked this conversation as resolved.
Show resolved Hide resolved

// In case of schema errors print and exit process
// For webpack@4 and webpack@5
if (error instanceof ValidationError) {
logger.error(error.message);
} else {
logger.error(error);
}
}

createCompiler(options, callback) {
let compiler;

try {
compiler = webpack(options);
compiler = webpack(options, callback);
} catch (error) {
// https://github.com/webpack/webpack/blob/master/lib/index.js#L267
// https://github.com/webpack/webpack/blob/v4.44.2/lib/webpack.js#L90
const ValidationError = webpack.ValidationError ? webpack.ValidationError : webpack.WebpackOptionsValidationError;

// In case of schema errors print and exit process
// For webpack@4 and webpack@5
if (error instanceof ValidationError) {
logger.error(error.message);
} else {
logger.error(error);
}

this.handleError(error);
process.exit(2);
}

Expand All @@ -245,54 +249,35 @@ class WebpackCLI extends GroupHelper {
async run(args, cliOptions) {
await this.processArgs(args, cliOptions);

const compiler = this.createCompiler(this.compilerConfiguration);

const options = this.compilerConfiguration;
const outputOptions = this.outputConfiguration;

if (outputOptions.interactive) {
const interactive = require('./utils/interactive');
let compiler;

return interactive(compiler, options, outputOptions);
}
let options = this.compilerConfiguration;
let outputOptions = this.outputConfiguration;

const compilers = compiler.compilers ? compiler.compilers : [compiler];
const isWatchMode = Boolean(compilers.find((compiler) => compiler.options.watch));
const isRawOutput = typeof outputOptions.json === 'undefined';

if (isRawOutput) {
for (const compiler of compilers) {
if (outputOptions.progress) {
const { ProgressPlugin } = webpack;

let progressPluginExists;

if (compiler.options.plugins) {
progressPluginExists = Boolean(compiler.options.plugins.find((e) => e instanceof ProgressPlugin));
}
const webpackCLIPlugin = new WebpackCLIPlugin({
progress: outputOptions.progress,
});

if (!progressPluginExists) {
new ProgressPlugin().apply(compiler);
}
const addPlugin = (options) => {
if (!options.plugins) {
options.plugins = [];
}
options.plugins.unshift(webpackCLIPlugin);
};
if (Array.isArray(options)) {
options.forEach(addPlugin);
} else {
addPlugin(options);
}

compiler.hooks.watchRun.tap('watchInfo', (compilation) => {
if (compilation.options.bail && isWatchMode) {
logger.warn('You are using "bail" with "watch". "bail" will still exit webpack when the first error is found.');
}

logger.success(`Compilation${compilation.name ? `${compilation.name}` : ''} starting...`);
});
compiler.hooks.done.tap('watchInfo', (compilation) => {
logger.success(`Compilation${compilation.name ? `${compilation.name}` : ''} finished`);
});
}

const callback = (error, stats) => {
if (error) {
logger.error(error);
process.exit(1);
this.handleError(error);
process.exit(2);
}

if (stats.hasErrors()) {
Expand Down Expand Up @@ -335,46 +320,17 @@ class WebpackCLI extends GroupHelper {
} else {
logger.raw(`${stats.toString(foundStats)}`);
}

if (isWatchMode) {
logger.success('watching files for updates...');
}
};

if (isWatchMode) {
const watchOptions = (compiler.options && compiler.options.watchOptions) || {};
compiler = this.createCompiler(options, callback);

if (watchOptions.stdin) {
process.stdin.on('end', function () {
process.exit();
});
process.stdin.resume();
}

return new Promise((resolve) => {
compiler.watch(watchOptions, (error, stats) => {
callback(error, stats);
if (compiler && outputOptions.interactive) {
const interactive = require('./utils/interactive');

resolve();
});
});
} else {
return new Promise((resolve) => {
compiler.run((error, stats) => {
if (compiler.close) {
compiler.close(() => {
callback(error, stats);

resolve();
});
} else {
callback(error, stats);

resolve();
}
});
});
interactive(compiler, options, outputOptions);
}

return Promise.resolve();
}
}

Expand Down
8 changes: 5 additions & 3 deletions test/analyze/analyze-flag.test.js
Expand Up @@ -8,13 +8,15 @@ describe('--analyze flag', () => {

proc.stdout.on('data', (chunk) => {
const data = chunk.toString();
// console.log(data)

if (data.includes('Webpack Bundle Analyzer is started at')) {
expect(data).toContain('Webpack Bundle Analyzer is started at');

proc.kill();
done();
}
// FIXME

proc.kill();
done();
});
});
});
10 changes: 10 additions & 0 deletions test/core-flags/cache-flags.test.js
@@ -1,10 +1,20 @@
'use strict';

const path = require('path');
const rimraf = require('rimraf');
const { run, isWindows } = require('../utils/test-utils');
const { existsSync, writeFileSync, unlinkSync } = require('fs');
const { resolve } = require('path');

describe('cache related flags from core', () => {
beforeEach((done) => {
if (isWindows) return;
piecyk marked this conversation as resolved.
Show resolved Hide resolved

rimraf(path.join(__dirname, '../../node_modules/.cache/webpack/*'), () => {
done();
});
});
piecyk marked this conversation as resolved.
Show resolved Hide resolved

it('should be successful with --cache ', () => {
const { stderr, stdout } = run(__dirname, ['--cache']);
expect(stderr).toBeFalsy();
Expand Down
2 changes: 1 addition & 1 deletion test/utils/cli-plugin-test/plugin.test.js
Expand Up @@ -10,6 +10,6 @@ describe('webpack-cli-test-plugin Test', () => {
if (typeof cli !== 'undefined') {
expect(stdout).toContain(`alias: { alias: [ 'alias1', 'alias2' ] }`);
}
expect(stdout).toContain('plugins: [ WebpackCLITestPlugin { opts: [Array], showAll: true } ]');
expect(stdout).toContain(` WebpackCLITestPlugin { opts: [Array], showAll: true }`);
});
});