Skip to content

Commit

Permalink
Backport changes from Node
Browse files Browse the repository at this point in the history
This doesn’t really change anything, just a different error
and some refactoring.

f13dbfd43a044fcd71adb88ab437274d59a8e362
416333fd503970227522602d5728d1ab31c81e3c
a717fa211194b735cdfc1044c7351c6cf96b8783
3fbe1579ce0c9842250f01392dcffc0c15af1924
f4a0a3b04b92596fd2cf40e1d9c5a003e81c77b8
651fa047c1035416c2992989a04a84cc5425465b
fc2862b7f55bbc0e54092423e5272c213df0ed32
ad0bcb9c0276d21869b09a38b0e8bed549d4367d
075c95f61f2be5b8571fe4c6fcf9c3744d0188a9
d6b57f6629da5616362be60ae0f3e9510a7f700e
a596af0819aeb9d607a722eaaa5a06aa9b22caec
  • Loading branch information
wooorm committed Apr 29, 2024
1 parent 80d8920 commit dbb53a5
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 116 deletions.
6 changes: 6 additions & 0 deletions lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@ codes.ERR_UNSUPPORTED_DIR_IMPORT = createError(
Error
)

codes.ERR_UNSUPPORTED_RESOLVE_REQUEST = createError(
'ERR_UNSUPPORTED_RESOLVE_REQUEST',
'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.',
TypeError
)

