From 50d5cfeec10ee9e4b4812f710b5360cc5e19b1ef Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Tue, 18 Apr 2023 02:11:37 +0300 Subject: [PATCH] refactor: rewrite implementation --- lib/util/entrypoints.js | 173 +++++----------------------------------- 1 file changed, 22 insertions(+), 151 deletions(-) diff --git a/lib/util/entrypoints.js b/lib/util/entrypoints.js index 5172411b..85f1f7c9 100644 --- a/lib/util/entrypoints.js +++ b/lib/util/entrypoints.js @@ -11,14 +11,6 @@ /** @typedef {Record|ConditionalMapping|DirectMapping} ExportsField */ /** @typedef {Record} ImportsField */ -/** - * @typedef {Object} PathTreeNode - * @property {Map|null} children - * @property {MappingValue} folder - * @property {Map|null} wildcards - * @property {Map} files - */ - /** * Processing exports/imports field * @callback FieldProcessor @@ -80,7 +72,6 @@ Conditional mapping nested in another conditional mapping is called nested mappi */ -const { fileURLToPath } = require("url"); const slashCode = "/".charCodeAt(0); const dotCode = ".".charCodeAt(0); const hashCode = "#".charCodeAt(0); @@ -94,7 +85,6 @@ module.exports.processExportsField = function processExportsField( ) { return createFieldProcessor( buildExportsFieldPathTree(exportsField), - exportsField, assertExportsFieldRequest, assertExportTarget ); @@ -109,20 +99,18 @@ module.exports.processImportsField = function processImportsField( ) { return createFieldProcessor( buildImportsFieldPathTree(importsField), - importsField, assertImportsFieldRequest, assertImportTarget ); }; /** - * @param {PathTreeNode} treeRoot root - * @param {ExportsField | ImportsField} field exports or import field + * @param {ExportsField | ImportsField} field root * @param {(s: string) => string} assertRequest assertRequest * @param {(s: string, f: boolean) => void} assertTarget assertTarget * @returns {FieldProcessor} field processor */ -function createFieldProcessor(treeRoot, field, assertRequest, assertTarget) { +function createFieldProcessor(field, assertRequest, assertTarget) { return function fieldProcessor(request, conditionNames) { request = assertRequest(request); @@ -264,25 +252,6 @@ function patternKeyCompare(a, b) { return 0; } -function isConditionalExportsMainSugar(exports) { - if (typeof exports === "string" || Array.isArray(exports)) return true; - if (typeof exports !== "object" || exports === null) return false; - - const keys = Object.getOwnPropertyNames(exports); - let isConditionalSugar = false; - let i = 0; - for (let j = 0; j < keys.length; j++) { - const key = keys[j]; - const curIsConditionalSugar = key === "" || key[0] !== "."; - if (i++ === 0) { - isConditionalSugar = curIsConditionalSugar; - } else if (isConditionalSugar !== curIsConditionalSugar) { - // TODO - } - } - return isConditionalSugar; -} - /** * Trying to match request to field * @param {string} request request @@ -290,20 +259,12 @@ function isConditionalExportsMainSugar(exports) { * @returns {[MappingValue, string, 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, field) { - if (isConditionalExportsMainSugar(field)) { - field = { ".": field }; - } - if ( - (Object.prototype.hasOwnProperty.call(field, "./" + request) || - Object.prototype.hasOwnProperty.call(field, "." + request)) && + Object.prototype.hasOwnProperty.call(field, request) && !request.includes("*") && !request.endsWith("/") ) { - const target = field["./" + request] || field["." + request]; - - if (target === "./") return null; - + const target = field[request]; const isDirectory = typeof target === "string" ? target.endsWith("/") : false; @@ -316,8 +277,7 @@ function findMatch(request, field) { const keys = Object.getOwnPropertyNames(field); for (let i = 0; i < keys.length; i++) { - const originalKey = keys[i]; - const key = keys[i].slice(2); + const key = keys[i]; const patternIndex = key.indexOf("*"); if (patternIndex !== -1 && request.startsWith(key.slice(0, patternIndex))) { @@ -336,19 +296,19 @@ function findMatch(request, field) { ); } } else if ( - originalKey[originalKey.length - 1] === "/" && + // For `./foo/` or `./` + key[key.length - 1] === "/" && request.startsWith(key) && patternKeyCompare(bestMatch, key) === 1 ) { - bestMatch = originalKey; + bestMatch = key; bestMatchSubpath = request.slice(key.length); } } if (bestMatch === "") return null; - const target = - field[bestMatch.startsWith("./") ? bestMatch : "./" + bestMatch]; + const target = field[bestMatch]; const pattern = bestMatch.endsWith("/"); return [target, /** @type {string} */ (bestMatchSubpath), pattern]; @@ -494,105 +454,17 @@ function conditionalMapping(conditionalMapping_, conditionNames) { return null; } -/** - * 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 look 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 the folder is a wildcard, create a new wildcard node or get an existing one. - if (folder === "*") { - if (node.wildcards === null) { - newNode = createNode(); - node.wildcards = new Map(); - node.wildcards.set("", newNode); - } else { - newNode = node.wildcards.get(folder) || createNode(); - node.wildcards.set("", newNode); - } - } else { - // If the folder is not a wildcard, create a new child node or get an existing one. - if (node.children === null) { - newNode = createNode(); - node.children = new Map(); - node.children.set(folder, newNode); - } else { - newNode = node.children.get(folder) || createNode(); - node.children.set(folder, newNode); - } - } - - node = /** @type {PathTreeNode} */ (newNode); - lastNonSlashIndex = slashIndex + 1; - slashIndex = path.indexOf("/", lastNonSlashIndex); - } - - if (lastNonSlashIndex >= path.length) { - node.folder = target; - } else { - const file = lastNonSlashIndex > 0 ? path.slice(lastNonSlashIndex) : path; - const wildcardsIndex = file.indexOf("*"); - if (wildcardsIndex !== -1) { - if (node.wildcards === null) node.wildcards = new Map(); - node.wildcards.set(file.slice(0, wildcardsIndex), 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(); - // 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 }; } + const newField = /** @type {ExportsField} */ ({}); const keys = Object.keys(field); for (let i = 0; i < keys.length; i++) { @@ -613,8 +485,7 @@ function buildExportsFieldPathTree(field) { i++; } - root.files.set("", field); - return root; + return { "": field }; } throw new Error( @@ -625,7 +496,8 @@ function buildExportsFieldPathTree(field) { } if (key.length === 1) { - root.files.set("", field[key]); + newField[""] = field[key]; + continue; } @@ -637,20 +509,19 @@ function buildExportsFieldPathTree(field) { ); } - walkPath(root, key.slice(2), field[key]); + newField[key.slice(2)] = field[key]; } - return root; + return newField; } /** * @param {ImportsField} field imports field - * @returns {PathTreeNode} root + * @returns {ImportsField} normalized imports field */ function buildImportsFieldPathTree(field) { - const root = createNode(); - const keys = Object.keys(field); + const newField = /** @type {ImportsField} */ ({}); for (let i = 0; i < keys.length; i++) { const key = keys[i]; @@ -677,8 +548,8 @@ function buildImportsFieldPathTree(field) { ); } - walkPath(root, key.slice(1), field[key]); + newField[key.slice(1)] = field[key]; } - return root; + return newField; }