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
53 changes: 53 additions & 0 deletions packages/webpack-cli/lib/plugins/WebpackCLIPlugin.js
@@ -0,0 +1,53 @@
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];

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);
}
}
}

const compilationName = (compilation) => (compilation.name ? ` ${compilation.name}` : '');

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${compilationName(compilation)} starting...`);
});

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

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

module.exports = WebpackCLIPlugin;
132 changes: 45 additions & 87 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.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);
}
}

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 All @@ -314,9 +299,11 @@ class WebpackCLI extends GroupHelper {
return stats;
};

const getStatsOptionsFromCompiler = (compiler) => getStatsOptions(compiler.options ? compiler.options.stats : undefined);

const foundStats = compiler.compilers
? { children: compiler.compilers.map((compiler) => getStatsOptions(compiler.options.stats)) }
: getStatsOptions(compiler.options.stats);
? { children: compiler.compilers.map(getStatsOptionsFromCompiler) }
: getStatsOptionsFromCompiler(compiler);

if (outputOptions.json === true) {
process.stdout.write(JSON.stringify(stats.toJson(foundStats), null, 2) + '\n');
Expand All @@ -335,46 +322,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: 8 additions & 0 deletions test/core-flags/cache-flags.test.js
@@ -1,10 +1,18 @@
'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) => {
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 }`);
});
});