Skip to content

Uncovered cases explored from a project migration #123

@timofei-iatsenko

Description

@timofei-iatsenko

Hey there! First off, thanks for this awesome tool—it's been a huge time-saver! 🙌

I recently migrated a TypeScript project to ESM and used ts2esm to correctly add .js extensions in imports. While it worked well overall, I ran into a few challenges that I think could improve the tool even further.

1️⃣ Partial Support for tsconfig Paths

I saw that tsconfig path support works to some extent, but I had issues in an Nx monorepo with /apps/* and /libs/*. These are linked using tsconfig paths, and ts2esm only resolved the imports correctly after I moved libs inside an apps folder. My workaround looked like this:

  1. Move libs/* into apps/myapp/libs/*
  2. Update tsconfig.json paths accordingly
  3. Run ts2esm
  4. Move libs back to their original location
  5. Repeat for each app

If ts2esm could resolve paths of aliases outside the current project dir, that would be amazing!

2️⃣ Handling Package Exports and Subpath Imports

My project had a mix of modern packages (with package.json exports) and older ones using subpath imports. It would be great if ts2esm could leverage TypeScript's built-in resolver to automatically handle these cases.

Example 1: Legacy Package (lodash)

lodash doesn't have an exports field, so it requires .js in the import path:

// Before (CommonJS)
import omit from 'lodash/omit';

// After (ESM)
import omit from 'lodash/omit.js';

Currently, ts2esm doesn’t handle this, so I had to fix it manually.

Example 2: Modern Package (firebase-functions)

firebase-functions has an exports field, so no changes are needed:

// This works in both CJS and ESM
import { HttpsError } from 'firebase-functions/v1/https';

However, in cases where an import incorrectly targets a private subpath, it breaks in ESM:

// This works in CJS but fails in ESM
import type { ObjectMetadata } from 'firebase-functions/lib/v1/providers/storage';

Since TypeScript correctly flags these issues, maybe ts2esm could warn or ignore them?

3️⃣ Dynamic Imports Are Not Converted

I also noticed that dynamic imports aren’t updated with .js extensions:

const module = await import('./my-function.function'); 
// Expected:
const module = await import('./my-function.function.js'); 

Handling this would make ts2esm even more powerful!

Would love to hear your thoughts! Let me know if I can help test anything. 😊

Sub-issues

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions