Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
"zipp",
"zippi",
"zizizi",
"codecov"
"codecov",
"xiaoxiaojx",
"Natsu",
"tsconfigs"
],
"ignorePaths": ["package.json", "yarn.lock", "coverage", "*.log"]
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ myResolver.resolve(
| restrictions | [] | A list of resolve restrictions |
| roots | [] | A list of root paths |
| symlinks | true | Whether to resolve symlinks to their symlinked location |
| tsconfig | true | Path to tsconfig.json for paths mapping. Default true loads tsconfig.json, or pass a string path. |
| unsafeCache | false | Use this cache object to unsafely cache the successful requests |

## Plugins
Expand Down
145 changes: 8 additions & 137 deletions lib/AliasPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@

"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 */

const { aliasResolveHandler } = require("./AliasUtils");

module.exports = class AliasPlugin {
/**
* @param {string | ResolveStepHook} source source
Expand All @@ -32,143 +30,16 @@ module.exports = class AliasPlugin {
*/
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(
aliasResolveHandler(
resolver,
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 = 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();
},
target,
request,
resolveContext,
callback,
);
});
Expand Down
172 changes: 172 additions & 0 deletions lib/AliasUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
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").ResolveContext} ResolveContext */
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
/** @typedef {import("./Resolver").ResolveCallback} ResolveCallback */
/** @typedef {string | Array<string> | false} Alias */
/** @typedef {{alias: Alias, name: string, onlyModule?: boolean}} AliasOption */

/** @typedef {(err?: null | Error, result?: null | ResolveRequest) => void} InnerCallback */
/**
* @param {Resolver} resolver resolver
* @param {Array<AliasOption>} options options
* @param {ResolveStepHook} target target
* @param {ResolveRequest} request request
* @param {ResolveContext} resolveContext resolve context
* @param {InnerCallback} callback callback
* @returns {void}
*/
function aliasResolveHandler(
resolver,
options,
target,
request,
resolveContext,
callback,
) {
const innerRequest = request.request || request.path;
if (!innerRequest) return callback();

/**
* @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);
};

forEachBail(
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 = 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,
);
}

module.exports.aliasResolveHandler = aliasResolveHandler;
Loading
Loading