Skip to content

markabrahams/tsx-export-problem

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tsx package.json exports Issue - MRE

This is a Minimal Reproducible Example (MRE) demonstrating an issue with tsx when handling package.json exports fields. tsx transpiles TypeScript source files in node_modules instead of respecting the exports field that points to built JavaScript files.

Note: This MRE includes a test using the real npm package @tastytrade/api which demonstrates the issue. The MRE also includes example local packages to show the structure, but the actual failure occurs with real npm packages.

Issue Description

When a package has both:

  • TypeScript source files in lib/ directory
  • Built JavaScript files in dist/ directory
  • package.json exports field pointing to dist/ files

Expected behavior: tsx should use the built files from dist/ as specified in the exports field.

Actual behavior with tsx: tsx finds and transpiles the TypeScript source files from lib/ instead, bypassing the exports field. This can cause module resolution failures when those source files import from other packages.

Files

  • src/test-tastytrade.ts - Main test using real npm package @tastytrade/api (demonstrates the issue)
  • example-package/ - Example package structure (for reference only, shows lib/ vs dist/ pattern)
    • lib/index.ts - TypeScript source (should NOT be used)
    • dist/index.js - Built JavaScript (SHOULD be used per exports field)
    • package.json - Package configuration with exports field
  • dependency-package/ - Example dependency using exports field (for reference only)
  • package.json - Project configuration with tsx and @tastytrade/api dependencies
  • tsconfig.json - TypeScript configuration

Reproduction Steps

  1. Install dependencies:

    npm install
  2. Test with tsx using real npm package (demonstrates the issue):

    npm run test:tastytrade
    # or: tsx src/test-tastytrade.ts

    Expected output: This will fail with:

    SyntaxError: The requested module '@dxfeed/dxlink-api' does not provide an export named 'DXLinkFeed'
        at /path/to/node_modules/@tastytrade/api/lib/quote-streamer.ts:1
    

    This demonstrates that tsx is transpiling the source files from lib/ instead of using the built files from dist/ as specified in the exports field.

  3. Test with tsx using local package:

    npm run test:tsx
    # or: tsx src/index.ts

    Note: With file: dependencies, this may work because tsx handles local packages differently. The issue manifests with real npm packages.

  4. Test with Node.js directly (shows correct behavior):

    npm run build
    npm run test:node
    # or: node dist/index.js

    Expected output: Works correctly because Node.js respects the package.json exports field and uses the built files from dist/.

The Problem

What Should Happen

  1. src/test-tastytrade.ts imports from @tastytrade/api
  2. Node.js/tsx resolves @tastytrade/api via package.json exports field
  3. exports field points to dist/tastytrade-api.js
  4. Uses the built file from dist/

What Actually Happens with tsx

  1. src/test-tastytrade.ts imports from @tastytrade/api
  2. tsx finds TypeScript source files in lib/ directory
  3. tsx transpiles lib/quote-streamer.ts instead of using the built file from dist/
  4. When that transpiled file imports from @dxfeed/dxlink-api, module resolution fails

Expected vs Actual Behavior

With Node.js (correct):

// Uses dist/tastytrade-api.js as specified in exports field
import TastytradeClient from '@tastytrade/api';
// ✅ Works correctly

With tsx (incorrect):

// Transpiles lib/quote-streamer.ts instead of using built file from dist/
import TastytradeClient from '@tastytrade/api';
// ❌ Fails with module resolution errors

Real-World Example

The MRE includes a test using @tastytrade/api which demonstrates the issue:

Test File: src/test-tastytrade.ts

import TastytradeClient from '@tastytrade/api';
console.log('TastytradeClient imported:', typeof TastytradeClient);

Running the Test

npm run test:tastytrade
# or: tsx src/test-tastytrade.ts

Expected Error

SyntaxError: The requested module '@dxfeed/dxlink-api' does not provide an export named 'DXLinkFeed'
    at /path/to/node_modules/@tastytrade/api/lib/quote-streamer.ts:1

Why this happens:

  • @tastytrade/api has TypeScript sources in lib/ and built files in dist/
  • The package.json exports field points to dist/tastytrade-api.js
  • tsx transpiles lib/quote-streamer.ts instead of using the built file
  • When that transpiled file imports from @dxfeed/dxlink-api, module resolution fails

Workaround:

# Compile first, then run with Node.js
tsc src/test-tastytrade.ts
node src/test-tastytrade.js  # Works correctly

Real-World Impact

This issue affects packages that:

  • Ship both TypeScript source and compiled JavaScript
  • Use the exports field to point consumers to built files
  • Have dependencies that also use exports fields

When tsx transpiles the source files, it bypasses the package's intended module resolution, which can cause:

  • Module resolution failures
  • Incorrect dependency resolution
  • Errors about missing exports

Known Affected Packages

  • @tastytrade/api - Has TypeScript sources in lib/ and built files in dist/, with exports pointing to dist/. When tsx transpiles lib/quote-streamer.ts, it fails to resolve imports from @dxfeed/dxlink-api because the transpilation context doesn't properly handle the exports field.

Environment

  • Node.js: Any version (exports field support)
  • tsx: 4.21.0 (tested version)
  • TypeScript: 5.6.3

Workaround

Until this is fixed in tsx, you can:

  1. Compile TypeScript first, then run with Node.js directly (as shown in test:node)
  2. Use tsc to build, then node to run (avoids tsx's transpilation)
  3. Avoid packages that have this structure, or ensure they don't ship TypeScript sources

Related Issues

This demonstrates that tsx does not properly respect the package.json exports field when resolving modules, instead preferring to transpile TypeScript source files it finds in node_modules.

Potentially Related tsx Issues

Note: No exact match found for this specific issue (transpiling source files instead of respecting exports field). This may be a new issue to report.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors