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: allow to use falsy loaders and plugins #17339

Merged
merged 14 commits into from Jun 14, 2023
22 changes: 13 additions & 9 deletions declarations/WebpackOptions.d.ts
Expand Up @@ -249,6 +249,10 @@ export type FilterItemTypes = RegExp | string | ((value: string) => boolean);
* Enable production optimizations or development hints.
*/
export type Mode = "development" | "production" | "none";
/**
* These values will be ignored by webpack and created to be used with '&&' or '||' to improve readability of configurations.
*/
export type Falsy = false | 0 | "" | null | undefined;
/**
* One or multiple rule conditions.
*/
Expand Down Expand Up @@ -325,14 +329,14 @@ export type ResolveAlias =
* A list of descriptions of loaders applied.
*/
export type RuleSetUse =
| RuleSetUseItem[]
| (Falsy | RuleSetUseItem)[]
| ((data: {
resource: string;
realResource: string;
resourceQuery: string;
issuer: string;
compiler: string;
}) => RuleSetUseItem[])
}) => (Falsy | RuleSetUseItem)[])
| RuleSetUseItem;
/**
* A description of an applied loader.
Expand All @@ -352,12 +356,12 @@ export type RuleSetUseItem =
*/
options?: RuleSetLoaderOptions;
}
| ((data: object) => RuleSetUseItem | RuleSetUseItem[])
| ((data: object) => RuleSetUseItem | (Falsy | RuleSetUseItem)[])
| RuleSetLoader;
/**
* A list of rules.
*/
export type RuleSetRules = ("..." | RuleSetRule)[];
export type RuleSetRules = ("..." | Falsy | RuleSetRule)[];
/**
* Specify options for each generator.
*/
Expand Down Expand Up @@ -596,7 +600,7 @@ export type Performance = false | PerformanceOptions;
/**
* Add additional plugins to the compiler.
*/
export type Plugins = (WebpackPluginInstance | WebpackPluginFunction)[];
export type Plugins = (Falsy | WebpackPluginInstance | WebpackPluginFunction)[];
/**
* Capture timing information for each module.
*/
Expand Down Expand Up @@ -1392,7 +1396,7 @@ export interface RuleSetRule {
/**
* Only execute the first matching rule in this array.
*/
oneOf?: RuleSetRule[];
oneOf?: (Falsy | RuleSetRule)[];
/**
* Shortcut for use.options.
*/
Expand Down Expand Up @@ -1426,7 +1430,7 @@ export interface RuleSetRule {
/**
* Match and execute these rules when this rule is matched.
*/
rules?: RuleSetRule[];
rules?: (Falsy | RuleSetRule)[];
/**
* Match module scheme.
*/
Expand Down Expand Up @@ -1577,7 +1581,7 @@ export interface ResolveOptions {
/**
* Plugins for the resolver.
*/
plugins?: ("..." | ResolvePluginInstance)[];
plugins?: ("..." | Falsy | ResolvePluginInstance)[];
/**
* Prefer to resolve server-relative URLs (starting with '/') as absolute paths before falling back to resolve in 'resolve.roots'.
*/
Expand Down Expand Up @@ -1695,7 +1699,7 @@ export interface Optimization {
/**
* Minimizer(s) to use for minimizing the output.
*/
minimizer?: ("..." | WebpackPluginInstance | WebpackPluginFunction)[];
minimizer?: ("..." | Falsy | WebpackPluginInstance | WebpackPluginFunction)[];
/**
* Define the algorithm to choose module ids (natural: numeric ids in order of usage, named: readable ids for better debugging, hashed: (deprecated) short hashes as ids for better long term caching, deterministic: numeric hash ids for better long term caching, size: numeric ids focused on minimal initial download size, false: no algorithm used, as custom one can be provided via plugin).
*/
Expand Down
4 changes: 3 additions & 1 deletion lib/Compiler.js
Expand Up @@ -1073,7 +1073,9 @@ ${other}`);
childCompiler.root = this.root;
if (Array.isArray(plugins)) {
for (const plugin of plugins) {
plugin.apply(childCompiler);
if (plugin) {
plugin.apply(childCompiler);
}
}
}
for (const name in this.hooks) {
Expand Down
2 changes: 1 addition & 1 deletion lib/WebpackOptionsApply.js
Expand Up @@ -566,7 +566,7 @@ class WebpackOptionsApply extends OptionsApply {
for (const minimizer of options.optimization.minimizer) {
if (typeof minimizer === "function") {
minimizer.call(compiler, compiler);
} else if (minimizer !== "...") {
} else if (minimizer !== "..." && minimizer) {
minimizer.apply(compiler);
}
}
Expand Down
6 changes: 3 additions & 3 deletions lib/rules/RuleSetCompiler.js
Expand Up @@ -150,9 +150,9 @@ class RuleSetCompiler {
* @returns {CompiledRule[]} rules
*/
compileRules(path, rules, refs) {
return rules.map((rule, i) =>
this.compileRule(`${path}[${i}]`, rule, refs)
);
return rules
.filter(Boolean)
.map((rule, i) => this.compileRule(`${path}[${i}]`, rule, refs));
}

/**
Expand Down
10 changes: 6 additions & 4 deletions lib/rules/UseEffectRulePlugin.js
Expand Up @@ -106,9 +106,11 @@ class UseEffectRulePlugin {
*/
const useToEffectsWithoutIdent = (path, items) => {
if (Array.isArray(items)) {
return items.map((item, idx) =>
useToEffectRaw(`${path}[${idx}]`, "[[missing ident]]", item)
);
return items
.filter(Boolean)
.map((item, idx) =>
useToEffectRaw(`${path}[${idx}]`, "[[missing ident]]", item)
);
}
return [useToEffectRaw(path, "[[missing ident]]", items)];
};
Expand All @@ -120,7 +122,7 @@ class UseEffectRulePlugin {
*/
const useToEffects = (path, items) => {
if (Array.isArray(items)) {
return items.map((item, idx) => {
return items.filter(Boolean).map((item, idx) => {
const subPath = `${path}[${idx}]`;
return useToEffect(subPath, subPath, item);
});
Expand Down
2 changes: 1 addition & 1 deletion lib/webpack.js
Expand Up @@ -69,7 +69,7 @@ const createCompiler = rawOptions => {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler);
} else {
} else if (plugin) {
plugin.apply(compiler);
}
}
Expand Down
6 changes: 3 additions & 3 deletions package.json
Expand Up @@ -14,7 +14,7 @@
"acorn-import-assertions": "^1.9.0",
"browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.14.1",
"enhanced-resolve": "^5.15.0",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
Expand All @@ -24,7 +24,7 @@
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^3.1.2",
"schema-utils": "^3.2.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.7",
"watchpack": "^2.4.0",
Expand Down Expand Up @@ -97,7 +97,7 @@
"style-loader": "^2.0.0",
"terser": "^5.17.0",
"toml": "^3.0.0",
"tooling": "webpack/tooling#v1.22.1",
"tooling": "webpack/tooling#v1.23.0",
"ts-loader": "^9.4.2",
"typescript": "^5.0.4",
"url-loader": "^4.1.0",
Expand Down
2 changes: 1 addition & 1 deletion schemas/WebpackOptions.check.js

Large diffs are not rendered by default.

40 changes: 35 additions & 5 deletions schemas/WebpackOptions.json
Expand Up @@ -1145,6 +1145,15 @@
"node-commonjs"
]
},
"Falsy": {
"description": "These values will be ignored by webpack and created to be used with '&&' or '||' to improve readability of configurations.",
"cli": {
"exclude": true
},
"enum": [false, 0, "", null],
"undefinedAsNull": true,
"tsType": "false | 0 | '' | null | undefined"
},
"FileCacheOptions": {
"description": "Options object for persistent file-based caching.",
"type": "object",
Expand Down Expand Up @@ -2476,6 +2485,9 @@
{
"enum": ["..."]
},
{
"$ref": "#/definitions/Falsy"
},
{
"$ref": "#/definitions/WebpackPluginInstance"
},
Expand Down Expand Up @@ -3577,6 +3589,9 @@
"items": {
"description": "Plugin of type object or instanceof Function.",
"anyOf": [
{
"$ref": "#/definitions/Falsy"
},
{
"$ref": "#/definitions/WebpackPluginInstance"
},
Expand Down Expand Up @@ -3927,6 +3942,9 @@
{
"enum": ["..."]
},
{
"$ref": "#/definitions/Falsy"
},
{
"$ref": "#/definitions/ResolvePluginInstance"
}
Expand Down Expand Up @@ -4287,7 +4305,10 @@
"type": "array",
"items": {
"description": "A rule.",
"oneOf": [
"anyOf": [
{
"$ref": "#/definitions/Falsy"
},
{
"$ref": "#/definitions/RuleSetRule"
}
Expand Down Expand Up @@ -4356,7 +4377,10 @@
"type": "array",
"items": {
"description": "A rule.",
"oneOf": [
"anyOf": [
{
"$ref": "#/definitions/Falsy"
},
{
"$ref": "#/definitions/RuleSetRule"
}
Expand Down Expand Up @@ -4409,6 +4433,9 @@
},
"enum": ["..."]
},
{
"$ref": "#/definitions/Falsy"
},
{
"$ref": "#/definitions/RuleSetRule"
}
Expand All @@ -4422,7 +4449,10 @@
"type": "array",
"items": {
"description": "An use item.",
"oneOf": [
"anyOf": [
{
"$ref": "#/definitions/Falsy"
},
{
"$ref": "#/definitions/RuleSetUseItem"
}
Expand All @@ -4431,7 +4461,7 @@
},
{
"instanceof": "Function",
"tsType": "((data: { resource: string, realResource: string, resourceQuery: string, issuer: string, compiler: string }) => RuleSetUseItem[])"
"tsType": "((data: { resource: string, realResource: string, resourceQuery: string, issuer: string, compiler: string }) => (Falsy | RuleSetUseItem)[])"
},
{
"$ref": "#/definitions/RuleSetUseItem"
Expand Down Expand Up @@ -4469,7 +4499,7 @@
},
{
"instanceof": "Function",
"tsType": "((data: object) => RuleSetUseItem|RuleSetUseItem[])"
"tsType": "((data: object) => RuleSetUseItem | (Falsy | RuleSetUseItem)[])"
},
{
"$ref": "#/definitions/RuleSetLoader"
Expand Down
22 changes: 17 additions & 5 deletions test/Validation.test.js
Expand Up @@ -309,15 +309,18 @@ describe("Validation", () => {
"Invalid plugin provided: bool",
{
entry: "foo.js",
plugins: [false]
plugins: [true]
},
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, … } | function
false | 0 | \\"\\" | null | undefined | object { apply, … } | function
-> Plugin of type object or instanceof Function.
Details:
* configuration.plugins[0] should be one of these:
false | 0 | \\"\\" | null | undefined
-> These values will be ignored by webpack and created to be used with '&&' or '||' to improve readability of configurations.
* configuration.plugins[0] should be an object:
object { apply, … }
-> Plugin instance.
Expand All @@ -336,9 +339,12 @@ describe("Validation", () => {
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, … } | function
false | 0 | \\"\\" | null | undefined | object { apply, … } | function
-> Plugin of type object or instanceof Function.
Details:
* configuration.plugins[0] should be one of these:
false | 0 | \\"\\" | null | undefined
-> These values will be ignored by webpack and created to be used with '&&' or '||' to improve readability of configurations.
* configuration.plugins[0] should be an object:
object { apply, … }
-> Plugin instance.
Expand All @@ -357,9 +363,12 @@ describe("Validation", () => {
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, … } | function
false | 0 | \\"\\" | null | undefined | object { apply, … } | function
-> Plugin of type object or instanceof Function.
Details:
* configuration.plugins[0] should be one of these:
false | 0 | \\"\\" | null | undefined
-> These values will be ignored by webpack and created to be used with '&&' or '||' to improve readability of configurations.
* configuration.plugins[0] should be an object:
object { apply, … }
-> Plugin instance.
Expand All @@ -378,9 +387,12 @@ describe("Validation", () => {
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, … } | function
false | 0 | \\"\\" | null | undefined | object { apply, … } | function
-> Plugin of type object or instanceof Function.
Details:
* configuration.plugins[0] should be one of these:
false | 0 | \\"\\" | null | undefined
-> These values will be ignored by webpack and created to be used with '&&' or '||' to improve readability of configurations.
* configuration.plugins[0] should be an object:
object { apply, … }
-> Plugin instance.
Expand Down
1 change: 1 addition & 0 deletions test/configCases/loaders-and-plugins-falsy/basic/bar.js
@@ -0,0 +1 @@
export default "test";
1 change: 1 addition & 0 deletions test/configCases/loaders-and-plugins-falsy/basic/baz.js
@@ -0,0 +1 @@
export default "test";
1 change: 1 addition & 0 deletions test/configCases/loaders-and-plugins-falsy/basic/foo.js
@@ -0,0 +1 @@
export default "test";
12 changes: 12 additions & 0 deletions test/configCases/loaders-and-plugins-falsy/basic/index.js
@@ -0,0 +1,12 @@
import foo from "./foo.js?external";
import bar from "./bar.js";
import baz from "./baz.js?custom-use";
import other from "./other.js";

it("should work with falsy plugins and loaders", function() {
expect(ONE).toBe("ONE");
expect(foo.endsWith("?external")).toBe(true);
expect(bar).toBe("test");
expect(baz).toBe("test");
expect(other).toBe("NEW");
});
4 changes: 4 additions & 0 deletions test/configCases/loaders-and-plugins-falsy/basic/loader.js
@@ -0,0 +1,4 @@
/** @type {import("../../../../").LoaderDefinition<{ value: any }>} */
module.exports = function loader(content) {
return content.replace(/test/, "NEW");
};
1 change: 1 addition & 0 deletions test/configCases/loaders-and-plugins-falsy/basic/other.js
@@ -0,0 +1 @@
export default "test";