Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
remorses opened this issue Jun 19, 2022 · 23 comments
Labels

Comments

@remorses
Copy link

remorses commented Jun 19, 2022

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
@privatenumber privatenumber added the bug Something isn't working label Jun 20, 2022
privatenumber added a commit that referenced this issue Jun 21, 2022
@privatenumber
Copy link
Owner

privatenumber commented Jun 21, 2022

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 (evanw/esbuild#2248) but arrived at the same conclusion. However, I managed to fix it for dynamic imports.

@loynoir

This comment was marked as off-topic.

@loynoir

This comment was marked as off-topic.

@privatenumber

This comment was marked as off-topic.

@loynoir

This comment was marked as off-topic.

@loynoir

This comment was marked as off-topic.

@privatenumber

This comment was marked as off-topic.

@loynoir

This comment was marked as off-topic.

@privatenumber
Copy link
Owner

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.

@kubijo
Copy link

kubijo commented Feb 5, 2023

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
Copy link

kubijo commented Feb 5, 2023

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 node@19.6.0.
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)

@privatenumber
Copy link
Owner

privatenumber commented Dec 5, 2023

Worth mentioning this bug prevents named imports from lodash: #361

Also, this is finally fixable when this lands in Node nodejs/node#50839

@raveclassic

This comment was marked as resolved.

junhoyeo added a commit to mint-cash/burndrop-contracts that referenced this issue Feb 19, 2024
maxpatiiuk added a commit to specify/specify7 that referenced this issue Feb 20, 2024
Workaround for privatenumber/tsx#38
But also, it's time for those to be TypeScript
Note, I added `// @ts-nocheck` to the top of the files to suppress
TypeScript errors for now, but this should be removed once we move way
from Backbone
@privatenumber
Copy link
Owner

Currently blocked by nodejs/node#51327

@thoroc

This comment has been minimized.

@privatenumber
Copy link
Owner

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
Copy link
Owner

Repository owner unlocked this conversation Jun 6, 2024
@privatenumber
Copy link
Owner

🎉 This issue has been resolved in v4.12.0

If you appreciate this project, please consider supporting this project by sponsoring ❤️ 🙏

@privatenumber
Copy link
Owner

I managed to fix this without nodejs/node#51327 for Node v20.11+ & v21.3+

CommonJS files loaded via ESM hook doesn't have full CJS support, so the approach I arrived at is actually more compatible.

Please try it out and let me know!

@jatwood
Copy link

jatwood commented Jun 17, 2024

Worth mentioning this bug prevents named imports from lodash: #361

I came here after running into this issue with named exports in lodash and as far as I can tell the issue with using named exports in lodash is an issue with the lodash package and not related to tsx, i.e. it still doesn't work if you just use tsc

@emilbonnek
Copy link

Just installed tsx to try it out, but also getting this error from lodash (and another import: http-errors).

import { trimEnd, trimStart } from 'lodash';
         ^
SyntaxError: The requested module 'lodash' does not provide an export named 'trimEnd'

I tested with these versions:
node: 21.7.1
tsx: 4.15.6
lodash: 4.17.21
http-errors: 2.0.0

@saltman424
Copy link

^ same using node-postgres with:
node: 20.11.1
tsx: 4.15.6
pg: 8.12.0

@privatenumber
Copy link
Owner

privatenumber commented Jun 18, 2024

tsx doesn't add more CJS parsing functionality to Node—it just signals how it could parse exports from TypeScript files.

That said, lodash is expected because it doesn't export in a way that's statically parsable by Node's CJS lexer. If you're importing from Module mode, you should probably just use lodash-es. Alternatively, you can file an issue with them to either directly export to module.exports instead of assigning module to a variable. Or, you can ask them to add export annotations like esbuild does at the bottom here for Node to parse from.

I was able to successfully do named imports from both http-errors and node-postgres. Please file a bug report if you can prove that it doesn't.

I'm going to lock this thread because it's being abused as a way to file bug reports without providing reproduction. This often wastes my time because I investigate and it turns out to be working fine.

Repository owner locked and limited conversation to collaborators Jun 18, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

9 participants