Skip to content
This repository was archived by the owner on Oct 18, 2023. It is now read-only.

Commit dcf4dd9

Browse files
fix: support query in specifier (#76)
1 parent a237b20 commit dcf4dd9

File tree

11 files changed

+112
-17
lines changed

11 files changed

+112
-17
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"prepack": "pnpm build && clean-pkg-json"
3333
},
3434
"dependencies": {
35-
"@esbuild-kit/core-utils": "^3.3.0",
35+
"@esbuild-kit/core-utils": "^3.3.2",
3636
"get-tsconfig": "^4.7.0"
3737
},
3838
"devDependencies": {

pnpm-lock.yaml

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/loaders-deprecated.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
tsExtensionsPattern,
1818
getFormatFromFileUrl,
1919
fileProtocol,
20+
isJsonPattern,
2021
type MaybePromise,
2122
type NodeError,
2223
} from './utils.js';
@@ -32,7 +33,7 @@ const _getFormat: getFormat = async function (
3233
context,
3334
defaultGetFormat,
3435
) {
35-
if (url.endsWith('.json')) {
36+
if (isJsonPattern.test(url)) {
3637
return { format: 'module' };
3738
}
3839

@@ -80,7 +81,7 @@ const _transformSource: transformSource = async function (
8081
}
8182

8283
if (
83-
url.endsWith('.json')
84+
isJsonPattern.test(url)
8485
|| tsExtensionsPattern.test(url)
8586
) {
8687
const transformed = await transform(

src/loaders.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ import {
1616
tsconfigPathsMatcher,
1717
fileMatcher,
1818
tsExtensionsPattern,
19+
isJsonPattern,
1920
getFormatFromFileUrl,
2021
fileProtocol,
2122
type MaybePromise,
2223
type NodeError,
2324
} from './utils.js';
2425

26+
const isDirectoryPattern = /\/(?:$|\?)/;
27+
2528
type NextResolve = (
2629
specifier: string,
2730
context?: ResolveHookContext,
@@ -75,11 +78,12 @@ async function tryExtensions(
7578
context: ResolveHookContext,
7679
defaultResolve: NextResolve,
7780
) {
81+
const [specifierWithoutQuery, query] = specifier.split('?');
7882
let throwError: Error | undefined;
7983
for (const extension of extensions) {
8084
try {
8185
return await resolve(
82-
specifier + extension,
86+
specifierWithoutQuery + extension + (query ? `?${query}` : ''),
8387
context,
8488
defaultResolve,
8589
true,
@@ -105,11 +109,12 @@ async function tryDirectory(
105109
context: ResolveHookContext,
106110
defaultResolve: NextResolve,
107111
) {
108-
const isExplicitDirectory = specifier.endsWith('/');
112+
const isExplicitDirectory = isDirectoryPattern.test(specifier);
109113
const appendIndex = isExplicitDirectory ? 'index' : '/index';
114+
const [specifierWithoutQuery, query] = specifier.split('?');
110115

111116
try {
112-
return await tryExtensions(specifier + appendIndex, context, defaultResolve);
117+
return await tryExtensions(specifierWithoutQuery + appendIndex + (query ? `?${query}` : ''), context, defaultResolve);
113118
} catch (_error) {
114119
if (!isExplicitDirectory) {
115120
try {
@@ -145,7 +150,7 @@ export const resolve: resolve = async function (
145150
}
146151

147152
// If directory, can be index.js, index.ts, etc.
148-
if (specifier.endsWith('/')) {
153+
if (isDirectoryPattern.test(specifier)) {
149154
return await tryDirectory(specifier, context, defaultResolve);
150155
}
151156

@@ -243,7 +248,7 @@ export const load: LoadHook = async function (
243248
});
244249
}
245250

246-
if (url.endsWith('.json')) {
251+
if (isJsonPattern.test(url)) {
247252
if (!context.importAssertions) {
248253
context.importAssertions = {};
249254
}

src/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ export const tsconfigPathsMatcher = tsconfig && createPathsMatcher(tsconfig);
2525

2626
export const fileProtocol = 'file://';
2727

28-
export const tsExtensionsPattern = /\.([cm]?ts|[tj]sx)$/;
28+
export const tsExtensionsPattern = /\.([cm]?ts|[tj]sx)($|\?)/;
29+
30+
export const isJsonPattern = /\.json(?:$|\?)/;
2931

3032
const getFormatFromExtension = (fileUrl: string): ModuleFormat | undefined => {
3133
const extension = path.extname(fileUrl);

tests/specs/javascript/esm.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import semver from 'semver';
33
import type { NodeApis } from '../../utils/node-with-loader.js';
44
import nodeSupports from '../../utils/node-supports.js';
55
import { assertNotFound } from '../../utils/assertions.js';
6+
import { query } from '../../utils/query.js';
67

78
export default testSuite(async ({ describe }, node: NodeApis) => {
89
describe('Load ESM', ({ describe }) => {
@@ -36,6 +37,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
3637
expect(nodeProcess.stdout).toMatch('{"default":1234}');
3738
});
3839

40+
test('Import with query', async () => {
41+
const nodeProcess = await node.import(importPath + query);
42+
assertResults(nodeProcess);
43+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
44+
});
45+
3946
test('TypeScript Import', async () => {
4047
const nodeProcess = await node.import(importPath, { typescript: true });
4148
assertResults(nodeProcess);
@@ -101,6 +108,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
101108
assertResults(nodeProcess);
102109
expect(nodeProcess.stdout).toMatch('{"default":1234}');
103110
});
111+
112+
test('Import with query', async () => {
113+
const nodeProcess = await node.import(importPath + query);
114+
assertResults(nodeProcess);
115+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
116+
});
104117
});
105118

106119
describe('extensionless', ({ test }) => {

tests/specs/json.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { testSuite, expect } from 'manten';
22
import { createFixture } from 'fs-fixture';
33
import type { NodeApis } from '../utils/node-with-loader.js';
4+
import { query } from '../utils/query.js';
45

56
const jsonFixture = {
67
'package.json': JSON.stringify({
@@ -28,6 +29,11 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
2829
const nodeProcess = await node.importFile(fixture.path, './index.json');
2930
expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }');
3031
});
32+
33+
test('Import with query', async () => {
34+
const nodeProcess = await node.importFile(fixture.path, `./index.json${query}`);
35+
expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }');
36+
});
3137
});
3238

3339
describe('extensionless', ({ test }) => {
@@ -41,14 +47,16 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
4147
const nodeProcess = await node.importFile(fixture.path, './index');
4248
expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }');
4349
});
50+
51+
test('Import with query', async () => {
52+
const nodeProcess = await node.importFile(fixture.path, `./index${query}`);
53+
expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }');
54+
});
4455
});
4556

4657
describe('directory', ({ test }) => {
47-
test('Load', async ({ onTestFail }) => {
58+
test('Load', async () => {
4859
const nodeProcess = await node.loadFile(fixture.path, '.');
49-
onTestFail(() => {
50-
console.log(nodeProcess);
51-
});
5260
expect(nodeProcess.exitCode).toBe(0);
5361
expect(nodeProcess.stdout).toBe('');
5462
});
@@ -57,6 +65,11 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
5765
const nodeProcess = await node.importFile(fixture.path, '.');
5866
expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }');
5967
});
68+
69+
test('Import with query', async () => {
70+
const nodeProcess = await node.importFile(fixture.path, `./${query}`);
71+
expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }');
72+
});
6073
});
6174

6275
describe('ambiguous path', async ({ describe, onFinish }) => {

tests/specs/typescript/mts.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import semver from 'semver';
33
import type { NodeApis } from '../../utils/node-with-loader.js';
44
import nodeSupports from '../../utils/node-supports.js';
55
import { assertNotFound } from '../../utils/assertions.js';
6+
import { query } from '../../utils/query.js';
67

78
export default testSuite(async ({ describe }, node: NodeApis) => {
89
describe('.mts extension', ({ describe }) => {
@@ -33,6 +34,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
3334
assertResults(nodeProcess.stdout);
3435
expect(nodeProcess.stdout).toMatch('{"default":1234}');
3536
});
37+
38+
test('Import with query', async () => {
39+
const nodeProcess = await node.import(importPath + query);
40+
assertResults(nodeProcess.stdout);
41+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
42+
});
3643
});
3744

3845
describe('full path via .mjs', ({ test }) => {
@@ -48,6 +55,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
4855
assertResults(nodeProcess.stdout);
4956
expect(nodeProcess.stdout).toMatch('{"default":1234}');
5057
});
58+
59+
test('Import with query', async () => {
60+
const nodeProcess = await node.import(importPath + query, { typescript: true });
61+
assertResults(nodeProcess.stdout);
62+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
63+
});
5164
});
5265

5366
describe('extensionless - should not work', ({ test }) => {
@@ -62,6 +75,11 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
6275
const nodeProcess = await node.import(importPath);
6376
assertNotFound(nodeProcess.stderr, importPath);
6477
});
78+
79+
test('Import with query', async () => {
80+
const nodeProcess = await node.import(importPath + query);
81+
assertNotFound(nodeProcess.stderr, importPath);
82+
});
6583
});
6684

6785
describe('directory - should not work', ({ test }) => {
@@ -76,6 +94,11 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
7694
const nodeProcess = await node.import(importPath);
7795
assertNotFound(nodeProcess.stderr, importPath);
7896
});
97+
98+
test('Import with query', async () => {
99+
const nodeProcess = await node.import(importPath + query);
100+
assertNotFound(nodeProcess.stderr, importPath);
101+
});
79102
});
80103
});
81104
});

tests/specs/typescript/ts.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import semver from 'semver';
33
import type { NodeApis } from '../../utils/node-with-loader.js';
44
import nodeSupports from '../../utils/node-supports.js';
55
import { assertNotFound } from '../../utils/assertions.js';
6+
import { query } from '../../utils/query.js';
67

78
export default testSuite(async ({ describe }, node: NodeApis) => {
89
describe('.ts extension', ({ describe }) => {
@@ -58,6 +59,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
5859
assertResults(nodeProcess.stdout);
5960
expect(nodeProcess.stdout).toMatch('{"default":1234}');
6061
});
62+
63+
test('Import with query', async () => {
64+
const nodeProcess = await node.import(importPath + query, { typescript: true });
65+
assertResults(nodeProcess.stdout);
66+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
67+
});
6168
});
6269

6370
describe('extensionless', ({ test }) => {
@@ -73,6 +80,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
7380
assertResults(nodeProcess.stdout);
7481
expect(nodeProcess.stdout).toMatch('{"default":1234}');
7582
});
83+
84+
test('Import with query', async () => {
85+
const nodeProcess = await node.import(importPath + query, { typescript: true });
86+
assertResults(nodeProcess.stdout);
87+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
88+
});
7689
});
7790

7891
describe('extensionless with subextension', ({ test }) => {
@@ -88,6 +101,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
88101
assertResults(nodeProcess.stdout, 'ts-ext-ts/index.tsx.ts');
89102
expect(nodeProcess.stdout).toMatch('{"default":1234}');
90103
});
104+
105+
test('Import with query', async () => {
106+
const nodeProcess = await node.import(importPath + query);
107+
assertResults(nodeProcess.stdout, 'ts-ext-ts/index.tsx.ts');
108+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
109+
});
91110
});
92111

93112
describe('directory', ({ test }) => {
@@ -103,6 +122,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
103122
assertResults(nodeProcess.stdout);
104123
expect(nodeProcess.stdout).toMatch('{"default":1234}');
105124
});
125+
126+
test('Import with query', async () => {
127+
const nodeProcess = await node.import(importPath + query);
128+
assertResults(nodeProcess.stdout);
129+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
130+
});
106131
});
107132

108133
describe('empty directory should fallback to file', ({ test }) => {
@@ -118,6 +143,12 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
118143
assertResults(nodeProcess.stdout);
119144
expect(nodeProcess.stdout).toMatch('{"default":1234}');
120145
});
146+
147+
test('Import with query', async () => {
148+
const nodeProcess = await node.import(importPath + query);
149+
assertResults(nodeProcess.stdout);
150+
expect(nodeProcess.stdout).toMatch('{"default":1234}');
151+
});
121152
});
122153

123154
describe('empty but explicit directory should not fallback to file', ({ test }) => {
@@ -127,6 +158,11 @@ export default testSuite(async ({ describe }, node: NodeApis) => {
127158
const nodeProcess = await node.import(importPath);
128159
assertNotFound(nodeProcess.stderr, importPath);
129160
});
161+
162+
test('Import with query', async () => {
163+
const nodeProcess = await node.import(importPath + query);
164+
assertNotFound(nodeProcess.stderr, importPath);
165+
});
130166
});
131167
});
132168
});

tests/utils/node-with-loader.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const nodeWithLoader = (
3636
nodePath: options.nodePath,
3737
cwd: options.cwd,
3838
reject: false,
39+
all: true,
3940
},
4041
);
4142

0 commit comments

Comments
 (0)