Skip to content

Commit

Permalink
feat(esm/api): tsImport() to support loading CommonJS files
Browse files Browse the repository at this point in the history
  • Loading branch information
privatenumber committed Jun 7, 2024
1 parent c703300 commit 0eb4e91
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 7 deletions.
4 changes: 0 additions & 4 deletions docs/node/ts-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ The current file path must be passed in as the second argument to resolve the im

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

::: warning Caveat
CommonJS files are currently not enhanced due to this [Node.js bug](https://github.com/nodejs/node/issues/51327).
:::

## ESM usage

```js
Expand Down
7 changes: 6 additions & 1 deletion src/cjs/api/module-resolve-filename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ export const interopCjsExports = (
}

const searchParams = new URLSearchParams(request.slice(queryIndex + 1));
const realPath = searchParams.get('filePath');
let realPath = searchParams.get('filePath');
if (realPath) {
const namespace = searchParams.get('namespace');
if (namespace) {
realPath += `?namespace=${encodeURIComponent(namespace)}`;
}

// The CJS module cache needs to be updated with the actual path for export parsing to work
// https://github.com/nodejs/node/blob/v22.2.0/lib/internal/modules/esm/translators.js#L338
Module._cache[realPath] = Module._cache[request];
Expand Down
6 changes: 6 additions & 0 deletions src/esm/api/ts-import.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { register as cjsRegister } from '../../cjs/api/index.js';
import { register, type TsconfigOptions } from './register.js';

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

// Keep registered for hanging require() calls
cjsRegister({
namespace,
});

/**
* We don't want to unregister this after load since there can be child import() calls
* that need TS support
Expand Down
6 changes: 5 additions & 1 deletion src/esm/hook/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const load: LoadHook = async (
return nextLoad(url, context);
}

if (data.namespace && data.namespace !== getNamespace(url)) {
const urlNamespace = getNamespace(url);
if (data.namespace && data.namespace !== urlNamespace) {
return nextLoad(url, context);
}

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

Expand Down
11 changes: 10 additions & 1 deletion tests/specs/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const tsFiles = {
export const foo = \`foo \${bar}\` as string
export const async = setTimeout(10).then(() => require('./async')).catch((error) => error);
`,
'cts.cts': 'export const cts = \'cts\' as string',
'bar.ts': 'export type A = 1; export { bar } from "pkg"',
'async.ts': 'export default "async"',
'node_modules/pkg': {
Expand Down Expand Up @@ -507,6 +508,9 @@ export default testSuite(({ describe }, node: NodeApis) => {
const { message } = await tsImport('./file.ts', import.meta.url);
console.log(message);
const cts = await tsImport('./cts.cts', import.meta.url).then(m => m.cts, err => err.constructor.name);
console.log(cts);
const { message: message2 } = await tsImport('./file.ts?with-query', import.meta.url);
console.log(message2);
Expand All @@ -522,7 +526,12 @@ export default testSuite(({ describe }, node: NodeApis) => {
nodePath: node.path,
nodeOptions: [],
});
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/);

if (node.supports.cjsInterop) {
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/);
} else {
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/);
}
});

test('commonjs', async () => {
Expand Down

0 comments on commit 0eb4e91

Please sign in to comment.