Skip to content

Commit

Permalink
fix: support top multi compiler options (#2874)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Aug 5, 2021
1 parent 61726c1 commit 82b1fb7
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 104 deletions.
227 changes: 123 additions & 104 deletions packages/webpack-cli/lib/webpack-cli.js
Expand Up @@ -847,7 +847,7 @@ class WebpackCLI {
options.entry = [...entries, ...(options.entry || [])];
}

await this.buildCommand(options, isWatchCommandUsed);
await this.runWebpack(options, isWatchCommandUsed);
},
);
} else if (isCommand(commandName, helpCommandOptions)) {
Expand Down Expand Up @@ -1521,107 +1521,124 @@ class WebpackCLI {
await this.program.parseAsync(args, parseOptions);
}

async resolveConfig(options) {
const loadConfig = async (configPath) => {
const { interpret } = this.utils;
const ext = path.extname(configPath);
const interpreted = Object.keys(interpret.jsVariants).find(
(variant) => variant === ext,
);
async loadConfig(configPath, argv = {}) {
const { interpret } = this.utils;
const ext = path.extname(configPath);
const interpreted = Object.keys(interpret.jsVariants).find((variant) => variant === ext);

if (interpreted) {
const { rechoir } = this.utils;

try {
rechoir.prepare(interpret.extensions, configPath);
} catch (error) {
if (error.failures) {
this.logger.error(`Unable load '${configPath}'`);
this.logger.error(error.message);

error.failures.forEach((failure) => {
this.logger.error(failure.error.message);
});
this.logger.error("Please install one of them");
process.exit(2);
}

this.logger.error(error);
process.exit(2);
}
}

let options;
if (interpreted) {
const { rechoir } = this.utils;

try {
options = await this.tryRequireThenImport(configPath, false);
rechoir.prepare(interpret.extensions, configPath);
} catch (error) {
this.logger.error(`Failed to load '${configPath}' config`);

if (this.isValidationError(error)) {
if (error.failures) {
this.logger.error(`Unable load '${configPath}'`);
this.logger.error(error.message);
} else {
this.logger.error(error);

error.failures.forEach((failure) => {
this.logger.error(failure.error.message);
});
this.logger.error("Please install one of them");
process.exit(2);
}

this.logger.error(error);
process.exit(2);
}
}

return { options, path: configPath };
};
let options;

const evaluateConfig = async (loadedConfig, argv) => {
const isMultiCompiler = Array.isArray(loadedConfig.options);
const config = isMultiCompiler ? loadedConfig.options : [loadedConfig.options];
try {
options = await this.tryRequireThenImport(configPath, false);
} catch (error) {
this.logger.error(`Failed to load '${configPath}' config`);

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

const evaluatedConfig = await Promise.all(
config.map(async (rawConfig) => {
if (typeof rawConfig.then === "function") {
rawConfig = await rawConfig;
process.exit(2);
}

if (Array.isArray(options)) {
await Promise.all(
options.map(async (_, i) => {
if (typeof options[i].then === "function") {
options[i] = await options[i];
}

// `Promise` may return `Function`
if (typeof rawConfig === "function") {
if (typeof options[i] === "function") {
// when config is a function, pass the env from args to the config function
rawConfig = await rawConfig(argv.env, argv);
options[i] = await options[i](argv.env, argv);
}

return rawConfig;
}),
);
} else {
if (typeof options.then === "function") {
options = await options;
}

loadedConfig.options = isMultiCompiler ? evaluatedConfig : evaluatedConfig[0];
// `Promise` may return `Function`
if (typeof options === "function") {
// when config is a function, pass the env from args to the config function
options = await options(argv.env, argv);
}
}

const isObject = (value) => typeof value === "object" && value !== null;
const isObject = (value) => typeof value === "object" && value !== null;

if (!isObject(loadedConfig.options) && !Array.isArray(loadedConfig.options)) {
this.logger.error(`Invalid configuration in '${loadedConfig.path}'`);
process.exit(2);
}
if (!isObject(options) && !Array.isArray(options)) {
this.logger.error(`Invalid configuration in '${configPath}'`);

return loadedConfig;
};
process.exit(2);
}

return { options, path: configPath };
}

async resolveConfig(options) {
const config = { options: {}, path: new WeakMap() };

if (options.config && options.config.length > 0) {
const evaluatedConfigs = await Promise.all(
options.config.map(async (value) =>
evaluateConfig(await loadConfig(path.resolve(value)), options.argv || {}),
const loadedConfigs = await Promise.all(
options.config.map((configPath) =>
this.loadConfig(path.resolve(configPath), options.argv),
),
);

config.options = [];

evaluatedConfigs.forEach((evaluatedConfig) => {
if (Array.isArray(evaluatedConfig.options)) {
evaluatedConfig.options.forEach((options) => {
config.options.push(options);
config.path.set(options, evaluatedConfig.path);
loadedConfigs.forEach((loadedConfig) => {
const isArray = Array.isArray(loadedConfig.options);

// TODO we should run webpack multiple times when the `--config` options have multiple values with `--merge`, need to solve for the next major release
if (config.options.length === 0) {
config.options = loadedConfig.options;
} else {
if (!Array.isArray(config.options)) {
config.options = [config.options];
}

if (isArray) {
loadedConfig.options.forEach((item) => {
config.options.push(item);
});
} else {
config.options.push(loadedConfig.options);
}
}

if (isArray) {
loadedConfig.options.forEach((options) => {
config.path.set(options, loadedConfig.path);
});
} else {
config.options.push(evaluatedConfig.options);
config.path.set(evaluatedConfig.options, evaluatedConfig.path);
config.path.set(loadedConfig.options, loadedConfig.path);
}
});

Expand Down Expand Up @@ -1657,23 +1674,25 @@ class WebpackCLI {
}

if (foundDefaultConfigFile) {
const loadedConfig = await loadConfig(foundDefaultConfigFile.path);
const evaluatedConfig = await evaluateConfig(loadedConfig, options.argv || {});
const loadedConfig = await this.loadConfig(
foundDefaultConfigFile.path,
options.argv,
);

config.options = evaluatedConfig.options;
config.options = loadedConfig.options;

if (Array.isArray(config.options)) {
config.options.forEach((options) => {
config.path.set(options, evaluatedConfig.path);
config.options.forEach((item) => {
config.path.set(item, loadedConfig.path);
});
} else {
config.path.set(evaluatedConfig.options, evaluatedConfig.path);
config.path.set(loadedConfig.options, loadedConfig.path);
}
}
}

if (options.configName) {
const notfoundConfigNames = [];
const notFoundConfigNames = [];

config.options = options.configName.map((configName) => {
let found;
Expand All @@ -1685,15 +1704,15 @@ class WebpackCLI {
}

if (!found) {
notfoundConfigNames.push(configName);
notFoundConfigNames.push(configName);
}

return found;
});

if (notfoundConfigNames.length > 0) {
if (notFoundConfigNames.length > 0) {
this.logger.error(
notfoundConfigNames
notFoundConfigNames
.map(
(configName) =>
`Configuration with the name "${configName}" was not found.`,
Expand Down Expand Up @@ -1731,6 +1750,18 @@ class WebpackCLI {
return config;
}

runFunctionOnOptions(options, fn) {
if (Array.isArray(options)) {
for (let item of options) {
item = fn(item);
}
} else {
options = fn(options);
}

return options;
}

// TODO refactor
async applyOptions(config, options) {
if (options.analyze) {
Expand Down Expand Up @@ -1786,9 +1817,7 @@ class WebpackCLI {
return configOptions;
};

config.options = Array.isArray(config.options)
? config.options.map((options) => outputHints(options))
: outputHints(config.options);
this.runFunctionOnOptions(config.options, outputHints);

if (this.webpack.cli) {
const processArguments = (configOptions) => {
Expand Down Expand Up @@ -1850,9 +1879,7 @@ class WebpackCLI {
return configOptions;
};

config.options = Array.isArray(config.options)
? config.options.map((options) => processArguments(options))
: processArguments(config.options);
this.runFunctionOnOptions(config.options, processArguments);

const setupDefaultOptions = (configOptions) => {
// No need to run for webpack@4
Expand Down Expand Up @@ -1881,9 +1908,7 @@ class WebpackCLI {
return configOptions;
};

config.options = Array.isArray(config.options)
? config.options.map((options) => setupDefaultOptions(options))
: setupDefaultOptions(config.options);
this.runFunctionOnOptions(config.options, setupDefaultOptions);
}

// Logic for webpack@4
Expand Down Expand Up @@ -1943,12 +1968,10 @@ class WebpackCLI {
return configOptions;
};

config.options = Array.isArray(config.options)
? config.options.map((options) => processLegacyArguments(options))
: processLegacyArguments(config.options);
this.runFunctionOnOptions(config.options, processLegacyArguments);

// Apply `stats` and `stats.colors` options
const applyStatsColors = (configOptions) => {
const applyStatsOption = (configOptions) => {
// TODO remove after drop webpack@4
const statsForWebpack4 = this.webpack.Stats && this.webpack.Stats.presetToOptions;

Expand Down Expand Up @@ -2005,24 +2028,22 @@ class WebpackCLI {
return configOptions;
};

config.options = Array.isArray(config.options)
? config.options.map((options) => applyStatsColors(options))
: applyStatsColors(config.options);
this.runFunctionOnOptions(config.options, applyStatsOption);

return config;
}

async applyCLIPlugin(config, cliOptions) {
const CLIPlugin = await this.tryRequireThenImport("./plugins/CLIPlugin");

const addCLIPlugin = (configOptions) => {
if (!configOptions.plugins) {
configOptions.plugins = [];
const addCLIPlugin = (options) => {
if (!options.plugins) {
options.plugins = [];
}

configOptions.plugins.unshift(
options.plugins.unshift(
new CLIPlugin({
configPath: config.path.get(configOptions),
configPath: config.path.get(options),
helpfulOutput: !cliOptions.json,
hot: cliOptions.hot,
progress: cliOptions.progress,
Expand All @@ -2031,12 +2052,10 @@ class WebpackCLI {
}),
);

return configOptions;
return options;
};

config.options = Array.isArray(config.options)
? config.options.map((options) => addCLIPlugin(options))
: addCLIPlugin(config.options);
this.runFunctionOnOptions(config.options, addCLIPlugin);

return config;
}
Expand Down Expand Up @@ -2102,7 +2121,7 @@ class WebpackCLI {
return compiler;
}

async buildCommand(options, isWatchCommand) {
async runWebpack(options, isWatchCommand) {
// eslint-disable-next-line prefer-const
let compiler;
let createJsonStringifyStream;
Expand Down
6 changes: 6 additions & 0 deletions test/build/config/top-multi-compilers-options/index.js
@@ -0,0 +1,6 @@
console.log(`test ${Math.random()}`);
console.log(`test ${Math.random()}`);
console.log(`test ${Math.random()}`);
console.log(`test ${Math.random()}`);
console.log(`test ${Math.random()}`);
console.log(`test ${Math.random()}`);

0 comments on commit 82b1fb7

Please sign in to comment.