nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.57k stars 2.36k forks source link

Unable to execute node when importing ES Modules - using @nx/js:node executor #21928

Open richmondp opened 8 months ago

richmondp commented 8 months ago

Current Behavior

When attempting to execute a node/express application using @nx/js:node executor, if the application imports an ES Module library with the @nrwl/node:node executor the following error is displayed.

.../node_modules/@nx/js/src/executors/node/node-with-require-overrides.js:18
        return originalLoader.apply(this, arguments);
                              ^
Error [ERR_REQUIRE_ESM]: require() of ES Module .../node_modules/lodash-es/lodash.js not supported.
Instead change the require of lodash.js in null to a dynamic import() which is available in all CommonJS modules.

If, I then make some changes to the application in an attempt to support importing ESM modules, I am then faced with another issue in that I cannot import any workspace libraries.

ERROR in ./apps/webpack-mod/src/main.ts 12:0-46 Module not found: Error: Can't resolve '@esm-test/cjs-lib' in '.../esm-test/apps/webpack-mod/src'

Expected Behavior

Should be able to execute a project that imports an ES Module library and workspace libraries which themselves, may or may not import other 3rd party ES Module libraries.

And/or potentially, in the interim, some documentation describing how this can best be achieved in current versions. From reading other threads, this seems to be a common issue raised and with ESM libraries being the recommendation moving forward more and more libraries will move this way. I am assuming there is a way to deal with this, but as yet, I have been unable to find a correct approach (see below for details of each approach). If it is not something that can be easily added to the library then I am open to writing my own executors/generators but I am a little lost as to how to proceed.

I know the examples in the attached repo are very basic and in some cases I could just downgrade a version of a library I need but I have some libraries that this is not possible.

As it stands right now, I am unable to continue using nx because of the issues in executing apps with ESM library dependencies but I don't want to do that because I like the NX ecosystem.

GitHub Repo

https://github.com/b-simpson12/esm-test

Steps to Reproduce

I have attached an git repo of an nx workspace containing a number of different node/express applications. In each app I am trying to import a 3rd party ESM library (in this example lodash-es) as well a 3 workspace JS libraries.

  1. The first library is just a very basic library with no additional imports (out-of-the-box JS library)
  2. The second is a JS library that also imports an ESM library (in this example lodash-es) but no other modifications have been made
  3. And the third library is similar to library #2 but it has been modified to set format: ["esm"] in the build config as well as modifications to package.json and tsconfig.lib.json to set it up to be an esm library.

Note: All app have a README outlining how it was created and what changes were made after the out-of-the-box generated code

Applications

dynamic-import: Express application using Dynamic Imports for esm libraries

  1. nx run dynamic-import:build - (Works)
  2. nx run dynamic-import:serve - (Fails with Error [ERR_REQUIRE_ESM]: require() of ES Module)

esbuild-mod: Express application which has been modified to use @nx/esbuild:esbuild as its build executor

  1. nx run esbuild-mod:build - (Fails - does not recognize libs) - WORKS with only esm module import
  2. nx run esbuild-mod:serve - (Fails - does not recognize libs) - WORKS with only esm module import

issue-10565: Node application - based of the directions in Issue 10565

I wanted to see what the outcome of PR 10414 was which seem to indicate that it would fix this issue.

nx run issue-10565:build - (Fails - does not recognize libs) - WORKS with only esm module imports nx run issue-10565:serve - (Fails - does not recognize libs) - WORKS with only esm module imports

issue-10565: Node application - based of the directions in Issue 10565

Basically the same as issue-105655 but just using the express generator

  1. nx run express-10565:build - (Fails - does not recognize libs) - WORKS with only esm module imports
  2. nx run express-10565:serve - (Fails - does not recognize libs) - WORKS with only esm module imports

node-app: Out-of-the-box Node application

Used to compare the differences between the standard generated Node app and the modifications made in issue-10565

  1. nx run node-app:build - Works
  2. nx run node-app:serve - Fails - Error [ERR_REQUIRE_ESM]: require()

preset-express: Express application created with the workspace using --preset=express

  1. nx run preset-express:build - Works
  2. nx run preset-express:serve - Fails - Error [ERR_REQUIRE_ESM]: require() on esm library import or workspace libraries that have esm imports

