Repository: webpack/enhanced-resolve
Pull request #439 (2024-12-20 02:46:15)

feat: add wildcards
Closes https://github.com/webpack/webpack/pull/19075

add wildcards pattern
```javascript
"@*": ["./src/*"]
``` 


Patch:
diff --git a/lib/AliasPlugin.js b/lib/AliasPlugin.js
index bbe9724..ce6ea24 100644
--- a/lib/AliasPlugin.js
+++ b/lib/AliasPlugin.js
@@ -58,20 +58,24 @@ module.exports = class AliasPlugin {
 			.tapAsync("AliasPlugin", (request, resolveContext, callback) => {
 				const innerRequest = request.request || request.path;
 				if (!innerRequest) return callback();
+
 				forEachBail(
 					this.options,
 					(item, callback) => {
 						/** @type {boolean} */
 						let shouldStop = false;
-						if (
+
+						const matchRequest =
 							innerRequest === item.name ||
 							(!item.onlyModule &&
 								(request.request
 									? innerRequest.startsWith(`${item.name}/`)
-									: isSubPath(innerRequest, item.name)))
-						) {
-							/** @type {string} */
-							const remainingRequest = innerRequest.slice(item.name.length);
+									: isSubPath(innerRequest, item.name)));
+
+						const splitName = item.name.split("*");
+						const matchWildcard = !item.onlyModule && splitName.length === 2;
+
+						if (matchRequest || matchWildcard) {
 							/**
 							 * @param {Alias} alias alias
 							 * @param {(err?: null|Error, result?: null|ResolveRequest) => void} callback callback
@@ -90,12 +94,34 @@ module.exports = class AliasPlugin {
 									}
 									return callback(null, ignoreObj);
 								}
+
+								let newRequestStr;
+
+								const [prefix, suffix] = splitName;
+								if (
+									matchWildcard &&
+									innerRequest.startsWith(prefix) &&
+									innerRequest.endsWith(suffix)
+								) {
+									const match = innerRequest.slice(
+										prefix.length,
+										innerRequest.length - suffix.length
+									);
+									newRequestStr = item.alias.toString().replace("*", match);
+								}
+
 								if (
+									matchRequest &&
 									innerRequest !== alias &&
 									!innerRequest.startsWith(alias + "/")
 								) {
+									/** @type {string} */
+									const remainingRequest = innerRequest.slice(item.name.length);
+									newRequestStr = alias + remainingRequest;
+								}
+
+								if (newRequestStr !== undefined) {
 									shouldStop = true;
-									const newRequestStr = alias + remainingRequest;
 									/** @type {ResolveRequest} */
 									const obj = {
 										...request,
@@ -122,6 +148,7 @@ module.exports = class AliasPlugin {
 								}
 								return callback();
 							};
+
 							/**
 							 * @param {null|Error} [err] error
 							 * @param {null|ResolveRequest} [result] result
@@ -135,6 +162,7 @@ module.exports = class AliasPlugin {
 								if (shouldStop) return callback(null, null);
 								return callback();
 							};
+
 							if (Array.isArray(item.alias)) {
 								return forEachBail(
 									item.alias,
@@ -145,6 +173,7 @@ module.exports = class AliasPlugin {
 								return resolveWithAlias(item.alias, stoppingCallback);
 							}
 						}
+
 						return callback();
 					},
 					callback


Test patch:
diff --git a/test/alias.test.js b/test/alias.test.js
index 81cf5bc..8a81cab 100644
--- a/test/alias.test.js
+++ b/test/alias.test.js
@@ -40,6 +40,9 @@ describe("alias", () => {
 				// alias configuration should work
 				"#": "/c/dir",
 				"@": "/c/dir",
+				"@*": "/*",
+				"@e*": "/e/*",
+				"@e*file": "/e*file",
 				ignored: false
 			},
 			modules: "/",
@@ -73,6 +76,16 @@ describe("alias", () => {
 		expect(resolver.resolveSync({}, "/", "@")).toEqual("/c/dir/index");
 		expect(resolver.resolveSync({}, "/", "@/index")).toEqual("/c/dir/index");
 	});
+
+	it("should resolve wildcard alias", () => {
+		expect(resolver.resolveSync({}, "/", "@a")).toEqual("/a/index");
+		expect(resolver.resolveSync({}, "/", "@a/dir")).toEqual("/a/dir/index");
+		expect(resolver.resolveSync({}, "/", "@e/dir/file")).toEqual("/e/dir/file");
+		expect(resolver.resolveSync({}, "/", "@e/anotherDir")).toEqual(
+			"/e/anotherDir/index"
+		);
+		expect(resolver.resolveSync({}, "/", "@e/dir/file")).toEqual("/e/dir/file");
+	});
 	it("should resolve an ignore module", () => {
 		expect(resolver.resolveSync({}, "/", "ignored")).toEqual(false);
 	});