enhanced-resolve
Advanced tools
Comparing version 5.12.0 to 5.13.0
@@ -48,6 +48,14 @@ /* | ||
/** @typedef {function((NodeJS.ErrnoException | null)=, (string | Buffer)[] | IDirent[]=): void} DirentArrayCallback */ | ||
/** | ||
* @typedef {Object} ReaddirOptions | ||
* @property {BufferEncoding | null | 'buffer'} [encoding] | ||
* @property {boolean | undefined} [withFileTypes=false] | ||
*/ | ||
/** | ||
* @typedef {Object} FileSystem | ||
* @property {(function(string, FileSystemCallback<Buffer | string>): void) & function(string, object, FileSystemCallback<Buffer | string>): void} readFile | ||
* @property {(function(string, FileSystemCallback<(Buffer | string)[] | FileSystemDirent[]>): void) & function(string, object, FileSystemCallback<(Buffer | string)[] | FileSystemDirent[]>): void} readdir | ||
* @property {function(string, (ReaddirOptions | BufferEncoding | null | undefined | 'buffer' | DirentArrayCallback)=, DirentArrayCallback=): void} readdir | ||
* @property {((function(string, FileSystemCallback<object>): void) & function(string, object, FileSystemCallback<object>): void)=} readJson | ||
@@ -54,0 +62,0 @@ * @property {(function(string, FileSystemCallback<Buffer | string>): void) & function(string, object, FileSystemCallback<Buffer | string>): void} readlink |
@@ -15,10 +15,2 @@ /* | ||
/** | ||
* @typedef {Object} PathTreeNode | ||
* @property {Map<string, PathTreeNode>|null} children | ||
* @property {MappingValue} folder | ||
* @property {Map<string, MappingValue>|null} wildcards | ||
* @property {Map<string, MappingValue>} files | ||
*/ | ||
/** | ||
* Processing exports/imports field | ||
@@ -87,2 +79,3 @@ * @callback FieldProcessor | ||
const hashCode = "#".charCodeAt(0); | ||
const patternRegEx = /\*/g; | ||
@@ -97,3 +90,4 @@ /** | ||
return createFieldProcessor( | ||
buildExportsFieldPathTree(exportsField), | ||
buildExportsField(exportsField), | ||
request => (request.length === 0 ? "." : "./" + request), | ||
assertExportsFieldRequest, | ||
@@ -112,3 +106,4 @@ assertExportTarget | ||
return createFieldProcessor( | ||
buildImportsFieldPathTree(importsField), | ||
buildImportsField(importsField), | ||
request => "#" + request, | ||
assertImportsFieldRequest, | ||
@@ -120,3 +115,4 @@ assertImportTarget | ||
/** | ||
* @param {PathTreeNode} treeRoot root | ||
* @param {ExportsField | ImportsField} field root | ||
* @param {(s: string) => string} normalizeRequest Normalize request, for `imports` field it adds `#`, for `exports` field it adds `.` or `./` | ||
* @param {(s: string) => string} assertRequest assertRequest | ||
@@ -126,11 +122,16 @@ * @param {(s: string, f: boolean) => void} assertTarget assertTarget | ||
*/ | ||
function createFieldProcessor(treeRoot, assertRequest, assertTarget) { | ||
function createFieldProcessor( | ||
field, | ||
normalizeRequest, | ||
assertRequest, | ||
assertTarget | ||
) { | ||
return function fieldProcessor(request, conditionNames) { | ||
request = assertRequest(request); | ||
const match = findMatch(request, treeRoot); | ||
const match = findMatch(normalizeRequest(request), field); | ||
if (match === null) return []; | ||
const [mapping, remainRequestIndex] = match; | ||
const [mapping, remainingRequest, isSubpathMapping, isPattern] = match; | ||
@@ -152,12 +153,6 @@ /** @type {DirectMapping|null} */ | ||
const remainingRequest = | ||
remainRequestIndex === request.length + 1 | ||
? undefined | ||
: remainRequestIndex < 0 | ||
? request.slice(-remainRequestIndex - 1) | ||
: request.slice(remainRequestIndex); | ||
return directMapping( | ||
remainingRequest, | ||
remainRequestIndex < 0, | ||
isPattern, | ||
isSubpathMapping, | ||
direct, | ||
@@ -261,97 +256,83 @@ conditionNames, | ||
function patternKeyCompare(a, b) { | ||
const aPatternIndex = a.indexOf("*"); | ||
const bPatternIndex = b.indexOf("*"); | ||
const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; | ||
const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; | ||
if (baseLenA > baseLenB) return -1; | ||
if (baseLenB > baseLenA) return 1; | ||
if (aPatternIndex === -1) return 1; | ||
if (bPatternIndex === -1) return -1; | ||
if (a.length > b.length) return -1; | ||
if (b.length > a.length) return 1; | ||
return 0; | ||
} | ||
/** | ||
* Trying to match request to field | ||
* @param {string} request request | ||
* @param {PathTreeNode} treeRoot path tree root | ||
* @returns {[MappingValue, number]|null} match or null, number is negative and one less when it's a folder mapping, number is request.length + 1 for direct mappings | ||
* @param {ExportsField | ImportsField} field exports or import field | ||
* @returns {[MappingValue, string, boolean, boolean]|null} match or null, number is negative and one less when it's a folder mapping, number is request.length + 1 for direct mappings | ||
*/ | ||
function findMatch(request, treeRoot) { | ||
if (request.length === 0) { | ||
const value = treeRoot.files.get(""); | ||
return value ? [value, 1] : null; | ||
} | ||
function findMatch(request, field) { | ||
if ( | ||
treeRoot.children === null && | ||
treeRoot.folder === null && | ||
treeRoot.wildcards === null | ||
Object.prototype.hasOwnProperty.call(field, request) && | ||
!request.includes("*") && | ||
!request.endsWith("/") | ||
) { | ||
const value = treeRoot.files.get(request); | ||
const target = field[request]; | ||
return value ? [value, request.length + 1] : null; | ||
return [target, "", false, false]; | ||
} | ||
let node = treeRoot; | ||
let lastNonSlashIndex = 0; | ||
let slashIndex = request.indexOf("/", 0); | ||
let bestMatch = ""; | ||
let bestMatchSubpath; | ||
/** @type {[MappingValue, number]|null} */ | ||
let lastFolderMatch = null; | ||
const keys = Object.getOwnPropertyNames(field); | ||
const applyFolderMapping = () => { | ||
const folderMapping = node.folder; | ||
if (folderMapping) { | ||
if (lastFolderMatch) { | ||
lastFolderMatch[0] = folderMapping; | ||
lastFolderMatch[1] = -lastNonSlashIndex - 1; | ||
} else { | ||
lastFolderMatch = [folderMapping, -lastNonSlashIndex - 1]; | ||
} | ||
} | ||
}; | ||
for (let i = 0; i < keys.length; i++) { | ||
const key = keys[i]; | ||
const patternIndex = key.indexOf("*"); | ||
const applyWildcardMappings = (wildcardMappings, remainingRequest) => { | ||
if (wildcardMappings) { | ||
for (const [key, target] of wildcardMappings) { | ||
if (remainingRequest.startsWith(key)) { | ||
if (!lastFolderMatch) { | ||
lastFolderMatch = [target, lastNonSlashIndex + key.length]; | ||
} else if (lastFolderMatch[1] < lastNonSlashIndex + key.length) { | ||
lastFolderMatch[0] = target; | ||
lastFolderMatch[1] = lastNonSlashIndex + key.length; | ||
} | ||
} | ||
if (patternIndex !== -1 && request.startsWith(key.slice(0, patternIndex))) { | ||
const patternTrailer = key.slice(patternIndex + 1); | ||
if ( | ||
request.length >= key.length && | ||
request.endsWith(patternTrailer) && | ||
patternKeyCompare(bestMatch, key) === 1 && | ||
key.lastIndexOf("*") === patternIndex | ||
) { | ||
bestMatch = key; | ||
bestMatchSubpath = request.slice( | ||
patternIndex, | ||
request.length - patternTrailer.length | ||
); | ||
} | ||
} | ||
}; | ||
while (slashIndex !== -1) { | ||
applyFolderMapping(); | ||
const wildcardMappings = node.wildcards; | ||
if (!wildcardMappings && node.children === null) return lastFolderMatch; | ||
const folder = request.slice(lastNonSlashIndex, slashIndex); | ||
applyWildcardMappings(wildcardMappings, folder); | ||
if (node.children === null) return lastFolderMatch; | ||
const newNode = node.children.get(folder); | ||
if (!newNode) { | ||
return lastFolderMatch; | ||
// For legacy `./foo/` | ||
else if ( | ||
key[key.length - 1] === "/" && | ||
request.startsWith(key) && | ||
patternKeyCompare(bestMatch, key) === 1 | ||
) { | ||
bestMatch = key; | ||
bestMatchSubpath = request.slice(key.length); | ||
} | ||
node = newNode; | ||
lastNonSlashIndex = slashIndex + 1; | ||
slashIndex = request.indexOf("/", lastNonSlashIndex); | ||
} | ||
const remainingRequest = | ||
lastNonSlashIndex > 0 ? request.slice(lastNonSlashIndex) : request; | ||
if (bestMatch === "") return null; | ||
const value = node.files.get(remainingRequest); | ||
const target = field[bestMatch]; | ||
const isSubpathMapping = bestMatch.endsWith("/"); | ||
const isPattern = bestMatch.includes("*"); | ||
if (value) { | ||
return [value, request.length + 1]; | ||
} | ||
applyFolderMapping(); | ||
applyWildcardMappings(node.wildcards, remainingRequest); | ||
return lastFolderMatch; | ||
return [ | ||
target, | ||
/** @type {string} */ (bestMatchSubpath), | ||
isSubpathMapping, | ||
isPattern | ||
]; | ||
} | ||
@@ -371,3 +352,4 @@ | ||
* @param {string|undefined} remainingRequest remaining request when folder mapping, undefined for file mappings | ||
* @param {boolean} subpathMapping true, for subpath mappings | ||
* @param {boolean} isPattern true, if mapping is a pattern (contains "*") | ||
* @param {boolean} isSubpathMapping true, for subpath mappings | ||
* @param {DirectMapping|null} mappingTarget direct export | ||
@@ -380,3 +362,4 @@ * @param {Set<string>} conditionNames condition names | ||
remainingRequest, | ||
subpathMapping, | ||
isPattern, | ||
isSubpathMapping, | ||
mappingTarget, | ||
@@ -390,3 +373,9 @@ conditionNames, | ||
return [ | ||
targetMapping(remainingRequest, subpathMapping, mappingTarget, assert) | ||
targetMapping( | ||
remainingRequest, | ||
isPattern, | ||
isSubpathMapping, | ||
mappingTarget, | ||
assert | ||
) | ||
]; | ||
@@ -400,3 +389,9 @@ } | ||
targets.push( | ||
targetMapping(remainingRequest, subpathMapping, exp, assert) | ||
targetMapping( | ||
remainingRequest, | ||
isPattern, | ||
isSubpathMapping, | ||
exp, | ||
assert | ||
) | ||
); | ||
@@ -410,3 +405,4 @@ continue; | ||
remainingRequest, | ||
subpathMapping, | ||
isPattern, | ||
isSubpathMapping, | ||
mapping, | ||
@@ -426,3 +422,4 @@ conditionNames, | ||
* @param {string|undefined} remainingRequest remaining request when folder mapping, undefined for file mappings | ||
* @param {boolean} subpathMapping true, for subpath mappings | ||
* @param {boolean} isPattern true, if mapping is a pattern (contains "*") | ||
* @param {boolean} isSubpathMapping true, for subpath mappings | ||
* @param {string} mappingTarget direct export | ||
@@ -434,3 +431,4 @@ * @param {(d: string, f: boolean) => void} assert asserting direct value | ||
remainingRequest, | ||
subpathMapping, | ||
isPattern, | ||
isSubpathMapping, | ||
mappingTarget, | ||
@@ -441,10 +439,24 @@ assert | ||
assert(mappingTarget, false); | ||
return mappingTarget; | ||
} | ||
if (subpathMapping) { | ||
if (isSubpathMapping) { | ||
assert(mappingTarget, true); | ||
return mappingTarget + remainingRequest; | ||
} | ||
assert(mappingTarget, false); | ||
return mappingTarget.replace(/\*/g, remainingRequest.replace(/\$/g, "$$")); | ||
let result = mappingTarget; | ||
if (isPattern) { | ||
result = result.replace( | ||
patternRegEx, | ||
remainingRequest.replace(/\$/g, "$$") | ||
); | ||
} | ||
return result; | ||
} | ||
@@ -507,88 +519,9 @@ | ||
/** | ||
* Internal helper to create path tree node | ||
* to ensure that each node gets the same hidden class | ||
* @returns {PathTreeNode} node | ||
*/ | ||
function createNode() { | ||
return { | ||
children: null, | ||
folder: null, | ||
wildcards: null, | ||
files: new Map() | ||
}; | ||
} | ||
/** | ||
* Internal helper for building path tree | ||
* @param {PathTreeNode} root root | ||
* @param {string} path path | ||
* @param {MappingValue} target target | ||
*/ | ||
function walkPath(root, path, target) { | ||
if (path.length === 0) { | ||
root.folder = target; | ||
return; | ||
} | ||
let node = root; | ||
// Typical path tree can looks like | ||
// root | ||
// - files: ["a.js", "b.js"] | ||
// - children: | ||
// node1: | ||
// - files: ["a.js", "b.js"] | ||
let lastNonSlashIndex = 0; | ||
let slashIndex = path.indexOf("/", 0); | ||
while (slashIndex !== -1) { | ||
const folder = path.slice(lastNonSlashIndex, slashIndex); | ||
let newNode; | ||
if (node.children === null) { | ||
newNode = createNode(); | ||
node.children = new Map(); | ||
node.children.set(folder, newNode); | ||
} else { | ||
newNode = node.children.get(folder); | ||
if (!newNode) { | ||
newNode = createNode(); | ||
node.children.set(folder, newNode); | ||
} | ||
} | ||
node = newNode; | ||
lastNonSlashIndex = slashIndex + 1; | ||
slashIndex = path.indexOf("/", lastNonSlashIndex); | ||
} | ||
if (lastNonSlashIndex >= path.length) { | ||
node.folder = target; | ||
} else { | ||
const file = lastNonSlashIndex > 0 ? path.slice(lastNonSlashIndex) : path; | ||
if (file.endsWith("*")) { | ||
if (node.wildcards === null) node.wildcards = new Map(); | ||
node.wildcards.set(file.slice(0, -1), target); | ||
} else { | ||
node.files.set(file, target); | ||
} | ||
} | ||
} | ||
/** | ||
* @param {ExportsField} field exports field | ||
* @returns {PathTreeNode} tree root | ||
* @returns {ExportsField} normalized exports field | ||
*/ | ||
function buildExportsFieldPathTree(field) { | ||
const root = createNode(); | ||
function buildExportsField(field) { | ||
// handle syntax sugar, if exports field is direct mapping for "." | ||
if (typeof field === "string") { | ||
root.files.set("", field); | ||
return root; | ||
} else if (Array.isArray(field)) { | ||
root.files.set("", field.slice()); | ||
return root; | ||
if (typeof field === "string" || Array.isArray(field)) { | ||
return { ".": field }; | ||
} | ||
@@ -616,4 +549,3 @@ | ||
root.files.set("", field); | ||
return root; | ||
return { ".": field }; | ||
} | ||
@@ -629,3 +561,2 @@ | ||
if (key.length === 1) { | ||
root.files.set("", field[key]); | ||
continue; | ||
@@ -641,7 +572,5 @@ } | ||
} | ||
walkPath(root, key.slice(2), field[key]); | ||
} | ||
return root; | ||
return field; | ||
} | ||
@@ -651,7 +580,5 @@ | ||
* @param {ImportsField} field imports field | ||
* @returns {PathTreeNode} root | ||
* @returns {ImportsField} normalized imports field | ||
*/ | ||
function buildImportsFieldPathTree(field) { | ||
const root = createNode(); | ||
function buildImportsField(field) { | ||
const keys = Object.keys(field); | ||
@@ -683,7 +610,5 @@ | ||
} | ||
walkPath(root, key.slice(1), field[key]); | ||
} | ||
return root; | ||
return field; | ||
} |
{ | ||
"name": "enhanced-resolve", | ||
"version": "5.12.0", | ||
"version": "5.13.0", | ||
"author": "Tobias Koppers @sokra", | ||
@@ -5,0 +5,0 @@ "description": "Offers a async require.resolve function. It's highly configurable.", |
@@ -66,6 +66,6 @@ # enhanced-resolve | ||
const context = {}; | ||
const resolveContext = {}; | ||
const lookupStartPath = "/Users/webpack/some/root/dir"; | ||
const request = "./path/to-look-up.js"; | ||
myResolver.resolve({}, lookupStartPath, request, resolveContext, ( | ||
const resolveContext = {}; | ||
myResolver.resolve(context, lookupStartPath, request, resolveContext, ( | ||
err /*Error*/, | ||
@@ -87,3 +87,3 @@ filepath /*string*/ | ||
| cacheWithContext | true | If unsafe cache is enabled, includes `request.context` in the cache key | | ||
| conditionNames | ["node"] | A list of exports field condition names | | ||
| conditionNames | [] | A list of exports field condition names | | ||
| descriptionFiles | ["package.json"] | A list of description files to read from | | ||
@@ -90,0 +90,0 @@ | enforceExtension | false | Enforce that a extension from extensions must be used | |
@@ -48,13 +48,27 @@ /* | ||
statSync: (arg0: string, arg1?: object) => FileSystemStats; | ||
readdir: { | ||
( | ||
arg0: string, | ||
arg1: FileSystemCallback<(string | Buffer)[] | FileSystemDirent[]> | ||
): void; | ||
( | ||
arg0: string, | ||
arg1: object, | ||
arg2: FileSystemCallback<(string | Buffer)[] | FileSystemDirent[]> | ||
): void; | ||
}; | ||
readdir: ( | ||
arg0: string, | ||
arg1?: | ||
| null | ||
| (( | ||
arg0?: null | NodeJS.ErrnoException, | ||
arg1?: (string | Buffer)[] | any[] | ||
) => void) | ||
| ReaddirOptions | ||
| "ascii" | ||
| "utf8" | ||
| "utf-8" | ||
| "utf16le" | ||
| "ucs2" | ||
| "ucs-2" | ||
| "base64" | ||
| "latin1" | ||
| "binary" | ||
| "hex" | ||
| "buffer", | ||
arg2?: ( | ||
arg0?: null | NodeJS.ErrnoException, | ||
arg1?: (string | Buffer)[] | any[] | ||
) => void | ||
) => void; | ||
readdirSync: ( | ||
@@ -111,13 +125,27 @@ arg0: string, | ||
}; | ||
readdir: { | ||
( | ||
arg0: string, | ||
arg1: FileSystemCallback<(string | Buffer)[] | FileSystemDirent[]> | ||
): void; | ||
( | ||
arg0: string, | ||
arg1: object, | ||
arg2: FileSystemCallback<(string | Buffer)[] | FileSystemDirent[]> | ||
): void; | ||
}; | ||
readdir: ( | ||
arg0: string, | ||
arg1?: | ||
| null | ||
| (( | ||
arg0?: null | NodeJS.ErrnoException, | ||
arg1?: (string | Buffer)[] | any[] | ||
) => void) | ||
| ReaddirOptions | ||
| "ascii" | ||
| "utf8" | ||
| "utf-8" | ||
| "utf16le" | ||
| "ucs2" | ||
| "ucs-2" | ||
| "base64" | ||
| "latin1" | ||
| "binary" | ||
| "hex" | ||
| "buffer", | ||
arg2?: ( | ||
arg0?: null | NodeJS.ErrnoException, | ||
arg1?: (string | Buffer)[] | any[] | ||
) => void | ||
) => void; | ||
readJson?: { | ||
@@ -190,2 +218,18 @@ (arg0: string, arg1: FileSystemCallback<object>): void; | ||
} | ||
declare interface ReaddirOptions { | ||
encoding?: | ||
| null | ||
| "ascii" | ||
| "utf8" | ||
| "utf-8" | ||
| "utf16le" | ||
| "ucs2" | ||
| "ucs-2" | ||
| "base64" | ||
| "latin1" | ||
| "binary" | ||
| "hex" | ||
| "buffer"; | ||
withFileTypes?: boolean; | ||
} | ||
@@ -192,0 +236,0 @@ /** |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
2
163886
5182
1