codes.ERR_UNKNOWN_FILE_EXTENSION = createError(
'ERR_UNKNOWN_FILE_EXTENSION',
/**
Expand Down
6 changes: 3 additions & 3 deletions lib/get-format.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Manually “tree shaken” from:
// <https://github.com/nodejs/node/blob/45f5c9b/lib/internal/modules/esm/get_format.js>
// Last checked on: Nov 2, 2023.
// <https://github.com/nodejs/node/blob/7c3dce0/lib/internal/modules/esm/get_format.js>
// Last checked on: Apr 29, 2023.

This comment has been minimized.

Copy link
@Jason3S

Jason3S Apr 30, 2024

2024?

This comment has been minimized.

Copy link
@wooorm

wooorm Apr 30, 2024

Author Owner

good point

This comment has been minimized.

Copy link
@wooorm

wooorm Apr 30, 2024

Author Owner

Fixed, thanks!

This comment has been minimized.

Copy link
@Jason3S

Jason3S Apr 30, 2024

Thank you for this package.


import {fileURLToPath} from 'node:url'
import {getPackageType} from './resolve-get-package-type.js'
import {getPackageType} from './package-json-reader.js'
import {codes} from './errors.js'

const {ERR_UNKNOWN_FILE_EXTENSION} = codes
Expand Down
55 changes: 0 additions & 55 deletions lib/package-config.js

This file was deleted.

70 changes: 59 additions & 11 deletions lib/package-json-reader.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Manually “tree shaken” from:
// <https://github.com/nodejs/node/blob/45f5c9b/lib/internal/modules/package_json_reader.js>
// Last checked on: Nov 2, 2023.
// <https://github.com/nodejs/node/blob/7c3dce0/lib/internal/modules/package_json_reader.js>
// Last checked on: Apr 29, 2023.
// Removed the native dependency.
// Also: no need to cache, we do that in resolve already.

Expand All @@ -12,11 +12,11 @@
* @typedef PackageConfig
* @property {string} pjsonPath
* @property {boolean} exists
* @property {string | undefined} main
* @property {string | undefined} name
* @property {string | undefined} [main]
* @property {string | undefined} [name]
* @property {PackageType} type
* @property {Record<string, unknown> | undefined} exports
* @property {Record<string, unknown> | undefined} imports
* @property {Record<string, unknown> | undefined} [exports]
* @property {Record<string, unknown> | undefined} [imports]
*/

import fs from 'node:fs'
Expand All @@ -31,15 +31,12 @@ const {ERR_INVALID_PACKAGE_CONFIG} = codes
/** @type {Map<string, PackageConfig>} */
const cache = new Map()

const reader = {read}
export default reader

/**
* @param {string} jsonPath
* @param {{specifier: URL | string, base?: URL}} options
* @returns {PackageConfig}
*/
function read(jsonPath, {base, specifier}) {
export function read(jsonPath, {base, specifier}) {
const existing = cache.get(jsonPath)

if (existing) {
Expand Down Expand Up @@ -83,7 +80,6 @@ function read(jsonPath, {base, specifier}) {
(base ? `"${specifier}" from ` : '') + fileURLToPath(base || specifier),
cause.message
)
// @ts-expect-error: fine.
error.cause = cause
throw error
}
Expand Down Expand Up @@ -127,3 +123,55 @@ function read(jsonPath, {base, specifier}) {

return result
}

/**
* @param {URL | string} resolved
* @returns {PackageConfig}
*/
export function getPackageScopeConfig(resolved) {
// Note: in Node, this is now a native module.
let packageJSONUrl = new URL('package.json', resolved)

while (true) {
const packageJSONPath = packageJSONUrl.pathname
if (packageJSONPath.endsWith('node_modules/package.json')) {
break
}

const packageConfig = read(fileURLToPath(packageJSONUrl), {
specifier: resolved
})

if (packageConfig.exists) {
return packageConfig
}

const lastPackageJSONUrl = packageJSONUrl
packageJSONUrl = new URL('../package.json', packageJSONUrl)

// Terminates at root where ../package.json equals ../../package.json
// (can't just check "/package.json" for Windows support).
if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) {
break
}
}

const packageJSONPath = fileURLToPath(packageJSONUrl)
// ^^ Note: in Node, this is now a native module.

return {
pjsonPath: packageJSONPath,
exists: false,
type: 'none'
}
}

/**
* Returns the package type for a given URL.
* @param {URL} url - The URL to get the package type for.
* @returns {PackageType}
*/
export function getPackageType(url) {
// To do @anonrig: Write a C++ function that returns only "type".
return getPackageScopeConfig(url).type
}
23 changes: 0 additions & 23 deletions lib/resolve-get-package-type.js

This file was deleted.

61 changes: 40 additions & 21 deletions lib/resolve.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Manually “tree shaken” from:
// <https://github.com/nodejs/node/blob/45f5c9b/lib/internal/modules/esm/resolve.js>
// Last checked on: Nov 2, 2023.
// <https://github.com/nodejs/node/blob/81a9a97/lib/internal/modules/esm/resolve.js>
// Last checked on: Apr 29, 2023.

/**
* @typedef {import('./errors.js').ErrnoException} ErrnoException
* @typedef {import('./package-config.js').PackageConfig} PackageConfig
* @typedef {import('./package-json-reader.js').PackageConfig} PackageConfig
*/

import assert from 'node:assert'
Expand All @@ -15,8 +15,7 @@ import path from 'node:path'
import {builtinModules} from 'node:module'
import {defaultGetFormatWithoutErrors} from './get-format.js'
import {codes} from './errors.js'
import {getPackageScopeConfig} from './package-config.js'
import packageJsonReader from './package-json-reader.js'
import {getPackageScopeConfig, read} from './package-json-reader.js'
import {getConditionsSet} from './utils.js'

const RegExpPrototypeSymbolReplace = RegExp.prototype[Symbol.replace]
Expand All @@ -29,7 +28,8 @@ const {
ERR_MODULE_NOT_FOUND,
ERR_PACKAGE_IMPORT_NOT_DEFINED,
ERR_PACKAGE_PATH_NOT_EXPORTED,
ERR_UNSUPPORTED_DIR_IMPORT
ERR_UNSUPPORTED_DIR_IMPORT,
ERR_UNSUPPORTED_RESOLVE_REQUEST
} = codes

const own = {}.hasOwnProperty
Expand Down Expand Up @@ -909,10 +909,6 @@ function packageImportsResolve(name, base, conditions) {
throw importNotDefined(name, packageJsonUrl, base)
}

// Note: In Node.js, `getPackageType` is here.
// To prevent a circular dependency, we move it to
// `resolve-get-package-type.js`.

/**
* @param {string} specifier
* @param {URL} base
Expand Down Expand Up @@ -1013,10 +1009,7 @@ function packageResolve(specifier, base, conditions) {
}

// Package match.
const packageConfig = packageJsonReader.read(packageJsonPath, {
base,
specifier
})
const packageConfig = read(packageJsonPath, {base, specifier})
if (packageConfig.exports !== undefined && packageConfig.exports !== null) {
return packageExportsResolve(
packageJsonUrl,
Expand Down Expand Up @@ -1082,24 +1075,38 @@ function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) {
* A URL object to the found thing.
*/
export function moduleResolve(specifier, base, conditions, preserveSymlinks) {
// Note: The Node code supports `base` as a string (in this internal API) too,
// we don’t.
const protocol = base.protocol
const isRemote = protocol === 'http:' || protocol === 'https:'
const isData = protocol === 'data:'
const isRemote = isData || protocol === 'http:' || protocol === 'https:'
// Order swapped from spec for minor perf gain.
// Ok since relative URLs cannot parse as URLs.
/** @type {URL | undefined} */
let resolved

if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
resolved = new URL(specifier, base)
} else if (!isRemote && specifier[0] === '#') {
try {
resolved = new URL(specifier, base)
} catch (error_) {
const error = new ERR_UNSUPPORTED_RESOLVE_REQUEST(specifier, base)
error.cause = error_
throw error
}
} else if (protocol === 'file:' && specifier[0] === '#') {
resolved = packageImportsResolve(specifier, base, conditions)
} else {
try {
resolved = new URL(specifier)
} catch {
if (!isRemote) {
resolved = packageResolve(specifier, base, conditions)
} catch (error_) {
// Note: actual code uses `canBeRequiredWithoutScheme`.
if (isRemote && !builtinModules.includes(specifier)) {
const error = new ERR_UNSUPPORTED_RESOLVE_REQUEST(specifier, base)
error.cause = error_
throw error
}

resolved = packageResolve(specifier, base, conditions)
}
}

Expand Down Expand Up @@ -1232,13 +1239,16 @@ export function defaultResolve(specifier, context = {}) {

/** @type {URL | undefined} */
let parsed
/** @type {string | undefined} */
let protocol

try {
parsed = shouldBeTreatedAsRelativeOrAbsolutePath(specifier)
? new URL(specifier, parsedParentURL)
: new URL(specifier)

// Avoid accessing the `protocol` property due to the lazy getters.
const protocol = parsed.protocol
protocol = parsed.protocol

if (protocol === 'data:') {
return {url: parsed.href, format: null}
Expand All @@ -1258,6 +1268,15 @@ export function defaultResolve(specifier, context = {}) {

if (maybeReturn) return maybeReturn

// This must come after checkIfDisallowedImport
if (protocol === undefined && parsed) {
protocol = parsed.protocol
}

if (protocol === 'node:') {
return {url: specifier}
}

// This must come after checkIfDisallowedImport
if (parsed && parsed.protocol === 'node:') return {url: specifier}

Expand Down
4 changes: 2 additions & 2 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Manually “tree shaken” from:
// <https://github.com/nodejs/node/blob/45f5c9b/lib/internal/modules/esm/utils.js>
// Last checked on: Nov 2, 2023.
// <https://github.com/nodejs/node/blob/81a9a97/lib/internal/modules/esm/utils.js>
// Last checked on: Apr 29, 2023.

import {codes} from './errors.js'

Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"emitDeclarationOnly": true,
"exactOptionalPropertyTypes": true,
"forceConsistentCasingInFileNames": true,
"lib": ["es2020"],
"lib": ["es2022"],
"module": "node16",
"newLine": "lf",
"strict": true,
Expand Down

0 comments on commit dbb53a5

Please sign in to comment.