Skip to content

Commit

Permalink
feat: add support for merging multiple configurations (#1768)
Browse files Browse the repository at this point in the history
  • Loading branch information
anshumanv committed Aug 26, 2020
1 parent ba1400e commit b1e7024
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 41 deletions.
2 changes: 1 addition & 1 deletion packages/webpack-cli/README.md
Expand Up @@ -39,7 +39,7 @@ yarn add webpack-cli --dev
--entry string[] The entry point(s) of your application.
-c, --config string[] Provide path to webpack configuration file(s)
--config-name string[] Name of the configuration to use
-m, --merge string Merge a configuration file using webpack-merge
-m, --merge Merge several configurations using webpack-merge
--progress Print compilation progress during build
--color Enables colors on console
--no-color Disable colors on console
Expand Down
27 changes: 27 additions & 0 deletions packages/webpack-cli/__tests__/ConfigGroup/ConfigGroup.test.js
@@ -0,0 +1,27 @@
const ConfigGroup = require('../../lib/groups/ConfigGroup');
const { resolve } = require('path');

describe('ConfigGroup', function () {
it('should handle merge properly', async () => {
const group = new ConfigGroup([
{
merge: true,
},
{
config: [resolve(__dirname, './webpack.config.cjs')],
},
]);

const result = await group.run();
const expectedOptions = {
output: { filename: './dist-commonjs.js', libraryTarget: 'commonjs' },
entry: './a.js',
name: 'amd',
mode: 'production',
devtool: 'eval-cheap-module-source-map',
target: 'node',
};
expect(result.options).toEqual(expectedOptions);
expect(result.outputOptions).toEqual({});
});
});
21 changes: 21 additions & 0 deletions packages/webpack-cli/__tests__/ConfigGroup/webpack.config.cjs
@@ -0,0 +1,21 @@
module.exports = [
{
output: {
filename: './dist-amd.js',
libraryTarget: 'amd',
},
entry: './a.js',
name: 'amd',
mode: 'development',
devtool: 'eval-cheap-module-source-map',
},
{
output: {
filename: './dist-commonjs.js',
libraryTarget: 'commonjs',
},
entry: './a.js',
mode: 'production',
target: 'node',
},
];
30 changes: 10 additions & 20 deletions packages/webpack-cli/lib/groups/ConfigGroup.js
Expand Up @@ -190,30 +190,20 @@ class ConfigGroup extends GroupHelper {
}

async resolveConfigMerging() {
// eslint-disable-next-line no-prototype-builtins
const { merge } = this.args;
if (merge) {
// try resolving merge config
const newConfigPath = this.resolveFilePath(merge);

if (!newConfigPath) {
throw new ConfigError("The supplied merge config doesn't exist.", 'MergeError');
// Get the current configuration options
const { options: configOptions } = this.opts;

// we can only merge when there are multiple configurations
// either by passing multiple configs by flags or passing a
// single config exporting an array
if (!Array.isArray(configOptions)) {
throw new ConfigError('Atleast two configurations are required for merge.', 'MergeError');
}

const configFiles = getConfigInfoFromFileName(newConfigPath);
const foundConfig = configFiles[0];
const resolvedConfig = this.requireConfig(foundConfig);
const newConfigurationsObject = await this.finalize(resolvedConfig);
let resolvedOptions = this.opts['options'];
let mergedOptions;
if (Array.isArray(resolvedOptions)) {
mergedOptions = [];
resolvedOptions.forEach((resolvedOption) => {
mergedOptions.push(webpackMerge(resolvedOption, newConfigurationsObject.options));
});
} else {
mergedOptions = webpackMerge(resolvedOptions, newConfigurationsObject.options);
}
// We return a single config object which is passed to the compiler
const mergedOptions = configOptions.reduce((currentConfig, mergedConfig) => webpackMerge(currentConfig, mergedConfig), {});
this.opts['options'] = mergedOptions;
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/webpack-cli/lib/utils/cli-flags.js
Expand Up @@ -108,11 +108,11 @@ const core = [
},
{
name: 'merge',
usage: '--merge <path to configuration to be merged>',
usage: '--merge',
alias: 'm',
type: String,
type: Boolean,
group: CONFIG_GROUP,
description: 'Merge a configuration file using webpack-merge e.g. ./webpack.config.js',
description: 'Merge two or more configurations using webpack-merge e.g. -c ./webpack.config.js -c ./webpack.test.config.js --merge',
link: 'https://github.com/survivejs/webpack-merge',
},
{
Expand Down
2 changes: 1 addition & 1 deletion test/help/help-flags.test.js
Expand Up @@ -17,7 +17,7 @@ describe('commands help', () => {
it('shows flag help with valid flag', () => {
const { stdout, stderr } = run(__dirname, ['--help', '--merge'], false);
expect(stdout).not.toContain(helpHeader);
expect(stdout).toContain('webpack -m, --merge <path to configuration to be merged>');
expect(stdout).toContain('webpack -m, --merge');
expect(stderr).toHaveLength(0);
});

Expand Down
2 changes: 1 addition & 1 deletion test/merge/config-absent/merge-config-absent.test.js
Expand Up @@ -13,7 +13,7 @@ describe('merge flag configuration', () => {
// Since the process will exit, nothing on stdout
expect(stdout).toBeFalsy();
// Confirm that the user is notified
expect(stderr).toContain(`MergeError: The supplied merge config doesn't exist.`);
expect(stderr).toContain(`MergeError: Atleast two configurations are required for merge.`);
// Default config would be used
expect(fs.existsSync(join(__dirname, './dist/merged.js'))).toBeFalsy();
// Since the process will exit so no compilation will be done
Expand Down
32 changes: 17 additions & 15 deletions test/merge/config/merge-config.test.js
@@ -1,27 +1,29 @@
'use strict';

const { stat } = require('fs');
const { existsSync } = require('fs');
const { resolve } = require('path');

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

describe('merge flag configuration', () => {
it('merges two configurations together', (done) => {
const { stdout } = run(__dirname, ['--config', './1.js', '--merge', './2.js'], false);
it('merges two configurations together', () => {
const { stdout, stderr, exitCode } = run(__dirname, ['--config', './1.js', '-c', './2.js', '--merge'], false);
expect(stdout).not.toContain('option has not been set, webpack will fallback to');
stat(resolve(__dirname, './dist/merged.js'), (err, stats) => {
expect(err).toBe(null);
expect(stats.isFile()).toBe(true);
done();
});
expect(existsSync(resolve(__dirname, './dist/merged.js'))).toBeTruthy();
expect(stderr).toBeFalsy();
expect(exitCode).toBe(0);
});
it('merges two configurations together with flag alias', (done) => {
const { stdout } = run(__dirname, ['--config', './1.js', '-m', './2.js'], false);
it('merges two configurations together with flag alias', () => {
const { stdout, stderr, exitCode } = run(__dirname, ['--config', './1.js', '--config', './2.js', '-m'], false);
expect(stdout).toContain('merged.js');
stat(resolve(__dirname, './dist/merged.js'), (err, stats) => {
expect(err).toBe(null);
expect(stats.isFile()).toBe(true);
done();
});
expect(existsSync(resolve(__dirname, './dist/merged.js'))).toBeTruthy();
expect(stderr).toBeFalsy();
expect(exitCode).toBe(0);
});
it('fails when there are less than 2 configurations to merge', () => {
const { stdout, stderr, exitCode } = run(__dirname, ['--config', './1.js', '-m'], false);
expect(stderr).toContain(`MergeError: Atleast two configurations are required for merge.`);
expect(stdout).toBeFalsy();
expect(exitCode).toBe(2);
});
});

0 comments on commit b1e7024

Please sign in to comment.