@jsenv/node-esm-resolution 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/main.js +5 -0
- package/package.json +26 -0
- package/src/determine_module_system.js +55 -0
- package/src/errors.js +83 -0
- package/src/filesystem_magic_resolution.js +71 -0
- package/src/lookup_package_scope.js +18 -0
- package/src/node_builtin_specifiers.js +84 -0
- package/src/node_esm_resolution.js +782 -0
- package/src/read_package_json.js +12 -0
- package/src/url_utils.js +37 -0
package/README.md
ADDED
package/main.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { applyNodeEsmResolution } from "./src/node_esm_resolution.js"
|
|
2
|
+
export { determineModuleSystem } from "./src/determine_module_system.js"
|
|
3
|
+
export { applyFileSystemMagicResolution } from "./src/filesystem_magic_resolution.js"
|
|
4
|
+
export { lookupPackageScope } from "./src/lookup_package_scope.js"
|
|
5
|
+
export { readPackageJson } from "./src/read_package_json.js"
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jsenv/node-esm-resolution",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/jsenv/jsenv-core",
|
|
8
|
+
"directory": "packages/node-esm-resolution"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./main.js"
|
|
17
|
+
},
|
|
18
|
+
"./*": "./*"
|
|
19
|
+
},
|
|
20
|
+
"main": "./main.js",
|
|
21
|
+
"files": [
|
|
22
|
+
"/dist/",
|
|
23
|
+
"/src/",
|
|
24
|
+
"/main.js"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { lookupPackageScope } from "./lookup_package_scope.js"
|
|
2
|
+
import { readPackageJson } from "./read_package_json.js"
|
|
3
|
+
|
|
4
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/packages.html#packages_determining_module_system)
|
|
5
|
+
export const determineModuleSystem = (
|
|
6
|
+
url,
|
|
7
|
+
{ ambiguousExtensions = [".js"] } = {},
|
|
8
|
+
) => {
|
|
9
|
+
const inputTypeArgv = process.execArgv.find((argv) =>
|
|
10
|
+
argv.startsWith("--input-type="),
|
|
11
|
+
)
|
|
12
|
+
if (inputTypeArgv) {
|
|
13
|
+
const value = inputTypeArgv.slice("--input-type=".length)
|
|
14
|
+
if (value === "module") {
|
|
15
|
+
return "module"
|
|
16
|
+
}
|
|
17
|
+
if (value === "commonjs") {
|
|
18
|
+
return "commonjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const extension = extensionFromUrl(url)
|
|
22
|
+
if (extension === ".mjs") {
|
|
23
|
+
return "module"
|
|
24
|
+
}
|
|
25
|
+
if (extension === ".cjs") {
|
|
26
|
+
return "commonjs"
|
|
27
|
+
}
|
|
28
|
+
if (extension === ".json") {
|
|
29
|
+
return "json"
|
|
30
|
+
}
|
|
31
|
+
if (ambiguousExtensions.includes(extension)) {
|
|
32
|
+
const packageUrl = lookupPackageScope(url)
|
|
33
|
+
if (!packageUrl) {
|
|
34
|
+
return "commonjs"
|
|
35
|
+
}
|
|
36
|
+
const packageJson = readPackageJson(packageUrl)
|
|
37
|
+
if (packageJson.type === "module") {
|
|
38
|
+
return "module"
|
|
39
|
+
}
|
|
40
|
+
return "commonjs"
|
|
41
|
+
}
|
|
42
|
+
throw new Error("unsupported file extension")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const extensionFromUrl = (url) => {
|
|
46
|
+
const { pathname } = new URL(url)
|
|
47
|
+
const slashLastIndex = pathname.lastIndexOf("/")
|
|
48
|
+
const filename =
|
|
49
|
+
slashLastIndex === -1 ? pathname : pathname.slice(slashLastIndex + 1)
|
|
50
|
+
const dotLastIndex = filename.lastIndexOf(".")
|
|
51
|
+
if (dotLastIndex === -1) return ""
|
|
52
|
+
// if (dotLastIndex === pathname.length - 1) return ""
|
|
53
|
+
const extension = filename.slice(dotLastIndex)
|
|
54
|
+
return extension
|
|
55
|
+
}
|
package/src/errors.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/tools/node_modules/eslint/node_modules/%40babel/core/lib/vendor/import-meta-resolve.js#L2473
|
|
2
|
+
import { fileURLToPath } from "node:url"
|
|
3
|
+
|
|
4
|
+
export const createInvalidModuleSpecifierError = ({
|
|
5
|
+
specifier,
|
|
6
|
+
parentUrl,
|
|
7
|
+
reason,
|
|
8
|
+
}) => {
|
|
9
|
+
const error = new Error(
|
|
10
|
+
`Invalid module "${specifier}" ${reason} imported from ${fileURLToPath(
|
|
11
|
+
parentUrl,
|
|
12
|
+
)}`,
|
|
13
|
+
)
|
|
14
|
+
error.code = "INVALID_MODULE_SPECIFIER"
|
|
15
|
+
return error
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const createInvalidPackageTargetError = ({
|
|
19
|
+
parentUrl,
|
|
20
|
+
packageUrl,
|
|
21
|
+
target,
|
|
22
|
+
key,
|
|
23
|
+
isImport,
|
|
24
|
+
reason,
|
|
25
|
+
}) => {
|
|
26
|
+
let message
|
|
27
|
+
if (key === ".") {
|
|
28
|
+
message = `Invalid "exports" main target defined in ${fileURLToPath(
|
|
29
|
+
packageUrl,
|
|
30
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}; ${reason}`
|
|
31
|
+
} else {
|
|
32
|
+
message = `Invalid "${
|
|
33
|
+
isImport ? "imports" : "exports"
|
|
34
|
+
}" target ${JSON.stringify(target)} defined for "${key}" in ${fileURLToPath(
|
|
35
|
+
packageUrl,
|
|
36
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}; ${reason}`
|
|
37
|
+
}
|
|
38
|
+
const error = new Error(message)
|
|
39
|
+
error.code = "INVALID_PACKAGE_TARGET"
|
|
40
|
+
return error
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const createPackagePathNotExportedError = ({
|
|
44
|
+
subpath,
|
|
45
|
+
parentUrl,
|
|
46
|
+
packageUrl,
|
|
47
|
+
}) => {
|
|
48
|
+
let message
|
|
49
|
+
if (subpath === ".") {
|
|
50
|
+
message = `No "exports" main defined in ${fileURLToPath(
|
|
51
|
+
packageUrl,
|
|
52
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}`
|
|
53
|
+
} else {
|
|
54
|
+
message = `Package subpath "${subpath}" is not defined by "exports" in ${fileURLToPath(
|
|
55
|
+
packageUrl,
|
|
56
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}`
|
|
57
|
+
}
|
|
58
|
+
const error = new Error(message)
|
|
59
|
+
error.code = "PACKAGE_PATH_NOT_EXPORTED"
|
|
60
|
+
return error
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const createModuleNotFoundError = ({ specifier, parentUrl }) => {
|
|
64
|
+
const error = new Error(
|
|
65
|
+
`Cannot find "${specifier}" imported from ${fileURLToPath(parentUrl)}`,
|
|
66
|
+
)
|
|
67
|
+
error.code = "MODULE_NOT_FOUND"
|
|
68
|
+
return error
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const createPackageImportNotDefinedError = ({
|
|
72
|
+
specifier,
|
|
73
|
+
packageUrl,
|
|
74
|
+
parentUrl,
|
|
75
|
+
}) => {
|
|
76
|
+
const error = new Error(
|
|
77
|
+
`Package import specifier "${specifier}" is not defined in ${fileURLToPath(
|
|
78
|
+
packageUrl,
|
|
79
|
+
)}package.json imported from ${fileURLToPath(parentUrl)}`,
|
|
80
|
+
)
|
|
81
|
+
error.code = "PACKAGE_IMPORT_NOT_DEFINED"
|
|
82
|
+
return error
|
|
83
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { statSync } from "node:fs"
|
|
2
|
+
|
|
3
|
+
import { urlToFilename } from "./url_utils.js"
|
|
4
|
+
|
|
5
|
+
export const applyFileSystemMagicResolution = (
|
|
6
|
+
fileUrl,
|
|
7
|
+
{ magicDirectoryIndex, magicExtensions },
|
|
8
|
+
) => {
|
|
9
|
+
let lastENOENTError = null
|
|
10
|
+
const fileStatOrNull = (url) => {
|
|
11
|
+
try {
|
|
12
|
+
return statSync(new URL(url))
|
|
13
|
+
} catch (e) {
|
|
14
|
+
if (e.code === "ENOENT") {
|
|
15
|
+
lastENOENTError = e
|
|
16
|
+
return null
|
|
17
|
+
}
|
|
18
|
+
throw e
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const fileStat = fileStatOrNull(fileUrl)
|
|
23
|
+
if (fileStat && fileStat.isFile()) {
|
|
24
|
+
return {
|
|
25
|
+
found: true,
|
|
26
|
+
url: fileUrl,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (fileStat && fileStat.isDirectory()) {
|
|
30
|
+
if (magicDirectoryIndex) {
|
|
31
|
+
const indexFileSuffix = fileUrl.endsWith("/") ? "index" : "/index"
|
|
32
|
+
const indexFileUrl = `${fileUrl}${indexFileSuffix}`
|
|
33
|
+
const result = applyFileSystemMagicResolution(indexFileUrl, {
|
|
34
|
+
magicDirectoryIndex: false,
|
|
35
|
+
magicExtensions,
|
|
36
|
+
})
|
|
37
|
+
return {
|
|
38
|
+
magicDirectoryIndex: true,
|
|
39
|
+
...result,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
isDirectory: true,
|
|
44
|
+
found: true,
|
|
45
|
+
url: fileUrl,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (magicExtensions && magicExtensions.length) {
|
|
49
|
+
const parentUrl = new URL("./", fileUrl).href
|
|
50
|
+
const urlFilename = urlToFilename(fileUrl)
|
|
51
|
+
const extensionLeadingToFile = magicExtensions.find((extensionToTry) => {
|
|
52
|
+
const urlCandidate = `${parentUrl}${urlFilename}${extensionToTry}`
|
|
53
|
+
const stat = fileStatOrNull(urlCandidate)
|
|
54
|
+
return stat
|
|
55
|
+
})
|
|
56
|
+
if (extensionLeadingToFile) {
|
|
57
|
+
// magic extension worked
|
|
58
|
+
return {
|
|
59
|
+
magicExtension: extensionLeadingToFile,
|
|
60
|
+
found: true,
|
|
61
|
+
url: `${fileUrl}${extensionLeadingToFile}`,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// magic extension not found
|
|
66
|
+
return {
|
|
67
|
+
found: false,
|
|
68
|
+
url: fileUrl,
|
|
69
|
+
lastENOENTError,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
|
|
3
|
+
import { filesystemRootUrl, getParentUrl, asDirectoryUrl } from "./url_utils.js"
|
|
4
|
+
|
|
5
|
+
export const lookupPackageScope = (url) => {
|
|
6
|
+
let scopeUrl = asDirectoryUrl(url)
|
|
7
|
+
while (scopeUrl !== filesystemRootUrl) {
|
|
8
|
+
if (scopeUrl.endsWith("node_modules/")) {
|
|
9
|
+
return null
|
|
10
|
+
}
|
|
11
|
+
const packageJsonUrlObject = new URL("package.json", scopeUrl)
|
|
12
|
+
if (existsSync(packageJsonUrlObject)) {
|
|
13
|
+
return scopeUrl
|
|
14
|
+
}
|
|
15
|
+
scopeUrl = getParentUrl(scopeUrl)
|
|
16
|
+
}
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export const isSpecifierForNodeBuiltin = (specifier) => {
|
|
2
|
+
return (
|
|
3
|
+
specifier.startsWith("node:") ||
|
|
4
|
+
NODE_BUILTIN_MODULE_SPECIFIERS.includes(specifier)
|
|
5
|
+
)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const NODE_BUILTIN_MODULE_SPECIFIERS = [
|
|
9
|
+
"assert",
|
|
10
|
+
"assert/strict",
|
|
11
|
+
"async_hooks",
|
|
12
|
+
"buffer_ieee754",
|
|
13
|
+
"buffer",
|
|
14
|
+
"child_process",
|
|
15
|
+
"cluster",
|
|
16
|
+
"console",
|
|
17
|
+
"constants",
|
|
18
|
+
"crypto",
|
|
19
|
+
"_debugger",
|
|
20
|
+
"dgram",
|
|
21
|
+
"dns",
|
|
22
|
+
"domain",
|
|
23
|
+
"events",
|
|
24
|
+
"freelist",
|
|
25
|
+
"fs",
|
|
26
|
+
"fs/promises",
|
|
27
|
+
"_http_agent",
|
|
28
|
+
"_http_client",
|
|
29
|
+
"_http_common",
|
|
30
|
+
"_http_incoming",
|
|
31
|
+
"_http_outgoing",
|
|
32
|
+
"_http_server",
|
|
33
|
+
"http",
|
|
34
|
+
"http2",
|
|
35
|
+
"https",
|
|
36
|
+
"inspector",
|
|
37
|
+
"_linklist",
|
|
38
|
+
"module",
|
|
39
|
+
"net",
|
|
40
|
+
"node-inspect/lib/_inspect",
|
|
41
|
+
"node-inspect/lib/internal/inspect_client",
|
|
42
|
+
"node-inspect/lib/internal/inspect_repl",
|
|
43
|
+
"os",
|
|
44
|
+
"path",
|
|
45
|
+
"perf_hooks",
|
|
46
|
+
"process",
|
|
47
|
+
"punycode",
|
|
48
|
+
"querystring",
|
|
49
|
+
"readline",
|
|
50
|
+
"repl",
|
|
51
|
+
"smalloc",
|
|
52
|
+
"_stream_duplex",
|
|
53
|
+
"_stream_transform",
|
|
54
|
+
"_stream_wrap",
|
|
55
|
+
"_stream_passthrough",
|
|
56
|
+
"_stream_readable",
|
|
57
|
+
"_stream_writable",
|
|
58
|
+
"stream",
|
|
59
|
+
"stream/promises",
|
|
60
|
+
"string_decoder",
|
|
61
|
+
"sys",
|
|
62
|
+
"timers",
|
|
63
|
+
"_tls_common",
|
|
64
|
+
"_tls_legacy",
|
|
65
|
+
"_tls_wrap",
|
|
66
|
+
"tls",
|
|
67
|
+
"trace_events",
|
|
68
|
+
"tty",
|
|
69
|
+
"url",
|
|
70
|
+
"util",
|
|
71
|
+
"v8/tools/arguments",
|
|
72
|
+
"v8/tools/codemap",
|
|
73
|
+
"v8/tools/consarray",
|
|
74
|
+
"v8/tools/csvparser",
|
|
75
|
+
"v8/tools/logreader",
|
|
76
|
+
"v8/tools/profile_view",
|
|
77
|
+
"v8/tools/splaytree",
|
|
78
|
+
"v8",
|
|
79
|
+
"vm",
|
|
80
|
+
"worker_threads",
|
|
81
|
+
"zlib",
|
|
82
|
+
// global is special
|
|
83
|
+
"global",
|
|
84
|
+
]
|
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* https://nodejs.org/api/esm.html#resolver-algorithm-specification
|
|
3
|
+
* https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/lib/internal/modules/esm/resolve.js#L1
|
|
4
|
+
* deviations from the spec:
|
|
5
|
+
* - take into account "browser", "module" and "jsnext"
|
|
6
|
+
* - the check for isDirectory -> throw is delayed is descoped to the caller
|
|
7
|
+
* - the call to real path ->
|
|
8
|
+
* delayed to the caller so that we can decide to
|
|
9
|
+
* maintain symlink as facade url when it's outside project directory
|
|
10
|
+
* or use the real path when inside
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
13
|
+
|
|
14
|
+
import { isSpecifierForNodeBuiltin } from "./node_builtin_specifiers.js"
|
|
15
|
+
import { lookupPackageScope } from "./lookup_package_scope.js"
|
|
16
|
+
import { readPackageJson } from "./read_package_json.js"
|
|
17
|
+
import { filesystemRootUrl, getParentUrl, isValidUrl } from "./url_utils.js"
|
|
18
|
+
import {
|
|
19
|
+
createInvalidModuleSpecifierError,
|
|
20
|
+
createModuleNotFoundError,
|
|
21
|
+
createPackageImportNotDefinedError,
|
|
22
|
+
createPackagePathNotExportedError,
|
|
23
|
+
createInvalidPackageTargetError,
|
|
24
|
+
} from "./errors.js"
|
|
25
|
+
|
|
26
|
+
export const applyNodeEsmResolution = ({
|
|
27
|
+
conditions = ["node", "import"],
|
|
28
|
+
parentUrl,
|
|
29
|
+
specifier,
|
|
30
|
+
}) => {
|
|
31
|
+
const resolution = applyPackageSpecifierResolution({
|
|
32
|
+
conditions,
|
|
33
|
+
parentUrl: String(parentUrl),
|
|
34
|
+
specifier,
|
|
35
|
+
})
|
|
36
|
+
const { url } = resolution
|
|
37
|
+
if (url.startsWith("file:")) {
|
|
38
|
+
if (url.includes("%2F") || url.includes("%5C")) {
|
|
39
|
+
throw createInvalidModuleSpecifierError({
|
|
40
|
+
specifier,
|
|
41
|
+
parentUrl,
|
|
42
|
+
reason: `must not include encoded "/" or "\\" characters`,
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
return resolution
|
|
46
|
+
}
|
|
47
|
+
return resolution
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const applyPackageSpecifierResolution = ({
|
|
51
|
+
conditions,
|
|
52
|
+
parentUrl,
|
|
53
|
+
specifier,
|
|
54
|
+
}) => {
|
|
55
|
+
// relative specifier
|
|
56
|
+
if (
|
|
57
|
+
specifier[0] === "/" ||
|
|
58
|
+
specifier.startsWith("./") ||
|
|
59
|
+
specifier.startsWith("../")
|
|
60
|
+
) {
|
|
61
|
+
if (specifier[0] !== "/") {
|
|
62
|
+
const browserFieldResolution = applyBrowserFieldResolution({
|
|
63
|
+
conditions,
|
|
64
|
+
parentUrl,
|
|
65
|
+
specifier,
|
|
66
|
+
})
|
|
67
|
+
if (browserFieldResolution) {
|
|
68
|
+
return browserFieldResolution
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
type: "relative_specifier",
|
|
73
|
+
url: new URL(specifier, parentUrl).href,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (specifier[0] === "#") {
|
|
77
|
+
return applyPackageImportsResolution({
|
|
78
|
+
conditions,
|
|
79
|
+
parentUrl,
|
|
80
|
+
specifier,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const urlObject = new URL(specifier)
|
|
85
|
+
return {
|
|
86
|
+
type: "absolute_specifier",
|
|
87
|
+
url: urlObject.href,
|
|
88
|
+
}
|
|
89
|
+
} catch (e) {
|
|
90
|
+
// bare specifier
|
|
91
|
+
const browserFieldResolution = applyBrowserFieldResolution({
|
|
92
|
+
conditions,
|
|
93
|
+
parentUrl,
|
|
94
|
+
packageSpecifier: specifier,
|
|
95
|
+
})
|
|
96
|
+
if (browserFieldResolution) {
|
|
97
|
+
return browserFieldResolution
|
|
98
|
+
}
|
|
99
|
+
return applyPackageResolve({
|
|
100
|
+
conditions,
|
|
101
|
+
parentUrl,
|
|
102
|
+
packageSpecifier: specifier,
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const applyBrowserFieldResolution = ({ conditions, parentUrl, specifier }) => {
|
|
108
|
+
const browserCondition = conditions.includes("browser")
|
|
109
|
+
if (!browserCondition) {
|
|
110
|
+
return null
|
|
111
|
+
}
|
|
112
|
+
const packageUrl = lookupPackageScope(parentUrl)
|
|
113
|
+
if (!packageUrl) {
|
|
114
|
+
return null
|
|
115
|
+
}
|
|
116
|
+
const packageJson = readPackageJson(packageUrl)
|
|
117
|
+
if (!packageJson) {
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
const { browser } = packageJson
|
|
121
|
+
if (!browser) {
|
|
122
|
+
return null
|
|
123
|
+
}
|
|
124
|
+
if (typeof browser !== "object") {
|
|
125
|
+
return null
|
|
126
|
+
}
|
|
127
|
+
let url
|
|
128
|
+
if (specifier.startsWith(".")) {
|
|
129
|
+
const specifierUrl = new URL(specifier, parentUrl).href
|
|
130
|
+
const specifierRelativeToPackage = specifierUrl.slice(packageUrl.length)
|
|
131
|
+
const specifierRelativeNotation = `./${specifierRelativeToPackage}`
|
|
132
|
+
const browserMapping = browser[specifierRelativeNotation]
|
|
133
|
+
if (typeof browserMapping === "string") {
|
|
134
|
+
url = new URL(browserMapping, packageUrl).href
|
|
135
|
+
} else if (browserMapping === false) {
|
|
136
|
+
url = `file:///@ignore/${specifierUrl.slice("file:///")}`
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
const browserMapping = browser[specifier]
|
|
140
|
+
if (typeof browserMapping === "string") {
|
|
141
|
+
url = new URL(browserMapping, packageUrl).href
|
|
142
|
+
} else if (browserMapping === false) {
|
|
143
|
+
url = `file:///@ignore/${specifier}`
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (url) {
|
|
147
|
+
return {
|
|
148
|
+
type: "browser",
|
|
149
|
+
packageUrl,
|
|
150
|
+
packageJson,
|
|
151
|
+
url,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const applyPackageImportsResolution = ({
|
|
158
|
+
conditions,
|
|
159
|
+
parentUrl,
|
|
160
|
+
specifier,
|
|
161
|
+
}) => {
|
|
162
|
+
if (!specifier.startsWith("#")) {
|
|
163
|
+
throw createInvalidModuleSpecifierError({
|
|
164
|
+
specifier,
|
|
165
|
+
parentUrl,
|
|
166
|
+
reason: "internal imports must start with #",
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
if (specifier === "#" || specifier.startsWith("#/")) {
|
|
170
|
+
throw createInvalidModuleSpecifierError({
|
|
171
|
+
specifier,
|
|
172
|
+
parentUrl,
|
|
173
|
+
reason: "not a valid internal imports specifier name",
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
const packageUrl = lookupPackageScope(parentUrl)
|
|
177
|
+
if (packageUrl !== null) {
|
|
178
|
+
const packageJson = readPackageJson(packageUrl)
|
|
179
|
+
const { imports } = packageJson
|
|
180
|
+
if (imports !== null && typeof imports === "object") {
|
|
181
|
+
const resolved = applyPackageImportsExportsResolution({
|
|
182
|
+
conditions,
|
|
183
|
+
parentUrl,
|
|
184
|
+
packageUrl,
|
|
185
|
+
packageJson,
|
|
186
|
+
matchObject: imports,
|
|
187
|
+
matchKey: specifier,
|
|
188
|
+
isImports: true,
|
|
189
|
+
})
|
|
190
|
+
if (resolved) {
|
|
191
|
+
return resolved
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
throw createPackageImportNotDefinedError({
|
|
196
|
+
specifier,
|
|
197
|
+
packageUrl,
|
|
198
|
+
parentUrl,
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const applyPackageResolve = ({ conditions, parentUrl, packageSpecifier }) => {
|
|
203
|
+
if (packageSpecifier === "") {
|
|
204
|
+
throw new Error("invalid module specifier")
|
|
205
|
+
}
|
|
206
|
+
if (
|
|
207
|
+
conditions.includes("node") &&
|
|
208
|
+
isSpecifierForNodeBuiltin(packageSpecifier)
|
|
209
|
+
) {
|
|
210
|
+
return {
|
|
211
|
+
type: "node_builtin_specifier",
|
|
212
|
+
url: `node:${packageSpecifier}`,
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const { packageName, packageSubpath } =
|
|
216
|
+
parsePackageSpecifier(packageSpecifier)
|
|
217
|
+
if (
|
|
218
|
+
packageName[0] === "." ||
|
|
219
|
+
packageName.includes("\\") ||
|
|
220
|
+
packageName.includes("%")
|
|
221
|
+
) {
|
|
222
|
+
throw createInvalidModuleSpecifierError({
|
|
223
|
+
specifier: packageName,
|
|
224
|
+
parentUrl,
|
|
225
|
+
reason: `is not a valid package name`,
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
if (packageSubpath.endsWith("/")) {
|
|
229
|
+
throw new Error("invalid module specifier")
|
|
230
|
+
}
|
|
231
|
+
const selfResolution = applyPackageSelfResolution({
|
|
232
|
+
conditions,
|
|
233
|
+
parentUrl,
|
|
234
|
+
packageName,
|
|
235
|
+
packageSubpath,
|
|
236
|
+
})
|
|
237
|
+
if (selfResolution) {
|
|
238
|
+
return selfResolution
|
|
239
|
+
}
|
|
240
|
+
let currentUrl = parentUrl
|
|
241
|
+
while (currentUrl !== filesystemRootUrl) {
|
|
242
|
+
const packageUrl = new URL(`node_modules/${packageName}/`, currentUrl).href
|
|
243
|
+
if (!existsSync(new URL(packageUrl))) {
|
|
244
|
+
currentUrl = getParentUrl(currentUrl)
|
|
245
|
+
continue
|
|
246
|
+
}
|
|
247
|
+
const packageJson = readPackageJson(packageUrl)
|
|
248
|
+
if (packageJson !== null) {
|
|
249
|
+
const { exports } = packageJson
|
|
250
|
+
if (exports !== null && exports !== undefined) {
|
|
251
|
+
return applyPackageExportsResolution({
|
|
252
|
+
conditions,
|
|
253
|
+
parentUrl,
|
|
254
|
+
packageUrl,
|
|
255
|
+
packageJson,
|
|
256
|
+
packageSubpath,
|
|
257
|
+
exports,
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return applyLegacySubpathResolution({
|
|
262
|
+
conditions,
|
|
263
|
+
parentUrl,
|
|
264
|
+
packageUrl,
|
|
265
|
+
packageJson,
|
|
266
|
+
packageSubpath,
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
throw createModuleNotFoundError({
|
|
270
|
+
specifier: packageName,
|
|
271
|
+
parentUrl,
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const applyPackageSelfResolution = ({
|
|
276
|
+
conditions,
|
|
277
|
+
parentUrl,
|
|
278
|
+
packageName,
|
|
279
|
+
packageSubpath,
|
|
280
|
+
}) => {
|
|
281
|
+
const packageUrl = lookupPackageScope(parentUrl)
|
|
282
|
+
if (!packageUrl) {
|
|
283
|
+
return undefined
|
|
284
|
+
}
|
|
285
|
+
const packageJson = readPackageJson(packageUrl)
|
|
286
|
+
if (!packageJson) {
|
|
287
|
+
return undefined
|
|
288
|
+
}
|
|
289
|
+
if (packageJson.name !== packageName) {
|
|
290
|
+
return undefined
|
|
291
|
+
}
|
|
292
|
+
const { exports } = packageJson
|
|
293
|
+
if (!exports) {
|
|
294
|
+
const subpathResolution = applyLegacySubpathResolution({
|
|
295
|
+
conditions,
|
|
296
|
+
parentUrl,
|
|
297
|
+
packageUrl,
|
|
298
|
+
packageJson,
|
|
299
|
+
packageSubpath,
|
|
300
|
+
})
|
|
301
|
+
if (subpathResolution && subpathResolution.type !== "subpath") {
|
|
302
|
+
return subpathResolution
|
|
303
|
+
}
|
|
304
|
+
return undefined
|
|
305
|
+
}
|
|
306
|
+
return applyPackageExportsResolution({
|
|
307
|
+
conditions,
|
|
308
|
+
parentUrl,
|
|
309
|
+
packageUrl,
|
|
310
|
+
packageJson,
|
|
311
|
+
packageSubpath,
|
|
312
|
+
exports,
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/lib/internal/modules/esm/resolve.js#L642
|
|
317
|
+
const applyPackageExportsResolution = ({
|
|
318
|
+
conditions,
|
|
319
|
+
parentUrl,
|
|
320
|
+
packageUrl,
|
|
321
|
+
packageJson,
|
|
322
|
+
packageSubpath,
|
|
323
|
+
exports,
|
|
324
|
+
}) => {
|
|
325
|
+
const exportsInfo = readExports({ exports, packageUrl })
|
|
326
|
+
if (packageSubpath === ".") {
|
|
327
|
+
const mainExport = applyMainExportResolution({ exports, exportsInfo })
|
|
328
|
+
if (!mainExport) {
|
|
329
|
+
throw createPackagePathNotExportedError({
|
|
330
|
+
subpath: packageSubpath,
|
|
331
|
+
parentUrl,
|
|
332
|
+
packageUrl,
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
const resolved = applyPackageTargetResolution({
|
|
336
|
+
conditions,
|
|
337
|
+
parentUrl,
|
|
338
|
+
packageUrl,
|
|
339
|
+
packageJson,
|
|
340
|
+
key: ".",
|
|
341
|
+
target: mainExport,
|
|
342
|
+
})
|
|
343
|
+
if (resolved) {
|
|
344
|
+
return resolved
|
|
345
|
+
}
|
|
346
|
+
throw createPackagePathNotExportedError({
|
|
347
|
+
subpath: packageSubpath,
|
|
348
|
+
parentUrl,
|
|
349
|
+
packageUrl,
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
if (exportsInfo.type === "object" && exportsInfo.allKeysAreRelative) {
|
|
353
|
+
const resolved = applyPackageImportsExportsResolution({
|
|
354
|
+
conditions,
|
|
355
|
+
parentUrl,
|
|
356
|
+
packageUrl,
|
|
357
|
+
packageJson,
|
|
358
|
+
matchObject: exports,
|
|
359
|
+
matchKey: packageSubpath,
|
|
360
|
+
isImports: false,
|
|
361
|
+
})
|
|
362
|
+
if (resolved) {
|
|
363
|
+
return resolved
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
throw createPackagePathNotExportedError({
|
|
367
|
+
subpath: packageSubpath,
|
|
368
|
+
parentUrl,
|
|
369
|
+
packageUrl,
|
|
370
|
+
})
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const applyPackageImportsExportsResolution = ({
|
|
374
|
+
conditions,
|
|
375
|
+
parentUrl,
|
|
376
|
+
packageUrl,
|
|
377
|
+
packageJson,
|
|
378
|
+
matchObject,
|
|
379
|
+
matchKey,
|
|
380
|
+
isImports,
|
|
381
|
+
}) => {
|
|
382
|
+
if (!matchKey.includes("*") && matchObject.hasOwnProperty(matchKey)) {
|
|
383
|
+
const target = matchObject[matchKey]
|
|
384
|
+
return applyPackageTargetResolution({
|
|
385
|
+
conditions,
|
|
386
|
+
parentUrl,
|
|
387
|
+
packageUrl,
|
|
388
|
+
packageJson,
|
|
389
|
+
key: matchKey,
|
|
390
|
+
target,
|
|
391
|
+
internal: isImports,
|
|
392
|
+
})
|
|
393
|
+
}
|
|
394
|
+
const expansionKeys = Object.keys(matchObject)
|
|
395
|
+
.filter((key) => key.split("*").length === 2)
|
|
396
|
+
.sort(comparePatternKeys)
|
|
397
|
+
for (const expansionKey of expansionKeys) {
|
|
398
|
+
const [patternBase, patternTrailer] = expansionKey.split("*")
|
|
399
|
+
if (matchKey === patternBase) continue
|
|
400
|
+
if (!matchKey.startsWith(patternBase)) continue
|
|
401
|
+
if (patternTrailer.length > 0) {
|
|
402
|
+
if (!matchKey.endsWith(patternTrailer)) continue
|
|
403
|
+
if (matchKey.length < expansionKey.length) continue
|
|
404
|
+
}
|
|
405
|
+
const target = matchObject[expansionKey]
|
|
406
|
+
const subpath = matchKey.slice(
|
|
407
|
+
patternBase.length,
|
|
408
|
+
matchKey.length - patternTrailer.length,
|
|
409
|
+
)
|
|
410
|
+
return applyPackageTargetResolution({
|
|
411
|
+
conditions,
|
|
412
|
+
parentUrl,
|
|
413
|
+
packageUrl,
|
|
414
|
+
packageJson,
|
|
415
|
+
key: matchKey,
|
|
416
|
+
target,
|
|
417
|
+
subpath,
|
|
418
|
+
pattern: true,
|
|
419
|
+
internal: isImports,
|
|
420
|
+
})
|
|
421
|
+
}
|
|
422
|
+
return null
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const applyPackageTargetResolution = ({
|
|
426
|
+
conditions,
|
|
427
|
+
parentUrl,
|
|
428
|
+
packageUrl,
|
|
429
|
+
packageJson,
|
|
430
|
+
key,
|
|
431
|
+
target,
|
|
432
|
+
subpath = "",
|
|
433
|
+
pattern = false,
|
|
434
|
+
internal = false,
|
|
435
|
+
}) => {
|
|
436
|
+
if (typeof target === "string") {
|
|
437
|
+
if (pattern === false && subpath !== "" && !target.endsWith("/")) {
|
|
438
|
+
throw new Error("invalid module specifier")
|
|
439
|
+
}
|
|
440
|
+
if (target.startsWith("./")) {
|
|
441
|
+
const targetUrl = new URL(target, packageUrl).href
|
|
442
|
+
if (!targetUrl.startsWith(packageUrl)) {
|
|
443
|
+
throw createInvalidPackageTargetError({
|
|
444
|
+
parentUrl,
|
|
445
|
+
packageUrl,
|
|
446
|
+
target,
|
|
447
|
+
key,
|
|
448
|
+
isImport: internal,
|
|
449
|
+
reason: `target must be inside package`,
|
|
450
|
+
})
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
type: internal ? "imports_subpath" : "exports_subpath",
|
|
454
|
+
packageUrl,
|
|
455
|
+
packageJson,
|
|
456
|
+
url: pattern
|
|
457
|
+
? targetUrl.replaceAll("*", subpath)
|
|
458
|
+
: new URL(subpath, targetUrl).href,
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (!internal || target.startsWith("../") || isValidUrl(target)) {
|
|
462
|
+
throw createInvalidPackageTargetError({
|
|
463
|
+
parentUrl,
|
|
464
|
+
packageUrl,
|
|
465
|
+
target,
|
|
466
|
+
key,
|
|
467
|
+
isImport: internal,
|
|
468
|
+
reason: `target must starst with "./"`,
|
|
469
|
+
})
|
|
470
|
+
}
|
|
471
|
+
return applyPackageResolve({
|
|
472
|
+
conditions,
|
|
473
|
+
parentUrl: packageUrl,
|
|
474
|
+
packageSpecifier: pattern
|
|
475
|
+
? target.replaceAll("*", subpath)
|
|
476
|
+
: `${target}${subpath}`,
|
|
477
|
+
})
|
|
478
|
+
}
|
|
479
|
+
if (Array.isArray(target)) {
|
|
480
|
+
if (target.length === 0) {
|
|
481
|
+
return null
|
|
482
|
+
}
|
|
483
|
+
let lastResult
|
|
484
|
+
let i = 0
|
|
485
|
+
while (i < target.length) {
|
|
486
|
+
const targetValue = target[i]
|
|
487
|
+
i++
|
|
488
|
+
try {
|
|
489
|
+
const resolved = applyPackageTargetResolution({
|
|
490
|
+
conditions,
|
|
491
|
+
parentUrl,
|
|
492
|
+
packageUrl,
|
|
493
|
+
packageJson,
|
|
494
|
+
key: `${key}[${i}]`,
|
|
495
|
+
target: targetValue,
|
|
496
|
+
subpath,
|
|
497
|
+
pattern,
|
|
498
|
+
internal,
|
|
499
|
+
})
|
|
500
|
+
if (resolved) {
|
|
501
|
+
return resolved
|
|
502
|
+
}
|
|
503
|
+
lastResult = resolved
|
|
504
|
+
} catch (e) {
|
|
505
|
+
if (e.code === "INVALID_PACKAGE_TARGET") {
|
|
506
|
+
continue
|
|
507
|
+
}
|
|
508
|
+
lastResult = e
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (lastResult) {
|
|
512
|
+
throw lastResult
|
|
513
|
+
}
|
|
514
|
+
return null
|
|
515
|
+
}
|
|
516
|
+
if (target === null) {
|
|
517
|
+
return null
|
|
518
|
+
}
|
|
519
|
+
if (typeof target === "object") {
|
|
520
|
+
const keys = Object.keys(target)
|
|
521
|
+
for (const key of keys) {
|
|
522
|
+
if (Number.isInteger(key)) {
|
|
523
|
+
throw new Error("Invalid package configuration")
|
|
524
|
+
}
|
|
525
|
+
if (key === "default" || conditions.includes(key)) {
|
|
526
|
+
const targetValue = target[key]
|
|
527
|
+
const resolved = applyPackageTargetResolution({
|
|
528
|
+
conditions,
|
|
529
|
+
parentUrl,
|
|
530
|
+
packageUrl,
|
|
531
|
+
packageJson,
|
|
532
|
+
key,
|
|
533
|
+
target: targetValue,
|
|
534
|
+
subpath,
|
|
535
|
+
pattern,
|
|
536
|
+
internal,
|
|
537
|
+
})
|
|
538
|
+
if (resolved) {
|
|
539
|
+
return resolved
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return null
|
|
544
|
+
}
|
|
545
|
+
throw createInvalidPackageTargetError({
|
|
546
|
+
parentUrl,
|
|
547
|
+
packageUrl,
|
|
548
|
+
target,
|
|
549
|
+
key,
|
|
550
|
+
isImport: internal,
|
|
551
|
+
reason: `target must be a string, array, object or null`,
|
|
552
|
+
})
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const readExports = ({ exports, packageUrl }) => {
|
|
556
|
+
if (Array.isArray(exports)) {
|
|
557
|
+
return {
|
|
558
|
+
type: "array",
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (exports === null) {
|
|
562
|
+
return {}
|
|
563
|
+
}
|
|
564
|
+
if (typeof exports === "object") {
|
|
565
|
+
const keys = Object.keys(exports)
|
|
566
|
+
const relativeKeys = []
|
|
567
|
+
const conditionalKeys = []
|
|
568
|
+
keys.forEach((availableKey) => {
|
|
569
|
+
if (availableKey.startsWith(".")) {
|
|
570
|
+
relativeKeys.push(availableKey)
|
|
571
|
+
} else {
|
|
572
|
+
conditionalKeys.push(availableKey)
|
|
573
|
+
}
|
|
574
|
+
})
|
|
575
|
+
const hasRelativeKey = relativeKeys.length > 0
|
|
576
|
+
if (hasRelativeKey && conditionalKeys.length > 0) {
|
|
577
|
+
throw new Error(
|
|
578
|
+
`Invalid package configuration: cannot mix relative and conditional keys in package.exports
|
|
579
|
+
--- unexpected keys ---
|
|
580
|
+
${conditionalKeys.map((key) => `"${key}"`).join("\n")}
|
|
581
|
+
--- package.json ---
|
|
582
|
+
${packageUrl}`,
|
|
583
|
+
)
|
|
584
|
+
}
|
|
585
|
+
return {
|
|
586
|
+
type: "object",
|
|
587
|
+
hasRelativeKey,
|
|
588
|
+
allKeysAreRelative: relativeKeys.length === keys.length,
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (typeof exports === "string") {
|
|
592
|
+
return { type: "string" }
|
|
593
|
+
}
|
|
594
|
+
return {}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
const parsePackageSpecifier = (packageSpecifier) => {
|
|
598
|
+
if (packageSpecifier[0] === "@") {
|
|
599
|
+
const firstSlashIndex = packageSpecifier.indexOf("/")
|
|
600
|
+
if (firstSlashIndex === -1) {
|
|
601
|
+
throw new Error("invalid module specifier")
|
|
602
|
+
}
|
|
603
|
+
const secondSlashIndex = packageSpecifier.indexOf("/", firstSlashIndex + 1)
|
|
604
|
+
if (secondSlashIndex === -1) {
|
|
605
|
+
return {
|
|
606
|
+
packageName: packageSpecifier,
|
|
607
|
+
packageSubpath: ".",
|
|
608
|
+
isScoped: true,
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const packageName = packageSpecifier.slice(0, secondSlashIndex)
|
|
612
|
+
const afterSecondSlash = packageSpecifier.slice(secondSlashIndex + 1)
|
|
613
|
+
const packageSubpath = `./${afterSecondSlash}`
|
|
614
|
+
return {
|
|
615
|
+
packageName,
|
|
616
|
+
packageSubpath,
|
|
617
|
+
isScoped: true,
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const firstSlashIndex = packageSpecifier.indexOf("/")
|
|
621
|
+
if (firstSlashIndex === -1) {
|
|
622
|
+
return {
|
|
623
|
+
packageName: packageSpecifier,
|
|
624
|
+
packageSubpath: ".",
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
const packageName = packageSpecifier.slice(0, firstSlashIndex)
|
|
628
|
+
const afterFirstSlash = packageSpecifier.slice(firstSlashIndex + 1)
|
|
629
|
+
const packageSubpath = `./${afterFirstSlash}`
|
|
630
|
+
return {
|
|
631
|
+
packageName,
|
|
632
|
+
packageSubpath,
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const applyMainExportResolution = ({ exports, exportsInfo }) => {
|
|
637
|
+
if (exportsInfo.type === "array" || exportsInfo.type === "string") {
|
|
638
|
+
return exports
|
|
639
|
+
}
|
|
640
|
+
if (exportsInfo.type === "object") {
|
|
641
|
+
if (exportsInfo.hasRelativeKey) {
|
|
642
|
+
return exports["."]
|
|
643
|
+
}
|
|
644
|
+
return exports
|
|
645
|
+
}
|
|
646
|
+
return undefined
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const applyLegacySubpathResolution = ({
|
|
650
|
+
conditions,
|
|
651
|
+
parentUrl,
|
|
652
|
+
packageUrl,
|
|
653
|
+
packageJson,
|
|
654
|
+
packageSubpath,
|
|
655
|
+
}) => {
|
|
656
|
+
if (packageSubpath === ".") {
|
|
657
|
+
return applyLegacyMainResolution({
|
|
658
|
+
conditions,
|
|
659
|
+
packageUrl,
|
|
660
|
+
packageJson,
|
|
661
|
+
})
|
|
662
|
+
}
|
|
663
|
+
const browserFieldResolution = applyBrowserFieldResolution({
|
|
664
|
+
conditions,
|
|
665
|
+
parentUrl,
|
|
666
|
+
specifier: packageSubpath,
|
|
667
|
+
})
|
|
668
|
+
if (browserFieldResolution) {
|
|
669
|
+
return browserFieldResolution
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
type: "subpath",
|
|
673
|
+
packageUrl,
|
|
674
|
+
packageJson,
|
|
675
|
+
url: new URL(packageSubpath, packageUrl).href,
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const applyLegacyMainResolution = ({ conditions, packageUrl, packageJson }) => {
|
|
680
|
+
for (const condition of conditions) {
|
|
681
|
+
const resolved = mainLegacyResolvers[condition](packageJson, packageUrl)
|
|
682
|
+
if (resolved) {
|
|
683
|
+
return {
|
|
684
|
+
type: resolved.type,
|
|
685
|
+
packageUrl,
|
|
686
|
+
packageJson,
|
|
687
|
+
url: new URL(resolved.path, packageUrl).href,
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
type: "default",
|
|
693
|
+
packageUrl,
|
|
694
|
+
packageJson,
|
|
695
|
+
url: new URL("index.js", packageUrl).href,
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const mainLegacyResolvers = {
|
|
699
|
+
import: ({ module, jsnext }) => {
|
|
700
|
+
if (typeof module === "string") {
|
|
701
|
+
return { type: "module", path: module }
|
|
702
|
+
}
|
|
703
|
+
if (typeof jsnext === "string") {
|
|
704
|
+
return { type: "jsnext", path: jsnext }
|
|
705
|
+
}
|
|
706
|
+
return null
|
|
707
|
+
},
|
|
708
|
+
browser: ({ browser, module }, packageUrl) => {
|
|
709
|
+
const browserMain =
|
|
710
|
+
typeof browser === "string"
|
|
711
|
+
? browser
|
|
712
|
+
: typeof browser === "object" && browser !== null
|
|
713
|
+
? browser["."]
|
|
714
|
+
: ""
|
|
715
|
+
if (!browserMain) {
|
|
716
|
+
return null
|
|
717
|
+
}
|
|
718
|
+
if (typeof module !== "string" || module === browserMain) {
|
|
719
|
+
return {
|
|
720
|
+
type: "browser",
|
|
721
|
+
path: browserMain,
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
const browserMainUrlObject = new URL(browserMain, packageUrl)
|
|
725
|
+
const content = readFileSync(browserMainUrlObject, "utf-8")
|
|
726
|
+
if (
|
|
727
|
+
(/typeof exports\s*==/.test(content) &&
|
|
728
|
+
/typeof module\s*==/.test(content)) ||
|
|
729
|
+
/module\.exports\s*=/.test(content)
|
|
730
|
+
) {
|
|
731
|
+
return {
|
|
732
|
+
type: "module",
|
|
733
|
+
path: module,
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return {
|
|
737
|
+
type: "browser",
|
|
738
|
+
path: browserMain,
|
|
739
|
+
}
|
|
740
|
+
},
|
|
741
|
+
node: ({ main }) => {
|
|
742
|
+
if (typeof main === "string") {
|
|
743
|
+
return {
|
|
744
|
+
type: "main",
|
|
745
|
+
path: main,
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return null
|
|
749
|
+
},
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const comparePatternKeys = (keyA, keyB) => {
|
|
753
|
+
if (!keyA.endsWith("/") && !keyA.contains("*")) {
|
|
754
|
+
throw new Error("Invalid package configuration")
|
|
755
|
+
}
|
|
756
|
+
if (!keyB.endsWith("/") && !keyB.contains("*")) {
|
|
757
|
+
throw new Error("Invalid package configuration")
|
|
758
|
+
}
|
|
759
|
+
const aStarIndex = keyA.indexOf("*")
|
|
760
|
+
const baseLengthA = aStarIndex > -1 ? aStarIndex + 1 : keyA.length
|
|
761
|
+
const bStarIndex = keyB.indexOf("*")
|
|
762
|
+
const baseLengthB = bStarIndex > -1 ? bStarIndex + 1 : keyB.length
|
|
763
|
+
if (baseLengthA > baseLengthB) {
|
|
764
|
+
return -1
|
|
765
|
+
}
|
|
766
|
+
if (baseLengthB > baseLengthA) {
|
|
767
|
+
return 1
|
|
768
|
+
}
|
|
769
|
+
if (aStarIndex === -1) {
|
|
770
|
+
return 1
|
|
771
|
+
}
|
|
772
|
+
if (bStarIndex === -1) {
|
|
773
|
+
return -1
|
|
774
|
+
}
|
|
775
|
+
if (keyA.length > keyB.length) {
|
|
776
|
+
return -1
|
|
777
|
+
}
|
|
778
|
+
if (keyB.length > keyA.length) {
|
|
779
|
+
return 1
|
|
780
|
+
}
|
|
781
|
+
return 0
|
|
782
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
|
|
3
|
+
export const readPackageJson = (packageUrl) => {
|
|
4
|
+
const packageJsonUrl = new URL("package.json", packageUrl)
|
|
5
|
+
const buffer = readFileSync(packageJsonUrl)
|
|
6
|
+
const string = String(buffer)
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(string)
|
|
9
|
+
} catch (e) {
|
|
10
|
+
throw new Error(`Invalid package configuration`)
|
|
11
|
+
}
|
|
12
|
+
}
|
package/src/url_utils.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const filesystemRootUrl =
|
|
2
|
+
process.platform === "win32" ? `file///${process.cwd()[0]}:/` : "file:///"
|
|
3
|
+
|
|
4
|
+
export const asDirectoryUrl = (url) => {
|
|
5
|
+
const { pathname } = new URL(url)
|
|
6
|
+
if (pathname.endsWith("/")) {
|
|
7
|
+
return url
|
|
8
|
+
}
|
|
9
|
+
return new URL("./", url).href
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const getParentUrl = (url) => {
|
|
13
|
+
return new URL(url.endsWith("/") ? "../" : "./", url).href
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const isValidUrl = (url) => {
|
|
17
|
+
try {
|
|
18
|
+
// eslint-disable-next-line no-new
|
|
19
|
+
new URL(url)
|
|
20
|
+
return true
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const urlToFilename = (url) => {
|
|
27
|
+
const { pathname } = new URL(url)
|
|
28
|
+
const pathnameBeforeLastSlash = pathname.endsWith("/")
|
|
29
|
+
? pathname.slice(0, -1)
|
|
30
|
+
: pathname
|
|
31
|
+
const slashLastIndex = pathnameBeforeLastSlash.lastIndexOf("/")
|
|
32
|
+
const filename =
|
|
33
|
+
slashLastIndex === -1
|
|
34
|
+
? pathnameBeforeLastSlash
|
|
35
|
+
: pathnameBeforeLastSlash.slice(slashLastIndex + 1)
|
|
36
|
+
return filename
|
|
37
|
+
}
|