From 14e73c5426da9fbc8c09e5ae612b7e60797bae1c Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Mon, 19 Apr 2021 16:19:53 +0200 Subject: [PATCH 01/16] prepare visiting package imports --- readme.md | 6 ++--- .../imports-basic/imports-basic.test.js | 22 +++++++++++++++++++ .../root/node_modules/foo/package.json | 9 ++++++++ .../imports-basic/root/package.json | 6 +++++ 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js create mode 100644 test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/package.json create mode 100644 test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/package.json diff --git a/readme.md b/readme.md index b6daa347..15916a5c 100644 --- a/readme.md +++ b/readme.md @@ -195,7 +195,7 @@ The following source of information are used to create complete and coherent map - Your `package.json` - All `dependencies` declared in `package.json` are searched into `node_modules`, recursively. -- The main file declared in your `package.json` +- In every `package.json`, "main", "exports" and "imports" field. - All static and dynamic import found in files, recursively.
@@ -232,7 +232,7 @@ const importMap = await getImportMapFromProjectFiles({ When enabled the following happens: 1. `devDependencies` declared in your `package.json` are included in the generated importMap. -2. `"development"` is favored over `"production"` in [package.json exports conditions](https://nodejs.org/dist/latest-v15.x/docs/api/packages.html#packages_conditions_definitions). +2. `"development"` is favored over `"production"` in [package.json conditions](https://nodejs.org/dist/latest-v15.x/docs/api/packages.html#packages_conditions_definitions)
@@ -241,7 +241,7 @@ When enabled the following happens: `runtime` parameter is a string indicating where the importmap will be used. This parameter is optional with a default of `"browser"`. -When `runtime` is `"browser"`, `"browser"` is favored over `"node"` in [package.json exports conditions](https://nodejs.org/dist/latest-v15.x/docs/api/packages.html#packages_conditions_definitions). +When `runtime` is `"browser"`, `"browser"` is favored over `"node"` in [package.json conditions](https://nodejs.org/dist/latest-v15.x/docs/api/packages.html#packages_conditions_definitions). When it is `"node"`, `"node"` is favored. diff --git a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js new file mode 100644 index 00000000..40eabf72 --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js @@ -0,0 +1,22 @@ +import { assert } from "@jsenv/assert" +import { resolveUrl } from "@jsenv/util" +import { getImportMapFromProjectFiles } from "@jsenv/node-module-import-map" + +const testDirectoryUrl = resolveUrl("./root/", import.meta.url) + +const actual = await getImportMapFromProjectFiles({ + projectDirectoryUrl: testDirectoryUrl, + jsFiles: false, +}) +const expected = { + imports: { + root: "./index", + foo: "./node_modules/foo/index", + }, + scopes: { + "./node_modules/foo/src/": { + "./a.js": "./node_modules/foo/src/b.js", + }, + }, +} +assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/package.json b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/package.json new file mode 100644 index 00000000..34f0967b --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/package.json @@ -0,0 +1,9 @@ +{ + "name": "foo", + "imports": { + "#env": { + "development": "./env.dev.js", + "import": "./env.prod.js" + } + } +} diff --git a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/package.json b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/package.json new file mode 100644 index 00000000..399e6ef7 --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/package.json @@ -0,0 +1,6 @@ +{ + "name": "root", + "dependencies": { + "foo": "*" + } +} From 4c5a50d40e95d330ace246149b75cc9147285a92 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Mon, 19 Apr 2021 17:09:02 +0200 Subject: [PATCH 02/16] rewrite package exports to prepare package imports --- .../getImportMapFromPackageFiles.js | 58 ++--- .../from-package/visitPackageExports.js | 239 +++++++++--------- 2 files changed, 142 insertions(+), 155 deletions(-) diff --git a/src/internal/from-package/getImportMapFromPackageFiles.js b/src/internal/from-package/getImportMapFromPackageFiles.js index f9031958..67ba5a29 100644 --- a/src/internal/from-package/getImportMapFromPackageFiles.js +++ b/src/internal/from-package/getImportMapFromPackageFiles.js @@ -249,42 +249,40 @@ export const getImportMapFromPackageFiles = async ({ if (packagesExportsIncluded && "exports" in packageJsonObject) { const mappingsFromPackageExports = {} - visitPackageExports({ + const packageExports = visitPackageExports({ warn, packageFileUrl, packageJsonObject, packageName, projectDirectoryUrl, packagesExportsPreference, - onExport: ({ key, value }) => { - const from = key - const to = value - - if (from.indexOf("*") === -1) { - mappingsFromPackageExports[from] = to - return - } - - if ( - from.endsWith("/*") && - to.endsWith("/*") && - // ensure ends with '*' AND there is only one '*' occurence - to.indexOf("*") === to.length - 1 - ) { - const fromWithouTrailingStar = from.slice(0, -1) - const toWithoutTrailingStar = to.slice(0, -1) - mappingsFromPackageExports[fromWithouTrailingStar] = toWithoutTrailingStar - return - } - - warn( - createExportsWildcardIgnoredWarning({ - key, - value, - packageFileUrl, - }), - ) - }, + }) + Object.keys(packageExports).forEach((from) => { + const to = packageExports[from] + if (from.indexOf("*") === -1) { + mappingsFromPackageExports[from] = to + return + } + + if ( + from.endsWith("/*") && + to.endsWith("/*") && + // ensure ends with '*' AND there is only one '*' occurence + to.indexOf("*") === to.length - 1 + ) { + const fromWithouTrailingStar = from.slice(0, -1) + const toWithoutTrailingStar = to.slice(0, -1) + mappingsFromPackageExports[fromWithouTrailingStar] = toWithoutTrailingStar + return + } + + warn( + createExportsWildcardIgnoredWarning({ + key: from, + value: to, + packageFileUrl, + }), + ) }) addMappingsForPackageAndImporter(mappingsFromPackageExports) } diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index 7ef8bda1..cd0feb78 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -4,143 +4,136 @@ import { urlToFileSystemPath, urlToRelativeUrl, resolveUrl } from "@jsenv/util" import { specifierIsRelative } from "./specifierIsRelative.js" export const visitPackageExports = ({ - warn, packageFileUrl, packageJsonObject, packageExports = packageJsonObject.exports, packageName = packageJsonObject.name, projectDirectoryUrl, - packagesExportsPreference, - onExport, + userConditions, + warn, }) => { + const exportsSubpaths = {} const packageDirectoryUrl = resolveUrl("./", packageFileUrl) const packageDirectoryRelativeUrl = urlToRelativeUrl(packageDirectoryUrl, projectDirectoryUrl) - visitExportsSubpath(packageExports, packagesExportsPreference, { - onUnexpectedPackageExports: ({ packageExportsValue, packageExportsValuePath }) => { + const onExportsSubpath = ({ key, value, trace }) => { + if (!specifierIsRelative(key)) { warn( - createExportsValueWarning({ - packageExportsValue, - packageExportsValuePath, + createExportsSubpathKeyMustBeRelativeWarning({ + key, + keyTrace: trace.slice(0, -1), packageFileUrl, }), ) - }, - onMixedPackageExports: ({ packageExportsValue, packageExportsValuePath }) => { - // see https://nodejs.org/dist/latest-v13.x/docs/api/esm.html#esm_exports_sugar + return + } + if (typeof value !== "string") { warn( - createExportsMixedWarning({ - packageExportsValue, - packageExportsValuePath, + createExportsSubpathValueMustBeAStringWarning({ + value, + valueTrace: trace, packageFileUrl, }), ) - }, - onSubpathPackageExport: ({ key, value, valuePath }) => { - if (!specifierIsRelative(key)) { - warn( - createExportsMappingKeyMustBeRelativeWarning({ - key, - keyPath: valuePath.slice(0, -1), - packageFileUrl, - }), - ) - return - } - if (typeof value !== "string") { - warn( - createExportsMappingValueMustBeAStringWarning({ - value, - valuePath, - packageFileUrl, - }), - ) - return - } - if (!specifierIsRelative(value)) { - warn( - createExportsMappingValueMustBeRelativeWarning({ - value, - valuePath, - packageFileUrl, - }), - ) - return - } + return + } + if (!specifierIsRelative(value)) { + warn( + createExportsSubpathValueMustBeRelativeWarning({ + value, + valueTrace: trace, + packageFileUrl, + }), + ) + return + } - onExport({ - key: specifierToSource(key, packageName), - value: addressToDestination(value, packageDirectoryRelativeUrl), - }) - }, - }) -} + const keyNormalized = specifierToSource(key, packageName) + const valueNormalized = addressToDestination(value, packageDirectoryRelativeUrl) -const visitExportsSubpath = ( - packageExports, - packageExportsConditions, - { onUnexpectedPackageExports, onMixedPackageExports, onSubpathPackageExport }, -) => { - const visitValue = (packageExportsValue, { valuePath }) => { + exportsSubpaths[keyNormalized] = valueNormalized + } + + const visitSubpathValue = (subpathValue, subpathValueTrace) => { // false is allowed as alternative to exports: {} - if (packageExportsValue === false) { + if (subpathValue === false) { + handleFalse() return } - if (typeof packageExportsValue === "string") { - const firstNonConditionKey = valuePath - .slice() - .reverse() - .find((key) => key.startsWith(".")) - const key = firstNonConditionKey || "." - onSubpathPackageExport({ - value: packageExportsValue, - valuePath, - key, - }) + if (typeof subpathValue === "string") { + handleString(subpathValue, subpathValueTrace) return } - if (typeof packageExportsValue !== "object" && packageExportsValue !== null) { - onUnexpectedPackageExports({ - packageExportsValue, - packageExportsValuePath: valuePath, - }) + if (typeof subpathValue === "object" && subpathValue !== null) { + handleObject(subpathValue, subpathValueTrace) return } - const keys = Object.keys(packageExportsValue) + handleRemaining(subpathValue, subpathValueTrace) + } + + const handleFalse = () => { + // nothing to do + } + + const handleString = (subpathValue, subpathValueTrace) => { + const firstNonConditionKey = subpathValueTrace + .slice() + .reverse() + .find((key) => key.startsWith(".")) + const key = firstNonConditionKey || "." + onExportsSubpath({ + key, + value: subpathValue, + trace: subpathValueTrace, + }) + } + + const handleObject = (subpathValue, subpathValueTrace) => { + const keys = Object.keys(subpathValue) const everyKeyDoesNotStartsWithDot = keys.every((key) => !key.startsWith(".")) if (everyKeyDoesNotStartsWithDot) { - const bestConditionKey = findBestConditionKey(keys, packageExportsConditions) + const bestConditionKey = findBestConditionKey(keys, userConditions) if (!bestConditionKey) { return } - const bestExports = packageExportsValue[bestConditionKey] - visitValue(bestExports, { - valuePath: [...valuePath, bestConditionKey], - }) + visitSubpathValue(subpathValue[bestConditionKey], [...subpathValueTrace, bestConditionKey]) return } const everyKeyStartsWithDot = keys.every((key) => key.startsWith(".")) if (everyKeyStartsWithDot) { keys.forEach((key) => { - visitValue(packageExportsValue[key], { - valuePath: [...valuePath, key], - }) + visitSubpathValue(subpathValue[key], [...subpathValueTrace, key]) }) return } - onMixedPackageExports({ - packageExportsValue, - packageExportsValuePath: valuePath, - }) + // see https://nodejs.org/dist/latest-v13.x/docs/api/esm.html#esm_exports_sugar + warn( + createUnexpectedExportsSubpathWarning({ + subpathValue, + subpathValueTrace, + packageFileUrl, + }), + ) + } + + const handleRemaining = (subpathValue, subpathValueTrace) => { + warn( + createMixedExportsSubpathWarning({ + subpathValue, + subpathValueTrace, + packageFileUrl, + }), + ) } - visitValue(packageExports, { - valuePath: ["exports"], - }) + + visitSubpathValue(packageExports, ["exports"]) + + return exportsSubpaths } const findBestConditionKey = (availableKeys, exportsConditions) => { @@ -184,74 +177,70 @@ const addressToDestination = (address, packageDirectoryRelativeUrl) => { return `./${packageDirectoryRelativeUrl}${address}` } -const createExportsValueWarning = ({ - packageExportsValue, - packageExportsValuePath, +const createUnexpectedExportsSubpathWarning = ({ + subpathValue, + subpathValueTrace, packageFileUrl, }) => { return { - code: "EXPORTS_VALUE", - message: `unexpected value in package.json exports field: value must be an object or a string. + code: "EXPORTS_SUBPATH_UNEXPECTED", + message: `unexpected value in package.json exports: value must be an object or a string. --- value --- -${packageExportsValue} ---- value path --- -${packageExportsValuePath.join(".")} +${subpathValue} +--- value at --- +${subpathValueTrace.join(".")} --- package.json path --- ${urlToFileSystemPath(packageFileUrl)}`, } } -const createExportsMixedWarning = ({ - packageExportsValue, - packageExportsValuePath, - packageFileUrl, -}) => { +const createMixedExportsSubpathWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => { return { - code: "EXPORTS_MIXED", - message: `unexpected package.json exports field: cannot mix conditional and subpath exports. + code: "EXPORTS_SUBPATH_MIXED", + message: `unexpected value in package.json exports: cannot mix conditional and subpath. --- value --- -${JSON.stringify(packageExportsValue, null, " ")} ---- value path --- -${packageExportsValuePath.join(".")} +${JSON.stringify(subpathValue, null, " ")} +--- value at --- +${subpathValueTrace.join(".")} --- package.json path --- ${urlToFileSystemPath(packageFileUrl)}`, } } -const createExportsMappingKeyMustBeRelativeWarning = ({ key, keyPath, packageFileUrl }) => { +const createExportsSubpathKeyMustBeRelativeWarning = ({ key, keyTrace, packageFileUrl }) => { return { - code: "EXPORTS_MAPPING_KEY_MUST_BE_RELATIVE", - message: `unexpected key in package.json exports field: key must be relative. + code: "EXPORTS_SUBPATH_KEY_MUST_BE_RELATIVE", + message: `unexpected key in package.json exports: key must be relative. --- key --- ${key} ---- key path --- -${keyPath.join(".")} +--- key at --- +${keyTrace.join(".")} --- package.json path --- ${urlToFileSystemPath(packageFileUrl)}`, } } -const createExportsMappingValueMustBeAStringWarning = ({ value, valuePath, packageFileUrl }) => { +const createExportsSubpathValueMustBeAStringWarning = ({ value, valueTrace, packageFileUrl }) => { return { - code: "EXPORTS_MAPPING_VALUE_MUST_BE_A_STRING", - message: `unexpected value in package.json exports field: value must be a string. + code: "EXPORTS_SUBPATH_VALUE_MUST_BE_A_STRING", + message: `unexpected value in package.json exports: value must be a string. --- value --- ${value} ---- value path --- -${valuePath.join(".")} +--- value at --- +${valueTrace.join(".")} --- package.json path --- ${urlToFileSystemPath(packageFileUrl)}`, } } -const createExportsMappingValueMustBeRelativeWarning = ({ value, valuePath, packageFileUrl }) => { +const createExportsSubpathValueMustBeRelativeWarning = ({ value, valueTrace, packageFileUrl }) => { return { - code: "EXPORTS_MAPPING_VALUE_MUST_BE_RELATIVE", - message: `unexpected value in package.json exports field: value must be relative. + code: "EXPORTS_SUBPATH_VALUE_MUST_BE_RELATIVE", + message: `unexpected value in package.json exports: value must be relative. --- value --- ${value} ---- value path --- -${valuePath.join(".")} +--- value at --- +${valueTrace.join(".")} --- package.json path --- ${urlToFileSystemPath(packageFileUrl)}`, } From 378cf0da5082de66360085c81e2239832cea45e6 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Mon, 19 Apr 2021 17:12:41 +0200 Subject: [PATCH 03/16] Rename packagesExportsPreference into packageConditions --- src/getImportMapFromProjectFiles.js | 12 ++++++------ .../from-package/getImportMapFromPackageFiles.js | 6 +++--- src/internal/from-package/resolvePackageMain.js | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/getImportMapFromProjectFiles.js b/src/getImportMapFromProjectFiles.js index a5989120..839a3920 100644 --- a/src/getImportMapFromProjectFiles.js +++ b/src/getImportMapFromProjectFiles.js @@ -17,9 +17,9 @@ export const getImportMapFromProjectFiles = async ({ }, ...rest }) => { - const packagesExportsPreference = [ - ...(moduleFormatPreferences[moduleFormat] || [moduleFormat]), - ...(runtimeExportsPreferences[runtime] || [runtime]), + const packageConditions = [ + ...(packageConditionsFromModuleFormat[moduleFormat] || [moduleFormat]), + ...(packageConditionsFromRuntime[runtime] || [runtime]), ...(dev ? "development" : "production"), ] @@ -35,7 +35,7 @@ export const getImportMapFromProjectFiles = async ({ logger, warn, projectDirectoryUrl, - packagesExportsPreference, + packageConditions, projectPackageDevDependenciesIncluded: dev, ...rest, }) @@ -56,12 +56,12 @@ export const getImportMapFromProjectFiles = async ({ return importMapFromJsFiles } -const runtimeExportsPreferences = { +const packageConditionsFromRuntime = { browser: ["browser"], node: ["node"], } -const moduleFormatPreferences = { +const packageConditionsFromModuleFormat = { esm: ["import"], cjs: ["require"], } diff --git a/src/internal/from-package/getImportMapFromPackageFiles.js b/src/internal/from-package/getImportMapFromPackageFiles.js index 67ba5a29..70a9c9b5 100644 --- a/src/internal/from-package/getImportMapFromPackageFiles.js +++ b/src/internal/from-package/getImportMapFromPackageFiles.js @@ -21,7 +21,7 @@ export const getImportMapFromPackageFiles = async ({ warn, projectDirectoryUrl, projectPackageDevDependenciesIncluded = process.env.NODE_ENV !== "production", - packagesExportsPreference = ["import", "browser"], + packageConditions = ["import", "browser"], packagesExportsIncluded = true, packagesManualOverrides = {}, packageIncludedPredicate = () => true, @@ -255,7 +255,7 @@ export const getImportMapFromPackageFiles = async ({ packageJsonObject, packageName, projectDirectoryUrl, - packagesExportsPreference, + packageConditions, }) Object.keys(packageExports).forEach((from) => { const to = packageExports[from] @@ -301,7 +301,7 @@ export const getImportMapFromPackageFiles = async ({ }) => { const mainFileUrl = await resolvePackageMain({ warn, - packagesExportsPreference, + packageConditions, packageFileUrl, packageJsonObject, }) diff --git a/src/internal/from-package/resolvePackageMain.js b/src/internal/from-package/resolvePackageMain.js index 23f9c539..873a77fb 100644 --- a/src/internal/from-package/resolvePackageMain.js +++ b/src/internal/from-package/resolvePackageMain.js @@ -6,11 +6,11 @@ const magicExtensions = [".js", ".json", ".node"] export const resolvePackageMain = ({ warn, - packagesExportsPreference, + packageConditions, packageFileUrl, packageJsonObject, }) => { - if (packagesExportsPreference.includes("import") && "module" in packageJsonObject) { + if (packageConditions.includes("import") && "module" in packageJsonObject) { return resolveMainFile({ warn, packageFileUrl, @@ -19,7 +19,7 @@ export const resolvePackageMain = ({ }) } - if (packagesExportsPreference.includes("import") && "jsnext:main" in packageJsonObject) { + if (packageConditions.includes("import") && "jsnext:main" in packageJsonObject) { return resolveMainFile({ warn, packageFileUrl, @@ -29,7 +29,7 @@ export const resolvePackageMain = ({ } if ( - packagesExportsPreference.includes("browser") && + packageConditions.includes("browser") && "browser" in packageJsonObject && // when it's an object it means some files // should be replaced with an other, let's ignore this when we are searching From c26a0e5b4aad539d50ce65ff2e4679a39514c66f Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Mon, 19 Apr 2021 17:50:24 +0200 Subject: [PATCH 04/16] match nodejs spec with condition subpaths --- .../from-package/visitPackageExports.js | 105 ++++++++++++------ 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index cd0feb78..3c19c34d 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -9,13 +9,12 @@ export const visitPackageExports = ({ packageExports = packageJsonObject.exports, packageName = packageJsonObject.name, projectDirectoryUrl, - userConditions, + packageConditions, warn, }) => { const exportsSubpaths = {} const packageDirectoryUrl = resolveUrl("./", packageFileUrl) const packageDirectoryRelativeUrl = urlToRelativeUrl(packageDirectoryUrl, projectDirectoryUrl) - const onExportsSubpath = ({ key, value, trace }) => { if (!specifierIsRelative(key)) { warn( @@ -54,6 +53,8 @@ export const visitPackageExports = ({ exportsSubpaths[keyNormalized] = valueNormalized } + const conditions = [...packageConditions, "default"] + const visitSubpathValue = (subpathValue, subpathValueTrace) => { // false is allowed as alternative to exports: {} if (subpathValue === false) { @@ -92,33 +93,76 @@ export const visitPackageExports = ({ } const handleObject = (subpathValue, subpathValueTrace) => { - const keys = Object.keys(subpathValue) - const everyKeyDoesNotStartsWithDot = keys.every((key) => !key.startsWith(".")) - if (everyKeyDoesNotStartsWithDot) { - const bestConditionKey = findBestConditionKey(keys, userConditions) - if (!bestConditionKey) { - return + // From Node.js documentation: + // "If a nested conditional does not have any mapping it will continue + // checking the remaining conditions of the parent condition" + // https://nodejs.org/docs/latest-v14.x/api/packages.html#packages_nested_conditions + // + // So it seems what we do here is not sufficient + // -> if the condition finally does not lead to something + // it should be ignored and an other branch be taken until + // something resolves + const followConditionBranch = (subpathValue, conditionTrace) => { + const relativeKeys = [] + const conditionalKeys = [] + Object.keys(subpathValue).forEach((availableKey) => { + if (availableKey.startsWith(".")) { + relativeKeys.push(availableKey) + } else { + conditionalKeys.push(availableKey) + } + }) + + if (relativeKeys.length > 0 && conditionalKeys.length > 0) { + // see https://nodejs.org/dist/latest-v13.x/docs/api/esm.html#esm_exports_sugar + warn( + createUnexpectedExportsSubpathWarning({ + subpathValue, + subpathValueTrace: [...subpathValueTrace, ...conditionTrace], + packageFileUrl, + relativeKeys, + conditionalKeys, + }), + ) + return null } - visitSubpathValue(subpathValue[bestConditionKey], [...subpathValueTrace, bestConditionKey]) - return - } - const everyKeyStartsWithDot = keys.every((key) => key.startsWith(".")) - if (everyKeyStartsWithDot) { - keys.forEach((key) => { - visitSubpathValue(subpathValue[key], [...subpathValueTrace, key]) + // there is no condition + if (conditionalKeys.length === 0) { + return { + value: subpathValue, + trace: subpathValueTrace, + } + } + + let condition = null + conditions.find((keyCandidate) => { + if (!conditionalKeys.includes(keyCandidate)) { + return false + } + + const valueCandidate = subpathValue[keyCandidate] + if (typeof valueCandidate === "string") { + condition = { + value: valueCandidate, + trace: conditionTrace, + } + return true + } + if (typeof valueCandidate === "object" && valueCandidate !== null) { + condition = followConditionBranch(valueCandidate, [...conditionTrace, keyCandidate]) + return Boolean(condition) + } + return false }) - return + return condition } - // see https://nodejs.org/dist/latest-v13.x/docs/api/esm.html#esm_exports_sugar - warn( - createUnexpectedExportsSubpathWarning({ - subpathValue, - subpathValueTrace, - packageFileUrl, - }), - ) + const condition = followConditionBranch(subpathValue, []) + if (condition) { + visitSubpathValue(condition.value, [...subpathValueTrace, condition.trace]) + return + } } const handleRemaining = (subpathValue, subpathValueTrace) => { @@ -136,19 +180,6 @@ export const visitPackageExports = ({ return exportsSubpaths } -const findBestConditionKey = (availableKeys, exportsConditions) => { - const conditionKey = exportsConditions.find((key) => availableKeys.includes(key)) - if (conditionKey) { - return conditionKey - } - - if (availableKeys.includes("default")) { - return "default" - } - - return undefined -} - const specifierToSource = (specifier, packageName) => { if (specifier === ".") { return packageName From dcbac458a65c16238f9964ccdfb2352dd13cd54d Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Mon, 19 Apr 2021 18:15:26 +0200 Subject: [PATCH 05/16] test and fix subpath condition resolution --- .vscode/launch.json | 1 + package.json | 2 +- src/getImportMapFromProjectFiles.js | 2 +- .../from-package/visitPackageExports.js | 55 ++++++++----------- .../exports-branch/exports-branch.test.js | 44 +++++++++++++++ .../exports-branch/root/main.js | 2 + .../root/node_modules/foo/main.cjs | 0 .../root/node_modules/foo/main.js | 0 .../root/node_modules/foo/package.json | 14 +++++ .../exports-branch/root/package.json | 7 +++ 10 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 test/getImportMapFromProjectFiles/package-exports-field/exports-branch/exports-branch.test.js create mode 100644 test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/main.js create mode 100644 test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/main.cjs create mode 100644 test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/main.js create mode 100644 test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/package.json create mode 100644 test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/package.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 1e677261..3de984ea 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,6 +9,7 @@ "program": "${file}", // "runtimeVersion": "14.5.0", "runtimeArgs": [ + "--conditions=development", "--unhandled-rejections=strict", "--experimental-json-modules", "--experimental-top-level-await" diff --git a/package.json b/package.json index 583351ff..27f70c7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/node-module-import-map", - "version": "13.2.1", + "version": "13.3.0", "description": "Generate importmap for node_modules.", "license": "MIT", "repository": { diff --git a/src/getImportMapFromProjectFiles.js b/src/getImportMapFromProjectFiles.js index 839a3920..4d03b30e 100644 --- a/src/getImportMapFromProjectFiles.js +++ b/src/getImportMapFromProjectFiles.js @@ -20,7 +20,7 @@ export const getImportMapFromProjectFiles = async ({ const packageConditions = [ ...(packageConditionsFromModuleFormat[moduleFormat] || [moduleFormat]), ...(packageConditionsFromRuntime[runtime] || [runtime]), - ...(dev ? "development" : "production"), + ...(dev ? ["development"] : ["production"]), ] const logger = createLogger({ logLevel }) diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index 3c19c34d..f10d7c7e 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -58,25 +58,23 @@ export const visitPackageExports = ({ const visitSubpathValue = (subpathValue, subpathValueTrace) => { // false is allowed as alternative to exports: {} if (subpathValue === false) { - handleFalse() - return + return handleFalse() } if (typeof subpathValue === "string") { - handleString(subpathValue, subpathValueTrace) - return + return handleString(subpathValue, subpathValueTrace) } if (typeof subpathValue === "object" && subpathValue !== null) { - handleObject(subpathValue, subpathValueTrace) - return + return handleObject(subpathValue, subpathValueTrace) } - handleRemaining(subpathValue, subpathValueTrace) + return handleRemaining(subpathValue, subpathValueTrace) } const handleFalse = () => { // nothing to do + return true } const handleString = (subpathValue, subpathValueTrace) => { @@ -90,6 +88,7 @@ export const visitPackageExports = ({ value: subpathValue, trace: subpathValueTrace, }) + return true } const handleObject = (subpathValue, subpathValueTrace) => { @@ -124,45 +123,34 @@ export const visitPackageExports = ({ conditionalKeys, }), ) - return null + return false } // there is no condition if (conditionalKeys.length === 0) { - return { - value: subpathValue, - trace: subpathValueTrace, - } + return relativeKeys.some((key) => { + return visitSubpathValue(subpathValue[key], [ + ...subpathValueTrace, + ...conditionTrace, + key, + ]) + }) } - let condition = null - conditions.find((keyCandidate) => { + return conditions.some((keyCandidate) => { if (!conditionalKeys.includes(keyCandidate)) { return false } - const valueCandidate = subpathValue[keyCandidate] - if (typeof valueCandidate === "string") { - condition = { - value: valueCandidate, - trace: conditionTrace, - } - return true - } - if (typeof valueCandidate === "object" && valueCandidate !== null) { - condition = followConditionBranch(valueCandidate, [...conditionTrace, keyCandidate]) - return Boolean(condition) - } - return false + return visitSubpathValue(valueCandidate, [ + ...subpathValueTrace, + ...conditionTrace, + keyCandidate, + ]) }) - return condition } - const condition = followConditionBranch(subpathValue, []) - if (condition) { - visitSubpathValue(condition.value, [...subpathValueTrace, condition.trace]) - return - } + return followConditionBranch(subpathValue, []) } const handleRemaining = (subpathValue, subpathValueTrace) => { @@ -173,6 +161,7 @@ export const visitPackageExports = ({ packageFileUrl, }), ) + return false } visitSubpathValue(packageExports, ["exports"]) diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/exports-branch.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/exports-branch.test.js new file mode 100644 index 00000000..e5448139 --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/exports-branch.test.js @@ -0,0 +1,44 @@ +import { assert } from "@jsenv/assert" +import { resolveUrl } from "@jsenv/util" +import { getImportMapFromProjectFiles } from "@jsenv/node-module-import-map" + +const testDirectoryUrl = resolveUrl("./root/", import.meta.url) + +const getImportMap = async ({ runtime, moduleFormat } = {}) => { + return getImportMapFromProjectFiles({ + projectDirectoryUrl: testDirectoryUrl, + jsFiles: false, + moduleFormat, + runtime, + dev: true, + }) +} + +{ + const actual = await getImportMap({ + runtime: "node", + }) + const expected = { + imports: { + whatever: "./index", + foo: "./node_modules/foo/main.js", + }, + scopes: {}, + } + assert({ actual, expected }) +} + +{ + const actual = await getImportMap({ + runtime: "node", + moduleFormat: "cjs", + }) + const expected = { + imports: { + whatever: "./index", + foo: "./node_modules/foo/main.cjs", + }, + scopes: {}, + } + assert({ actual, expected }) +} diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/main.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/main.js new file mode 100644 index 00000000..05eed085 --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/main.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/no-unresolved +import "foo" diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/main.cjs b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/main.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/main.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/main.js new file mode 100644 index 00000000..e69de29b diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/package.json b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/package.json new file mode 100644 index 00000000..87c7e890 --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/node_modules/foo/package.json @@ -0,0 +1,14 @@ +{ + "name": "foo", + "type": "module", + "exports": { + ".": { + "development": { + "require": "./main.cjs" + }, + "import": { + "development": "./main.js" + } + } + } +} diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/package.json b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/package.json new file mode 100644 index 00000000..62dd616a --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/root/package.json @@ -0,0 +1,7 @@ +{ + "name": "whatever", + "type": "module", + "dependencies": { + "foo": "*" + } +} From 461177b401fd60f4ba80396b9179a229eada4a1e Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Mon, 19 Apr 2021 18:20:48 +0200 Subject: [PATCH 06/16] Fix some exports ignored --- src/internal/from-package/visitPackageExports.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index f10d7c7e..674b5a04 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -126,17 +126,20 @@ export const visitPackageExports = ({ return false } - // there is no condition + // there is no condition, visit all relative keys if (conditionalKeys.length === 0) { - return relativeKeys.some((key) => { - return visitSubpathValue(subpathValue[key], [ + let someExportAdded = false + relativeKeys.forEach((key) => { + someExportAdded = visitSubpathValue(subpathValue[key], [ ...subpathValueTrace, ...conditionTrace, key, ]) }) + return someExportAdded } + // there is a condition, keep the first one leading to something return conditions.some((keyCandidate) => { if (!conditionalKeys.includes(keyCandidate)) { return false From 60a6814d1bfb41adb7035de56b85e88ff7cec1b9 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 09:08:22 +0200 Subject: [PATCH 07/16] Update complex export test --- src/internal/from-package/visitPackageExports.js | 1 - .../exports-complex/exports-complex.test.js | 6 +++--- .../exports-complex/root/node_modules/foo/package.json | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index 674b5a04..0948c4f2 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -49,7 +49,6 @@ export const visitPackageExports = ({ const keyNormalized = specifierToSource(key, packageName) const valueNormalized = addressToDestination(value, packageDirectoryRelativeUrl) - exportsSubpaths[keyNormalized] = valueNormalized } diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/exports-complex.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/exports-complex.test.js index 9659769e..7381a1de 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/exports-complex.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/exports-complex.test.js @@ -21,7 +21,7 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { imports: { "foo/dist/": "./node_modules/foo/dist/", "whatever": "./index", - "foo": "./node_modules/foo/dist/es/rollup.js", + "foo": "./node_modules/foo/dist/rollup.mjs", }, scopes: {}, } @@ -53,7 +53,7 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { imports: { "foo/dist/": "./node_modules/foo/dist/", "whatever": "./index", - "foo": "./node_modules/foo/file.cjs", + "foo": "./node_modules/foo/dist/rollup.browser.mjs", }, scopes: {}, } @@ -66,7 +66,7 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { imports: { "foo/dist/": "./node_modules/foo/dist/", "whatever": "./index", - "foo": "./node_modules/foo/dist/es/rollup.browser.js", + "foo": "./node_modules/foo/dist/rollup.browser.mjs", }, scopes: {}, } diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/root/node_modules/foo/package.json b/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/root/node_modules/foo/package.json index 9c3a8c64..39feb289 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/root/node_modules/foo/package.json +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/root/node_modules/foo/package.json @@ -4,9 +4,9 @@ ".": { "node": { "require": "./dist/rollup.js", - "import": "./dist/es/rollup.js" + "import": "./dist/rollup.mjs" }, - "default": "./dist/es/rollup.browser.js" + "default": "./dist/rollup.browser.mjs" }, "./dist/": "./dist/" } From 34e0a134cc922921f5bb069bc19c2b81acaec487 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 09:25:37 +0200 Subject: [PATCH 08/16] ensure package self reference mapping is generated --- .../from-package/getImportMapFromPackageFiles.js | 10 ++++++++-- .../self-import-1/{index.js => root/file.js} | 0 .../self-import/self-import-1/root/index.js | 2 ++ .../self-import/self-import-1/{ => root}/package.json | 0 .../self-import/self-import-1/self-import.test.js | 5 +++-- 5 files changed, 13 insertions(+), 4 deletions(-) rename test/getImportMapFromProjectFiles/self-import/self-import-1/{index.js => root/file.js} (100%) create mode 100644 test/getImportMapFromProjectFiles/self-import/self-import-1/root/index.js rename test/getImportMapFromProjectFiles/self-import/self-import-1/{ => root}/package.json (100%) diff --git a/src/internal/from-package/getImportMapFromPackageFiles.js b/src/internal/from-package/getImportMapFromPackageFiles.js index 70a9c9b5..e28c54a2 100644 --- a/src/internal/from-package/getImportMapFromPackageFiles.js +++ b/src/internal/from-package/getImportMapFromPackageFiles.js @@ -22,7 +22,6 @@ export const getImportMapFromPackageFiles = async ({ projectDirectoryUrl, projectPackageDevDependenciesIncluded = process.env.NODE_ENV !== "production", packageConditions = ["import", "browser"], - packagesExportsIncluded = true, packagesManualOverrides = {}, packageIncludedPredicate = () => true, }) => { @@ -239,6 +238,13 @@ export const getImportMapFromPackageFiles = async ({ }) } + // https://nodejs.org/docs/latest-v15.x/api/packages.html#packages_name + addImportMapForPackage({ + imports: { + [`${packageName}/`]: `./${packageDirectoryRelativeUrl}`, + }, + }) + const importsFromPackageField = await visitPackageImportMap({ warn, packageFileUrl, @@ -247,7 +253,7 @@ export const getImportMapFromPackageFiles = async ({ }) addImportMapForPackage(importsFromPackageField) - if (packagesExportsIncluded && "exports" in packageJsonObject) { + if ("exports" in packageJsonObject) { const mappingsFromPackageExports = {} const packageExports = visitPackageExports({ warn, diff --git a/test/getImportMapFromProjectFiles/self-import/self-import-1/index.js b/test/getImportMapFromProjectFiles/self-import/self-import-1/root/file.js similarity index 100% rename from test/getImportMapFromProjectFiles/self-import/self-import-1/index.js rename to test/getImportMapFromProjectFiles/self-import/self-import-1/root/file.js diff --git a/test/getImportMapFromProjectFiles/self-import/self-import-1/root/index.js b/test/getImportMapFromProjectFiles/self-import/self-import-1/root/index.js new file mode 100644 index 00000000..bdecdff1 --- /dev/null +++ b/test/getImportMapFromProjectFiles/self-import/self-import-1/root/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/no-unresolved +import "root/file.js" diff --git a/test/getImportMapFromProjectFiles/self-import/self-import-1/package.json b/test/getImportMapFromProjectFiles/self-import/self-import-1/root/package.json similarity index 100% rename from test/getImportMapFromProjectFiles/self-import/self-import-1/package.json rename to test/getImportMapFromProjectFiles/self-import/self-import-1/root/package.json diff --git a/test/getImportMapFromProjectFiles/self-import/self-import-1/self-import.test.js b/test/getImportMapFromProjectFiles/self-import/self-import-1/self-import.test.js index dbe0aa61..588a3f78 100644 --- a/test/getImportMapFromProjectFiles/self-import/self-import-1/self-import.test.js +++ b/test/getImportMapFromProjectFiles/self-import/self-import-1/self-import.test.js @@ -2,14 +2,15 @@ import { assert } from "@jsenv/assert" import { resolveUrl } from "@jsenv/util" import { getImportMapFromProjectFiles } from "@jsenv/node-module-import-map" -const testDirectoryUrl = resolveUrl("./", import.meta.url) +const testDirectoryUrl = resolveUrl("./root/", import.meta.url) const actual = await getImportMapFromProjectFiles({ projectDirectoryUrl: testDirectoryUrl, }) const expected = { imports: { - root: "./index.js", + "root/": "./", + "root": "./index.js", }, scopes: {}, } From 37ad3ac9a4192973cde5c827726a6bbfeb417f7b Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 09:26:05 +0200 Subject: [PATCH 09/16] mpre accurate internal variable name --- src/internal/from-package/visitPackageExports.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index 0948c4f2..876bf1bd 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -127,15 +127,15 @@ export const visitPackageExports = ({ // there is no condition, visit all relative keys if (conditionalKeys.length === 0) { - let someExportAdded = false + let leadsToSomething = false relativeKeys.forEach((key) => { - someExportAdded = visitSubpathValue(subpathValue[key], [ + leadsToSomething = visitSubpathValue(subpathValue[key], [ ...subpathValueTrace, ...conditionTrace, key, ]) }) - return someExportAdded + return leadsToSomething } // there is a condition, keep the first one leading to something From a7efcfb09ce131b4d3ef1f1d3b0895ae0a4f6caa Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 09:41:22 +0200 Subject: [PATCH 10/16] update test after allowing self import --- .../getImportMapFromPackageFiles.js | 2 +- .../core/inside/inside.test.js | 28 ++++++++++--------- .../importmap-scope/importmap-scope.test.js | 8 ++++-- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/internal/from-package/getImportMapFromPackageFiles.js b/src/internal/from-package/getImportMapFromPackageFiles.js index e28c54a2..c108ca07 100644 --- a/src/internal/from-package/getImportMapFromPackageFiles.js +++ b/src/internal/from-package/getImportMapFromPackageFiles.js @@ -241,7 +241,7 @@ export const getImportMapFromPackageFiles = async ({ // https://nodejs.org/docs/latest-v15.x/api/packages.html#packages_name addImportMapForPackage({ imports: { - [`${packageName}/`]: `./${packageDirectoryRelativeUrl}`, + [`${packageName}/`]: `./`, }, }) diff --git a/test/getImportMapFromProjectFiles/core/inside/inside.test.js b/test/getImportMapFromProjectFiles/core/inside/inside.test.js index a6c60cb7..cb867111 100644 --- a/test/getImportMapFromProjectFiles/core/inside/inside.test.js +++ b/test/getImportMapFromProjectFiles/core/inside/inside.test.js @@ -12,13 +12,21 @@ const importMap = await getImportMapFromProjectFiles({ const actual = importMap const expected = { imports: { - root: "./index", - bar: "./node_modules/bar/bar.js", - foo: "./node_modules/foo/foo.js", + "root/": "./", + "root": "./index", + "bar": "./node_modules/bar/bar.js", + "foo": "./node_modules/foo/foo.js", }, scopes: { + "./node_modules/foo/node_modules/bar/": { + "bar/": "./node_modules/foo/node_modules/bar/", + }, + "./node_modules/bar/": { + "bar/": "./node_modules/bar/", + }, "./node_modules/foo/": { - bar: "./node_modules/foo/node_modules/bar/bar.js", + "foo/": "./node_modules/foo/", + "bar": "./node_modules/foo/node_modules/bar/bar.js", }, }, } @@ -48,18 +56,12 @@ const importMapNormalized = normalizeImportMap(importMap, "http://example.com") } // import 'bar/file.js' inside 'bar' -try { - resolveImport({ +{ + const actual = resolveImport({ specifier: `bar/file.js`, importer: `http://example.com/node_modules/foo/node_modules/bar/bar.js`, importMap: importMapNormalized, }) - throw new Error("should throw") -} catch (actual) { - const expected = new Error(`Unmapped bare specifier. ---- specifier --- -bar/file.js ---- importer --- -http://example.com/node_modules/foo/node_modules/bar/bar.js`) + const expected = `http://example.com/node_modules/foo/node_modules/bar/file.js` assert({ actual, expected }) } diff --git a/test/getImportMapFromProjectFiles/package-importmap-field/importmap-scope/importmap-scope.test.js b/test/getImportMapFromProjectFiles/package-importmap-field/importmap-scope/importmap-scope.test.js index 40eabf72..fd9fd163 100644 --- a/test/getImportMapFromProjectFiles/package-importmap-field/importmap-scope/importmap-scope.test.js +++ b/test/getImportMapFromProjectFiles/package-importmap-field/importmap-scope/importmap-scope.test.js @@ -10,13 +10,17 @@ const actual = await getImportMapFromProjectFiles({ }) const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/index", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/index", }, scopes: { "./node_modules/foo/src/": { "./a.js": "./node_modules/foo/src/b.js", }, + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, } assert({ actual, expected }) From d18ef3cfe0268a589e884749f1eb3b094f49eb27 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 09:51:46 +0200 Subject: [PATCH 11/16] update some tests after enabling self ref all the time --- .../core/circular/circular.test.js | 16 ++++++++++++---- .../exports-string/exports-string.test.js | 11 ++++++++--- .../exports-sugar/exports-sugar.test.js | 11 ++++++++--- .../importmap-top-level.test.js | 6 ++++-- .../imports-basic/imports-basic.test.js | 9 +++++---- .../main-directory-trailing.test.js | 7 ++++++- .../main-jsnext/main-jsnext.test.js | 7 ++++++- .../main-relative/main-relative.test.js | 7 ++++++- .../main-undefined/main-undefined.test.js | 7 ++++++- .../main-without-extension.test.js | 7 ++++++- .../self-import-6/self-import-6.test.js | 2 ++ 11 files changed, 69 insertions(+), 21 deletions(-) diff --git a/test/getImportMapFromProjectFiles/core/circular/circular.test.js b/test/getImportMapFromProjectFiles/core/circular/circular.test.js index 45056061..a976f09f 100644 --- a/test/getImportMapFromProjectFiles/core/circular/circular.test.js +++ b/test/getImportMapFromProjectFiles/core/circular/circular.test.js @@ -10,10 +10,18 @@ const actual = await getImportMapFromProjectFiles({ }) const expected = { imports: { - whatever: "./index", - bar: "./node_modules/bar/bar.js", - foo: "./node_modules/foo/foo.js", + "whatever/": "./", + "whatever": "./index", + "bar": "./node_modules/bar/bar.js", + "foo": "./node_modules/foo/foo.js", + }, + scopes: { + "./node_modules/bar/": { + "bar/": "./node_modules/bar/", + }, + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, - scopes: {}, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-string/exports-string.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-string/exports-string.test.js index 9263362a..40482dac 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-string/exports-string.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-string/exports-string.test.js @@ -11,9 +11,14 @@ const importMap = await getImportMapFromProjectFiles({ const actual = importMap const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/foo.js", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/foo.js", + }, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, - scopes: {}, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-sugar/exports-sugar.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-sugar/exports-sugar.test.js index 9263362a..40482dac 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-sugar/exports-sugar.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-sugar/exports-sugar.test.js @@ -11,9 +11,14 @@ const importMap = await getImportMapFromProjectFiles({ const actual = importMap const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/foo.js", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/foo.js", + }, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, - scopes: {}, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-importmap-field/importmap-top-level/importmap-top-level.test.js b/test/getImportMapFromProjectFiles/package-importmap-field/importmap-top-level/importmap-top-level.test.js index dd16f5da..9d538912 100644 --- a/test/getImportMapFromProjectFiles/package-importmap-field/importmap-top-level/importmap-top-level.test.js +++ b/test/getImportMapFromProjectFiles/package-importmap-field/importmap-top-level/importmap-top-level.test.js @@ -10,12 +10,14 @@ const actual = await getImportMapFromProjectFiles({ }) const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/index", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/index", }, scopes: { "./node_modules/foo/": { "./a.js": "./node_modules/foo/b.js", + "foo/": "./node_modules/foo/", }, }, } diff --git a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js index 40eabf72..2bc5e308 100644 --- a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js +++ b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js @@ -10,12 +10,13 @@ const actual = await getImportMapFromProjectFiles({ }) const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/index", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/index", }, scopes: { - "./node_modules/foo/src/": { - "./a.js": "./node_modules/foo/src/b.js", + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", }, }, } diff --git a/test/getImportMapFromProjectFiles/package-main-field/main-directory-trailing/main-directory-trailing.test.js b/test/getImportMapFromProjectFiles/package-main-field/main-directory-trailing/main-directory-trailing.test.js index e36c29fb..019d61fb 100644 --- a/test/getImportMapFromProjectFiles/package-main-field/main-directory-trailing/main-directory-trailing.test.js +++ b/test/getImportMapFromProjectFiles/package-main-field/main-directory-trailing/main-directory-trailing.test.js @@ -11,8 +11,13 @@ const actual = await getImportMapFromProjectFiles({ const expected = { imports: { "main-folder-trailing": "./node_modules/main-folder-trailing/lib/index.js", + "root/": "./", "root": "./index", }, - scopes: {}, + scopes: { + "./node_modules/main-folder-trailing/": { + "main-folder-trailing/": "./node_modules/main-folder-trailing/", + }, + }, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-main-field/main-jsnext/main-jsnext.test.js b/test/getImportMapFromProjectFiles/package-main-field/main-jsnext/main-jsnext.test.js index a36f5068..ab84155e 100644 --- a/test/getImportMapFromProjectFiles/package-main-field/main-jsnext/main-jsnext.test.js +++ b/test/getImportMapFromProjectFiles/package-main-field/main-jsnext/main-jsnext.test.js @@ -11,8 +11,13 @@ const actual = await getImportMapFromProjectFiles({ const expected = { imports: { "main-jsnext": "./node_modules/main-jsnext/jsnext.js", + "root/": "./", "root": "./index", }, - scopes: {}, + scopes: { + "./node_modules/main-jsnext/": { + "main-jsnext/": "./node_modules/main-jsnext/", + }, + }, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-main-field/main-relative/main-relative.test.js b/test/getImportMapFromProjectFiles/package-main-field/main-relative/main-relative.test.js index f0b83b7a..70aac6b4 100644 --- a/test/getImportMapFromProjectFiles/package-main-field/main-relative/main-relative.test.js +++ b/test/getImportMapFromProjectFiles/package-main-field/main-relative/main-relative.test.js @@ -11,8 +11,13 @@ const actual = await getImportMapFromProjectFiles({ const expected = { imports: { "main-relative": "./node_modules/main-relative/lib/index.js", + "root/": "./", "root": "./index", }, - scopes: {}, + scopes: { + "./node_modules/main-relative/": { + "main-relative/": "./node_modules/main-relative/", + }, + }, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-main-field/main-undefined/main-undefined.test.js b/test/getImportMapFromProjectFiles/package-main-field/main-undefined/main-undefined.test.js index 7fac4da8..4d7c8920 100644 --- a/test/getImportMapFromProjectFiles/package-main-field/main-undefined/main-undefined.test.js +++ b/test/getImportMapFromProjectFiles/package-main-field/main-undefined/main-undefined.test.js @@ -11,8 +11,13 @@ const actual = await getImportMapFromProjectFiles({ const expected = { imports: { "main-undefined": "./node_modules/main-undefined/index.js", + "root/": "./", "root": "./index", }, - scopes: {}, + scopes: { + "./node_modules/main-undefined/": { + "main-undefined/": "./node_modules/main-undefined/", + }, + }, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-main-field/main-without-extension/main-without-extension.test.js b/test/getImportMapFromProjectFiles/package-main-field/main-without-extension/main-without-extension.test.js index 76569743..a2407d9f 100644 --- a/test/getImportMapFromProjectFiles/package-main-field/main-without-extension/main-without-extension.test.js +++ b/test/getImportMapFromProjectFiles/package-main-field/main-without-extension/main-without-extension.test.js @@ -11,8 +11,13 @@ const actual = await getImportMapFromProjectFiles({ const expected = { imports: { "main-without-extension": "./node_modules/main-without-extension/file.js", + "root/": "./", "root": "./index", }, - scopes: {}, + scopes: { + "./node_modules/main-without-extension/": { + "main-without-extension/": "./node_modules/main-without-extension/", + }, + }, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/self-import/self-import-6/self-import-6.test.js b/test/getImportMapFromProjectFiles/self-import/self-import-6/self-import-6.test.js index 4abef5a8..af707254 100644 --- a/test/getImportMapFromProjectFiles/self-import/self-import-6/self-import-6.test.js +++ b/test/getImportMapFromProjectFiles/self-import/self-import-6/self-import-6.test.js @@ -13,11 +13,13 @@ const expected = { "@jsenv/core/conflict": "./root.js", "@jsenv/core/rootonly": "./rootonly.js", "@jsenv/core/deponly": "./node_modules/@jsenv/core/deponly.js", + "@jsenv/core/": "./", "@jsenv/core": "./index.js", }, scopes: { "./node_modules/@jsenv/core/": { "@jsenv/core/conflict": "./node_modules/@jsenv/core/dep.js", + "@jsenv/core/": "./node_modules/@jsenv/core/", "@jsenv/core": "./node_modules/@jsenv/core/maindep.js", }, }, From 4ef151d9849141f67ff4c0491c8ee5d69094b8c8 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 09:59:48 +0200 Subject: [PATCH 12/16] update remaining tests --- .../missing-dependency-package.test.js | 3 +- .../scoped-inside-scoped.test.js | 7 +++++ .../manual-override/manual-override.test.js | 1 + .../exports-and-main/exports-and-main.test.js | 11 ++++++-- .../exports-branch/exports-branch.test.js | 22 +++++++++++---- .../exports-complex/exports-complex.test.js | 28 ++++++++++++++++--- .../exports-condition-nested.test.js | 22 +++++++++++---- .../exports-directory-scoped.test.js | 2 ++ .../exports-file-conditional.test.js | 14 ++++++++-- .../exports-file-deep-2.test.js | 7 +++-- .../exports-file-deep.test.js | 7 +++-- .../exports-file/exports-file.test.js | 7 ++++- 12 files changed, 104 insertions(+), 27 deletions(-) diff --git a/test/getImportMapFromProjectFiles/core/missing-dependency-package/missing-dependency-package.test.js b/test/getImportMapFromProjectFiles/core/missing-dependency-package/missing-dependency-package.test.js index 013453ae..4877cd6e 100644 --- a/test/getImportMapFromProjectFiles/core/missing-dependency-package/missing-dependency-package.test.js +++ b/test/getImportMapFromProjectFiles/core/missing-dependency-package/missing-dependency-package.test.js @@ -11,7 +11,8 @@ const actual = await getImportMapFromProjectFiles({ }) const expected = { imports: { - root: "./index", + "root/": "./", + "root": "./index", }, scopes: {}, } diff --git a/test/getImportMapFromProjectFiles/core/scoped-inside-scoped/scoped-inside-scoped.test.js b/test/getImportMapFromProjectFiles/core/scoped-inside-scoped/scoped-inside-scoped.test.js index 88410219..c2bbad00 100644 --- a/test/getImportMapFromProjectFiles/core/scoped-inside-scoped/scoped-inside-scoped.test.js +++ b/test/getImportMapFromProjectFiles/core/scoped-inside-scoped/scoped-inside-scoped.test.js @@ -12,14 +12,21 @@ const actual = await getImportMapFromProjectFiles({ const expected = { imports: { "@jsenv/bundling": "./node_modules/@jsenv/bundling/whatever.js", + "root/": "./", "root": "./index", }, scopes: { + "./node_modules/@jsenv/bundling/node_modules/@jsenv/core/node_modules/@dmail/project-structure/": { + "@dmail/project-structure/": + "./node_modules/@jsenv/bundling/node_modules/@jsenv/core/node_modules/@dmail/project-structure/", + }, "./node_modules/@jsenv/bundling/node_modules/@jsenv/core/": { "@dmail/project-structure": "./node_modules/@jsenv/bundling/node_modules/@jsenv/core/node_modules/@dmail/project-structure/whatever.js", + "@jsenv/core/": "./node_modules/@jsenv/bundling/node_modules/@jsenv/core/", }, "./node_modules/@jsenv/bundling/": { + "@jsenv/bundling/": "./node_modules/@jsenv/bundling/", "@jsenv/core": "./node_modules/@jsenv/bundling/node_modules/@jsenv/core/whatever.js", }, }, diff --git a/test/getImportMapFromProjectFiles/manual-override/manual-override.test.js b/test/getImportMapFromProjectFiles/manual-override/manual-override.test.js index 8d56be4c..8b754b8a 100644 --- a/test/getImportMapFromProjectFiles/manual-override/manual-override.test.js +++ b/test/getImportMapFromProjectFiles/manual-override/manual-override.test.js @@ -18,6 +18,7 @@ const actual = await getImportMapFromProjectFiles({ }) const expected = { imports: { + "root/": "./", "bar/": "./node_modules/bar/", "root": "./index", "bar": "./node_modules/bar/bar.js", diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-and-main/exports-and-main.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-and-main/exports-and-main.test.js index fbb32029..caeca14d 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-and-main/exports-and-main.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-and-main/exports-and-main.test.js @@ -10,9 +10,14 @@ const actual = await getImportMapFromProjectFiles({ }) const expected = { imports: { - whatever: "./index", - foo: "./node_modules/foo/file.js", + "whatever/": "./", + "whatever": "./index", + "foo": "./node_modules/foo/file.js", + }, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, - scopes: {}, } assert({ actual, expected }) diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/exports-branch.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/exports-branch.test.js index e5448139..48529bba 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/exports-branch.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-branch/exports-branch.test.js @@ -20,10 +20,15 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { }) const expected = { imports: { - whatever: "./index", - foo: "./node_modules/foo/main.js", + "whatever/": "./", + "whatever": "./index", + "foo": "./node_modules/foo/main.js", + }, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, - scopes: {}, } assert({ actual, expected }) } @@ -35,10 +40,15 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { }) const expected = { imports: { - whatever: "./index", - foo: "./node_modules/foo/main.cjs", + "whatever/": "./", + "whatever": "./index", + "foo": "./node_modules/foo/main.cjs", + }, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, - scopes: {}, } assert({ actual, expected }) } diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/exports-complex.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/exports-complex.test.js index 7381a1de..3b40f292 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/exports-complex.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-complex/exports-complex.test.js @@ -20,10 +20,15 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { const expected = { imports: { "foo/dist/": "./node_modules/foo/dist/", + "whatever/": "./", "whatever": "./index", "foo": "./node_modules/foo/dist/rollup.mjs", }, - scopes: {}, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, + }, } assert({ actual, expected }) } @@ -36,10 +41,15 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { const expected = { imports: { "foo/dist/": "./node_modules/foo/dist/", + "whatever/": "./", "whatever": "./index", "foo": "./node_modules/foo/dist/rollup.js", }, - scopes: {}, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, + }, } assert({ actual, expected }) } @@ -52,10 +62,15 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { const expected = { imports: { "foo/dist/": "./node_modules/foo/dist/", + "whatever/": "./", "whatever": "./index", "foo": "./node_modules/foo/dist/rollup.browser.mjs", }, - scopes: {}, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, + }, } assert({ actual, expected }) } @@ -65,10 +80,15 @@ const getImportMap = async ({ runtime, moduleFormat } = {}) => { const expected = { imports: { "foo/dist/": "./node_modules/foo/dist/", + "whatever/": "./", "whatever": "./index", "foo": "./node_modules/foo/dist/rollup.browser.mjs", }, - scopes: {}, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, + }, } assert({ actual, expected }) } diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-condition-nested/exports-condition-nested.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-condition-nested/exports-condition-nested.test.js index 9e3ca3d6..5e24d5b5 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-condition-nested/exports-condition-nested.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-condition-nested/exports-condition-nested.test.js @@ -13,10 +13,15 @@ const testDirectoryUrl = resolveUrl("./root/", import.meta.url) const actual = importMap const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/feature-node.mjs", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/feature-node.mjs", + }, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, - scopes: {}, } assert({ actual, expected }) } @@ -30,10 +35,15 @@ const testDirectoryUrl = resolveUrl("./root/", import.meta.url) const actual = importMap const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/feature.mjs", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/feature.mjs", + }, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, }, - scopes: {}, } assert({ actual, expected }) } diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-directory-scoped/exports-directory-scoped.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-directory-scoped/exports-directory-scoped.test.js index a320b779..87d7a150 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-directory-scoped/exports-directory-scoped.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-directory-scoped/exports-directory-scoped.test.js @@ -11,6 +11,7 @@ const actual = await getImportMapFromProjectFiles({ const expected = { imports: { "foo/ding": "./node_modules/foo/dong", + "root/": "./", "root": "./index", "foo": "./node_modules/foo/index.js", }, @@ -21,6 +22,7 @@ const expected = { "./node_modules/foo/": { "exporting-folder/": "./node_modules/foo/node_modules/exporting-folder/", "exporting-folder": "./node_modules/foo/node_modules/exporting-folder/index.js", + "foo/": "./node_modules/foo/", }, }, } diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-file-conditional/exports-file-conditional.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-file-conditional/exports-file-conditional.test.js index b064d002..b71e4552 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-file-conditional/exports-file-conditional.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-file-conditional/exports-file-conditional.test.js @@ -14,10 +14,15 @@ const testDirectoryUrl = resolveUrl("./root/", import.meta.url) const expected = { imports: { "foo/file.js": "./node_modules/foo/file.browser.js", + "root/": "./", "root": "./index", "foo": "./node_modules/foo/index", }, - scopes: {}, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, + }, } assert({ actual, expected }) } @@ -32,10 +37,15 @@ const testDirectoryUrl = resolveUrl("./root/", import.meta.url) const expected = { imports: { "foo/file.js": "./node_modules/foo/file.default.js", + "root/": "./", "root": "./index", "foo": "./node_modules/foo/index", }, - scopes: {}, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, + }, } assert({ actual, expected }) } diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-file-deep-2/exports-file-deep-2.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-file-deep-2/exports-file-deep-2.test.js index 042c724a..76ec840d 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-file-deep-2/exports-file-deep-2.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-file-deep-2/exports-file-deep-2.test.js @@ -11,15 +11,18 @@ const importMap = await getImportMapFromProjectFiles({ const actual = importMap const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/index", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/index", }, scopes: { "./node_modules/foo/node_modules/bar/": { "bar/file.js": "./node_modules/foo/node_modules/bar/src/file.js", + "bar/": "./node_modules/foo/node_modules/bar/", }, "./node_modules/foo/": { "bar/file.js": "./node_modules/foo/node_modules/bar/src/file.js", + "foo/": "./node_modules/foo/", "bar": "./node_modules/foo/node_modules/bar/index", }, }, diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-file-deep/exports-file-deep.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-file-deep/exports-file-deep.test.js index cead984c..80382c55 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-file-deep/exports-file-deep.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-file-deep/exports-file-deep.test.js @@ -11,15 +11,18 @@ const importMap = await getImportMapFromProjectFiles({ const actual = importMap const expected = { imports: { - root: "./index", - foo: "./node_modules/foo/index", + "root/": "./", + "root": "./index", + "foo": "./node_modules/foo/index", }, scopes: { "./node_modules/bar/": { "bar/file.js": "./node_modules/bar/src/file.js", + "bar/": "./node_modules/bar/", }, "./node_modules/foo/": { "bar/file.js": "./node_modules/bar/src/file.js", + "foo/": "./node_modules/foo/", "bar": "./node_modules/bar/index", }, }, diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-file/exports-file.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-file/exports-file.test.js index df8b572f..9af08707 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-file/exports-file.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-file/exports-file.test.js @@ -12,9 +12,14 @@ const actual = importMap const expected = { imports: { "foo/file.js": "./node_modules/foo/src/file.js", + "root/": "./", "root": "./index", "foo": "./node_modules/foo/index.js", }, - scopes: {}, + scopes: { + "./node_modules/foo/": { + "foo/": "./node_modules/foo/", + }, + }, } assert({ actual, expected }) From 15bb3d50102f5f8b148d74b4b1fedf4959058e94 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 10:00:47 +0200 Subject: [PATCH 13/16] update last test --- .../exports-directory/exports-directory.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/getImportMapFromProjectFiles/package-exports-field/exports-directory/exports-directory.test.js b/test/getImportMapFromProjectFiles/package-exports-field/exports-directory/exports-directory.test.js index 39cd3a18..d4a6b0f5 100644 --- a/test/getImportMapFromProjectFiles/package-exports-field/exports-directory/exports-directory.test.js +++ b/test/getImportMapFromProjectFiles/package-exports-field/exports-directory/exports-directory.test.js @@ -12,6 +12,7 @@ const expected = { imports: { "@jsenv/whatever/": "./node_modules/@jsenv/whatever/", "@jsenv/whatever": "./node_modules/@jsenv/whatever/index.js", + "root/": "./", "root": "./index", }, scopes: {}, From 2b1509cce5775b93b0ee803ee46b9224d428bcdf Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 10:30:25 +0200 Subject: [PATCH 14/16] simplify warning of package exports --- .../from-package/visitPackageExports.js | 68 +++---------------- 1 file changed, 9 insertions(+), 59 deletions(-) diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index 876bf1bd..88c3e139 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -16,29 +16,9 @@ export const visitPackageExports = ({ const packageDirectoryUrl = resolveUrl("./", packageFileUrl) const packageDirectoryRelativeUrl = urlToRelativeUrl(packageDirectoryUrl, projectDirectoryUrl) const onExportsSubpath = ({ key, value, trace }) => { - if (!specifierIsRelative(key)) { - warn( - createExportsSubpathKeyMustBeRelativeWarning({ - key, - keyTrace: trace.slice(0, -1), - packageFileUrl, - }), - ) - return - } - if (typeof value !== "string") { - warn( - createExportsSubpathValueMustBeAStringWarning({ - value, - valueTrace: trace, - packageFileUrl, - }), - ) - return - } if (!specifierIsRelative(value)) { warn( - createExportsSubpathValueMustBeRelativeWarning({ + createSubpathValueMustBeRelativeWarning({ value, valueTrace: trace, packageFileUrl, @@ -114,7 +94,7 @@ export const visitPackageExports = ({ if (relativeKeys.length > 0 && conditionalKeys.length > 0) { // see https://nodejs.org/dist/latest-v13.x/docs/api/esm.html#esm_exports_sugar warn( - createUnexpectedExportsSubpathWarning({ + createSubpathKeysAreMixedWarning({ subpathValue, subpathValueTrace: [...subpathValueTrace, ...conditionTrace], packageFileUrl, @@ -157,7 +137,7 @@ export const visitPackageExports = ({ const handleRemaining = (subpathValue, subpathValueTrace) => { warn( - createMixedExportsSubpathWarning({ + createSubpathIsUnexpectedWarning({ subpathValue, subpathValueTrace, packageFileUrl, @@ -199,11 +179,7 @@ const addressToDestination = (address, packageDirectoryRelativeUrl) => { return `./${packageDirectoryRelativeUrl}${address}` } -const createUnexpectedExportsSubpathWarning = ({ - subpathValue, - subpathValueTrace, - packageFileUrl, -}) => { +const createSubpathIsUnexpectedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => { return { code: "EXPORTS_SUBPATH_UNEXPECTED", message: `unexpected value in package.json exports: value must be an object or a string. @@ -216,10 +192,10 @@ ${urlToFileSystemPath(packageFileUrl)}`, } } -const createMixedExportsSubpathWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => { +const createSubpathKeysAreMixedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => { return { - code: "EXPORTS_SUBPATH_MIXED", - message: `unexpected value in package.json exports: cannot mix conditional and subpath. + code: "EXPORTS_SUBPATH_MIXED_KEYS", + message: `unexpected subpath keys in package.json exports: cannot mix conditional and relative keys. --- value --- ${JSON.stringify(subpathValue, null, " ")} --- value at --- @@ -229,36 +205,10 @@ ${urlToFileSystemPath(packageFileUrl)}`, } } -const createExportsSubpathKeyMustBeRelativeWarning = ({ key, keyTrace, packageFileUrl }) => { - return { - code: "EXPORTS_SUBPATH_KEY_MUST_BE_RELATIVE", - message: `unexpected key in package.json exports: key must be relative. ---- key --- -${key} ---- key at --- -${keyTrace.join(".")} ---- package.json path --- -${urlToFileSystemPath(packageFileUrl)}`, - } -} - -const createExportsSubpathValueMustBeAStringWarning = ({ value, valueTrace, packageFileUrl }) => { - return { - code: "EXPORTS_SUBPATH_VALUE_MUST_BE_A_STRING", - message: `unexpected value in package.json exports: value must be a string. ---- value --- -${value} ---- value at --- -${valueTrace.join(".")} ---- package.json path --- -${urlToFileSystemPath(packageFileUrl)}`, - } -} - -const createExportsSubpathValueMustBeRelativeWarning = ({ value, valueTrace, packageFileUrl }) => { +const createSubpathValueMustBeRelativeWarning = ({ value, valueTrace, packageFileUrl }) => { return { code: "EXPORTS_SUBPATH_VALUE_MUST_BE_RELATIVE", - message: `unexpected value in package.json exports: value must be relative. + message: `unexpected subpath value in package.json exports: value must be a relative to the package. --- value --- ${value} --- value at --- From 7f7b717b89ea99cd98640f02014692727e184317 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 10:34:05 +0200 Subject: [PATCH 15/16] small renamings --- src/internal/from-package/visitPackageExports.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index 88c3e139..b2a4db00 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -57,11 +57,11 @@ export const visitPackageExports = ({ } const handleString = (subpathValue, subpathValueTrace) => { - const firstNonConditionKey = subpathValueTrace + const firstRelativeKey = subpathValueTrace .slice() .reverse() .find((key) => key.startsWith(".")) - const key = firstNonConditionKey || "." + const key = firstRelativeKey || "." onExportsSubpath({ key, value: subpathValue, @@ -92,7 +92,6 @@ export const visitPackageExports = ({ }) if (relativeKeys.length > 0 && conditionalKeys.length > 0) { - // see https://nodejs.org/dist/latest-v13.x/docs/api/esm.html#esm_exports_sugar warn( createSubpathKeysAreMixedWarning({ subpathValue, @@ -182,7 +181,7 @@ const addressToDestination = (address, packageDirectoryRelativeUrl) => { const createSubpathIsUnexpectedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => { return { code: "EXPORTS_SUBPATH_UNEXPECTED", - message: `unexpected value in package.json exports: value must be an object or a string. + message: `unexpected subpath in package.json exports: value must be an object or a string. --- value --- ${subpathValue} --- value at --- @@ -195,7 +194,7 @@ ${urlToFileSystemPath(packageFileUrl)}`, const createSubpathKeysAreMixedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => { return { code: "EXPORTS_SUBPATH_MIXED_KEYS", - message: `unexpected subpath keys in package.json exports: cannot mix conditional and relative keys. + message: `unexpected subpath keys in package.json exports: cannot mix relative and conditional keys. --- value --- ${JSON.stringify(subpathValue, null, " ")} --- value at --- From bb06e1990b8079bdd39748b1fef7406ffdeaa968 Mon Sep 17 00:00:00 2001 From: Damien Maillard Date: Tue, 20 Apr 2021 10:42:53 +0200 Subject: [PATCH 16/16] generate mapping for package imports too --- .../getImportMapFromPackageFiles.js | 20 ++ .../from-package/visitPackageExports.js | 6 +- .../from-package/visitPackageImports.js | 179 ++++++++++++++++++ .../imports-basic/imports-basic.test.js | 8 +- .../imports-basic/root/index.js | 2 + .../root/node_modules/foo/env.prod.js | 0 .../root/node_modules/foo/index.js | 1 + 7 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 src/internal/from-package/visitPackageImports.js create mode 100644 test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/index.js create mode 100644 test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/env.prod.js create mode 100644 test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/index.js diff --git a/src/internal/from-package/getImportMapFromPackageFiles.js b/src/internal/from-package/getImportMapFromPackageFiles.js index c108ca07..c1e9793b 100644 --- a/src/internal/from-package/getImportMapFromPackageFiles.js +++ b/src/internal/from-package/getImportMapFromPackageFiles.js @@ -9,6 +9,7 @@ import { import { optimizeImportMap } from "../optimizeImportMap.js" import { resolvePackageMain } from "./resolvePackageMain.js" import { visitPackageImportMap } from "./visitPackageImportMap.js" +import { visitPackageImports } from "./visitPackageImports.js" import { visitPackageExports } from "./visitPackageExports.js" import { createFindNodeModulePackage } from "./node-module-resolution.js" @@ -253,6 +254,25 @@ export const getImportMapFromPackageFiles = async ({ }) addImportMapForPackage(importsFromPackageField) + if ("imports" in packageJsonObject) { + const mappingsFromPackageExports = {} + const packageImports = visitPackageImports({ + warn, + packageFileUrl, + packageJsonObject, + packageName, + projectDirectoryUrl, + packageConditions, + }) + Object.keys(packageImports).forEach((from) => { + const to = packageImports[from] + mappingsFromPackageExports[from] = to + }) + addImportMapForPackage({ + imports: mappingsFromPackageExports, + }) + } + if ("exports" in packageJsonObject) { const mappingsFromPackageExports = {} const packageExports = visitPackageExports({ diff --git a/src/internal/from-package/visitPackageExports.js b/src/internal/from-package/visitPackageExports.js index b2a4db00..42f6a868 100644 --- a/src/internal/from-package/visitPackageExports.js +++ b/src/internal/from-package/visitPackageExports.js @@ -1,4 +1,8 @@ -// https://nodejs.org/dist/latest-v13.x/docs/api/esm.html#esm_package_exports +/* + +https://nodejs.org/docs/latest-v15.x/api/packages.html#packages_node_js_package_json_field_definitions + +*/ import { urlToFileSystemPath, urlToRelativeUrl, resolveUrl } from "@jsenv/util" import { specifierIsRelative } from "./specifierIsRelative.js" diff --git a/src/internal/from-package/visitPackageImports.js b/src/internal/from-package/visitPackageImports.js new file mode 100644 index 00000000..713ec6b2 --- /dev/null +++ b/src/internal/from-package/visitPackageImports.js @@ -0,0 +1,179 @@ +/* + +https://nodejs.org/docs/latest-v15.x/api/packages.html#packages_node_js_package_json_field_definitions + +*/ + +import { urlToFileSystemPath } from "@jsenv/util" +import { specifierIsRelative } from "./specifierIsRelative.js" + +export const visitPackageImports = ({ + packageFileUrl, + packageJsonObject, + packageImports = packageJsonObject.imports, + packageConditions, + warn, +}) => { + const importsSubpaths = {} + const onImportsSubpath = ({ key, value, trace }) => { + if (!specifierIsRelative(value)) { + warn( + createSubpathValueMustBeRelativeWarning({ + value, + valueTrace: trace, + packageFileUrl, + }), + ) + return + } + + const keyNormalized = key + const valueNormalized = value + importsSubpaths[keyNormalized] = valueNormalized + } + + const conditions = [...packageConditions, "default"] + + const visitSubpathValue = (subpathValue, subpathValueTrace) => { + if (typeof subpathValue === "string") { + return handleString(subpathValue, subpathValueTrace) + } + + if (typeof subpathValue === "object" && subpathValue !== null) { + return handleObject(subpathValue, subpathValueTrace) + } + + return handleRemaining(subpathValue, subpathValueTrace) + } + + const handleString = (subpathValue, subpathValueTrace) => { + const firstBareKey = subpathValueTrace + .slice() + .reverse() + .find((key) => key.startsWith("#")) + onImportsSubpath({ + key: firstBareKey, + value: subpathValue, + trace: subpathValueTrace, + }) + return true + } + + const handleObject = (subpathValue, subpathValueTrace) => { + // From Node.js documentation: + // "If a nested conditional does not have any mapping it will continue + // checking the remaining conditions of the parent condition" + // https://nodejs.org/docs/latest-v14.x/api/packages.html#packages_nested_conditions + // + // So it seems what we do here is not sufficient + // -> if the condition finally does not lead to something + // it should be ignored and an other branch be taken until + // something resolves + const followConditionBranch = (subpathValue, conditionTrace) => { + const bareKeys = [] + const conditionalKeys = [] + Object.keys(subpathValue).forEach((availableKey) => { + if (availableKey.startsWith("#")) { + bareKeys.push(availableKey) + } else { + conditionalKeys.push(availableKey) + } + }) + + if (bareKeys.length > 0 && conditionalKeys.length > 0) { + warn( + createSubpathKeysAreMixedWarning({ + subpathValue, + subpathValueTrace: [...subpathValueTrace, ...conditionTrace], + packageFileUrl, + bareKeys, + conditionalKeys, + }), + ) + return false + } + + // there is no condition, visit all bare keys (starting with #) + if (conditionalKeys.length === 0) { + let leadsToSomething = false + bareKeys.forEach((key) => { + leadsToSomething = visitSubpathValue(subpathValue[key], [ + ...subpathValueTrace, + ...conditionTrace, + key, + ]) + }) + return leadsToSomething + } + + // there is a condition, keep the first one leading to something + return conditions.some((keyCandidate) => { + if (!conditionalKeys.includes(keyCandidate)) { + return false + } + const valueCandidate = subpathValue[keyCandidate] + return visitSubpathValue(valueCandidate, [ + ...subpathValueTrace, + ...conditionTrace, + keyCandidate, + ]) + }) + } + + return followConditionBranch(subpathValue, []) + } + + const handleRemaining = (subpathValue, subpathValueTrace) => { + warn( + createSubpathIsUnexpectedWarning({ + subpathValue, + subpathValueTrace, + packageFileUrl, + }), + ) + return false + } + + visitSubpathValue(packageImports, ["imports"]) + + return importsSubpaths +} + +const createSubpathIsUnexpectedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => { + return { + code: "IMPORTS_SUBPATH_UNEXPECTED", + message: `unexpected subpath in package.json imports: value must be an object or a string. +--- value --- +${subpathValue} +--- value at --- +${subpathValueTrace.join(".")} +--- package.json path --- +${urlToFileSystemPath(packageFileUrl)}`, + } +} + +const createSubpathKeysAreMixedWarning = ({ subpathValue, subpathValueTrace, packageFileUrl }) => { + return { + code: "IMPORTS_SUBPATH_MIXED_KEYS", + message: `unexpected subpath keys in package.json imports: cannot mix bare and conditional keys. +--- value --- +${JSON.stringify(subpathValue, null, " ")} +--- value at --- +${subpathValueTrace.join(".")} +--- package.json path --- +${urlToFileSystemPath(packageFileUrl)}`, + } +} + +const createSubpathValueMustBeRelativeWarning = ({ value, valueTrace, packageFileUrl }) => { + return { + code: "IMPORTS_SUBPATH_VALUE_UNEXPECTED", + message: `unexpected subpath value in package.json imports: value must be relative to package +--- value --- +${value} +--- value at --- +${valueTrace.join(".")} +--- package.json path --- +${urlToFileSystemPath(packageFileUrl)}`, + } +} diff --git a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js index 2bc5e308..1b332af6 100644 --- a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js +++ b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/imports-basic.test.js @@ -6,17 +6,15 @@ const testDirectoryUrl = resolveUrl("./root/", import.meta.url) const actual = await getImportMapFromProjectFiles({ projectDirectoryUrl: testDirectoryUrl, - jsFiles: false, }) const expected = { imports: { - "root/": "./", - "root": "./index", - "foo": "./node_modules/foo/index", + root: "./index.js", + foo: "./node_modules/foo/index.js", }, scopes: { "./node_modules/foo/": { - "foo/": "./node_modules/foo/", + "#env": "./node_modules/foo/env.prod.js", }, }, } diff --git a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/index.js b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/index.js new file mode 100644 index 00000000..05eed085 --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/no-unresolved +import "foo" diff --git a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/env.prod.js b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/env.prod.js new file mode 100644 index 00000000..e69de29b diff --git a/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/index.js b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/index.js new file mode 100644 index 00000000..77673f5f --- /dev/null +++ b/test/getImportMapFromProjectFiles/package-imports-field/imports-basic/root/node_modules/foo/index.js @@ -0,0 +1 @@ +import "#env" \ No newline at end of file