Tags: privatenumber/tsx
Tags
fix: decode typed loader source ## Problem TypeScript modules can silently evaluate as no-op modules when tsx's sync ESM hooks compose with an async `module.register()` hook. Node's load hook contract allows `source` to be `string | ArrayBuffer | TypedArray`. In the async-hook composition path, Node can hand tsx a plain `Uint8Array`; calling `.toString()` on that produces comma-joined byte values instead of source text. esbuild parses that as a valid no-op module, so a dynamic import can resolve with an empty namespace. ## Changes - Decode non-string loader source with `TextDecoder` - Keep string source unchanged - Apply the decode path in both async and sync ESM load hooks - Add a regression test for a TypeScript entrypoint that registers a pass-through async hook and dynamically imports a sibling `.ts` file
fix: support Node 20.11/21.2 import.meta paths ## Problem - Node officially exposes `import.meta.dirname` and `import.meta.filename` for local `file:` ESM starting in Node v20.11.0 and v21.2.0 ([nodejs/node#48740](nodejs/node#48740), [v20.11.0 docs](https://github.com/nodejs/node/blob/v20.11.0/doc/api/esm.md#L328-L362)). - tsx's CJS transform only preserved `import.meta.url`, so transformed modules on those Node versions saw `dirname` / `filename` as missing and `Object.hasOwn(import.meta, ...)` stayed false. - The failure shows up when tsx transforms an ESM file to CJS, including direct `.js` execution and CJS `require()` loading an ESM-shaped `.js` file. ```js // direct.js console.log({ url: import.meta.url, // before and after: file:///.../direct.js dirname: import.meta.dirname, // before: undefined; after on Node v20.11.0+ / v21.2.0+: /... filename: import.meta.filename, // before: undefined; after on Node v20.11.0+ / v21.2.0+: /.../direct.js }); // require.cjs require('./required.js'); // required.js console.log({ url: import.meta.url, // before and after: file:///.../required.js dirname: import.meta.dirname, // before: undefined; after on Node v20.11.0+ / v21.2.0+: /... filename: import.meta.filename, // before: undefined; after on Node v20.11.0+ / v21.2.0+: /.../required.js }); export const loaded = true; ``` ## Changes - Defines the whole `import.meta` object through esbuild for CJS transforms that actually reference `import.meta`, instead of patching transformed output after source maps are generated. - Preserves practical object access patterns: `import.meta.url`, `const meta = import.meta`, destructuring, computed property reads, and property descriptors. - Keeps `dirname` / `filename` gated to Node-supported versions in this major because exposing new properties on older Node could break user shims; a next major can backport them for all transformed CJS. - Adds coverage for helper-name collisions and source-map column preservation so the fix does not reintroduce shadowing bugs or post-transform map drift. - Keeps the PR scoped to path metadata; it does not try to emulate `import.meta.resolve`, `import.meta.main`, or native null-prototype `import.meta` shape.
PreviousNext