Skip to content

Commit

Permalink
feat: shareable webpack configs using extends (#3738)
Browse files Browse the repository at this point in the history
  • Loading branch information
burhanuday committed Apr 29, 2023
1 parent c09c918 commit d04d0b9
Show file tree
Hide file tree
Showing 38 changed files with 542 additions and 14 deletions.
12 changes: 9 additions & 3 deletions packages/configtest/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ class ConfigTestCommand {

if (Array.isArray(config.options)) {
config.options.forEach((options) => {
if (config.path.get(options)) {
configPaths.add(config.path.get(options) as string);
const loadedConfigPaths = config.path.get(options);

if (loadedConfigPaths) {
loadedConfigPaths.forEach((path) => configPaths.add(path));
}
});
} else {
if (config.path.get(config.options)) {
configPaths.add(config.path.get(config.options) as string);
const loadedConfigPaths = config.path.get(config.options);

if (loadedConfigPaths) {
loadedConfigPaths.forEach((path) => configPaths.add(path));
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion packages/webpack-cli/src/plugins/cli-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ export class CLIPlugin {
logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `);

if (configPath) {
this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`);
this.logger.log(
`Compiler${name ? ` ${name}` : ""} is using config: ${configPath
.map((path) => `'${path}'`)
.join(", ")}`,
);
}
});

Expand Down
11 changes: 7 additions & 4 deletions packages/webpack-cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ interface WebpackCLICommandOption extends CommanderOption {

interface WebpackCLIConfig {
options: WebpackConfiguration | WebpackConfiguration[];
path: WeakMap<object, string>;
path: WeakMap<object, string[]>;
}

interface WebpackCLICommand extends Command {
Expand Down Expand Up @@ -178,6 +178,7 @@ type WebpackDevServerOptions = DevServerConfig &
config: string[];
configName?: string[];
disableInterpret?: boolean;
extends?: string[];
argv: Argv;
};

Expand All @@ -186,8 +187,10 @@ type Callback<T extends unknown[]> = (...args: T) => void;
/**
* Webpack
*/

type WebpackConfiguration = Configuration;
type WebpackConfiguration = Configuration & {
// TODO add extends to webpack types
extends?: string | string[];
};
type ConfigOptions = PotentialPromise<WebpackConfiguration | CallableOption>;
type CallableOption = (env: Env | undefined, argv: Argv) => WebpackConfiguration;
type WebpackCompiler = Compiler | MultiCompiler;
Expand Down Expand Up @@ -236,7 +239,7 @@ interface BasicPackageJsonContent {
*/

interface CLIPluginOptions {
configPath?: string;
configPath?: string[];
helpfulOutput: boolean;
hot?: boolean | "only";
progress?: boolean | "profile";
Expand Down
113 changes: 107 additions & 6 deletions packages/webpack-cli/src/webpack-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,18 @@ class WebpackCLI implements IWebpackCLI {
description: "Stop webpack-cli process with non-zero exit code on warnings from webpack",
helpLevel: "minimum",
},
{
name: "extends",
alias: "e",
configs: [
{
type: "string",
},
],
multiple: true,
description: "Extend webpack configuration",
helpLevel: "minimum",
},
];

const minimumHelpFlags = [
Expand Down Expand Up @@ -1814,6 +1826,7 @@ class WebpackCLI implements IWebpackCLI {
return { options, path: configPath };
};

// TODO better name and better type
const config: WebpackCLIConfig = {
options: {} as WebpackConfiguration,
path: new WeakMap(),
Expand Down Expand Up @@ -1850,10 +1863,10 @@ class WebpackCLI implements IWebpackCLI {

if (isArray) {
(loadedConfig.options as ConfigOptions[]).forEach((options) => {
config.path.set(options, loadedConfig.path);
config.path.set(options, [loadedConfig.path]);
});
} else {
config.path.set(loadedConfig.options, loadedConfig.path);
config.path.set(loadedConfig.options, [loadedConfig.path]);
}
});

Expand Down Expand Up @@ -1892,10 +1905,10 @@ class WebpackCLI implements IWebpackCLI {

if (Array.isArray(config.options)) {
config.options.forEach((item) => {
config.path.set(item, loadedConfig.path);
config.path.set(item, [loadedConfig.path]);
});
} else {
config.path.set(loadedConfig.options, loadedConfig.path);
config.path.set(loadedConfig.options, [loadedConfig.path]);
}
}
}
Expand Down Expand Up @@ -1929,6 +1942,92 @@ class WebpackCLI implements IWebpackCLI {
}
}

const resolveExtends = async (
config: WebpackConfiguration,
configPaths: WebpackCLIConfig["path"],
extendsPaths: string[],
): Promise<WebpackConfiguration> => {
delete config.extends;

const loadedConfigs = await Promise.all(
extendsPaths.map((extendsPath) =>
loadConfigByPath(path.resolve(extendsPath), options.argv),
),
);

const merge = await this.tryRequireThenImport<typeof webpackMerge>("webpack-merge");
const loadedOptions = loadedConfigs.flatMap((config) => config.options);

if (loadedOptions.length > 0) {
const prevPaths = configPaths.get(config);
const loadedPaths = loadedConfigs.flatMap((config) => config.path);

if (prevPaths) {
const intersection = loadedPaths.filter((element) => prevPaths.includes(element));

if (intersection.length > 0) {
this.logger.error(`Recursive configuration detected, exiting.`);
process.exit(2);
}
}

config = merge(
...(loadedOptions as [WebpackConfiguration, ...WebpackConfiguration[]]),
config,
);

if (prevPaths) {
configPaths.set(config, [...prevPaths, ...loadedPaths]);
}
}

if (config.extends) {
const extendsPaths = typeof config.extends === "string" ? [config.extends] : config.extends;

config = await resolveExtends(config, configPaths, extendsPaths);
}

return config;
};

// The `extends` param in CLI gets priority over extends in config file
if (options.extends && options.extends.length > 0) {
const extendsPaths = options.extends;

if (Array.isArray(config.options)) {
config.options = await Promise.all(
config.options.map((options) => resolveExtends(options, config.path, extendsPaths)),
);
} else {
// load the config from the extends option
config.options = await resolveExtends(config.options, config.path, extendsPaths);
}
}
// if no extends option is passed, check if the config file has extends
else if (Array.isArray(config.options) && config.options.some((options) => options.extends)) {
config.options = await Promise.all(
config.options.map((options) => {
if (options.extends) {
return resolveExtends(
options,
config.path,
typeof options.extends === "string" ? [options.extends] : options.extends,
);
} else {
return options;
}
}),
);
} else if (!Array.isArray(config.options) && config.options.extends) {
config.options = await resolveExtends(
config.options,
config.path,
typeof config.options.extends === "string"
? [config.options.extends]
: config.options.extends,
);
}

if (options.merge) {
const merge = await this.tryRequireThenImport<typeof webpackMerge>("webpack-merge");

Expand All @@ -1946,11 +2045,13 @@ class WebpackCLI implements IWebpackCLI {
const configPath = config.path.get(options);
const mergedOptions = merge(accumulator, options);

mergedConfigPaths.push(configPath as string);
if (configPath) {
mergedConfigPaths.push(...configPath);
}

return mergedOptions;
}, {});
config.path.set(config.options, mergedConfigPaths as unknown as string);
config.path.set(config.options, mergedConfigPaths);
}

return config;
Expand Down
9 changes: 9 additions & 0 deletions test/build/extends/extends-cli-option/base.webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = () => {
console.log("base.webpack.config.js");

return {
name: "base_config",
mode: "development",
entry: "./src/index.js",
};
};
10 changes: 10 additions & 0 deletions test/build/extends/extends-cli-option/deep.base.webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = () => {
console.log("deep.base.webpack.config.js");

return {
name: "base_config",
mode: "development",
entry: "./src/index.js",
bail: true,
};
};
1 change: 1 addition & 0 deletions test/build/extends/extends-cli-option/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log("i am index.js")
9 changes: 9 additions & 0 deletions test/build/extends/extends-cli-option/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin");

module.exports = () => {
console.log("derived.webpack.config.js");

return {
plugins: [new WebpackCLITestPlugin()],
};
};

0 comments on commit d04d0b9

Please sign in to comment.