177 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| const forEachBail = require("./forEachBail");
 | |
| const { PathType, getType } = require("./util/path");
 | |
| 
 | |
| /** @typedef {import("./Resolver")} Resolver */
 | |
| /** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
 | |
| /** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
 | |
| /** @typedef {string | Array<string> | false} Alias */
 | |
| /** @typedef {{alias: Alias, name: string, onlyModule?: boolean}} AliasOption */
 | |
| 
 | |
| module.exports = class AliasPlugin {
 | |
| 	/**
 | |
| 	 * @param {string | ResolveStepHook} source source
 | |
| 	 * @param {AliasOption | Array<AliasOption>} options options
 | |
| 	 * @param {string | ResolveStepHook} target target
 | |
| 	 */
 | |
| 	constructor(source, options, target) {
 | |
| 		this.source = source;
 | |
| 		this.options = Array.isArray(options) ? options : [options];
 | |
| 		this.target = target;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param {Resolver} resolver the resolver
 | |
| 	 * @returns {void}
 | |
| 	 */
 | |
| 	apply(resolver) {
 | |
| 		const target = resolver.ensureHook(this.target);
 | |
| 		/**
 | |
| 		 * @param {string} maybeAbsolutePath path
 | |
| 		 * @returns {null|string} absolute path with slash ending
 | |
| 		 */
 | |
| 		const getAbsolutePathWithSlashEnding = (maybeAbsolutePath) => {
 | |
| 			const type = getType(maybeAbsolutePath);
 | |
| 			if (type === PathType.AbsolutePosix || type === PathType.AbsoluteWin) {
 | |
| 				return resolver.join(maybeAbsolutePath, "_").slice(0, -1);
 | |
| 			}
 | |
| 			return null;
 | |
| 		};
 | |
| 		/**
 | |
| 		 * @param {string} path path
 | |
| 		 * @param {string} maybeSubPath sub path
 | |
| 		 * @returns {boolean} true, if path is sub path
 | |
| 		 */
 | |
| 		const isSubPath = (path, maybeSubPath) => {
 | |
| 			const absolutePath = getAbsolutePathWithSlashEnding(maybeSubPath);
 | |
| 			if (!absolutePath) return false;
 | |
| 			return path.startsWith(absolutePath);
 | |
| 		};
 | |
| 		resolver
 | |
| 			.getHook(this.source)
 | |
| 			.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;
 | |
| 
 | |
| 						const matchRequest =
 | |
| 							innerRequest === item.name ||
 | |
| 							(!item.onlyModule &&
 | |
| 								(request.request
 | |
| 									? innerRequest.startsWith(`${item.name}/`)
 | |
| 									: 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
 | |
| 							 * @returns {void}
 | |
| 							 */
 | |
| 							const resolveWithAlias = (alias, callback) => {
 | |
| 								if (alias === false) {
 | |
| 									/** @type {ResolveRequest} */
 | |
| 									const ignoreObj = {
 | |
| 										...request,
 | |
| 										path: false,
 | |
| 									};
 | |
| 									if (typeof resolveContext.yield === "function") {
 | |
| 										resolveContext.yield(ignoreObj);
 | |
| 										return callback(null, null);
 | |
| 									}
 | |
| 									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;
 | |
| 									/** @type {ResolveRequest} */
 | |
| 									const obj = {
 | |
| 										...request,
 | |
| 										request: newRequestStr,
 | |
| 										fullySpecified: false,
 | |
| 									};
 | |
| 									return resolver.doResolve(
 | |
| 										target,
 | |
| 										obj,
 | |
| 										`aliased with mapping '${item.name}': '${alias}' to '${newRequestStr}'`,
 | |
| 										resolveContext,
 | |
| 										(err, result) => {
 | |
| 											if (err) return callback(err);
 | |
| 											if (result) return callback(null, result);
 | |
| 											return callback();
 | |
| 										},
 | |
| 									);
 | |
| 								}
 | |
| 								return callback();
 | |
| 							};
 | |
| 
 | |
| 							/**
 | |
| 							 * @param {(null | Error)=} err error
 | |
| 							 * @param {(null | ResolveRequest)=} result result
 | |
| 							 * @returns {void}
 | |
| 							 */
 | |
| 							const stoppingCallback = (err, result) => {
 | |
| 								if (err) return callback(err);
 | |
| 
 | |
| 								if (result) return callback(null, result);
 | |
| 								// Don't allow other aliasing or raw request
 | |
| 								if (shouldStop) return callback(null, null);
 | |
| 								return callback();
 | |
| 							};
 | |
| 
 | |
| 							if (Array.isArray(item.alias)) {
 | |
| 								return forEachBail(
 | |
| 									item.alias,
 | |
| 									resolveWithAlias,
 | |
| 									stoppingCallback,
 | |
| 								);
 | |
| 							}
 | |
| 							return resolveWithAlias(item.alias, stoppingCallback);
 | |
| 						}
 | |
| 
 | |
| 						return callback();
 | |
| 					},
 | |
| 					callback,
 | |
| 				);
 | |
| 			});
 | |
| 	}
 | |
| };
 |