diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2fdbfc2..686bbbf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,5 +20,5 @@ jobs: - ubuntu-latest - windows-latest node: - - lts/hydrogen + - 18 - node diff --git a/.gitignore b/.gitignore index cd8a172..ac6d815 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ *.log coverage/ /node_modules -test/baseline.js +test/baseline*.js yarn.lock diff --git a/index.js b/index.js index 272bf1d..55e0964 100644 --- a/index.js +++ b/index.js @@ -15,11 +15,11 @@ import {defaultResolve} from './lib/resolve.js' * @param {string} parent * The absolute parent module URL to resolve from. * You should pass `import.meta.url` or something else. - * @returns {Promise} - * Returns a promise that resolves to a full `file:`, `data:`, or `node:` URL + * @returns {string} + * Returns a string to a full `file:`, `data:`, or `node:` URL * to the found thing. */ -export async function resolve(specifier, parent) { +export function resolve(specifier, parent) { if (!parent) { throw new Error( 'Please pass `parent`: `import-meta-resolve` cannot ponyfill that' @@ -31,10 +31,14 @@ export async function resolve(specifier, parent) { } catch (error) { const exception = /** @type {ErrnoException} */ (error) - return exception.code === 'ERR_UNSUPPORTED_DIR_IMPORT' && + if ( + exception.code === 'ERR_UNSUPPORTED_DIR_IMPORT' && typeof exception.url === 'string' - ? exception.url - : Promise.reject(error) + ) { + return exception.url + } + + throw error } } diff --git a/package.json b/package.json index 06fcf06..1aa0cf7 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "generate": "node --conditions development script.js", "build": "tsc --build --clean && tsc --build && type-coverage", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", - "test-api": "node --experimental-import-meta-resolve test/baseline.js && node test/index.js", + "test-api": "node --experimental-import-meta-resolve test/baseline.js && node --experimental-import-meta-resolve test/baseline-async.js && node test/index.js", "test-coverage": "c8 --check-coverage --branches 75 --functions 75 --lines 75 --statements 75 --reporter lcov npm run test-api", "test": "npm run generate && npm run build && npm run format && npm run test-coverage" }, diff --git a/script.js b/script.js index 7383a2b..cfade1b 100644 --- a/script.js +++ b/script.js @@ -1,10 +1,32 @@ import path from 'node:path' import fs from 'node:fs/promises' -const base = await fs.readFile(path.join('test', 'core.js')) +const base = await fs.readFile(path.join('test', 'core.js'), 'utf8') -const lines = String(base) - .replace(/\bresolve(?=\(|,)/g, 'import.meta.resolve') - .split('\n') +const asyncLines = base + // Special baseline test for Node < 20, that doesn't support sync `import.meta.resolve` + .replace(/\bresolve(?=\()/g, 'await import.meta.resolve') + .replace(/\bresolve(?=,)/g, 'import.meta.resolve') + .replace( + /const run = .*$/g, + 'const run = async (/** @type {() => Promise} */ f) => f()' + ) + .replace(/run\(/g, 'await run(async ') -await fs.writeFile(path.join('test', 'baseline.js'), lines.join('\n')) +await fs.writeFile(path.join('test', 'baseline-async.js'), asyncLines) + +const syncLines = base + // Node < 20 does not support sync import.meta.resolve, so skipping these tests if so + .replace(/\bresolve(?=\()/g, 'import.meta.resolve') + .replace(/\bresolve(?=,)/g, 'import.meta.resolve') + .replace( + '{skip: false}', + "{skip: semver.lt(process.versions.node, '20.0.0')}" + ) + .replace( + /const run = .*$/g, + 'const run = (/** @type {() => void} */ f) => f()' + ) + .replace(/run\(/g, 'run(async ') + +await fs.writeFile(path.join('test', 'baseline.js'), syncLines) diff --git a/test/core.js b/test/core.js index d26198d..eb196b9 100644 --- a/test/core.js +++ b/test/core.js @@ -11,617 +11,597 @@ import semver from 'semver' import {resolve} from '../index.js' const windows = process.platform === 'win32' -const veryOldNode = semver.lt(process.versions.node, '16.0.0') const oldNode = semver.lt(process.versions.node, '18.0.0') +const run = (/** @type {() => void} */ f) => f() + process.on('exit', async () => { - // Has to be sync. - renameSync('package.json.bak', 'package.json') + try { + // Has to be sync. + renameSync('package.json.bak', 'package.json') + } catch (/** @type {any} */ error) { + // ignore if not found, which will happen because baseline.js sometimes skips the test + if (error.code !== 'ENOENT') throw error + } }) -test('resolve(specifier, base?, conditions?)', async function () { - assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') +test( + 'resolve(specifier, base?, conditions?)', + // // Note: this is toggled by the `baseline*.js` tests (see `script.js` for details on why and how) + {skip: false}, + async function () { + assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') - await fs.rename('package.json', 'package.json.bak') + await fs.rename('package.json', 'package.json.bak') - try { - await resolve('', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - assert.equal(exception.code, 'ERR_MODULE_NOT_FOUND', 'empty string') - } + try { + resolve('', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal(exception.code, 'ERR_MODULE_NOT_FOUND', 'empty string') + } - try { - await resolve('abc', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'unfound bare specifier' - ) - } + try { + resolve('abc', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_MODULE_NOT_FOUND', + 'unfound bare specifier' + ) + } - try { - await resolve('/abc', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'unfound absolute path' - ) - } + try { + resolve('/abc', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_MODULE_NOT_FOUND', + 'unfound absolute path' + ) + } - try { - await resolve('./abc', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'unfound relative path' - ) - } + try { + resolve('./abc', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_MODULE_NOT_FOUND', + 'unfound relative path' + ) + } - try { - await resolve('../abc', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'unfound relative parental path' - ) - } + try { + resolve('../abc', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_MODULE_NOT_FOUND', + 'unfound relative parental path' + ) + } + + try { + resolve('#', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_INVALID_MODULE_SPECIFIER', + 'empty import specifier' + ) + } + + try { + resolve('#/', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_INVALID_MODULE_SPECIFIER', + 'empty absolute import specifier' + ) + } - try { - await resolve('#', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_INVALID_MODULE_SPECIFIER', - 'empty import specifier' + resolve('../tsconfig.json', import.meta.url), + pathToFileURL('tsconfig.json').href, + 'should resolve a json file' ) - } - try { - await resolve('#/', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_INVALID_MODULE_SPECIFIER', - 'empty absolute import specifier' + resolve('./index.js', import.meta.url), + pathToFileURL('test/index.js').href, + 'should resolve a js file' ) - } - - assert.equal( - await resolve('../tsconfig.json', import.meta.url), - pathToFileURL('tsconfig.json').href, - 'should resolve a json file' - ) - - assert.equal( - await resolve('./index.js', import.meta.url), - pathToFileURL('test/index.js').href, - 'should resolve a js file' - ) - - assert.equal( - await resolve('..', import.meta.url), - pathToFileURL('../import-meta-resolve/').href, - 'should resolve a directory (1)' - ) - - assert.equal( - await resolve('../lib', import.meta.url), - pathToFileURL('../import-meta-resolve/lib').href, - 'should resolve a directory (2)' - ) - - assert.equal( - await resolve('../lib/', import.meta.url), - pathToFileURL('../import-meta-resolve/lib/').href, - 'should resolve a directory (3)' - ) - - assert.equal( - await resolve('micromark', import.meta.url), - new URL('../node_modules/micromark/index.js', import.meta.url).href, - 'should resolve a bare specifier to a package' - ) - - assert.equal( - await resolve('mdast-util-to-string/index.js', import.meta.url), - new URL('../node_modules/mdast-util-to-string/index.js', import.meta.url) - .href, - 'should resolve a bare specifier plus path' - ) - - assert.equal( - await resolve('@bcoe/v8-coverage', import.meta.url), - new URL( - '../node_modules/@bcoe/v8-coverage/dist/lib/index.js', - import.meta.url - ).href, - 'should resolve a bare specifier w/ scope to a package' - ) - try { - await resolve('xxx-missing', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'missing bare specifier' + resolve('..', import.meta.url), + pathToFileURL('../import-meta-resolve/').href, + 'should resolve a directory (1)' ) - } - try { - await resolve('@a/b', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'missing scoped bare specifier' + resolve('../lib', import.meta.url), + pathToFileURL('../import-meta-resolve/lib').href, + 'should resolve a directory (2)' ) - } - try { - await resolve('@scope-only', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_INVALID_MODULE_SPECIFIER', - 'invalid scoped specifier' + resolve('../lib/', import.meta.url), + pathToFileURL('../import-meta-resolve/lib/').href, + 'should resolve a directory (3)' ) - } - try { - await resolve('%20', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_INVALID_MODULE_SPECIFIER', - 'invalid package name as specifier' + resolve('micromark', import.meta.url), + new URL('../node_modules/micromark/index.js', import.meta.url).href, + 'should resolve a bare specifier to a package' ) - } - try { - await resolve('micromark/index.js', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_PACKAGE_PATH_NOT_EXPORTED', - 'bare specifier w/ path that’s not exported' + resolve('mdast-util-to-string/index.js', import.meta.url), + new URL('../node_modules/mdast-util-to-string/index.js', import.meta.url) + .href, + 'should resolve a bare specifier plus path' ) - } - - assert.equal( - await resolve('micromark/stream', import.meta.url), - new URL('../node_modules/micromark/stream.js', import.meta.url).href, - 'should resolve a bare specifier + path which is exported' - ) - - assert.equal( - await resolve('micromark', import.meta.url), - new URL('../node_modules/micromark/index.js', import.meta.url).href, - 'should cache results' - ) - - assert.equal( - await resolve('fs', import.meta.url), - 'node:fs', - 'should support internal node modules' - ) - - assert.equal( - await resolve('node:fs', import.meta.url), - 'node:fs', - 'should support `node:` protocols' - ) - - assert.equal( - await resolve('data:1', import.meta.url), - 'data:1', - 'should support `data:` protocols' - ) - try { - await resolve('xss:1', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_UNSUPPORTED_ESM_URL_SCHEME', - 'should not support other protocols' + resolve('@bcoe/v8-coverage', import.meta.url), + new URL( + '../node_modules/@bcoe/v8-coverage/dist/lib/index.js', + import.meta.url + ).href, + 'should resolve a bare specifier w/ scope to a package' ) - } - if (!oldNode) { try { - await resolve('node:fs', 'https://example.com/file.html') + resolve('xxx-missing', import.meta.url) assert.fail() } catch (error) { const exception = /** @type {ErrnoException} */ (error) assert.equal( exception.code, - 'ERR_NETWORK_IMPORT_DISALLOWED', - 'should not support loading builtins from http' + 'ERR_MODULE_NOT_FOUND', + 'missing bare specifier' ) } - } - assert.equal( - await resolve('./index.js?1', import.meta.url), - new URL('index.js?1', import.meta.url).href, - 'should support a `search` in specifiers' - ) + try { + resolve('@a/b', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_MODULE_NOT_FOUND', + 'missing scoped bare specifier' + ) + } - assert.equal( - await resolve('./index.js#1', import.meta.url), - new URL('index.js#1', import.meta.url).href, - 'should support a `hash` in specifiers' - ) + try { + resolve('@scope-only', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_INVALID_MODULE_SPECIFIER', + 'invalid scoped specifier' + ) + } - try { - await resolve('./example.js', 'data:1') - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - if (!oldNode) { + try { + resolve('%20', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) assert.equal( exception.code, - oldNode ? 'ERR_INVALID_URL_SCHEME' : 'ERR_INVALID_URL', - 'should not be able to resolve relative to a `data:` parent url' + 'ERR_INVALID_MODULE_SPECIFIER', + 'invalid package name as specifier' ) } - } - assert.equal( - await resolve('./index.js', import.meta.url), - new URL('index.js', import.meta.url).href, - 'should be able to find files w/o `package.json`' - ) + try { + resolve('micromark/index.js', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_PACKAGE_PATH_NOT_EXPORTED', + 'bare specifier w/ path that’s not exported' + ) + } - assert.equal( - await resolve('micromark', import.meta.url), - new URL('../node_modules/micromark/index.js', import.meta.url).href, - 'should be able to find packages w/o `package.json`' - ) + assert.equal( + resolve('micromark/stream', import.meta.url), + new URL('../node_modules/micromark/stream.js', import.meta.url).href, + 'should resolve a bare specifier + path which is exported' + ) - try { - await resolve('xxx-missing', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'missing packages w/o `package.json`' + resolve('micromark', import.meta.url), + new URL('../node_modules/micromark/index.js', import.meta.url).href, + 'should cache results' ) - } - try { - await resolve('#local', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_PACKAGE_IMPORT_NOT_DEFINED', - 'missing import map w/o `package.json`' + resolve('fs', import.meta.url), + 'node:fs', + 'should support internal node modules' ) - } - try { - await resolve('no-package-json', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'should not be able to import packages that themselves don’t have `package.json`s (1)' + resolve('node:fs', import.meta.url), + 'node:fs', + 'should support `node:` protocols' ) - } - try { - await resolve('package-no-main', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_MODULE_NOT_FOUND', - 'should not be able to import packages w/o index files' + resolve('data:1', import.meta.url), + 'data:1', + 'should support `data:` protocols' ) - } - assert.equal( - await resolve('package-no-main-2', import.meta.url), - new URL('node_modules/package-no-main-2/index.js', import.meta.url).href, - 'should be able to import CJS packages w/o `main`' - ) + try { + resolve('xss:1', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_UNSUPPORTED_ESM_URL_SCHEME', + 'should not support other protocols' + ) + } + + if (!oldNode) { + try { + resolve('node:fs', 'https://example.com/file.html') + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_NETWORK_IMPORT_DISALLOWED', + 'should not support loading builtins from http' + ) + } + } + + assert.equal( + resolve('./index.js?1', import.meta.url), + new URL('index.js?1', import.meta.url).href, + 'should support a `search` in specifiers' + ) - await (async () => { - assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') + assert.equal( + resolve('./index.js#1', import.meta.url), + new URL('index.js#1', import.meta.url).href, + 'should support a `hash` in specifiers' + ) - const oldEmitWarning = process.emitWarning - /** @type {string|undefined} */ - let deprecation - - // @ts-expect-error hush - process.emitWarning = - /** - * @param {unknown} _1 - * @param {unknown} _2 - * @param {string} code - */ - (_1, _2, code) => { - deprecation = code + try { + resolve('./example.js', 'data:1') + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + if (!oldNode) { + assert.equal( + exception.code, + oldNode ? 'ERR_INVALID_URL_SCHEME' : 'ERR_INVALID_URL', + 'should not be able to resolve relative to a `data:` parent url' + ) } + } + + assert.equal( + resolve('./index.js', import.meta.url), + new URL('index.js', import.meta.url).href, + 'should be able to find files w/o `package.json`' + ) assert.equal( - await resolve('package-no-main-3', import.meta.url), - new URL('node_modules/package-no-main-3/index.js', import.meta.url).href, - 'should be able to import ESM packages w/o `main`, but warn (1)' + resolve('micromark', import.meta.url), + new URL('../node_modules/micromark/index.js', import.meta.url).href, + 'should be able to find packages w/o `package.json`' ) - if (oldNode) { - // Empty. - } else { + try { + resolve('xxx-missing', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) assert.equal( - deprecation, - 'DEP0151', - 'should be able to import ESM packages w/o `main`, but warn (2)' + exception.code, + 'ERR_MODULE_NOT_FOUND', + 'missing packages w/o `package.json`' ) } - process.emitWarning = oldEmitWarning - })() + try { + resolve('#local', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_PACKAGE_IMPORT_NOT_DEFINED', + 'missing import map w/o `package.json`' + ) + } - await (async () => { - assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') + try { + resolve('no-package-json', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_MODULE_NOT_FOUND', + 'should not be able to import packages that themselves don’t have `package.json`s (1)' + ) + } - const oldEmitWarning = process.emitWarning - /** @type {string|undefined} */ - let deprecation - - // @ts-expect-error hush - process.emitWarning = - /** - * @param {unknown} _1 - * @param {unknown} _2 - * @param {string} code - */ - (_1, _2, code) => { - deprecation = code - } + try { + resolve('package-no-main', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_MODULE_NOT_FOUND', + 'should not be able to import packages w/o index files' + ) + } assert.equal( - await resolve('package-no-main-4', import.meta.url), - new URL('node_modules/package-no-main-4/index.js', import.meta.url).href, - 'should be able to import ESM packages w/ non-full `main`, but warn (1)' + resolve('package-no-main-2', import.meta.url), + new URL('node_modules/package-no-main-2/index.js', import.meta.url).href, + 'should be able to import CJS packages w/o `main`' ) - if (oldNode) { - // Empty. - } else { + run(() => { + assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') + + const oldEmitWarning = process.emitWarning + /** @type {string|undefined} */ + let deprecation + + // @ts-expect-error hush + process.emitWarning = + /** + * @param {unknown} _1 + * @param {unknown} _2 + * @param {string} code + */ + (_1, _2, code) => { + deprecation = code + } + assert.equal( - deprecation, - 'DEP0151', - 'should be able to import ESM packages w/ non-full `main`, but warn (2)' + resolve('package-no-main-3', import.meta.url), + new URL('node_modules/package-no-main-3/index.js', import.meta.url) + .href, + 'should be able to import ESM packages w/o `main`, but warn (1)' ) - } - process.emitWarning = oldEmitWarning - })() + if (oldNode) { + // Empty. + } else { + assert.equal( + deprecation, + 'DEP0151', + 'should be able to import ESM packages w/o `main`, but warn (2)' + ) + } + + process.emitWarning = oldEmitWarning + }) - try { - await resolve('package-invalid-json', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - assert.equal( - exception.code, - 'ERR_INVALID_PACKAGE_CONFIG', - 'should not be able to import packages w/ broken `package.json`s' - ) - } + run(() => { + assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') - assert.equal( - await resolve('package-export-map-1/a', import.meta.url), - new URL('node_modules/package-export-map-1/b.js', import.meta.url).href, - 'should be able to resolve to something from an export map (1)' - ) + const oldEmitWarning = process.emitWarning + /** @type {string|undefined} */ + let deprecation - assert.equal( - await resolve('package-export-map-1/lib/c', import.meta.url), - new URL('node_modules/package-export-map-1/lib/c.js', import.meta.url).href, - 'should be able to resolve to something from an export map (2)' - ) + // @ts-expect-error hush + process.emitWarning = + /** + * @param {unknown} _1 + * @param {unknown} _2 + * @param {string} code + */ + (_1, _2, code) => { + deprecation = code + } - assert.equal( - await resolve('package-export-map-2', import.meta.url), - new URL('node_modules/package-export-map-2/main.js', import.meta.url).href, - 'should be able to resolve to something from a main export map' - ) + assert.equal( + resolve('package-no-main-4', import.meta.url), + new URL('node_modules/package-no-main-4/index.js', import.meta.url) + .href, + 'should be able to import ESM packages w/ non-full `main`, but warn (1)' + ) + + if (oldNode) { + // Empty. + } else { + assert.equal( + deprecation, + 'DEP0151', + 'should be able to import ESM packages w/ non-full `main`, but warn (2)' + ) + } + + process.emitWarning = oldEmitWarning + }) + + try { + resolve('package-invalid-json', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_INVALID_PACKAGE_CONFIG', + 'should not be able to import packages w/ broken `package.json`s' + ) + } - try { - await resolve('package-export-map-2/missing', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_PACKAGE_PATH_NOT_EXPORTED', - 'should not be able to import things not in an export map' + resolve('package-export-map-1/a', import.meta.url), + new URL('node_modules/package-export-map-1/b.js', import.meta.url).href, + 'should be able to resolve to something from an export map (1)' ) - } - try { - await resolve('package-export-map-4', import.meta.url) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) assert.equal( - exception.code, - 'ERR_PACKAGE_PATH_NOT_EXPORTED', - 'should not be able to import things from an empty export map' + resolve('package-export-map-1/lib/c', import.meta.url), + new URL('node_modules/package-export-map-1/lib/c.js', import.meta.url) + .href, + 'should be able to resolve to something from an export map (2)' ) - } - - await (async () => { - assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') - - const oldEmitWarning = process.emitWarning - /** @type {string} */ - let deprecation - - // Windows doesn’t like `/` as a final path separator here. - if (windows) return - - // @ts-expect-error hush - process.emitWarning = - /** - * @param {unknown} _1 - * @param {unknown} _2 - * @param {string} code - */ - (_1, _2, code) => { - if (deprecation) assert.fail() - deprecation = code - } assert.equal( - await resolve( - './a/', - new URL('node_modules/package-export-map-5/', import.meta.url).href - ), - new URL('node_modules/package-export-map-5/a/', import.meta.url).href + resolve('package-export-map-2', import.meta.url), + new URL('node_modules/package-export-map-2/main.js', import.meta.url) + .href, + 'should be able to resolve to something from a main export map' ) try { - // Twice for coverage: deprecation should fire only once. - await resolve( - './a/b.js', - new URL('node_modules/package-export-map-5/', import.meta.url).href + resolve('package-export-map-2/missing', import.meta.url) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_PACKAGE_PATH_NOT_EXPORTED', + 'should not be able to import things not in an export map' ) + } + + try { + resolve('package-export-map-4', import.meta.url) assert.fail() - } catch {} + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_PACKAGE_PATH_NOT_EXPORTED', + 'should not be able to import things from an empty export map' + ) + } - process.emitWarning = oldEmitWarning - })() + run(() => { + assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') - assert.equal( - await resolve( - '#a', - new URL('node_modules/package-import-map-1/', import.meta.url).href - ), - new URL('node_modules/package-import-map-1/index.js', import.meta.url).href, - 'should be able to resolve to something from a main export map w/ package name' - ) + const oldEmitWarning = process.emitWarning + /** @type {string} */ + let deprecation - try { - await resolve( - '#b', - new URL('node_modules/package-import-map-1/', import.meta.url).href - ) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - assert.equal( - exception.code, - 'ERR_PACKAGE_IMPORT_NOT_DEFINED', - 'should not be able to import things not in an import map' - ) - } + // Windows doesn’t like `/` as a final path separator here. + if (windows) return - try { - await resolve( - '#a', - new URL('node_modules/package-import-map-2/', import.meta.url).href - ) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - assert.equal( - exception.code, - 'ERR_PACKAGE_IMPORT_NOT_DEFINED', - 'should not be able to import things not in an import map incorrectly defined w/o `#`' - ) - } + // @ts-expect-error hush + process.emitWarning = + /** + * @param {unknown} _1 + * @param {unknown} _2 + * @param {string} code + */ + (_1, _2, code) => { + if (deprecation) assert.fail() + deprecation = code + } - assert.equal( - await resolve( - '#a/b.js', - new URL('node_modules/package-import-map-3/', import.meta.url).href - ), - new URL('node_modules/package-import-map-3/index.js', import.meta.url).href, - 'should be able to resolve to something to import map splats' - ) + assert.equal( + resolve( + './a/', + new URL('node_modules/package-export-map-5/', import.meta.url).href + ), + new URL('node_modules/package-export-map-5/a/', import.meta.url).href + ) - try { - await resolve( - '#a/b.js', - new URL('node_modules/package-import-map-4/', import.meta.url).href + try { + // Twice for coverage: deprecation should fire only once. + resolve( + './a/b.js', + new URL('node_modules/package-export-map-5/', import.meta.url).href + ) + assert.fail() + } catch {} + + process.emitWarning = oldEmitWarning + }) + + assert.equal( + resolve( + '#a', + new URL('node_modules/package-import-map-1/', import.meta.url).href + ), + new URL('node_modules/package-import-map-1/index.js', import.meta.url) + .href, + 'should be able to resolve to something from a main export map w/ package name' ) - assert.fail() - } catch (error) { - const exception = /** @type {ErrnoException} */ (error) - if (!oldNode) { + + try { + resolve( + '#b', + new URL('node_modules/package-import-map-1/', import.meta.url).href + ) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) assert.equal( exception.code, 'ERR_PACKAGE_IMPORT_NOT_DEFINED', - 'should not be able to import an invalid import package target' + 'should not be able to import things not in an import map' ) } - } - await (async () => { - assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') + try { + resolve( + '#a', + new URL('node_modules/package-import-map-2/', import.meta.url).href + ) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + assert.equal( + exception.code, + 'ERR_PACKAGE_IMPORT_NOT_DEFINED', + 'should not be able to import things not in an import map incorrectly defined w/o `#`' + ) + } - const oldEmitWarning = process.emitWarning - /** @type {string|undefined} */ - let deprecation - - // @ts-expect-error hush - process.emitWarning = - /** - * @param {unknown} _1 - * @param {unknown} _2 - * @param {string} code - */ - (_1, _2, code) => { - if (deprecation) assert.fail() - deprecation = code - } + assert.equal( + resolve( + '#a/b.js', + new URL('node_modules/package-import-map-3/', import.meta.url).href + ), + new URL('node_modules/package-import-map-3/index.js', import.meta.url) + .href, + 'should be able to resolve to something to import map splats' + ) try { - await resolve( + resolve( '#a/b.js', - new URL('node_modules/package-import-map-5/', import.meta.url).href + new URL('node_modules/package-import-map-4/', import.meta.url).href ) assert.fail() } catch (error) { @@ -630,59 +610,94 @@ test('resolve(specifier, base?, conditions?)', async function () { assert.equal( exception.code, 'ERR_PACKAGE_IMPORT_NOT_DEFINED', - 'should support legacy folders in import maps (1)' + 'should not be able to import an invalid import package target' ) } } - process.emitWarning = oldEmitWarning - })() + run(() => { + assert(resolve, 'expected `resolve` to exist (needed for TS in baseline)') + + const oldEmitWarning = process.emitWarning + /** @type {string|undefined} */ + let deprecation + + // @ts-expect-error hush + process.emitWarning = + /** + * @param {unknown} _1 + * @param {unknown} _2 + * @param {string} code + */ + (_1, _2, code) => { + if (deprecation) assert.fail() + deprecation = code + } + + try { + resolve( + '#a/b.js', + new URL('node_modules/package-import-map-5/', import.meta.url).href + ) + assert.fail() + } catch (error) { + const exception = /** @type {ErrnoException} */ (error) + if (!oldNode) { + assert.equal( + exception.code, + 'ERR_PACKAGE_IMPORT_NOT_DEFINED', + 'should support legacy folders in import maps (1)' + ) + } + } + + process.emitWarning = oldEmitWarning + }) - if (!veryOldNode) { assert.equal( - await resolve( + resolve( '#a', new URL('node_modules/package-import-map-6/', import.meta.url).href ), new URL('node:net').href, 'should be able to resolve to a built-in node module' ) - } - assert.equal( - await resolve( - 'package-self-import-1', - new URL('node_modules/package-self-import-1/', import.meta.url).href - ), - new URL('node_modules/package-self-import-1/index.js', import.meta.url) - .href, - 'should be able to resolve a self-import' - ) - - assert.equal( - await resolve( - 'package-self-import-1', - new URL( - 'node_modules/package-self-import-1/test/index.js', - import.meta.url - ).href - ), - new URL('node_modules/package-self-import-1/index.js', import.meta.url) - .href, - 'should be able to resolve a self-import from a sub-file' - ) - - assert.equal( - await resolve('package-custom-extensions', import.meta.url), - new URL('node_modules/package-custom-extensions/b.ts', import.meta.url) - .href, - 'should be able to resolve a custom `.ts` extension' - ) - - assert.equal( - await resolve('package-custom-extensions/c', import.meta.url), - new URL('node_modules/package-custom-extensions/d.wasm', import.meta.url) - .href, - 'should be able to resolve a custom `.wasm` extension' - ) -}) + assert.equal( + resolve( + 'package-self-import-1', + new URL('node_modules/package-self-import-1/', import.meta.url).href + ), + new URL('node_modules/package-self-import-1/index.js', import.meta.url) + .href, + 'should be able to resolve a self-import' + ) + + assert.equal( + resolve( + 'package-self-import-1', + new URL( + 'node_modules/package-self-import-1/test/index.js', + import.meta.url + ).href + ), + new URL('node_modules/package-self-import-1/index.js', import.meta.url) + .href, + 'should be able to resolve a self-import from a sub-file' + ) + + assert.equal( + resolve('package-custom-extensions', import.meta.url), + new URL('node_modules/package-custom-extensions/b.ts', import.meta.url) + .href, + 'should be able to resolve a custom `.ts` extension' + ) + + assert.equal( + resolve('package-custom-extensions/c', import.meta.url), + new URL('node_modules/package-custom-extensions/d.wasm', import.meta.url) + .href, + 'should be able to resolve a custom `.wasm` extension' + ) + } +) diff --git a/test/ponyfill.js b/test/ponyfill.js index 800964b..d91959a 100644 --- a/test/ponyfill.js +++ b/test/ponyfill.js @@ -2,10 +2,10 @@ import assert from 'node:assert/strict' import test from 'node:test' import {resolve} from '../index.js' -test('ponyfill', async function () { +test('ponyfill', function () { try { // @ts-expect-error - await resolve('x') + resolve('x') assert.fail() } catch (error) { assert.equal(