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: add support for merging multiple configurations #1768

Merged
merged 5 commits into from Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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', () => {
anshumanv marked this conversation as resolved.
Show resolved Hide resolved
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);
});
});