esm-resolve
Advanced tools
Comparing version 1.0.4 to 1.0.5
124
index.js
@@ -21,5 +21,5 @@ /* | ||
import * as fs from 'fs'; | ||
import {statIsFile, statOrNull, isLocal, splitImport} from './lib/helper.js'; | ||
import {createRequire} from 'module'; | ||
import {matchModuleNode} from './lib/node.js'; | ||
import { statIsFile, statOrNull, isLocal } from './lib/helper.js'; | ||
import { createRequire } from 'module'; | ||
import { matchModuleNode } from './lib/node.js'; | ||
@@ -36,2 +36,3 @@ | ||
includeMainFallback: true, | ||
checkNestedPackages: true, | ||
}; | ||
@@ -91,3 +92,3 @@ | ||
/** | ||
* @return {{resolved?: string, info?: types.InternalPackageJson}} | ||
* @return {{resolved: string, info: types.InternalPackageJson} | undefined} | ||
*/ | ||
@@ -97,3 +98,3 @@ loadSelfPackage() { | ||
if (candidatePath === undefined) { | ||
return {}; | ||
return; | ||
} | ||
@@ -106,5 +107,5 @@ | ||
} catch (e) { | ||
return {}; | ||
return; | ||
} | ||
return {info, resolved: candidatePath}; | ||
return { info, resolved: candidatePath }; | ||
} | ||
@@ -114,3 +115,3 @@ | ||
* @param {string} name | ||
* @return {{resolved?: string, info?: types.InternalPackageJson}} | ||
* @return {{resolved: string, info: types.InternalPackageJson} | undefined} | ||
*/ | ||
@@ -120,3 +121,3 @@ loadPackage(name) { | ||
if (!candidatePaths?.length) { | ||
return {}; | ||
return; | ||
} | ||
@@ -126,4 +127,4 @@ | ||
const self = this.loadSelfPackage(); | ||
if (self.info?.['name'] === name) { | ||
return {resolved: self.resolved, info: self.info}; | ||
if (self?.info['name'] === name) { | ||
return { resolved: self.resolved, info: self.info }; | ||
} | ||
@@ -140,7 +141,7 @@ | ||
if (!packagePath) { | ||
return {}; | ||
return; | ||
} | ||
const info = JSON.parse(fs.readFileSync(packagePath, 'utf-8')); | ||
return {resolved: path.dirname(packagePath), info}; | ||
return { resolved: path.dirname(packagePath), info }; | ||
} | ||
@@ -209,3 +210,3 @@ | ||
const self = this.loadSelfPackage(); | ||
if (!self.info || !self.resolved) { | ||
if (!self) { | ||
return; | ||
@@ -225,52 +226,63 @@ } | ||
const {name, rest} = splitImport(importee); | ||
if (!name) { | ||
return; | ||
} | ||
const pathComponents = importee.split('/'); | ||
let index = (pathComponents[0].startsWith('@') ? 2 : 1); | ||
const {resolved, info} = this.loadPackage(name); | ||
if (!resolved || !info) { | ||
return; | ||
} | ||
/** @type {string=} */ | ||
let fallbackBest = undefined; | ||
// If we find exports, then use a modern resolution mechanism. | ||
if (info.exports) { | ||
const matched = matchModuleNode(info.exports, rest, this.#options.constraints); | ||
if (matched) { | ||
if (!isLocal(matched)) { | ||
// This module is trying to export something that is not part of its own package. | ||
// This isn't allowed although perhaps we should let it happen. | ||
// This loop only exists to check "bad" nested paths. | ||
do { | ||
const name = pathComponents.slice(0, index).join('/'); | ||
const rest = ['.', ...pathComponents.slice(index)].join('/'); | ||
const pkg = this.loadPackage(name); | ||
if (!pkg) { | ||
continue; | ||
} | ||
// Match exports. | ||
if (pkg.info.exports) { | ||
const matched = matchModuleNode(pkg.info.exports, rest, this.#options.constraints); | ||
if (matched && isLocal(matched)) { | ||
return `file://${path.join(pkg.resolved, matched)}`; | ||
} | ||
// This module could be trying to export something that is not part of its own package. | ||
// This isn't allowed although perhaps we should let it happen. | ||
if (!this.#options.allowExportFallback) { | ||
return; | ||
} | ||
return `file://${path.join(resolved, matched)}`; | ||
} | ||
if (!this.#options.allowExportFallback) { | ||
return; | ||
} | ||
// If we couldn't find a node, then fallback to running the legacy resolution algorithm. This | ||
// is agaist Node's rules: if an exports field is found, it's all that should be used. | ||
} | ||
// Check a few legacy options. | ||
let simple = rest; | ||
if (simple === '.') { | ||
let found = false; | ||
for (const key of modulePackageNames) { | ||
if (typeof pkg.info[key] === 'string') { | ||
simple = /** @type {string} */ (pkg.info[key]); | ||
found = true; | ||
break; | ||
} | ||
} | ||
// Check a few legacy options and fall back to allowing any path within the package. | ||
let simple = rest; | ||
if (simple === '.') { | ||
let found = false; | ||
for (const key of modulePackageNames) { | ||
if (typeof info[key] === 'string') { | ||
simple = /** @type {string} */ (info[key]); | ||
found = true; | ||
break; | ||
// If we can't find a name which implies a module import, optionally fall back to 'main' even | ||
// if the type of the package isn't correct. | ||
if (!found && | ||
(this.#options.includeMainFallback || pkg.info['type'] === 'module') && | ||
typeof pkg.info['main'] === 'string') { | ||
simple = pkg.info['main']; | ||
} | ||
return `file://${path.join(pkg.resolved, simple)}`; | ||
} | ||
// If we can't find a name which implies a module import, optionally fall back to 'main' even | ||
// if the type of the package isn't correct. | ||
if (!found && | ||
(this.#options.includeMainFallback || info['type'] === 'module') && | ||
typeof info['main'] === 'string') { | ||
simple = info['main']; | ||
// Otherwise, choose the best (if we're the 1st pass) based on the guessed filename. | ||
if (!fallbackBest) { | ||
fallbackBest = `file://${path.join(pkg.resolved, rest)}`; | ||
} | ||
} | ||
return `file://${path.join(resolved, simple)}`; | ||
} while (this.#options.checkNestedPackages && ++index <= pathComponents.length); | ||
return fallbackBest; | ||
} | ||
@@ -286,3 +298,3 @@ | ||
return; // ignore, is valid URL | ||
} catch {} | ||
} catch { } | ||
@@ -302,3 +314,3 @@ /** @type {URL} */ | ||
let {pathname} = url; | ||
let { pathname } = url; | ||
const suffix = url.search + url.hash; | ||
@@ -320,3 +332,3 @@ | ||
} | ||
// Find the relative path from the request. | ||
@@ -323,0 +335,0 @@ let out = path.relative(this.#importerDir.pathname, pathname); |
@@ -23,2 +23,3 @@ /* | ||
* @param {string} p | ||
* @return {fs.Stats?} | ||
*/ | ||
@@ -46,21 +47,1 @@ export const statOrNull = (p) => { | ||
export const isLocal = (p) => p === '.' || p.startsWith('./'); | ||
/** | ||
* @param {string} importee | ||
* @return {{name?: string, rest: string}} | ||
*/ | ||
export function splitImport(importee) { | ||
const split = importee.split('/', 2); | ||
let name = ''; | ||
if (!split[0].startsWith('@')) { | ||
name = split[0]; | ||
} else if (split[0] && split[1]) { | ||
name = split.join('/'); | ||
} | ||
if (!name || name.startsWith('.')) { | ||
return {rest: importee}; | ||
} | ||
return {name, rest: '.' + importee.substr(name.length)}; | ||
} |
@@ -20,3 +20,3 @@ /* | ||
import * as path from 'path'; | ||
import {isLocal} from './helper.js'; | ||
import { isLocal } from './helper.js'; | ||
@@ -35,3 +35,3 @@ | ||
if (typeof exports !== 'object') { | ||
return {node: exports}; | ||
return { node: exports }; | ||
} | ||
@@ -46,3 +46,3 @@ let fallback; | ||
if (key === rest) { | ||
return {node: exports[key]}; | ||
return { node: exports[key] }; | ||
} | ||
@@ -63,7 +63,7 @@ | ||
return {node: exports[key], subpath}; | ||
return { node: exports[key], subpath }; | ||
} | ||
if (fallback) { | ||
return {node: fallback}; | ||
return { node: fallback }; | ||
} | ||
@@ -80,3 +80,3 @@ return {}; | ||
export function matchModuleNode(exports, rest, constraints) { | ||
let {node, subpath} = matchModuleNodePath(exports, rest); | ||
let { node, subpath } = matchModuleNodePath(exports, rest); | ||
@@ -83,0 +83,0 @@ // Traverse looking for the best conditional. These can be nested. |
{ | ||
"name": "esm-resolve", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"description": "Resolves ESM imports in JS", | ||
@@ -8,3 +8,4 @@ "main": "index.js", | ||
".": { | ||
"import": "./index.js" | ||
"import": "./index.js", | ||
"require": "./bundle.cjs" | ||
} | ||
@@ -14,3 +15,4 @@ }, | ||
"scripts": { | ||
"test": "ava" | ||
"test": "ava", | ||
"prepublishOnly": "rollup -o bundle.cjs -f cjs index.js --output.exports=named" | ||
}, | ||
@@ -22,5 +24,6 @@ "author": "Sam Thorogood <sam.thorogood@gmail.com>", | ||
"@types/node": "^14.14.25", | ||
"ava": "^3.15.0" | ||
"ava": "^3.15.0", | ||
"rollup": "^2.60.2" | ||
}, | ||
"types": "index.d.ts" | ||
} |
@@ -78,2 +78,11 @@ /* | ||
/** | ||
* If we can't find a match in "node_modules/package" (or under "node_modules/@user/package"), | ||
* then see if there's a nested package. These aren't allowed by Node, but actually show up a | ||
* bit in the wild. | ||
* | ||
* @default true | ||
*/ | ||
checkNestedPackages: boolean, | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
45405
11
962
3
6