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

Unexpected top-level await error for type: module + top level await in node_modules #552

Open
4 of 6 tasks
JounQin opened this issue May 13, 2024 · 1 comment
Open
4 of 6 tasks
Labels
bug Something isn't working

Comments

@JounQin
Copy link

JounQin commented May 13, 2024

Acknowledgements

  • I read the documentation and searched existing issues to avoid duplicates
  • I understand this is a place to report a confirmed bug (not seek debugging help)
  • I understand this is a collaborative open source project, and relies on community contributions
  • I have read and understood the Contribution guide

Minimal reproduction URL

https://github.com/JounQin/test/tree/tsx

Version

4.10.1

Node.js version

v20.11.0

Package manager

yarn

Operating system

macOS

Problem & expected behavior (under 200 words)

// test.ts
import prettier from 'prettier'

const main = async () => {
  await prettier.resolveConfig('README.md')
}

main()

prettier@v3 is a dual package, I have type: module in my package.json, it should be run in ESM module, and it will resolve its own configs with import(), @1stg/prettier-config is a pure ESM package with top level await used, it should just work without runtime exception

But it just throws like the following:
node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

Error: Transform failed with 1 error:
/Users/JounQin/Workspaces/GitHub/test/node_modules/@1stg/prettier-config/base.js:20:11: ERROR: Top-level await is currently not supported with the "cjs" output format
    at failureErrorWithLog (/Users/JounQin/Workspaces/GitHub/test/node_modules/esbuild/lib/main.js:1651:15)
    at /Users/JounQin/Workspaces/GitHub/test/node_modules/esbuild/lib/main.js:849:29
    at responseCallbacks.<computed> (/Users/JounQin/Workspaces/GitHub/test/node_modules/esbuild/lib/main.js:704:9)
    at handleIncomingPacket (/Users/JounQin/Workspaces/GitHub/test/node_modules/esbuild/lib/main.js:764:9)
    at Socket.readFromStdout (/Users/JounQin/Workspaces/GitHub/test/node_modules/esbuild/lib/main.js:680:7)
    at Socket.emit (node:events:518:28)
    at addChunk (node:internal/streams/readable:559:12)
    at readableAddChunkPushByteMode (node:internal/streams/readable:510:3)
    at Readable.push (node:internal/streams/readable:390:5)
    at Pipe.onStreamRead (node:internal/stream_base_commons:190:23) {
  name: 'TransformError'
}

Node.js v20.11.0

I'm not for sure why node_modules packages are also transformed by esbuild, is that expected to be transformed or configurable?

Contributions

  • I'm interested in working on this issue
  • I'm willing to offer financial support
@JounQin JounQin added bug Something isn't working pending triage labels May 13, 2024
@privatenumber
Copy link
Owner

privatenumber commented May 13, 2024

Thanks for the bug report @JounQin

tsx supports loading ESM in Node's CommonJS mode by compiling it to CommonJS syntax... so if a module file (.mjs or package.json#type: 'module') gets require()'d, it gets converted to CommonJS. Since require() is sync, it cannot handle top-level await. Basically, this is the same limitation as the recent improvement in Node: nodejs/node#51977

Looking at Prettier's code, it looks like it tries require() first, and if it fails, it tries import(). Because we want it to go to import() (which supports TLA), you can opt-in only to the ESM enhancement:

node --import tsx/esm scripts/test

This way, the require() will fail, and skip to the import() and the TLA file will get loaded expectedly.

Since it catches the ERR_REQUIRE_ESM error, it might be a good idea to make tsx emit that instead of or in addition to the TLA error.


By the way, if you have any sway in how Prettier loads the config files, the new tsImport() function might be useful for supporting TypeScript configs.

It's being experimented in ESLint via eslint/eslint#18440

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants