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

feat: implement new configtest command #2303

Merged
merged 13 commits into from Jan 6, 2021
1 change: 1 addition & 0 deletions .eslintignore
Expand Up @@ -10,3 +10,4 @@ test/typescript/webpack.config.ts
test/config/error-commonjs/syntax-error.js
test/config/error-mjs/syntax-error.mjs
test/config/error-array/webpack.config.js
test/configtest/syntax-error.config.js
1 change: 1 addition & 0 deletions .prettierignore
Expand Up @@ -9,3 +9,4 @@ test/config/error-mjs/syntax-error.mjs
packages/webpack-cli/__tests__/test-assets/.yo-rc.json
test/build-errors/stats.json
packages/**/lib
test/configtest/syntax-error.config.js
7 changes: 4 additions & 3 deletions OPTIONS.md
Expand Up @@ -549,7 +549,7 @@ Options:
--watch-options-ignored-reset Clear all items provided in configuration. Ignore some files from watching (glob pattern or regexp).
--watch-options-poll <value> `number`: use polling with specified interval. `true`: use polling.
--watch-options-stdin Stop watching when stdin stream has ended.
--no-watch-options-stdin Negative 'watch-options-stdin' option.
--no-watch-options-stdin Do not stop watching when stdin stream has ended.

Global options:
--color Enable colors on console.
Expand All @@ -559,14 +559,15 @@ Global options:

Commands:
bundle|b [options] Run webpack (default command, can be omitted).
help|h Display help for commands and options.
version|v Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands.
help|h Display help for commands and options.
serve|s [options] Run the webpack dev server.
info|i [options] Outputs information about your system.
init|c [options] [scaffold...] Initialize a new webpack configuration.
loader|l [output-path] Scaffold a loader.
plugin|p [output-path] Scaffold a plugin.
migrate|m <config-path> [new-config-path] Migrate a configuration to a new version.
configtest|t <config-path> Tests webpack configuration against validation errors.
plugin|p [output-path] Scaffold a plugin.

To see list of all supported commands and options run 'webpack --help=verbose'.

Expand Down
18 changes: 18 additions & 0 deletions packages/configtest/package.json
@@ -0,0 +1,18 @@
{
"name": "@webpack-cli/configtest",
"version": "1.0.0",
"description": "Tests webpack configuration against validation errors.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"files": [
"lib"
],
"peerDependencies": {
"webpack": "4.x.x || 5.x.x",
"webpack-cli": "4.x.x"
}
}
54 changes: 54 additions & 0 deletions packages/configtest/src/index.ts
@@ -0,0 +1,54 @@
import webpack from 'webpack';

class ConfigTestCommand {
async apply(cli): Promise<void> {
const { logger } = cli;

await cli.makeCommand(
{
name: 'configtest <config-path>',
alias: 't',
description: 'Tests webpack configuration against validation errors.',
usage: '<config-path>',
pkg: '@webpack-cli/configtest',
},
[],
async (configPath: string): Promise<void> => {
const config = await cli.resolveConfig({ config: [configPath] });

try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const error: any = webpack.validate(config.options);

// TODO remove this after drop webpack@4
if (error && error.length > 0) {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
throw new webpack.WebpackOptionsValidationError(error);
}
} catch (error) {
const isValidationError = (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
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const ValidationError = (webpack.ValidationError || webpack.WebpackOptionsValidationError) as any;

return error instanceof ValidationError;
};

if (isValidationError(error)) {
logger.error(error.message);
} else {
logger.error(error);
}

process.exit(2);
snitin315 marked this conversation as resolved.
Show resolved Hide resolved
}

logger.success('There are no validation errors in the given webpack configuration.');
},
);
}
}

export default ConfigTestCommand;
8 changes: 8 additions & 0 deletions packages/configtest/tsconfig.json
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
"rootDir": "./src"
},
"include": ["./src"]
}
19 changes: 10 additions & 9 deletions packages/webpack-cli/README.md
Expand Up @@ -63,15 +63,16 @@ npx webpack-cli --help verbose
### Available Commands

```
bundle | b Run webpack
help | h Display help for commands and options
version | v Output version number of the 'webpack', 'webpack-cli' and other related packages
init | c Initialize a new webpack configuration
migrate | m Migrate a configuration to a new version
loader | l Scaffold a loader repository
plugin | p Scaffold a plugin repository
info | i Outputs information about your system and dependencies
serve | s Run the webpack Dev Server
bundle | b Run webpack
help | h Display help for commands and options
version | v Output version number of the 'webpack', 'webpack-cli' and other related packages
init | c Initialize a new webpack configuration
migrate | m Migrate a configuration to a new version
loader | l Scaffold a loader repository
plugin | p Scaffold a plugin repository
info | i Outputs information about your system and dependencies
serve | s Run the webpack Dev Server
configtest | t Tests webpack configuration against validation errors.
```