webpack-mod: Express applications with modifications to the webpack executor (among other things)

This was based off information in a number of different threads and was pieced together to form this app

  1. nx run webpack-mod:build - (Fails - does not recognize libs) - WORKS with only esm module import
  2. nx run webpack-mod:serve - (Fails - does not recognize libs) - WORKS with only esm module import

control: An express app with no additional imports

Used as a baseline

  1. nx run control:build - Works
  2. nx run control:serve - Works

Nx Report

Node   : 18.18.0
   OS     : darwin-arm64
   npm    : 9.8.1

   nx (global)        : 18.0.2
   nx                 : 18.0.4
   @nx/js             : 18.0.4
   @nx/jest           : 18.0.4
   @nx/linter         : 18.0.4
   @nx/eslint         : 18.0.4
   @nx/workspace      : 18.0.4
   @nx/devkit         : 18.0.4
   @nx/esbuild        : 18.0.4
   @nx/eslint-plugin  : 18.0.4
   @nx/express        : 18.0.4
   @nx/nest           : 18.0.4
   @nx/node           : 18.0.4
   @nrwl/tao          : 18.0.4
   @nx/vite           : 18.0.4
   @nx/web            : 18.0.4
   @nx/webpack        : 18.0.4
   typescript         : 5.3.3

Failure Logs

.../node_modules/@nx/js/src/executors/node/node-with-require-overrides.js:18
        return originalLoader.apply(this, arguments);
                              ^
Error [ERR_REQUIRE_ESM]: require() of ES Module .../node_modules/lodash-es/lodash.js not supported.
Instead change the require of lodash.js in null to a dynamic import() which is available in all CommonJS modules.

Package Manager Version

No response

Operating System

Additional Information

No response

alx-andru commented 7 months ago

@richmondp have you found a solution to this? Running into the same issue

enchorb commented 5 months ago

+1, occurring in v19 also

danielsharvey commented 5 months ago

See also #11335 and my comment https://github.com/nrwl/nx/issues/11335#issuecomment-2136663019

danielsharvey commented 5 months ago

In case it is useful, I've published a Node executor including ESM module resolution for buildable libraries within Nx workspaces.

https://www.npmjs.com/package/@harves/nx-node-esm-plugin

scriptify commented 4 months ago

+1. When will @nx/js:node be ESM compatible?

lourd commented 4 months ago

Also just ran headlong into this. Using nx v19.0.3, developing a Node.js application using @nx/esbuild and @nx/node. Added a library that's published as esm and ran into the same error described by OP.

I switched the format option in the build target from cjs, the default, to esm, which solved the original problem, but then created another problem in that all my imports don't have .js file suffixes and therefore couldn't be found:

Error: Cannot find module '/home/ubuntu/.../app' imported from /home/ubuntu/.../dist/.../main.js
    at finalizeResolution (node:internal/modules/esm/resolve:265:11)
    at moduleResolve (node:internal/modules/esm/resolve:933:10)
    at defaultResolve (node:internal/modules/esm/resolve:1157:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:383:12)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:352:25)
    at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:227:38)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:87:39)
    at link (node:internal/modules/esm/module_job:86:36)

I searched around for and found some issues on the esbuild repo like this one about what to do https://github.com/evanw/esbuild/issues/1343 — the TS team says you should manually add .js extensions to your imports instead of having your bundler do it.

I went ahead and did that for my relative imports. But then still ran into an error where I'm importing libraries from elsewhere in my monorepo and those can't be found.

What I ended up going with was specifying "bundle": true for the production and development configurations of my build target, which allowed me to side-step the library import error and having to add .js extensions to my imports. If it's all in one file there's no need to import anything! 🙈

Would definitely appreciate an easier, clearer path here.

Niewdanka commented 1 month ago

Is there any workaround? This problem has been around for about 2 years now.

gionkunz commented 1 month ago

For me it works by just changing the target and module in tsconfig.json to ES2022 and changing the @nx/esbuild:esbuild executor options to "format": ["esm"],

itizarsa commented 2 weeks ago

For me it works by just changing the target and module in tsconfig.json to ES2022 and changing the @nx/esbuild:esbuild executor options to "format": ["esm"],

@gionkunz Could you share a working repo? I have the same options, but it's not working