Skip to content

Commit

Permalink
Merge pull request #17339 from webpack/feat-allow-to-have-falsy-values
Browse files Browse the repository at this point in the history
feat: allow to use `falsy` loaders and plugins
  • Loading branch information
TheLarkInn committed Jun 14, 2023
2 parents 90ee051 + b2a09da commit fc703e2
Show file tree
Hide file tree
Showing 19 changed files with 384 additions and 74 deletions.
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";

0 comments on commit fc703e2

Please sign in to comment.