Skip to content

Commit efb3509

Browse files
fix(esm): dont add namespace query to bare specifier (#21)
1 parent b273848 commit efb3509

File tree

5 files changed

+64
-18
lines changed

5 files changed

+64
-18
lines changed

src/cjs/api/module-resolve-filename.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Module from 'node:module';
33
import { createPathsMatcher } from 'get-tsconfig';
44
import { resolveTsPath } from '../../utils/resolve-ts-path.js';
55
import type { NodeError } from '../../types.js';
6-
import { isRelativePathPattern } from '../../utils/is-relative-path-pattern.js';
6+
import { isRelativePath } from '../../utils/path-utils.js';
77
import {
88
isTsFilePatten,
99
tsconfig,
@@ -73,7 +73,7 @@ export const resolveFilename: ResolveFilename = (
7373
tsconfigPathsMatcher
7474

7575
// bare specifier
76-
&& !isRelativePathPattern.test(request)
76+
&& !isRelativePath(request)
7777

7878
// Dependency paths should not be resolved using tsconfig.json
7979
&& !parent?.filename?.includes(nodeModulesPath)

src/esm/hook/resolve.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type {
55
} from 'node:module';
66
import { resolveTsPath } from '../../utils/resolve-ts-path.js';
77
import type { NodeError } from '../../types.js';
8-
import { isRelativePathPattern } from '../../utils/is-relative-path-pattern.js';
8+
import { requestAcceptsQuery } from '../../utils/path-utils.js';
99
import {
1010
tsconfigPathsMatcher,
1111
tsExtensionsPattern,
@@ -121,14 +121,8 @@ export const resolve: resolve = async (
121121
return nextResolve(specifier, context);
122122
}
123123

124-
const isPath = (
125-
specifier.startsWith(fileProtocol)
126-
|| path.isAbsolute(specifier)
127-
|| isRelativePathPattern.test(specifier)
128-
);
129-
130124
const parentNamespace = context.parentURL && getNamespace(context.parentURL);
131-
if (isPath) {
125+
if (requestAcceptsQuery(specifier)) {
132126
// Inherit namespace from parent
133127
let requestNamespace = getNamespace(specifier);
134128
if (parentNamespace && !requestNamespace) {
@@ -190,9 +184,15 @@ export const resolve: resolve = async (
190184

191185
try {
192186
const resolved = await resolveExplicitPath(nextResolve, specifier, context);
193-
const resolvedNamespace = getNamespace(resolved.url);
194-
if (parentNamespace && !resolvedNamespace) {
195-
resolved.url += `${resolved.url.includes('?') ? '&' : '?'}${namespaceQuery}${parentNamespace}`;
187+
// Could be a core Node module (e.g. `fs`)
188+
if (requestAcceptsQuery(resolved.url)) {
189+
const resolvedNamespace = getNamespace(resolved.url);
190+
if (
191+
parentNamespace
192+
&& !resolvedNamespace
193+
) {
194+
resolved.url += `${resolved.url.includes('?') ? '&' : '?'}${namespaceQuery}${parentNamespace}`;
195+
}
196196
}
197197
return resolved;
198198
} catch (error) {

src/utils/is-relative-path-pattern.ts

-1
This file was deleted.

src/utils/path-utils.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import path from 'node:path';
2+
3+
/**
4+
* Prior to calling this function, it's expected that Windows paths have been filtered out
5+
* via path.isAbsolute()
6+
*
7+
* Windows paths cannot be correctly parsed (e.g. new URL('C:\Users\Example\file.txt')
8+
*/
9+
const getScheme = (url: string) => {
10+
const schemeIndex = url.indexOf(':');
11+
if (schemeIndex === -1) { return; }
12+
return url.slice(0, schemeIndex);
13+
};
14+
15+
export const isRelativePath = (request: string) => (
16+
request[0] === '.'
17+
&& (
18+
request[1] === '/'
19+
|| (request[1] === '.' || request[2] === '/')
20+
)
21+
);
22+
23+
const isUnixPath = (request: string) => (
24+
isRelativePath(request)
25+
|| path.isAbsolute(request)
26+
);
27+
28+
// In Node, bare specifiers (packages and core modules) do not accept queries
29+
export const requestAcceptsQuery = (request: string) => {
30+
// ./foo.js?query
31+
// /foo.js?query in UNIX
32+
if (isUnixPath(request)) {
33+
return true;
34+
}
35+
36+
const scheme = getScheme(request);
37+
return (
38+
// Expected to be file, https, etc...
39+
scheme
40+
41+
// node:url maps to a bare-specifier, which does not accept queries
42+
// But URLs like file:// or https:// do
43+
&& scheme !== 'node'
44+
);
45+
};

tests/specs/api.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const tsFiles = {
2626
type: 'module',
2727
exports: './index.js',
2828
}),
29-
'index.js': 'export const bar = "bar"',
29+
'index.js': 'import "node:process"; export const bar = "bar";',
3030
},
3131
};
3232

@@ -254,13 +254,14 @@ export default testSuite(({ describe }, node: NodeApis) => {
254254
nodePath: node.path,
255255
nodeOptions: [],
256256
});
257-
expect(stdout).toBe('file.ts\nfoo.ts\nbar.ts\nindex.js');
257+
expect(stdout).toBe('file.ts\nfoo.ts\nbar.ts\nindex.js\nnode:process');
258258
});
259259

260260
test('namespace & onImport', async () => {
261261
await using fixture = await createFixture({
262262
'package.json': JSON.stringify({ type: 'module' }),
263263
'register.mjs': `
264+
import { setTimeout } from 'node:timers/promises';
264265
import { register } from ${JSON.stringify(tsxEsmApiPath)};
265266
266267
const api = register({
@@ -272,7 +273,7 @@ export default testSuite(({ describe }, node: NodeApis) => {
272273
273274
await api.import('./file', import.meta.url);
274275
275-
api.unregister();
276+
await setTimeout(100)
276277
`,
277278
...tsFiles,
278279
});
@@ -394,6 +395,7 @@ export default testSuite(({ describe }, node: NodeApis) => {
394395
await using fixture = await createFixture({
395396
'package.json': JSON.stringify({ type: 'module' }),
396397
'import.mjs': `
398+
import { setTimeout } from 'node:timers/promises';
397399
import { tsImport } from ${JSON.stringify(tsxEsmApiPath)};
398400
const dependenciesA = [];
399401
await tsImport('./file.ts', {
@@ -412,7 +414,7 @@ export default testSuite(({ describe }, node: NodeApis) => {
412414
});
413415
414416
// wait for async import() to finish
415-
await new Promise((resolve) => setTimeout(resolve, 10));
417+
await setTimeout(100)
416418
417419
if (JSON.stringify(dependenciesA) !== JSON.stringify(dependenciesB)) {
418420
console.log({

0 commit comments

Comments
 (0)