### webpack 4
Expand Down
5 changes: 5 additions & 0 deletions packages/webpack-cli/lib/webpack-cli.js
Expand Up @@ -279,6 +279,11 @@ class WebpackCLI {
alias: 'm',
pkg: '@webpack-cli/migrate',
},
{
name: 'configtest',
alias: 't',
pkg: '@webpack-cli/configtest',
},
];

const knownCommands = [bundleCommandOptions, versionCommandOptions, helpCommandOptions, ...externalBuiltInCommandsInfo];
Expand Down
3 changes: 3 additions & 0 deletions packages/webpack-cli/package.json
Expand Up @@ -54,6 +54,9 @@
"@webpack-cli/migrate": {
"optional": true
},
"@webpack-cli/configtest": {
"optional": true
},
"webpack-bundle-analyzer": {
"optional": true
},
Expand Down
55 changes: 55 additions & 0 deletions test/configtest/configtest.test.js
@@ -0,0 +1,55 @@
'use strict';

const { run } = require('../utils/test-utils');

describe('basic info usage', () => {
it('should validate webpack config successfully', () => {
const { exitCode, stderr, stdout } = run(__dirname, ['configtest', './webpack.config.js'], false);

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(stdout).toContain('There are no validation errors in the given webpack configuration.');
});

it('should throw validation error', () => {
const { exitCode, stderr, stdout } = run(__dirname, ['configtest', './error.config.js'], false);

expect(exitCode).toBe(2);
expect(stderr).toContain('Invalid configuration object.');
expect(stderr).toContain('configuration.mode should be one of these:');
expect(stdout).toBeFalsy();
});

it('should throw syntax error', () => {
const { exitCode, stderr, stdout } = run(__dirname, ['configtest', './syntax-error.config.js'], false);

expect(exitCode).toBe(2);
expect(stderr).toContain(`SyntaxError: Unexpected token ';'`);
expect(stdout).toBeFalsy();
});

it(`should validate the config with alias 't'`, () => {
const { exitCode, stderr, stdout } = run(__dirname, ['t', './error.config.js'], false);

expect(exitCode).toBe(2);
expect(stderr).toContain('Invalid configuration object.');
expect(stderr).toContain('configuration.mode should be one of these:');
expect(stdout).toBeFalsy();
});

it('should throw error if configuration does not exist', () => {
const { exitCode, stderr, stdout } = run(__dirname, ['configtest', './a.js'], false);

expect(exitCode).toBe(2);
expect(stderr).toContain(`The specified config file doesn't exist`);
expect(stdout).toBeFalsy();
});

it('should throw error if no configuration was provided', () => {
const { exitCode, stderr, stdout } = run(__dirname, ['configtest'], false);

expect(exitCode).toBe(2);
expect(stderr).toContain(`error: missing required argument 'config-path'`);
expect(stdout).toBeFalsy();
});
});
5 changes: 5 additions & 0 deletions test/configtest/error.config.js
@@ -0,0 +1,5 @@
module.exports = {
mode: 'dev', // error
target: 'node',
stats: 'normal',
};
1 change: 1 addition & 0 deletions test/configtest/src/index.js
@@ -0,0 +1 @@
console.log('configtest command');
5 changes: 5 additions & 0 deletions test/configtest/syntax-error.config.js
@@ -0,0 +1,5 @@
module.exports = {
name: 'config-error',
mode: 'development',
target: 'node'; //SyntaxError: Unexpected token ';'
};
5 changes: 5 additions & 0 deletions test/configtest/webpack.config.js
@@ -0,0 +1,5 @@
module.exports = {
mode: 'development',
target: 'node',
stats: 'verbose',
};
snitin315 marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions test/help/help.test.js
Expand Up @@ -28,6 +28,7 @@ describe('help', () => {
expect(stdout.match(/loader\|l/g)).toHaveLength(1);
expect(stdout.match(/migrate\|m/g)).toHaveLength(1);
expect(stdout.match(/plugin\|p/g)).toHaveLength(1);
expect(stdout.match(/configtest\|t/g)).toHaveLength(1);
expect(stdout).toContain("To see list of all supported commands and options run 'webpack --help=verbose'.");
expect(stdout).toContain('CLI documentation: https://webpack.js.org/api/cli/.');
// TODO buggy on windows
Expand Down
27 changes: 21 additions & 6 deletions tsconfig.json
Expand Up @@ -20,11 +20,26 @@
"declaration": true
},
"references": [
{ "path": "packages/generators" },
{ "path": "packages/info" },
{ "path": "packages/init" },
{ "path": "packages/migrate" },
{ "path": "packages/serve" },
{ "path": "packages/utils" }
{
"path": "packages/generators"
},
{
"path": "packages/info"
},
{
"path": "packages/init"
},
{
"path": "packages/migrate"
},
{
"path": "packages/serve"
},
{
"path": "packages/utils"
},
{
"path": "packages/configtest"
}
]
}