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: shareable webpack configs using extends #3738

Merged
merged 20 commits into from
Apr 29, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
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 @@
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 @@
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 @@

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 @@

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 @@
}
}

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

Check warning on line 1950 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1949-L1950

Added lines #L1949 - L1950 were not covered by tests

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

Check warning on line 1954 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1952-L1954

Added lines #L1952 - L1954 were not covered by tests
),
);

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

Check warning on line 1959 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1958-L1959

Added lines #L1958 - L1959 were not covered by tests

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

Check warning on line 1963 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1962-L1963

Added lines #L1962 - L1963 were not covered by tests

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

Check warning on line 1966 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1966

Added line #L1966 was not covered by tests

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

Check warning on line 1970 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1969-L1970

Added lines #L1969 - L1970 were not covered by tests
}
}

config = merge(

Check warning on line 1974 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1974

Added line #L1974 was not covered by tests
...(loadedOptions as [WebpackConfiguration, ...WebpackConfiguration[]]),
config,
);

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

Check warning on line 1980 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1980

Added line #L1980 was not covered by tests
}
}

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

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

Check warning on line 1987 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1987

Added line #L1987 was not covered by tests
}

return config;

Check warning on line 1990 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1990

Added line #L1990 was not covered by tests
};

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

Check warning on line 1995 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1995

Added line #L1995 was not covered by tests

if (Array.isArray(config.options)) {
config.options = await Promise.all(
config.options.map((options) => resolveExtends(options, config.path, extendsPaths)),

Check warning on line 1999 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L1998-L1999

Added lines #L1998 - L1999 were not covered by tests
);
} else {
// load the config from the extends option
config.options = await resolveExtends(config.options, config.path, extendsPaths);

Check warning on line 2003 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L2003

Added line #L2003 was not covered by tests
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move it to own function and just use && { extend: string[] } to avoid ts-except-error

}
// 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) => {

Check warning on line 2009 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L2008-L2009

Added lines #L2008 - L2009 were not covered by tests
if (options.extends) {
return resolveExtends(

Check warning on line 2011 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L2011

Added line #L2011 was not covered by tests
options,
config.path,
typeof options.extends === "string" ? [options.extends] : options.extends,

Check warning on line 2014 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L2014

Added line #L2014 was not covered by tests
);
} else {
return options;

Check warning on line 2017 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L2017

Added line #L2017 was not covered by tests
}
}),
);
} else if (!Array.isArray(config.options) && config.options.extends) {
config.options = await resolveExtends(

Check warning on line 2022 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L2022

Added line #L2022 was not covered by tests
config.options,
config.path,
typeof config.options.extends === "string"
? [config.options.extends]
: config.options.extends,

Check warning on line 2027 in packages/webpack-cli/src/webpack-cli.ts

View check run for this annotation

Codecov / codecov/patch

packages/webpack-cli/src/webpack-cli.ts#L2026-L2027

Added lines #L2026 - L2027 were not covered by tests
);
}

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

Expand All @@ -1946,11 +2045,13 @@
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
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",
};
};
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()],
};
};