Skip to content

Commit

Permalink
refactor: use webpack infrastructure logger
Browse files Browse the repository at this point in the history
  • Loading branch information
piecyk committed Nov 8, 2020
1 parent 8651f49 commit b9a51fb
Show file tree
Hide file tree
Showing 126 changed files with 586 additions and 768 deletions.
5 changes: 4 additions & 1 deletion packages/utils/__tests__/run-prettier.test.ts
Expand Up @@ -5,10 +5,13 @@ import path from 'path';
//eslint-disable-next-line node/no-extraneous-import
import rimraf from 'rimraf';
import { runPrettier } from '../src/run-prettier';
import { utils } from 'webpack-cli';

const { logger } = utils;
const outputPath = path.join(__dirname, 'test-assets');
const outputFile = path.join(outputPath, 'test.js');
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();

const consoleSpy = jest.spyOn(logger, 'warn').mockImplementation();

describe('runPrettier', () => {
beforeEach(() => {
Expand Down
6 changes: 2 additions & 4 deletions packages/webpack-cli/__tests__/info.test.js
Expand Up @@ -3,18 +3,17 @@ const path = require('path');

describe('Info', () => {
it('should run with cli', () => {
const { stdout, stderr } = spawnSync(path.resolve(__dirname, '../bin/cli.js'), ['info'], {
const { stdout } = spawnSync(path.resolve(__dirname, '../bin/cli.js'), ['info'], {
cwd: path.resolve(__dirname),
reject: false,
});
expect(stdout).toContain('System');
expect(stdout).toContain('Binaries');
expect(stdout).toContain('OS');
expect(stderr).toBeFalsy();
});

it('should work with flags', () => {
const { stdout, stderr } = spawnSync(path.resolve(__dirname, '../bin/cli.js'), ['info', '--output=json'], {
const { stdout } = spawnSync(path.resolve(__dirname, '../bin/cli.js'), ['info', '--output=json'], {
cwd: path.resolve(__dirname),
reject: false,
});
Expand All @@ -25,6 +24,5 @@ describe('Info', () => {
expect(output['System']['OS']).toBeTruthy();
};
expect(testJSON).not.toThrow();
expect(stderr).toBeFalsy();
});
});
7 changes: 3 additions & 4 deletions packages/webpack-cli/__tests__/init.test.js
Expand Up @@ -18,12 +18,11 @@ describe('init', () => {
});

it('should work with cli', () => {
const { stdout, stderr } = spawnSync(path.resolve(__dirname, '../bin/cli.js'), ['init'], {
const { stdout } = spawnSync(path.resolve(__dirname, '../bin/cli.js'), ['init'], {
cwd: genPath,
reject: false,
});
expect(stdout).toBeTruthy();
expect(stderr).toBeFalsy();

expect(stdout).toContain(firstPrompt);
});
it('should run with cli when auto is supplied', () => {
Expand All @@ -32,7 +31,7 @@ describe('init', () => {
reject: false,
});
// Test no prompts are present
expect(stdout).toBeTruthy();

expect(stdout).not.toContain(firstPrompt);

// Skip test in case installation fails
Expand Down
6 changes: 2 additions & 4 deletions packages/webpack-cli/__tests__/serve/serve.test.js
Expand Up @@ -12,16 +12,14 @@ describe('Serve', () => {
}

it('should run with cli', async () => {
const { stdout, stderr } = await runServe([], __dirname);
const { stdout } = await runServe([], __dirname);
expect(stdout).toContain('main.js');
expect(stdout).not.toContain('HotModuleReplacementPlugin');
expect(stderr).toHaveLength(0);
});

it('should work with flags', async () => {
const { stdout, stderr } = await runServe(['--hot'], __dirname);
const { stdout } = await runServe(['--hot'], __dirname);
expect(stdout).toContain('main.js');
expect(stdout).toContain('HotModuleReplacementPlugin');
expect(stderr).toHaveLength(0);
});
});
125 changes: 91 additions & 34 deletions packages/webpack-cli/lib/plugins/WebpackCLIPlugin.js
@@ -1,63 +1,120 @@
const packageExists = require('../utils/package-exists');
const webpack = packageExists('webpack') ? require('webpack') : undefined;
const logger = require('../utils/logger');

const PluginName = 'webpack-cli';
const { getStatsOptions } = require('../utils/stats-options');
const { PluginName } = require('../utils/name');

class WebpackCLIPlugin {
constructor(options) {
this.options = options;

this.isWatch = false;
}

async apply(compiler) {
const compilers = compiler.compilers || [compiler];
const { progress } = this.options;
if (progress) {
this.appendProgressPlugin(compiler, progress);
}

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

let progressPluginExists;
const logger = compiler.getInfrastructureLogger(PluginName);

if (compiler.options.plugins) {
progressPluginExists = Boolean(compiler.options.plugins.find((plugin) => plugin instanceof ProgressPlugin));
}
const resolveName = (obj) => (obj.name ? `${obj.name} ` : '');

if (!progressPluginExists) {
if (typeof this.options.progress === 'string' && this.options.progress !== 'profile') {
logger.error(
`'${this.options.progress}' is an invalid value for the --progress option. Only 'profile' is allowed.`,
);
process.exit(2);
}
const done = (stats) => {
const printStats = (childCompiler, childStats) => {
const name = resolveName(childCompiler);

const status = childStats.toString({
all: false,
version: true,
timings: true,
});

const normalizedStatus = (() => {
if (isWebpack5) {
return status;
} else {
const [version, time] = status.split('\n').map((line) => {
const value = line.split(':')[1] || '';
return value.trim();
});

const isProfile = this.options.progress === 'profile';
return `${name ? name + `(${version})` : version} compiled in ${time}`;
}
})();

new ProgressPlugin({ profile: isProfile }).apply(compiler);
if (childStats.hasErrors()) {
logger.error(normalizedStatus);
} else if (childStats.hasWarnings()) {
logger.warn(normalizedStatus);
} else {
logger.info(normalizedStatus);
}
}
}

const compilationName = (compilation) => (compilation.name ? ` ${compilation.name}` : '');
const statsString = childStats.toString(getStatsOptions(childCompiler));
process.stdout.write(statsString + '\n');

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.');
if (this.isWatch) {
logger.info(name + 'watching files for updates...');
}
};

if (compiler.compilers) {
compiler.compilers.forEach((compilerFromMultiCompileMode, index) => {
printStats(compilerFromMultiCompileMode, stats.stats[index]);
});
} else {
printStats(compiler, stats);
}
};

logger.success(`Compilation${compilationName(compilation)} starting...`);
compiler.hooks.run.tap(PluginName, (compilation) => {
logger.info(resolveName(compilation) + 'compilation starting...');
});
compiler.hooks.watchRun.tap(PluginName, (compilation) => {
logger.info(resolveName(compilation) + 'compilation starting...');

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

if (compilation.options.bail) {
logger.warn('you are using "bail" with "watch". "bail" will still exit webpack when the first error is found.');
}
this.isWatch = true;
});
compiler.hooks.done.tap(PluginName, (stats) => {
process.nextTick(() => {
if (compiler.watchMode) {
logger.success('watching files for updates...');
}
done(stats);
});
});
}

appendProgressPlugin(compiler, progress) {
const { ProgressPlugin } = compiler.webpack || webpack;

const logger = compiler.getInfrastructureLogger(PluginName);

const compilers = compiler.compilers || [compiler];

for (const compiler of compilers) {
let progressPluginExists;

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

if (!progressPluginExists) {
if (typeof progress === 'string' && progress !== 'profile') {
logger.error(`'${progress}' is an invalid value for the --progress option. Only 'profile' is allowed.`);

process.exit(2);
}

const isProfile = progress === 'profile';

new ProgressPlugin({ profile: isProfile }).apply(compiler);
}
}
}
}

module.exports = WebpackCLIPlugin;
5 changes: 5 additions & 0 deletions packages/webpack-cli/lib/utils/name.js
@@ -0,0 +1,5 @@
const PluginName = 'webpack-cli';

module.exports = {
PluginName,
};
26 changes: 26 additions & 0 deletions packages/webpack-cli/lib/utils/stats-options.js
@@ -0,0 +1,26 @@
const packageExists = require('./package-exists');
const webpack = packageExists('webpack') ? require('webpack') : undefined;
const { options: coloretteOptions } = require('colorette');

const getStatsOptions = (compiler) => {
let options = compiler.options
? typeof compiler.options.stats === 'object'
? Object.assign({}, compiler.options.stats)
: compiler.options.stats
: undefined;

// TODO remove after drop webpack@4
if (webpack.Stats && webpack.Stats.presetToOptions) {
if (!options) {
options = {};
} else if (typeof options === 'boolean' || typeof options === 'string') {
options = webpack.Stats.presetToOptions(options);
}
}

options.colors = typeof options.colors !== 'undefined' ? options.colors : coloretteOptions.enabled;

return options;
};

module.exports = { getStatsOptions };
58 changes: 22 additions & 36 deletions packages/webpack-cli/lib/webpack-cli.js
Expand Up @@ -3,14 +3,15 @@ const packageExists = require('./utils/package-exists');
const webpack = packageExists('webpack') ? require('webpack') : undefined;
const webpackMerge = require('webpack-merge');
const { writeFileSync } = require('fs');
const { options: coloretteOptions, yellow } = require('colorette');
const { yellow } = require('colorette');

const logger = require('./utils/logger');
const { core, groups, coreFlagMap } = require('./utils/cli-flags');
const argParser = require('./utils/arg-parser');
const assignFlagDefaults = require('./utils/flag-defaults');
const WebpackCLIPlugin = require('./plugins/WebpackCLIPlugin');
const promptInstallation = require('./utils/prompt-installation');
const { getStatsOptions } = require('./utils/stats-options');

// CLI arg resolvers
const handleConfigResolution = require('./groups/resolveConfig');
Expand Down Expand Up @@ -327,9 +328,9 @@ class WebpackCLI {
let options = this.compilerConfiguration;
let outputOptions = this.outputConfiguration;

const isRawOutput = typeof outputOptions.json === 'undefined';
const isJsonOutput = typeof outputOptions.json !== 'undefined';

if (isRawOutput) {
if (!isJsonOutput) {
const webpackCLIPlugin = new WebpackCLIPlugin({
progress: outputOptions.progress,
});
Expand Down Expand Up @@ -357,43 +358,28 @@ class WebpackCLI {
process.exitCode = 1;
}

const getStatsOptions = (stats) => {
// TODO remove after drop webpack@4
if (webpack.Stats && webpack.Stats.presetToOptions) {
if (!stats) {
stats = {};
} else if (typeof stats === 'boolean' || typeof stats === 'string') {
stats = webpack.Stats.presetToOptions(stats);
}
}

stats.colors = typeof stats.colors !== 'undefined' ? stats.colors : coloretteOptions.enabled;

return stats;
};

const getStatsOptionsFromCompiler = (compiler) => getStatsOptions(compiler.options ? compiler.options.stats : undefined);
if (isJsonOutput) {
process.nextTick(() => {
const statsOptions = compiler.compilers
? { children: compiler.compilers.map(getStatsOptions) }
: getStatsOptions(compiler);

const foundStats = compiler.compilers
? { children: compiler.compilers.map(getStatsOptionsFromCompiler) }
: getStatsOptionsFromCompiler(compiler);
const json = JSON.stringify(stats.toJson(statsOptions), null, 2);

if (outputOptions.json === true) {
process.stdout.write(JSON.stringify(stats.toJson(foundStats), null, 2) + '\n');
} else if (typeof outputOptions.json === 'string') {
const JSONStats = JSON.stringify(stats.toJson(foundStats), null, 2);
if (typeof outputOptions.json === 'string') {
try {
writeFileSync(outputOptions.json, json);

try {
writeFileSync(outputOptions.json, JSONStats);
logger.info(`stats are successfully stored as json to ${outputOptions.json}`);
} catch (error) {
logger.error(error);

logger.success(`stats are successfully stored as json to ${outputOptions.json}`);
} catch (error) {
logger.error(error);

process.exit(2);
}
} else {
logger.raw(`${stats.toString(foundStats)}`);
process.exit(2);
}
} else {
process.stdout.write(json + '\n');
}
});
}
};

Expand Down
8 changes: 5 additions & 3 deletions test/analyze/analyze-flag.test.js
@@ -1,16 +1,18 @@
'use strict';

const stripAnsi = require('strip-ansi');
const { runAndGetWatchProc } = require('../utils/test-utils');

describe('--analyze flag', () => {
it('should load webpack-bundle-analyzer plugin with --analyze flag', (done) => {
const proc = runAndGetWatchProc(__dirname, ['--analyze'], false, '', true);

proc.stdout.on('data', (chunk) => {
const data = chunk.toString();
const data = stripAnsi(chunk.toString());
const str = 'Webpack Bundle Analyzer is started at';

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

proc.kill();
done();
Expand Down

0 comments on commit b9a51fb

Please sign in to comment.