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

validate compiler options in multicompiler mode #15460

Merged
merged 12 commits into from
May 8, 2024
36 changes: 36 additions & 0 deletions lib/MultiCompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const { SyncHook, MultiHook } = require("tapable");
const ConcurrentCompilationError = require("./ConcurrentCompilationError");
const MultiStats = require("./MultiStats");
const MultiWatching = require("./MultiWatching");
const WebpackError = require("./WebpackError");
const { defaultCacheName } = require("./config/defaults");
const ArrayQueue = require("./util/ArrayQueue");

/** @template T @typedef {import("tapable").AsyncSeriesHook<T>} AsyncSeriesHook<T> */
Expand Down Expand Up @@ -109,6 +111,40 @@ module.exports = class MultiCompiler {
}
});
}
this._validateCompilersOptions();
}

_validateCompilersOptions() {
if (this.compilers.length < 2) return;
/**
* @param {Compiler} compiler compiler
* @param {WebpackError} warning warning
*/
const addWarning = (compiler, warning) => {
compiler.hooks.thisCompilation.tap("MultiCompiler", compilation => {
compilation.warnings.push(warning);
});
};
for (const compiler of this.compilers) {
if (compiler.options.cache && "name" in compiler.options.cache) {
const name = compiler.options.cache.name;
const match = /(.+)__compiler(\d+)__$/.exec(name);
// if there is no match it is unique name
if (!match || match[1] === defaultCacheName) continue;
addWarning(
compiler,
new WebpackError(
`${
compiler.name
? `Compiler with name "${compiler.name}" doesn't use unique cache name. `
: ""
}Please set unique "cache.name" option. Name "${
match[1]
}" already used.`
)
);
}
}
}

