Repository: webpack/enhanced-resolve
Pull request #389 (2023-06-06 20:49:27)

feat: ignore `false`/`null`/`undefined` plugins
Part of https://github.com/webpack/webpack-cli/issues/3836

Allow to use:
```js
// `false` value for an example, for example when you are in development mode and don't want to use plugin
// so you don't need to use `.filter(Boolean)`, it is faster and has a better DX
const isProduction = false; 

module.exports = {
  //...
  resolve: {
    plugins: [
      isProduction && new DirectoryNamedWebpackPlugin()
    ],
  },
};
```

Note - you an use any `falsy` values, `NaN` is `falsy` too, but https://github.com/Microsoft/TypeScript/issues/15135, and I don't think it is a good idea allow to pass `NaN`


Patch:
diff --git a/lib/ResolverFactory.js b/lib/ResolverFactory.js
index 9eb5bff..037567b 100644
--- a/lib/ResolverFactory.js
+++ b/lib/ResolverFactory.js
@@ -50,7 +50,8 @@ const UseFilePlugin = require("./UseFilePlugin");
 /** @typedef {string|string[]|false} AliasOptionNewRequest */
 /** @typedef {{[k: string]: AliasOptionNewRequest}} AliasOptions */
 /** @typedef {{[k: string]: string|string[] }} ExtensionAliasOptions */
-/** @typedef {{apply: function(Resolver): void} | function(this: Resolver, Resolver): void} Plugin */
+/** @typedef {false | 0 | "" | null | undefined} Falsy */
+/** @typedef {{apply: function(Resolver): void} | (function(this: Resolver, Resolver): void) | Falsy} Plugin */
 
 /**
  * @typedef {Object} UserResolveOptions
@@ -657,7 +658,7 @@ exports.createResolver = function (options) {
 		if (typeof plugin === "function") {
 			/** @type {function(this: Resolver, Resolver): void} */
 			(plugin).call(resolver, resolver);
-		} else {
+		} else if (plugin) {
 			plugin.apply(resolver);
 		}
 	}
diff --git a/types.d.ts b/types.d.ts
index 05fc03a..e88d59c 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -281,6 +281,11 @@ declare interface ParsedIdentifier {
 	internal: boolean;
 }
 type Plugin =
+	| undefined
+	| null
+	| false
+	| ""
+	| 0
 	| { apply: (arg0: Resolver) => void }
 	| ((this: Resolver, arg1: Resolver) => void);
 declare interface PnpApiImpl {


Test patch:
diff --git a/test/plugins.test.js b/test/plugins.test.js
index c9bf3b4..6a9e770 100644
--- a/test/plugins.test.js
+++ b/test/plugins.test.js
@@ -33,4 +33,46 @@ describe("plugins", function () {
 			}
 		);
 	});
+
+	it("should ignore 'false'/'null'/'undefined' plugins", done => {
+		const FailedPlugin = class {
+			apply() {
+				throw new Error("FailedPlugin");
+			}
+		};
+		const falsy = false;
+		const resolver = ResolverFactory.createResolver({
+			fileSystem: require("fs"),
+			plugins: [
+				0,
+				"",
+				false,
+				null,
+				undefined,
+				falsy && new FailedPlugin(),
+				new CloneBasenamePlugin(
+					"after-existing-directory",
+					"undescribed-raw-file"
+				)
+			]
+		});
+
+		resolver.resolve(
+			{},
+			__dirname,
+			"./fixtures/directory-default",
+			{},
+			function (err, result) {
+				if (err) return done(err);
+				if (!result) return done(new Error("No result"));
+				expect(result).toEqual(
+					path.resolve(
+						__dirname,
+						"fixtures/directory-default/directory-default.js"
+					)
+				);
+				done();
+			}
+		);
+	});
 });