@jsenv/node-module-import-map
Version:
Generate importmap for node_modules.
43 lines • 147 kB
Source Map (JSON)
{
"version": 3,
"file": "jsenv_node_module_importmap.cjs",
"sources": [
"../../src/internal/memoizeAsyncFunction.js",
"../../node_modules/@jsenv/core/helpers/import-meta/import-meta-url-commonjs.js",
"../../src/internal/from-js/parseSpecifiersFromFile.js",
"../../src/internal/from-js/showSource.js",
"../../src/internal/resolveFile.js",
"../../src/internal/warnings.js",
"../../src/internal/from-js/getImportMapFromJsFiles.js",
"../../src/internal/optimizeImportMap.js",
"../../src/internal/from-package/resolvePackageMain.js",
"../../src/internal/from-package/visitPackageImportMap.js",
"../../src/internal/from-package/specifierIsRelative.js",
"../../src/internal/from-package/visitPackageImports.js",
"../../src/internal/from-package/visitPackageExports.js",
"../../src/internal/from-package/applyPackageManualOverride.js",
"../../src/internal/from-package/readPackageFile.js",
"../../src/internal/from-package/node-module-resolution.js",
"../../src/internal/from-package/getImportMapFromPackageFiles.js",
"../../src/getImportMapFromProjectFiles.js",
"../../src/getImportMapFromFile.js",
"../../src/internal/importMapToVsCodeConfigPaths.js",
"../../src/writeImportMapFile.js"
],
"sourcesContent": [
"export const memoizeAsyncFunctionByUrl = (fn) => {\n const cache = {}\n return memoizeAsyncFunction(fn, {\n getMemoryEntryFromArguments: ([url]) => {\n return {\n get: () => {\n return cache[url]\n },\n set: (promise) => {\n cache[url] = promise\n },\n delete: () => {\n delete cache[url]\n },\n }\n },\n })\n}\n\nexport const memoizeAsyncFunctionBySpecifierAndImporter = (fn) => {\n const importerCache = {}\n return memoizeAsyncFunction(fn, {\n getMemoryEntryFromArguments: ([specifier, importer]) => {\n return {\n get: () => {\n const specifierCacheForImporter = importerCache[importer]\n return specifierCacheForImporter ? specifierCacheForImporter[specifier] : null\n },\n set: (promise) => {\n const specifierCacheForImporter = importerCache[importer]\n if (specifierCacheForImporter) {\n specifierCacheForImporter[specifier] = promise\n } else {\n importerCache[importer] = {\n [specifier]: promise,\n }\n }\n },\n delete: () => {\n const specifierCacheForImporter = importerCache[importer]\n if (specifierCacheForImporter) {\n delete specifierCacheForImporter[specifier]\n }\n },\n }\n },\n })\n}\n\nconst memoizeAsyncFunction = (fn, { getMemoryEntryFromArguments }) => {\n const memoized = async (...args) => {\n const memoryEntry = getMemoryEntryFromArguments(args)\n const promiseFromMemory = memoryEntry.get()\n if (promiseFromMemory) {\n return promiseFromMemory\n }\n const { promise, resolve, reject } = createControllablePromise()\n memoryEntry.set(promise)\n let value\n let error\n try {\n value = fn(...args)\n error = false\n } catch (e) {\n value = e\n error = true\n memoryEntry.delete()\n }\n if (error) {\n reject(error)\n } else {\n resolve(value)\n }\n return promise\n }\n memoized.isInMemory = (...args) => {\n return Boolean(getMemoryEntryFromArguments(args).get())\n }\n return memoized\n}\n\nconst createControllablePromise = () => {\n let resolve\n let reject\n const promise = new Promise((res, rej) => {\n resolve = res\n reject = rej\n })\n return { promise, resolve, reject }\n}\n",
"/* global __filename */\n\nconst filenameContainsBackSlashes = __filename.indexOf(\"\\\\\") > -1\n\nconst url = filenameContainsBackSlashes\n ? `file:///${__filename.replace(/\\\\/g, \"/\")}`\n : `file://${__filename}`\n\nexport default url\n",
"import { createRequire } from \"module\"\nimport { readFile, urlToFileSystemPath, urlToExtension } from \"@jsenv/util\"\n\nconst require = createRequire(import.meta.url)\n\nconst parser = require(\"@babel/parser\")\nconst traverse = require(\"@babel/traverse\")\n\nexport const parseSpecifiersFromFile = async (\n fileUrl,\n { fileContent, jsFilesParsingOptions } = {},\n) => {\n fileContent = fileContent === undefined ? await readFile(fileUrl, { as: \"string\" }) : fileContent\n\n const fileExtension = urlToExtension(fileUrl)\n\n const {\n jsx = [\".jsx\", \".tsx\"].includes(fileExtension),\n typescript = [\".ts\", \".tsx\"].includes(urlToExtension(fileUrl)),\n flow = false,\n } = jsFilesParsingOptions\n\n const ast = parser.parse(fileContent, {\n sourceType: \"module\",\n sourceFilename: urlToFileSystemPath(fileUrl),\n plugins: [\n \"topLevelAwait\",\n \"exportDefaultFrom\",\n ...(jsx ? [\"jsx\"] : []),\n ...(typescript ? [\"typescript\"] : []),\n ...(flow ? [\"flow\"] : []),\n ],\n ...jsFilesParsingOptions,\n ranges: true,\n })\n\n const specifiers = {}\n\n const addSpecifier = ({ path, type }) => {\n const specifier = path.node.value\n specifiers[specifier] = {\n line: path.node.loc.start.line,\n column: path.node.loc.start.column,\n type,\n }\n }\n\n traverse.default(ast, {\n // \"ImportExpression is replaced with a CallExpression whose callee is an Import node.\"\n // https://babeljs.io/docs/en/babel-parser#output\n // ImportExpression: (path) => {\n // if (path.node.arguments[0].type !== \"StringLiteral\") {\n // // Non-string argument, probably a variable or expression, e.g.\n // // import(moduleId)\n // // import('./' + moduleName)\n // return\n // }\n // addSpecifier(path.get(\"arguments\")[0])\n // },\n CallExpression: (path) => {\n if (path.node.callee.type !== \"Import\") {\n // Some other function call, not import();\n return\n }\n if (path.node.arguments[0].type !== \"StringLiteral\") {\n // Non-string argument, probably a variable or expression, e.g.\n // import(moduleId)\n // import('./' + moduleName)\n return\n }\n addSpecifier({\n path: path.get(\"arguments\")[0],\n type: \"import-dynamic\",\n })\n },\n ExportAllDeclaration: (path) => {\n addSpecifier({\n path: path.get(\"source\"),\n type: \"export-all\",\n })\n },\n ExportNamedDeclaration: (path) => {\n if (!path.node.source) {\n // This export has no \"source\", so it's probably\n // a local variable or function, e.g.\n // export { varName }\n // export const constName = ...\n // export function funcName() {}\n return\n }\n addSpecifier({\n path: path.get(\"source\"),\n type: \"export-named\",\n })\n },\n ImportDeclaration: (path) => {\n addSpecifier({\n path: path.get(\"source\"),\n type: \"import-static\",\n })\n },\n })\n\n return specifiers\n}\n",
"// https://github.com/postcss/postcss/blob/fd30d3df5abc0954a0ec642a3cdc644ab2aacf9c/lib/css-syntax-error.js#L43\n// https://github.com/postcss/postcss/blob/fd30d3df5abc0954a0ec642a3cdc644ab2aacf9c/lib/terminal-highlight.js#L50\n// https://github.com/babel/babel/blob/eea156b2cb8deecfcf82d52aa1b71ba4995c7d68/packages/babel-code-frame/src/index.js#L1\n\nexport const showSource = ({ url, line, column, source }) => {\n let message = \"\"\n\n message += typeof url === \"undefined\" ? \"Anonymous\" : url\n if (typeof line !== \"number\") {\n return message\n }\n\n message += `:${line}`\n if (typeof column === \"number\") {\n message += `:${column}`\n }\n\n if (!source) {\n return message\n }\n\n return `${message}\n${showSourceLocation(source, {\n line,\n column,\n})}`\n}\n\nconst red = \"\\x1b[31m\"\nconst grey = \"\\x1b[39m\"\nconst ansiResetSequence = \"\\x1b[0m\"\n\nconst showSourceLocation = (\n source,\n {\n line,\n column,\n numberOfSurroundingLinesToShow = 1,\n lineMaxLength = 120,\n color = false,\n markColor = red,\n asideColor = grey,\n colorMark = (string) => `${markColor}${string}${ansiResetSequence}`,\n colorAside = (string) => `${asideColor}${string}${ansiResetSequence}`,\n },\n) => {\n const mark = color ? colorMark : (string) => string\n const aside = color ? colorAside : (string) => string\n\n const lines = source.split(/\\r?\\n/)\n let lineRange = {\n start: line - 1,\n end: line,\n }\n lineRange = moveLineRangeUp(lineRange, numberOfSurroundingLinesToShow)\n lineRange = moveLineRangeDown(lineRange, numberOfSurroundingLinesToShow)\n lineRange = lineRangeWithinLines(lineRange, lines)\n const linesToShow = lines.slice(lineRange.start, lineRange.end)\n const endLineNumber = lineRange.end\n const lineNumberMaxWidth = String(endLineNumber).length\n\n const columnRange = {}\n if (column === undefined) {\n columnRange.start = 0\n columnRange.end = lineMaxLength\n } else if (column > lineMaxLength) {\n columnRange.start = column - Math.floor(lineMaxLength / 2)\n columnRange.end = column + Math.ceil(lineMaxLength / 2)\n } else {\n columnRange.start = 0\n columnRange.end = lineMaxLength\n }\n\n return linesToShow.map((lineSource, index) => {\n const lineNumber = lineRange.start + index + 1\n const isMainLine = lineNumber === line\n const lineSourceTruncated = applyColumnRange(columnRange, lineSource)\n const lineNumberWidth = String(lineNumber).length\n // ensure if line moves from 7,8,9 to 10 the display is still great\n const lineNumberRightSpacing = \" \".repeat(lineNumberMaxWidth - lineNumberWidth)\n const asideSource = `${lineNumber}${lineNumberRightSpacing} |`\n const lineFormatted = `${aside(asideSource)} ${lineSourceTruncated}`\n if (isMainLine) {\n if (column === undefined) {\n return `${mark(\">\")} ${lineFormatted}`\n }\n const lineSourceUntilColumn = lineSourceTruncated.slice(0, column - columnRange.start)\n const spacing = stringToSpaces(lineSourceUntilColumn)\n const mainLineFormatted = `${mark(\">\")} ${lineFormatted}\n ${\" \".repeat(lineNumberWidth)} ${aside(\"|\")}${spacing}${mark(\"^\")}`\n return mainLineFormatted\n }\n return ` ${lineFormatted}`\n }).join(`\n`)\n}\n\nconst applyColumnRange = ({ start, end }, line) => {\n if (typeof start !== \"number\") {\n throw new TypeError(`start must be a number, received ${start}`)\n }\n if (typeof end !== \"number\") {\n throw new TypeError(`end must be a number, received ${end}`)\n }\n if (end < start) {\n throw new Error(`end must be greater than start, but ${end} is smaller than ${start}`)\n }\n\n const prefix = \"…\"\n const suffix = \"…\"\n const lastIndex = line.length\n\n if (line.length === 0) {\n // don't show any ellipsis if the line is empty\n // because it's not truncated in that case\n return \"\"\n }\n\n const startTruncated = start > 0\n const endTruncated = lastIndex > end\n\n let from = startTruncated ? start + prefix.length : start\n let to = endTruncated ? end - suffix.length : end\n if (to > lastIndex) to = lastIndex\n\n if (start >= lastIndex || from === to) {\n return \"\"\n }\n\n let result = \"\"\n while (from < to) {\n result += line[from]\n from++\n }\n\n if (result.length === 0) {\n return \"\"\n }\n if (startTruncated && endTruncated) {\n return `${prefix}${result}${suffix}`\n }\n if (startTruncated) {\n return `${prefix}${result}`\n }\n if (endTruncated) {\n return `${result}${suffix}`\n }\n return result\n}\n\nconst stringToSpaces = (string) => string.replace(/[^\\t]/g, \" \")\n\n// const getLineRangeLength = ({ start, end }) => end - start\n\nconst moveLineRangeUp = ({ start, end }, number) => {\n return {\n start: start - number,\n end,\n }\n}\n\nconst moveLineRangeDown = ({ start, end }, number) => {\n return {\n start,\n end: end + number,\n }\n}\n\nconst lineRangeWithinLines = ({ start, end }, lines) => {\n return {\n start: start < 0 ? 0 : start,\n end: end > lines.length ? lines.length : end,\n }\n}\n",
"import { resolveUrl, readFileSystemNodeStat, urlToFilename, urlToExtension } from \"@jsenv/util\"\nimport { firstOperationMatching } from \"@jsenv/cancellation\"\n\nexport const resolveFile = async (fileUrl, { magicExtensions }) => {\n const fileStat = await readFileSystemNodeStat(fileUrl, {\n nullIfNotFound: true,\n })\n\n // file found\n if (fileStat && fileStat.isFile()) {\n return fileUrl\n }\n\n // directory found\n if (fileStat && fileStat.isDirectory()) {\n const indexFileSuffix = fileUrl.endsWith(\"/\") ? \"index\" : \"/index\"\n const indexFileUrl = `${fileUrl}${indexFileSuffix}`\n const extensionLeadingToAFile = await findExtensionLeadingToFile(indexFileUrl, magicExtensions)\n if (extensionLeadingToAFile === null) {\n return null\n }\n return `${indexFileUrl}${extensionLeadingToAFile}`\n }\n\n // file not found and it has an extension\n const extension = urlToExtension(fileUrl)\n if (extension !== \"\") {\n return null\n }\n\n const extensionLeadingToAFile = await findExtensionLeadingToFile(fileUrl, magicExtensions)\n // magic extension not found\n if (extensionLeadingToAFile === null) {\n return null\n }\n\n // magic extension worked\n return `${fileUrl}${extensionLeadingToAFile}`\n}\n\nconst findExtensionLeadingToFile = async (fileUrl, magicExtensions) => {\n const urlDirectoryUrl = resolveUrl(\"./\", fileUrl)\n const urlFilename = urlToFilename(fileUrl)\n const extensionLeadingToFile = await firstOperationMatching({\n array: magicExtensions,\n start: async (extensionCandidate) => {\n const urlCandidate = `${urlDirectoryUrl}${urlFilename}${extensionCandidate}`\n const stats = await readFileSystemNodeStat(urlCandidate, { nullIfNotFound: true })\n return stats && stats.isFile() ? extensionCandidate : null\n },\n predicate: (extension) => Boolean(extension),\n })\n return extensionLeadingToFile || null\n}\n",
"export const createPackageNameMustBeAStringWarning = ({ packageName, packageFileUrl }) => {\n return {\n code: \"PACKAGE_NAME_MUST_BE_A_STRING\",\n message: `package name field must be a string\n--- package name field ---\n${packageName}\n--- package.json file path ---\n${packageFileUrl}`,\n }\n}\n",
"import { createDetailedMessage } from \"@jsenv/logger\"\nimport { resolveUrl, readFile, urlToExtension, urlToRelativeUrl } from \"@jsenv/util\"\nimport {\n normalizeImportMap,\n resolveImport,\n sortImportMap,\n composeTwoImportMaps,\n} from \"@jsenv/import-map\"\nimport { isSpecifierForNodeCoreModule } from \"@jsenv/import-map/src/isSpecifierForNodeCoreModule.js\"\n\nimport {\n memoizeAsyncFunctionByUrl,\n memoizeAsyncFunctionBySpecifierAndImporter,\n} from \"../memoizeAsyncFunction.js\"\n\nimport { parseSpecifiersFromFile } from \"./parseSpecifiersFromFile.js\"\nimport { showSource } from \"./showSource.js\"\nimport { resolveFile } from \"../resolveFile.js\"\nimport { createPackageNameMustBeAStringWarning } from \"../warnings.js\"\n\nconst BARE_SPECIFIER_ERROR = {}\n\nexport const getImportMapFromJsFiles = async ({\n logger,\n warn,\n projectDirectoryUrl,\n importMap,\n magicExtensions,\n runtime,\n treeshakeMappings,\n jsFilesParsingOptions,\n}) => {\n const projectPackageFileUrl = resolveUrl(\"./package.json\", projectDirectoryUrl)\n\n const imports = {}\n const scopes = {}\n const addMapping = ({ scope, from, to }) => {\n if (scope) {\n scopes[scope] = {\n ...(scopes[scope] || {}),\n [from]: to,\n }\n } else {\n imports[from] = to\n }\n }\n\n const topLevelMappingsUsed = []\n const scopedMappingsUsed = {}\n const markMappingAsUsed = ({ scope, from, to }) => {\n if (scope) {\n if (scope in scopedMappingsUsed) {\n scopedMappingsUsed[scope].push({ from, to })\n } else {\n scopedMappingsUsed[scope] = [{ from, to }]\n }\n } else {\n topLevelMappingsUsed.push({ from, to })\n }\n }\n const importMapNormalized = normalizeImportMap(importMap, projectDirectoryUrl)\n const trackAndResolveImport = (specifier, importer) => {\n return resolveImport({\n specifier,\n importer,\n importMap: importMapNormalized,\n defaultExtension: false,\n onImportMapping: ({ scope, from }) => {\n if (scope) {\n // make scope relative again\n scope = `./${urlToRelativeUrl(scope, projectDirectoryUrl)}`\n // make from relative again\n if (from.startsWith(projectDirectoryUrl)) {\n from = `./${urlToRelativeUrl(from, projectDirectoryUrl)}`\n }\n }\n\n markMappingAsUsed({\n scope,\n from,\n to: scope ? importMap.scopes[scope][from] : importMap.imports[from],\n })\n },\n createBareSpecifierError: () => BARE_SPECIFIER_ERROR,\n })\n }\n\n const resolveFileSystemUrl = memoizeAsyncFunctionBySpecifierAndImporter(\n async (specifier, importer, { importedBy }) => {\n if (runtime === \"node\" && isSpecifierForNodeCoreModule(specifier)) {\n return null\n }\n\n let fileUrl\n let gotBareSpecifierError = false\n\n try {\n fileUrl = trackAndResolveImport(specifier, importer)\n } catch (e) {\n if (e !== BARE_SPECIFIER_ERROR) {\n throw e\n }\n if (importer === projectPackageFileUrl) {\n // cannot find package main file (package.main is \"\" for instance)\n // we can't discover main file and parse dependencies\n return null\n }\n gotBareSpecifierError = true\n fileUrl = resolveUrl(specifier, importer)\n }\n\n const fileUrlOnFileSystem = await resolveFile(fileUrl, {\n magicExtensions: magicExtensionWithImporterExtension(magicExtensions, importer),\n })\n\n if (!fileUrlOnFileSystem) {\n warn(\n createFileNotFoundWarning({\n specifier,\n importedBy,\n fileUrl,\n magicExtensions,\n }),\n )\n return null\n }\n\n const needsAutoMapping = fileUrlOnFileSystem !== fileUrl || gotBareSpecifierError\n if (needsAutoMapping) {\n const packageDirectoryUrl = packageDirectoryUrlFromUrl(fileUrl, projectDirectoryUrl)\n const packageFileUrl = resolveUrl(\"package.json\", packageDirectoryUrl)\n const autoMapping = {\n scope:\n packageFileUrl === projectPackageFileUrl\n ? undefined\n : `./${urlToRelativeUrl(packageDirectoryUrl, projectDirectoryUrl)}`,\n from: specifier,\n to: `./${urlToRelativeUrl(fileUrlOnFileSystem, projectDirectoryUrl)}`,\n }\n addMapping(autoMapping)\n markMappingAsUsed(autoMapping)\n\n const closestPackageObject = await readFile(packageFileUrl, { as: \"json\" })\n // it's imprecise because we are not ensuring the wildcard correspond to automapping\n // but good enough for now\n const containsWildcard = Object.keys(closestPackageObject.exports || {}).some((key) =>\n key.includes(\"*\"),\n )\n\n const autoMappingWarning = formatAutoMappingSpecifierWarning({\n specifier,\n importedBy,\n autoMapping,\n closestPackageDirectoryUrl: packageDirectoryUrl,\n closestPackageObject,\n })\n if (containsWildcard) {\n logger.debug(autoMappingWarning)\n } else {\n warn(autoMappingWarning)\n }\n }\n\n return fileUrlOnFileSystem\n },\n )\n\n const visitFile = memoizeAsyncFunctionByUrl(async (fileUrl) => {\n const fileContent = await readFile(fileUrl, { as: \"string\" })\n const specifiers = await parseSpecifiersFromFile(fileUrl, {\n fileContent,\n jsFilesParsingOptions,\n })\n\n const dependencies = await Promise.all(\n Object.keys(specifiers).map(async (specifier) => {\n const specifierInfo = specifiers[specifier]\n const dependencyUrlOnFileSystem = await resolveFileSystemUrl(specifier, fileUrl, {\n importedBy: showSource({\n url: fileUrl,\n line: specifierInfo.line,\n column: specifierInfo.column,\n source: fileContent,\n }),\n })\n return dependencyUrlOnFileSystem\n }),\n )\n const dependenciesToVisit = dependencies.filter((dependency) => {\n return dependency && !visitFile.isInMemory(dependency)\n })\n await Promise.all(\n dependenciesToVisit.map((dependency) => {\n return visitFile(dependency)\n }),\n )\n })\n\n const projectPackageObject = await readFile(projectPackageFileUrl, { as: \"json\" })\n const projectPackageName = projectPackageObject.name\n if (typeof projectPackageName !== \"string\") {\n warn(\n createPackageNameMustBeAStringWarning({\n packageName: projectPackageName,\n packageFileUrl: projectPackageFileUrl,\n }),\n )\n return importMap\n }\n\n const projectMainFileUrlOnFileSystem = await resolveFileSystemUrl(\n projectPackageName,\n projectPackageFileUrl,\n {\n importedBy: projectPackageObject.exports\n ? `${projectPackageFileUrl}#exports`\n : `${projectPackageFileUrl}`,\n },\n )\n if (projectMainFileUrlOnFileSystem) {\n await visitFile(projectMainFileUrlOnFileSystem)\n }\n\n if (treeshakeMappings) {\n const importsUsed = {}\n topLevelMappingsUsed.forEach(({ from, to }) => {\n importsUsed[from] = to\n })\n const scopesUsed = {}\n Object.keys(scopedMappingsUsed).forEach((scope) => {\n const mappingsUsed = scopedMappingsUsed[scope]\n const scopedMappings = {}\n mappingsUsed.forEach(({ from, to }) => {\n scopedMappings[from] = to\n })\n scopesUsed[scope] = scopedMappings\n })\n return sortImportMap({\n imports: importsUsed,\n scopes: scopesUsed,\n })\n }\n\n return sortImportMap(composeTwoImportMaps(importMap, { imports, scopes }))\n}\n\nconst packageDirectoryUrlFromUrl = (url, projectDirectoryUrl) => {\n const relativeUrl = urlToRelativeUrl(url, projectDirectoryUrl)\n\n const lastNodeModulesDirectoryStartIndex = relativeUrl.lastIndexOf(\"node_modules/\")\n if (lastNodeModulesDirectoryStartIndex === -1) {\n return projectDirectoryUrl\n }\n\n const lastNodeModulesDirectoryEndIndex =\n lastNodeModulesDirectoryStartIndex + `node_modules/`.length\n\n const beforeNodeModulesLastDirectory = relativeUrl.slice(0, lastNodeModulesDirectoryEndIndex)\n const afterLastNodeModulesDirectory = relativeUrl.slice(lastNodeModulesDirectoryEndIndex)\n const remainingDirectories = afterLastNodeModulesDirectory.split(\"/\")\n\n if (afterLastNodeModulesDirectory[0] === \"@\") {\n // scoped package\n return `${projectDirectoryUrl}${beforeNodeModulesLastDirectory}${remainingDirectories\n .slice(0, 2)\n .join(\"/\")}`\n }\n return `${projectDirectoryUrl}${beforeNodeModulesLastDirectory}${remainingDirectories[0]}/`\n}\n\nconst magicExtensionWithImporterExtension = (magicExtensions, importer) => {\n const importerExtension = urlToExtension(importer)\n const magicExtensionsWithoutImporterExtension = magicExtensions.filter(\n (ext) => ext !== importerExtension,\n )\n return [importerExtension, ...magicExtensionsWithoutImporterExtension]\n}\n\nconst createFileNotFoundWarning = ({ specifier, importedBy, fileUrl, magicExtensions }) => {\n return {\n code: \"FILE_NOT_FOUND\",\n message: createDetailedMessage(`Cannot find file for \"${specifier}\"`, {\n \"specifier origin\": importedBy,\n \"file url tried\": fileUrl,\n ...(urlToExtension(fileUrl) === \"\"\n ? { [\"extensions tried\"]: magicExtensions.join(`, `) }\n : {}),\n }),\n }\n}\n\nconst formatAutoMappingSpecifierWarning = ({\n importedBy,\n autoMapping,\n closestPackageDirectoryUrl,\n closestPackageObject,\n}) => {\n return {\n code: \"AUTO_MAPPING\",\n message: createDetailedMessage(`Auto mapping ${autoMapping.from} to ${autoMapping.to}.`, {\n \"specifier origin\": importedBy,\n \"suggestion\": decideAutoMappingSuggestion({\n autoMapping,\n closestPackageDirectoryUrl,\n closestPackageObject,\n }),\n }),\n }\n}\n\nconst decideAutoMappingSuggestion = ({\n autoMapping,\n closestPackageDirectoryUrl,\n closestPackageObject,\n}) => {\n if (typeof closestPackageObject.importmap === \"string\") {\n const packageImportmapFileUrl = resolveUrl(\n closestPackageObject.importmap,\n closestPackageDirectoryUrl,\n )\n\n return `To get rid of this warning, add an explicit mapping into importmap file.\n${mappingToImportmapString(autoMapping)}\ninto ${packageImportmapFileUrl}.`\n }\n\n return `To get rid of this warning, add an explicit mapping into package.json.\n${mappingToExportsFieldString(autoMapping)}\ninto ${closestPackageDirectoryUrl}package.json.`\n}\n\nconst mappingToImportmapString = ({ scope, from, to }) => {\n if (scope) {\n return JSON.stringify(\n {\n scopes: {\n [scope]: {\n [from]: to,\n },\n },\n },\n null,\n \" \",\n )\n }\n\n return JSON.stringify(\n {\n imports: {\n [from]: to,\n },\n },\n null,\n \" \",\n )\n}\n\nconst mappingToExportsFieldString = ({ scope, from, to }) => {\n if (scope) {\n const scopeUrl = resolveUrl(scope, \"file://\")\n const toUrl = resolveUrl(to, \"file://\")\n to = `./${urlToRelativeUrl(toUrl, scopeUrl)}`\n }\n\n return JSON.stringify(\n {\n exports: {\n [from]: to,\n },\n },\n null,\n \" \",\n )\n}\n",
"export const optimizeImportMap = ({ imports, scopes }) => {\n // remove useless duplicates (scoped key+value already defined on imports)\n const scopesOptimized = {}\n Object.keys(scopes).forEach((scope) => {\n const scopeMappings = scopes[scope]\n const scopeMappingsOptimized = {}\n Object.keys(scopeMappings).forEach((mappingKey) => {\n const topLevelMappingValue = imports[mappingKey]\n const mappingValue = scopeMappings[mappingKey]\n if (!topLevelMappingValue || topLevelMappingValue !== mappingValue) {\n scopeMappingsOptimized[mappingKey] = mappingValue\n }\n })\n if (Object.keys(scopeMappingsOptimized).length > 0) {\n scopesOptimized[scope] = scopeMappingsOptimized\n }\n })\n return { imports, scopes: scopesOptimized }\n}\n",
"import { createDetailedMessage } from \"@jsenv/logger\"\nimport { resolveUrl, urlToFileSystemPath, urlToExtension } from \"@jsenv/util\"\nimport { resolveFile } from \"../resolveFile.js\"\n\nconst magicExtensions = [\".js\", \".json\", \".node\"]\n\nexport const resolvePackageMain = ({\n warn,\n packageConditions,\n packageFileUrl,\n packageJsonObject,\n}) => {\n // we should remove \"module\", \"browser\", \"jsenext:main\" because Node.js native resolution\n // ignores them\n if (packageConditions.includes(\"import\") && \"module\" in packageJsonObject) {\n return resolveMainFile({\n warn,\n packageFileUrl,\n packageMainFieldName: \"module\",\n packageMainFieldValue: packageJsonObject.module,\n })\n }\n\n if (packageConditions.includes(\"import\") && \"jsnext:main\" in packageJsonObject) {\n return resolveMainFile({\n warn,\n packageFileUrl,\n packageMainFieldName: \"jsnext:main\",\n packageMainFieldValue: packageJsonObject[\"jsnext:main\"],\n })\n }\n\n if (\n packageConditions.includes(\"browser\") &&\n \"browser\" in packageJsonObject &&\n // when it's an object it means some files\n // should be replaced with an other, let's ignore this when we are searching\n // for the main file\n typeof packageJsonObject.browser === \"string\"\n ) {\n return resolveMainFile({\n warn,\n packageFileUrl,\n packageMainFieldName: \"browser\",\n packageMainFieldValue: packageJsonObject.browser,\n })\n }\n\n if (\"main\" in packageJsonObject) {\n return resolveMainFile({\n warn,\n packageFileUrl,\n packageMainFieldName: \"main\",\n packageMainFieldValue: packageJsonObject.main,\n })\n }\n\n return resolveMainFile({\n warn,\n packageFileUrl,\n packageMainFieldName: \"default\",\n packageMainFieldValue: \"index\",\n })\n}\n\nconst resolveMainFile = async ({\n warn,\n packageFileUrl,\n packageMainFieldName,\n packageMainFieldValue,\n}) => {\n // main is explicitely empty meaning\n // it is assumed that we should not find a file\n if (packageMainFieldValue === \"\") {\n return null\n }\n\n const packageDirectoryUrl = resolveUrl(\"./\", packageFileUrl)\n const mainFileRelativeUrl = packageMainFieldValue.endsWith(\"/\")\n ? `${packageMainFieldValue}index`\n : packageMainFieldValue\n\n const mainFileUrlFirstCandidate = resolveUrl(mainFileRelativeUrl, packageFileUrl)\n\n if (!mainFileUrlFirstCandidate.startsWith(packageDirectoryUrl)) {\n warn(\n createPackageMainFileMustBeRelativeWarning({\n packageMainFieldName,\n packageMainFieldValue,\n packageFileUrl,\n }),\n )\n return null\n }\n\n const mainFileUrl = await resolveFile(mainFileUrlFirstCandidate, {\n magicExtensions,\n })\n\n if (!mainFileUrl) {\n // we know in advance this remapping does not lead to an actual file.\n // we only warn because we have no guarantee this remapping will actually be used\n // in the codebase.\n // warn only if there is actually a main field\n // otherwise the package.json is missing the main field\n // it certainly means it's not important\n if (packageMainFieldName !== \"default\") {\n warn(\n createPackageMainFileNotFoundWarning({\n specifier: packageMainFieldValue,\n importedIn: `${packageFileUrl}#${packageMainFieldName}`,\n fileUrl: mainFileUrlFirstCandidate,\n magicExtensions,\n }),\n )\n }\n return mainFileUrlFirstCandidate\n }\n\n return mainFileUrl\n}\n\nconst createPackageMainFileMustBeRelativeWarning = ({\n packageMainFieldName,\n packageMainFieldValue,\n packageFileUrl,\n}) => {\n return {\n code: \"PACKAGE_MAIN_FILE_MUST_BE_RELATIVE\",\n message: `${packageMainFieldName} field in package.json must be inside package.json folder.\n--- ${packageMainFieldName} ---\n${packageMainFieldValue}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}`,\n }\n}\n\nconst createPackageMainFileNotFoundWarning = ({\n specifier,\n importedIn,\n fileUrl,\n magicExtensions,\n}) => {\n return {\n code: \"PACKAGE_MAIN_FILE_NOT_FOUND\",\n message: createDetailedMessage(`Cannot find package main file \"${specifier}\"`, {\n \"imported in\": importedIn,\n \"file url tried\": fileUrl,\n ...(urlToExtension(fileUrl) === \"\"\n ? { [\"extensions tried\"]: magicExtensions.join(`, `) }\n : {}),\n }),\n }\n}\n",
"import { resolveUrl, moveImportMap } from \"@jsenv/import-map\"\nimport { readFile, urlToFileSystemPath } from \"@jsenv/util\"\n\nexport const visitPackageImportMap = async ({\n warn,\n packageFileUrl,\n packageJsonObject,\n packageImportmap = packageJsonObject.importmap,\n projectDirectoryUrl,\n}) => {\n if (typeof packageImportmap === \"undefined\") {\n return {}\n }\n\n if (typeof packageImportmap === \"string\") {\n const importmapFileUrl = resolveUrl(packageImportmap, packageFileUrl)\n try {\n const importmap = await readFile(importmapFileUrl, { as: \"json\" })\n return moveImportMap(importmap, importmapFileUrl, projectDirectoryUrl)\n } catch (e) {\n if (e.code === \"ENOENT\") {\n warn(\n createPackageImportMapNotFoundWarning({\n importmapFileUrl,\n packageFileUrl,\n }),\n )\n return {}\n }\n throw e\n }\n }\n\n if (typeof packageImportmap === \"object\" && packageImportmap !== null) {\n return packageImportmap\n }\n\n warn(\n createPackageImportMapUnexpectedWarning({\n packageImportmap,\n packageFileUrl,\n }),\n )\n return {}\n}\n\nconst createPackageImportMapNotFoundWarning = ({ importmapFileUrl, packageFileUrl }) => {\n return {\n code: \"PACKAGE_IMPORTMAP_NOT_FOUND\",\n message: `importmap file specified in a package.json cannot be found,\n--- importmap file path ---\n${importmapFileUrl}\n--- package.json path ---\n${packageFileUrl}`,\n }\n}\n\nconst createPackageImportMapUnexpectedWarning = ({ packageImportmap, packageFileUrl }) => {\n return {\n code: \"PACKAGE_IMPORTMAP_UNEXPECTED\",\n message: `unexpected value in package.json importmap field: value must be a string or an object.\n--- value ---\n${packageImportmap}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}`,\n }\n}\n",
"export const specifierIsRelative = (specifier) => {\n if (specifier.startsWith(\"//\")) {\n return false\n }\n if (specifier.startsWith(\"../\")) {\n return false\n }\n // starts with http:// or file:// or ftp: for instance\n if (/^[a-zA-Z]+\\:/.test(specifier)) {\n return false\n }\n return true\n}\n",
"/*\n\nhttps://nodejs.org/docs/latest-v15.x/api/packages.html#packages_node_js_package_json_field_definitions\n\n*/\n\nimport { urlToFileSystemPath } from \"@jsenv/util\"\nimport { specifierIsRelative } from \"./specifierIsRelative.js\"\n\nexport const visitPackageImports = ({\n packageFileUrl,\n packageJsonObject,\n packageImports = packageJsonObject.imports,\n packageConditions,\n warn,\n}) => {\n const importsSubpaths = {}\n const onImportsSubpath = ({ key, value, trace }) => {\n if (!specifierIsRelative(value)) {\n warn(\n createSubpathValueMustBeRelativeWarning({\n value,\n valueTrace: trace,\n packageFileUrl,\n }),\n )\n return\n }\n\n const keyNormalized = key\n const valueNormalized = value\n importsSubpaths[keyNormalized] = valueNormalized\n }\n\n const conditions = [...packageConditions, \"default\"]\n\n const visitSubpathValue = (subpathValue, subpathValueTrace) => {\n if (typeof subpathValue === \"string\") {\n return handleString(subpathValue, subpathValueTrace)\n }\n\n if (typeof subpathValue === \"object\" && subpathValue !== null) {\n return handleObject(subpathValue, subpathValueTrace)\n }\n\n return handleRemaining(subpathValue, subpathValueTrace)\n }\n\n const handleString = (subpathValue, subpathValueTrace) => {\n const firstBareKey = subpathValueTrace\n .slice()\n .reverse()\n .find((key) => key.startsWith(\"#\"))\n onImportsSubpath({\n key: firstBareKey,\n value: subpathValue,\n trace: subpathValueTrace,\n })\n return true\n }\n\n const handleObject = (subpathValue, subpathValueTrace) => {\n // From Node.js documentation:\n // \"If a nested conditional does not have any mapping it will continue\n // checking the remaining conditions of the parent condition\"\n // https://nodejs.org/docs/latest-v14.x/api/packages.html#packages_nested_conditions\n //\n // So it seems what we do here is not sufficient\n // -> if the condition finally does not lead to something\n // it should be ignored and an other branch be taken until\n // something resolves\n const followConditionBranch = (subpathValue, conditionTrace) => {\n const bareKeys = []\n const conditionalKeys = []\n Object.keys(subpathValue).forEach((availableKey) => {\n if (availableKey.startsWith(\"#\")) {\n bareKeys.push(availableKey)\n } else {\n conditionalKeys.push(availableKey)\n }\n })\n\n if (bareKeys.length > 0 && conditionalKeys.length > 0) {\n warn(\n createSubpathKeysAreMixedWarning({\n subpathValue,\n subpathValueTrace: [...subpathValueTrace, ...conditionTrace],\n packageFileUrl,\n bareKeys,\n conditionalKeys,\n }),\n )\n return false\n }\n\n // there is no condition, visit all bare keys (starting with #)\n if (conditionalKeys.length === 0) {\n let leadsToSomething = false\n bareKeys.forEach((key) => {\n leadsToSomething = visitSubpathValue(subpathValue[key], [\n ...subpathValueTrace,\n ...conditionTrace,\n key,\n ])\n })\n return leadsToSomething\n }\n\n // there is a condition, keep the first one leading to something\n return conditionalKeys.some((keyCandidate) => {\n if (!conditions.includes(keyCandidate)) {\n return false\n }\n const valueCandidate = subpathValue[keyCandidate]\n return visitSubpathValue(valueCandidate, [\n ...subpathValueTrace,\n ...conditionTrace,\n keyCandidate,\n ])\n })\n }\n\n return followConditionBranch(subpathValue, [])\n }\n\n const handleRemaining = (subpathValue, subpathValueTrace) => {\n warn(\n createSubpathIsUnexpectedWarning({\n subpathValue,\n subpathValueTrace,\n packageFileUrl,\n }),\n )\n return false\n }\n\n visitSubpathValue(packageImports, [\"imports\"])\n\n return importsSubpaths\n}\n\nconst createSubpathIsUnexpectedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => {\n return {\n code: \"IMPORTS_SUBPATH_UNEXPECTED\",\n message: `unexpected subpath in package.json imports: value must be an object or a string.\n--- value ---\n${subpathValue}\n--- value at ---\n${subpathValueTrace.join(\".\")}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}`,\n }\n}\n\nconst createSubpathKeysAreMixedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => {\n return {\n code: \"IMPORTS_SUBPATH_MIXED_KEYS\",\n message: `unexpected subpath keys in package.json imports: cannot mix bare and conditional keys.\n--- value ---\n${JSON.stringify(subpathValue, null, \" \")}\n--- value at ---\n${subpathValueTrace.join(\".\")}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}`,\n }\n}\n\nconst createSubpathValueMustBeRelativeWarning = ({ value, valueTrace, packageFileUrl }) => {\n return {\n code: \"IMPORTS_SUBPATH_VALUE_UNEXPECTED\",\n message: `unexpected subpath value in package.json imports: value must be relative to package\n--- value ---\n${value}\n--- value at ---\n${valueTrace.join(\".\")}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}`,\n }\n}\n",
"/*\n\nhttps://nodejs.org/docs/latest-v15.x/api/packages.html#packages_node_js_package_json_field_definitions\n\n*/\n\nimport { urlToFileSystemPath, urlToRelativeUrl, resolveUrl } from \"@jsenv/util\"\nimport { specifierIsRelative } from \"./specifierIsRelative.js\"\n\nexport const visitPackageExports = ({\n packageFileUrl,\n packageJsonObject,\n packageExports = packageJsonObject.exports,\n packageName = packageJsonObject.name,\n projectDirectoryUrl,\n packageConditions,\n warn,\n}) => {\n const exportsSubpaths = {}\n const packageDirectoryUrl = resolveUrl(\"./\", packageFileUrl)\n const packageDirectoryRelativeUrl = urlToRelativeUrl(packageDirectoryUrl, projectDirectoryUrl)\n const onExportsSubpath = ({ key, value, trace }) => {\n if (!specifierIsRelative(value)) {\n warn(\n createSubpathValueMustBeRelativeWarning({\n value,\n valueTrace: trace,\n packageFileUrl,\n }),\n )\n return\n }\n\n const keyNormalized = specifierToSource(key, packageName)\n const valueNormalized = addressToDestination(value, packageDirectoryRelativeUrl)\n exportsSubpaths[keyNormalized] = valueNormalized\n }\n\n const conditions = [...packageConditions, \"default\"]\n\n const visitSubpathValue = (subpathValue, subpathValueTrace) => {\n // false is allowed as alternative to exports: {}\n if (subpathValue === false) {\n return handleFalse()\n }\n\n if (typeof subpathValue === \"string\") {\n return handleString(subpathValue, subpathValueTrace)\n }\n\n if (typeof subpathValue === \"object\" && subpathValue !== null) {\n return handleObject(subpathValue, subpathValueTrace)\n }\n\n return handleRemaining(subpathValue, subpathValueTrace)\n }\n\n const handleFalse = () => {\n // nothing to do\n return true\n }\n\n const handleString = (subpathValue, subpathValueTrace) => {\n const firstRelativeKey = subpathValueTrace\n .slice()\n .reverse()\n .find((key) => key.startsWith(\".\"))\n const key = firstRelativeKey || \".\"\n onExportsSubpath({\n key,\n value: subpathValue,\n trace: subpathValueTrace,\n })\n return true\n }\n\n const handleObject = (subpathValue, subpathValueTrace) => {\n // From Node.js documentation:\n // \"If a nested conditional does not have any mapping it will continue\n // checking the remaining conditions of the parent condition\"\n // https://nodejs.org/docs/latest-v14.x/api/packages.html#packages_nested_conditions\n //\n // So it seems what we do here is not sufficient\n // -> if the condition finally does not lead to something\n // it should be ignored and an other branch be taken until\n // something resolves\n const followConditionBranch = (subpathValue, conditionTrace) => {\n const relativeKeys = []\n const conditionalKeys = []\n Object.keys(subpathValue).forEach((availableKey) => {\n if (availableKey.startsWith(\".\")) {\n relativeKeys.push(availableKey)\n } else {\n conditionalKeys.push(availableKey)\n }\n })\n\n if (relativeKeys.length > 0 && conditionalKeys.length > 0) {\n warn(\n createSubpathKeysAreMixedWarning({\n subpathValue,\n subpathValueTrace: [...subpathValueTrace, ...conditionTrace],\n packageFileUrl,\n relativeKeys,\n conditionalKeys,\n }),\n )\n return false\n }\n\n // there is no condition, visit all relative keys\n if (conditionalKeys.length === 0) {\n let leadsToSomething = false\n relativeKeys.forEach((key) => {\n leadsToSomething = visitSubpathValue(subpathValue[key], [\n ...subpathValueTrace,\n ...conditionTrace,\n key,\n ])\n })\n return leadsToSomething\n }\n\n // there is a condition, keep the first one leading to something\n return conditionalKeys.some((keyCandidate) => {\n if (!conditions.includes(keyCandidate)) {\n return false\n }\n const valueCandidate = subpathValue[keyCandidate]\n return visitSubpathValue(valueCandidate, [\n ...subpathValueTrace,\n ...conditionTrace,\n keyCandidate,\n ])\n })\n }\n\n if (Array.isArray(subpathValue)) {\n subpathValue = exportsObjectFromExportsArray(subpathValue)\n }\n return followConditionBranch(subpathValue, [])\n }\n\n const handleRemaining = (subpathValue, subpathValueTrace) => {\n warn(\n createSubpathIsUnexpectedWarning({\n subpathValue,\n subpathValueTrace,\n packageFileUrl,\n }),\n )\n return false\n }\n\n visitSubpathValue(packageExports, [\"exports\"])\n\n return exportsSubpaths\n}\n\nconst exportsObjectFromExportsArray = (exportsArray) => {\n const exportsObject = {}\n\n exportsArray.forEach((exportValue) => {\n if (typeof exportValue === \"object\") {\n Object.assign(exportsObject, exportValue)\n return\n }\n if (typeof exportValue === \"string\") {\n exportsObject.default = exportValue\n }\n })\n\n return exportsObject\n}\n\nconst specifierToSource = (specifier, packageName) => {\n if (specifier === \".\") {\n return packageName\n }\n\n if (specifier[0] === \"/\") {\n return specifier\n }\n\n if (specifier.startsWith(\"./\")) {\n return `${packageName}${specifier.slice(1)}`\n }\n\n return `${packageName}/${specifier}`\n}\n\nconst addressToDestination = (address, packageDirectoryRelativeUrl) => {\n if (address[0] === \"/\") {\n return address\n }\n\n if (address.startsWith(\"./\")) {\n return `./${packageDirectoryRelativeUrl}${address.slice(2)}`\n }\n\n return `./${packageDirectoryRelativeUrl}${address}`\n}\n\nconst createSubpathIsUnexpectedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => {\n return {\n code: \"EXPORTS_SUBPATH_UNEXPECTED\",\n message: `unexpected subpath in package.json exports: value must be an object or a string.\n--- value ---\n${subpathValue}\n--- value at ---\n${subpathValueTrace.join(\".\")}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}`,\n }\n}\n\nconst createSubpathKeysAreMixedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => {\n return {\n code: \"EXPORTS_SUBPATH_MIXED_KEYS\",\n message: `unexpected subpath keys in package.json exports: cannot mix relative and conditional keys.\n--- value ---\n${JSON.stringify(subpathValue, null, \" \")}\n--- value at ---\n${subpathValueTrace.join(\".\")}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}`,\n }\n}\n\nconst createSubpathValueMustBeRelativeWarning = ({ value, valueTrace, packageFileUrl }) => {\n return {\n code: \"EXPORTS_SUBPATH_VALUE_MUST_BE_RELATIVE\",\n message: `unexpected subpath value in package.json exports: value must be a relative to the package.\n--- value ---\n${value}\n--- value at ---\n${valueTrace.join(\".\")}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}`,\n }\n}\n",
"export const applyPackageManualOverride = (packageObject, packagesManualOverrides) => {\n const { name, version } = packageObject\n const overrideKey = Object.keys(packagesManualOverrides).find((overrideKeyCandidate) => {\n if (name === overrideKeyCandidate) {\n return true\n }\n if (`${name}@${version}` === overrideKeyCandidate) {\n return true\n }\n return false\n })\n if (overrideKey) {\n return composeObject(packageObject, packagesManualOverrides[overrideKey])\n }\n return packageObject\n}\n\nconst composeObject = (leftObject, rightObject) => {\n const composedObject = {\n ...leftObject,\n }\n Object.keys(rightObject).forEach((key) => {\n const rightValue = rightObject[key]\n\n if (rightValue === null || typeof rightValue !== \"object\" || key in leftObject === false) {\n composedObject[key] = rightValue\n } else {\n const leftValue = leftObject[key]\n if (leftValue === null || typeof leftValue !== \"object\") {\n composedObject[key] = rightValue\n } else {\n composedObject[key] = composeObject(leftValue, rightValue)\n }\n }\n })\n return composedObject\n}\n",
"import { readFile, urlToFileSystemPath } from \"@jsenv/util\"\nimport { applyPackageManualOverride } from \"./applyPackageManualOverride.js\"\n\nexport const PACKAGE_NOT_FOUND = {}\nexport const PACKAGE_WITH_SYNTAX_ERROR = {}\n\nexport const readPackageFile = async (packageFileUrl, packagesManualOverrides) => {\n try {\n const packageObject = await readFile(packageFileUrl, { as: \"json\" })\n return applyPackageManualOverride(packageObject, packagesManualOverrides)\n } catch (e) {\n if (e.code === \"ENOENT\") {\n return PACKAGE_NOT_FOUND\n }\n\n if (e.name === \"SyntaxError\") {\n console.error(formatPackageSyntaxErrorLog({ syntaxError: e, packageFileUrl }))\n return PACKAGE_WITH_SYNTAX_ERROR\n }\n\n throw e\n }\n}\n\nconst formatPackageSyntaxErrorLog = ({ syntaxError, packageFileUrl }) => {\n return `\nerror while parsing package.json.\n--- syntax error message ---\n${syntaxError.message}\n--- package.json path ---\n${urlToFileSystemPath(packageFileUrl)}\n`\n}\n",
"import { firstOperationMatching } from \"@jsenv/cancellation\"\nimport { urlToRelativeUrl, resolveUrl } from \"@jsenv/util\"\nimport { memoizeAsyncFunctionByUrl } from \"../memoizeAsyncFunction.js\"\nimport { readPackageFile, PACKAGE_NOT_FOUND, PACKAGE_WITH_SYNTAX_ERROR } from \"./readPackageFile.js\"\n\nexport const createFindNodeModulePackage = (packagesManualOverrides) => {\n const readPackageFileMemoized = memoizeAsyncFunctionByUrl((packageFileUrl) => {\n return readPackageFile(packageFileUrl, packagesManualOverrides)\n })\n\n return ({ projectDirectoryUrl, packageFileUrl, dependencyName }) => {\n const nodeModuleCandidates = getNodeModuleCandidates(packageFileUrl, projectDirectoryUrl)\n\n return firstOperationMatching({\n array: nodeModuleCandidates,\n start: async (nodeModuleCandidate) => {\n const packageFileUrlCandidate = `${projectDirectoryUrl}${nodeModuleCandidate}${dependencyName}/package.json`\n const packageObjectCandidate = await readPackageFileMemoized(packageFileUrlCandidate)\n return {\n packageFileUrl: packageFileUrlCandidate,\n packageJsonObject: packageObjectCandidate,\n syntaxError: packageObjectCandidate === PACKAGE_WITH_SYNTAX_ERROR,\n }\n },\n predicate: ({ packageJsonObject }) => {\n return packageJsonObject !== PACKAGE_NOT_FOUND\n },\n })\n }\n}\n\nconst getNodeModuleCandidates = (fileUrl, projectDirectoryUrl) => {\n const fileDirectoryUrl = resolveUrl(\"./\", fileUrl)\n\n if (fileDirectoryUrl === projectDirectoryUrl) {\n return [`node_modules/`]\n }\n\n const fileDirectoryRelativeUrl = urlToRelativeUrl(fileDirectoryUrl, project