tsx icon indicating copy to clipboard operation
tsx copied to clipboard

Cannot import named export from a typescript file in a workspace package if entry is using es modules and other package is not.

Open remorses opened this issue 2 years ago • 16 comments

Cannot import named export from a typescript file in a workspace package if entry is using es modules and other package is not.

tsx will throw

SyntaxError: The requested module 'ts-package/src/file' does not provide an export named 'x'

I know this could be considered a node limitation but maybe we can compile typescript files to esm if entry is esm (even if workspace package does not have "type": "module"

Repro: https://github.com/remorses/tsx-repros

pnpm i
pnpm run import-ts-file

remorses avatar Jun 19 '22 10:06 remorses

I investigated this and it might be tough to fix.

When a ESM file imports a CommonJS file, you would imagine a loader could simply transform the CommonJS file to ESM syntax on import so it's interoperable.

However, Node.js actually bypasses the CJS loader and reads the CommonJS file directly from disk to statically detect named exports with cjs-module-lexer.

Static analysis is likely necessary because the import statement must assert that the named export exists before running the file. And even if Node.js adds a hook to transform the file before statically analyzing it, we won't be able to support older Node.js versions.

I tackled this problem before (https://github.com/evanw/esbuild/issues/2248) but arrived at the same conclusion. However, I managed to fix it for dynamic imports.

privatenumber avatar Jun 21 '22 03:06 privatenumber

😢 Same issue here, go back to esbuild-node-loader for a while.

loynoir avatar Aug 01 '22 03:08 loynoir

Found some ^bind.*$ and ^[a-z]{3}$ and [A-Z]+_PROJECT_[A-Z]+ not working

loynoir avatar Aug 01 '22 03:08 loynoir

@loynoir

esbuild-node-loader doesn't support this either so I think you're confusing this with a different issue.

Found some ^bind.*$ and ^[a-z]{3}$ and [A-Z]+_PROJECT_[A-Z]+ not working

I have no idea what you're talking about here. Can you open a new issue?

privatenumber avatar Aug 01 '22 16:08 privatenumber

@privatenumber No, all three exports works in esbuild-node-loader.

loynoir avatar Aug 01 '22 16:08 loynoir

$ tsx ./src/cli.mts
SyntaxError: The requested module './util' does not provide 
an export named 'XXX_PROJECT_XXX'

$ node --loader esbuild-node-loader ./src/cli.mts
OK

loynoir avatar Aug 01 '22 16:08 loynoir

Can you upload that reproduction?

privatenumber avatar Aug 01 '22 16:08 privatenumber

Ah, found workaround, maybe reproduce another day.

Given

  • cli.mts
  • util.ts, .ts by mistakenly not aligned to .mts

Known

  • tsx syntax error about no export name
  • esbuild-node-loader working

Workaround

  • rename util.ts to util.mts

After

  • both tsx and esbuild-node-loader working

loynoir avatar Aug 02 '22 00:08 loynoir

Sorry, I don't understand what you're saying. I'm going to hide your comments because they're cryptic and makes this thread harder to follow.

In the future, please provide a reproduction when you report a problem. That would be the best way to communicate it, especially if it's not easy to describe it.

privatenumber avatar Aug 02 '22 01:08 privatenumber

I have just crashed against what seems like the same problem that @loynoir was trying to explain...

Essentially, there doesn't seem to be a way to import ts / js + d.ts modules from an mts entry point module.

My situation is that I have a non-module workspace in which I'm creating a typescript server module. This has to have an mts extension to convince typescript that it doesn't share a single huge scope with everything around.

The modules that I am trying to import are generated from proto-gen-es and the most correct & compatible version of generated artefacts is the aforementioned js + d.ts option, with imports explicitly including .js.

That however doesn't work with tsx since it keeps replacing the .js imports extension to .ts. What does work is renaming everything to mts and importing with mjs

To illustrate, here is what I observed

/**
 * - "a.js" & "a.d.ts" exist
 * - results in "tsx" trying to import "a.ts"
 */
import {a} from `../gen/a.js`;

/**
 * - same files as before
 * - tries and fails to import verbatin (extentionless)
 */
import {a} from `../gen/a`;

/**
 * - "a.ts" exists
 * - produces following error, probably meaning it does't consider it a module:
 *     index.mts:1
 *     import {a} from 'a.js';
 *             ^
 *     SyntaxError: The requested module 'a.js' does not provide an export named 'a'
 */
import {a} from '../gen/net_pb.js';

Considering that these are my options of generated code, I'm seemingly left with an only option… generate the code with options target=ts,import_extension=.mjs and rename all files to mts

kubijo avatar Feb 05 '23 17:02 kubijo

I have also tried adding "type": "module" into package.json, renaming the entry module to ts and generating js+dts modules with js imports, but that also doesn't work producing errors in transitive imports where it replaces js extensions to ts and fails.

What does work is:

  • "type": "module" context
  • index.ts
  • ts files with js imports

With all that said, my context is Yarn V3 workspace with PNP enabled and [email protected]. Yarn, however, doesn't seem to be the problem here, the algorithm for manipulation with extensions does.

It might also be worth mentioning that the type: module thing can yet prove problematic, since ES modules are still a bag of wet mice when it comes to interop between the plethora of modules for frontend development (storybook, yarn PnP, linters)

kubijo avatar Feb 05 '23 17:02 kubijo

Worth mentioning this bug prevents named imports from lodash: https://github.com/privatenumber/tsx/issues/361

Also, this is finally fixable when this lands in Node https://github.com/nodejs/node/issues/50839

privatenumber avatar Dec 05 '23 15:12 privatenumber

Hi @privatenumber, I've just bumped into this issue trying to migrate a CJS project to ESM adding "type": "module" to its package.json. The same thing - either I can't load non-esm packages from the monorepo, or (if I add "type": "module" everywhere) load named exports from lodash.

I see the PR you mentioned is merged to node, so is there any change of revisiting this bug in tsx now?

raveclassic avatar Jan 18 '24 16:01 raveclassic

Currently blocked by https://github.com/nodejs/node/issues/51327

privatenumber avatar Feb 22 '24 00:02 privatenumber

So no work around? It seems to be affecting https://github.com/automation-stack/node-machine-id as well

thoroc avatar Feb 26 '24 13:02 thoroc

The issue is acknowledged, and the blocker is referenced above. Contributions to the Node project to fix the issue will be appreciated.

To maintain focus, I'm locking this thread until unblocked, but encourage constructive solution-oriented dialogue through PRs.

privatenumber avatar Feb 27 '24 03:02 privatenumber