Skip to content

Commit 0eb4e91

Browse files
feat(esm/api): tsImport() to support loading CommonJS files
1 parent c703300 commit 0eb4e91

File tree

5 files changed

+27
-7
lines changed

5 files changed

+27
-7
lines changed

docs/node/ts-import.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ The current file path must be passed in as the second argument to resolve the im
88

99
Since this is designed for one-time use, it does not cache loaded modules.
1010

11-
::: warning Caveat
12-
CommonJS files are currently not enhanced due to this [Node.js bug](https://github.com/nodejs/node/issues/51327).
13-
:::
14-
1511
## ESM usage
1612

1713
```js

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,13 @@ export const interopCjsExports = (
2626
}
2727

2828
const searchParams = new URLSearchParams(request.slice(queryIndex + 1));
29-
const realPath = searchParams.get('filePath');
29+
let realPath = searchParams.get('filePath');
3030
if (realPath) {
31+
const namespace = searchParams.get('namespace');
32+
if (namespace) {
33+
realPath += `?namespace=${encodeURIComponent(namespace)}`;
34+
}
35+
3136
// The CJS module cache needs to be updated with the actual path for export parsing to work
3237
// https://github.com/nodejs/node/blob/v22.2.0/lib/internal/modules/esm/translators.js#L338
3338
Module._cache[realPath] = Module._cache[request];

src/esm/api/ts-import.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { register as cjsRegister } from '../../cjs/api/index.js';
12
import { register, type TsconfigOptions } from './register.js';
23

34
type Options = {
@@ -20,6 +21,11 @@ const tsImport = (
2021
const parentURL = isOptionsString ? options : options.parentURL;
2122
const namespace = Date.now().toString();
2223

24+
// Keep registered for hanging require() calls
25+
cjsRegister({
26+
namespace,
27+
});
28+
2329
/**
2430
* We don't want to unregister this after load since there can be child import() calls
2531
* that need TS support

src/esm/hook/load.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export const load: LoadHook = async (
2929
return nextLoad(url, context);
3030
}
3131

32-
if (data.namespace && data.namespace !== getNamespace(url)) {
32+
const urlNamespace = getNamespace(url);
33+
if (data.namespace && data.namespace !== urlNamespace) {
3334
return nextLoad(url, context);
3435
}
3536

@@ -76,6 +77,9 @@ export const load: LoadHook = async (
7677
exports.map(exported => exported.n).filter(name => name !== 'default').join(',')
7778
}}`;
7879
const parameters = new URLSearchParams({ filePath });
80+
if (urlNamespace) {
81+
parameters.set('namespace', urlNamespace);
82+
}
7983
loaded.responseURL = `data:text/javascript,${encodeURIComponent(cjsExports)}?${parameters.toString()}`;
8084
}
8185

tests/specs/api.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const tsFiles = {
2323
export const foo = \`foo \${bar}\` as string
2424
export const async = setTimeout(10).then(() => require('./async')).catch((error) => error);
2525
`,
26+
'cts.cts': 'export const cts = \'cts\' as string',
2627
'bar.ts': 'export type A = 1; export { bar } from "pkg"',
2728
'async.ts': 'export default "async"',
2829
'node_modules/pkg': {
@@ -507,6 +508,9 @@ export default testSuite(({ describe }, node: NodeApis) => {
507508
const { message } = await tsImport('./file.ts', import.meta.url);
508509
console.log(message);
509510
511+
const cts = await tsImport('./cts.cts', import.meta.url).then(m => m.cts, err => err.constructor.name);
512+
console.log(cts);
513+
510514
const { message: message2 } = await tsImport('./file.ts?with-query', import.meta.url);
511515
console.log(message2);
512516
@@ -522,7 +526,12 @@ export default testSuite(({ describe }, node: NodeApis) => {
522526
nodePath: node.path,
523527
nodeOptions: [],
524528
});
525-
expect(stdout).toMatch(/Fails as expected 1\nfoo bar file\.ts\?tsx-namespace=\d+\nfoo bar file\.ts\?with-query=&tsx-namespace=\d+\nFails as expected 2/);
529+
530+
if (node.supports.cjsInterop) {
531+
expect(stdout).toMatch(/Fails as expected 1\nfoo bar file\.ts\?tsx-namespace=\d+\ncts\nfoo bar file\.ts\?with-query=&tsx-namespace=\d+\nFails as expected 2/);
532+
} else {
533+
expect(stdout).toMatch(/Fails as expected 1\nfoo bar file\.ts\?tsx-namespace=\d+\nSyntaxError\nfoo bar file\.ts\?with-query=&tsx-namespace=\d+\nFails as expected 2/);
534+
}
526535
});
527536

528537
test('commonjs', async () => {

0 commit comments

Comments
 (0)