get options() {
Expand Down
68 changes: 59 additions & 9 deletions lib/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const {
getDefaultTarget
} = require("./target");

/** @typedef {import("../../declarations/WebpackOptions").CacheOptionsNormalized} CacheOptions */
/** @typedef {import("../../declarations/WebpackOptions").CacheOptions} CacheOptions */
/** @typedef {import("../../declarations/WebpackOptions").CacheOptionsNormalized} CacheOptionsNormalized */
/** @typedef {import("../../declarations/WebpackOptions").Context} Context */
/** @typedef {import("../../declarations/WebpackOptions").CssGeneratorOptions} CssGeneratorOptions */
/** @typedef {import("../../declarations/WebpackOptions").CssParserOptions} CssParserOptions */
Expand Down Expand Up @@ -60,12 +61,14 @@ const {
/** @typedef {import("../../declarations/WebpackOptions").RuleSetRules} RuleSetRules */
/** @typedef {import("../../declarations/WebpackOptions").SnapshotOptions} SnapshotOptions */
/** @typedef {import("../../declarations/WebpackOptions").Target} Target */
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptionsNormalized */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
/** @typedef {import("./target").TargetProperties} TargetProperties */

const NODE_MODULES_REGEXP = /[\\/]node_modules[\\/]/i;
const DEFAULT_CACHE_NAME = "default";

/**
* Sets a constant default value when undefined
Expand Down Expand Up @@ -137,7 +140,7 @@ const A = (obj, prop, factory) => {
};

/**
* @param {WebpackOptions} options options to be modified
* @param {WebpackOptionsNormalized} options options to be modified
* @returns {void}
*/
const applyWebpackOptionsBaseDefaults = options => {
Expand All @@ -146,7 +149,50 @@ const applyWebpackOptionsBaseDefaults = options => {
};

/**
* @param {WebpackOptions} options options to be modified
* @returns {(cache: CacheOptions, i: number) => CacheOptions} cache options
*/
const applyDefaultCacheNamesForMultiCompiler = () => {
/** @type {Set<string>} */
const cacheNames = new Set();
return (cache, i) => {
if (!cache) return cache;
if (cache === true) return true;
Copy link
Member

Choose a reason for hiding this comment

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

Let's add a test where we have two true values and so don't have any problems

if (cache.type !== "filesystem") return { ...cache };
const name = cache.name ? cache.name : DEFAULT_CACHE_NAME;
if (i === 0) {
cacheNames.add(name);
return { ...cache };
}
if (cacheNames.has(name)) {
return { ...cache, name: `${name}__compiler${i + 1}__` };
}
cacheNames.add(cache.name);
return { ...cache };
};
};

/**
* @param {ReadonlyArray<WebpackOptions>} options input config
* @returns {ReadonlyArray<WebpackOptions>} normalized options
*/
const applyWebpackMultiCompilerOptions = options => {
/** @type {Array<WebpackOptions>} */
const resultedOptions = [];
const applyCacheOptions = applyDefaultCacheNamesForMultiCompiler();

for (let i = 0; i < options.length; i++) {
const config = options[i];
const cache = config.cache;
const newConfig = { ...config };
resultedOptions.push(newConfig);
newConfig.cache = applyCacheOptions(cache, i);
}

return /** @type {ReadonlyArray<WebpackOptions>} */ (resultedOptions);
};

/**
* @param {WebpackOptionsNormalized} options options to be modified
* @returns {void}
*/
const applyWebpackOptionsDefaults = options => {
Expand Down Expand Up @@ -201,7 +247,7 @@ const applyWebpackOptionsDefaults = options => {
development ? { type: /** @type {"memory"} */ ("memory") } : false
);
applyCacheDefaults(options.cache, {
name: name || "default",
name: name || DEFAULT_CACHE_NAME,
mode: mode || "production",
development,
cacheUnaffected: options.experiments.cacheUnaffected
Expand Down Expand Up @@ -251,7 +297,9 @@ const applyWebpackOptionsDefaults = options => {
});

applyLoaderDefaults(
/** @type {NonNullable<WebpackOptions["loader"]>} */ (options.loader),
/** @type {NonNullable<WebpackOptionsNormalized["loader"]>} */ (
options.loader
),
{ targetProperties, environment: options.output.environment }
);

Expand All @@ -268,7 +316,7 @@ const applyWebpackOptionsDefaults = options => {

applyNodeDefaults(options.node, {
futureDefaults:
/** @type {NonNullable<WebpackOptions["experiments"]["futureDefaults"]>} */
/** @type {NonNullable<WebpackOptionsNormalized["experiments"]["futureDefaults"]>} */
(options.experiments.futureDefaults),
outputModule: options.output.module,
targetProperties
Expand All @@ -282,7 +330,7 @@ const applyWebpackOptionsDefaults = options => {
: false
);
applyPerformanceDefaults(
/** @type {NonNullable<WebpackOptions["performance"]>} */
/** @type {NonNullable<WebpackOptionsNormalized["performance"]>} */
(options.performance),
{
production
Expand Down Expand Up @@ -354,7 +402,7 @@ const applyExperimentsDefaults = (
};

/**
* @param {CacheOptions} cache options
* @param {CacheOptionsNormalized} cache options
* @param {Object} options options
* @param {string} options.name name
* @param {Mode} options.mode mode
Expand Down Expand Up @@ -1595,5 +1643,7 @@ const applyInfrastructureLoggingDefaults = infrastructureLogging => {
D(infrastructureLogging, "appendOnly", !tty);
};

exports.defaultCacheName = DEFAULT_CACHE_NAME;
exports.applyWebpackOptionsBaseDefaults = applyWebpackOptionsBaseDefaults;
exports.applyWebpackOptionsDefaults = applyWebpackOptionsDefaults;
exports.applyWebpackMultiCompilerOptions = applyWebpackMultiCompilerOptions;
4 changes: 3 additions & 1 deletion lib/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const MultiCompiler = require("./MultiCompiler");
const WebpackOptionsApply = require("./WebpackOptionsApply");
const {
applyWebpackOptionsDefaults,
applyWebpackMultiCompilerOptions,
applyWebpackOptionsBaseDefaults
} = require("./config/defaults");
const { getNormalizedWebpackOptions } = require("./config/normalization");
Expand Down Expand Up @@ -42,7 +43,8 @@ const getValidateSchema = memoize(() => require("./validateSchema"));
* @returns {MultiCompiler} a multi-compiler
*/
const createMultiCompiler = (childOptions, options) => {
const compilers = childOptions.map(options => createCompiler(options));
const appliedOptions = applyWebpackMultiCompilerOptions(childOptions);
const compilers = appliedOptions.map(options => createCompiler(options));
const compiler = new MultiCompiler(compilers, options);
for (const childCompiler of compilers) {
if (childCompiler.options.dependencies) {
Expand Down
15 changes: 12 additions & 3 deletions test/ConfigTestCases.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,21 @@ const describeCases = config => {
(options.experiments && options.experiments.outputModule
? ".mjs"
: ".js");
if (config.cache) {
if (
(config.cache && config.cache !== true) ||
(options.cache && options.cache !== true)
) {
options.cache = {
cacheDirectory,
name: `config-${idx}`,
...config.cache
name:
options.cache && options.cache !== true
? options.cache.name
: `config-${idx}`,
...config.cache,
...options.cache
};
}
if (config.cache) {
options.infrastructureLogging = {
debug: true,
console: createLogger(infraStructureLog)
Expand Down
11 changes: 11 additions & 0 deletions test/StatsTestCases.basictest.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ describe("StatsTestCases", () => {
if (!options.optimization) options.optimization = {};
if (options.optimization.minimize === undefined)
options.optimization.minimize = false;
if (
options.cache &&
options.cache !== true &&
options.cache.type === "filesystem"
) {
options.cache.cacheDirectory = path.resolve(
outputBase,
".cache",
testName
);
}
});
const c = webpack(options);
const compilers = c.compilers ? c.compilers : [c];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
it("should build", () => {});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use strict";

// default settings. should just work

/** @type {import("../../../../").Configuration} */
module.exports = [
{
mode: "production",
entry: "./index",
cache: {
type: "filesystem",
name: "name2"
}
},
{
mode: "production",
entry: "./index",
cache: {
type: "filesystem",
name: "name1"
}
}
];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
it("should build", () => {});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use strict";

// no cache names

/** @type {import("../../../../").Configuration} */
module.exports = [
{
mode: "production",
entry: "./index",
cache: {
type: "filesystem"
}
},
{
mode: "production",
entry: "./index",
cache: {
type: "filesystem"
}
},
{
name: "3rd compiler",
mode: "production",
entry: "./index",
cache: {
type: "filesystem"
}
}
];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
it("should build", () => {});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = [
/Please set unique "cache\.name" option/,
/Compiler with name "3rd compiler" doesn't use unique cache name/
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"use strict";

// with explicit cache names

/** @type {import("../../../../").Configuration} */
module.exports = [
{
mode: "production",
entry: "./index",
cache: {
name: "filesystem",
type: "filesystem"
}
},
{
mode: "production",
entry: "./index",
cache: {
name: "filesystem",
type: "filesystem"
}
},
{
name: "3rd compiler",
mode: "production",
entry: "./index",
cache: {
name: "filesystem",
type: "filesystem"
}
}
];