evanw / esbuild

An extremely fast bundler for the web
https://esbuild.github.io/
MIT License
37.86k stars 1.12k forks source link

Uncaught SyntaxError: Cannot use import statement outside a module in Rails project #1765

Closed ghost closed 2 years ago

ghost commented 2 years ago

In my Rails project I am facing the following issues. The first issue was a known one as in #946 "ReferenceError: require is not defined". By reading through those issues and PR's I've added the following line to my build (with --banner).

--banner:js='import { createRequire as topLevelCreateRequire } from \"module\";\n const require = topLevelCreateRequire(import.meta.url);'

Now the error beneath occurs. I've already tried set the type to module in the package.json etc.

Uncaught SyntaxError: Cannot use import statement outside a module

This is a webapplication requiring some of the commonJS stuff it seems I've also added --platform=node to the build script to fix issues as:

ReferenceError: fs is not defined

Is there a way to bundle this all for the web with all requirements?

{
  "name": "app",
  "type": "module",
  "private": "true",
  "dependencies": {
    "@hotwired/stimulus": "^3.0.1",
    "@hotwired/turbo-rails": "^7.0.1",
    "@nomiclabs/hardhat-ethers": "^2.0.2",
    "@openzeppelin/contracts": "^4.3.2",
    "dotenv": "^10.0.0",
    "esbuild": "^0.13.13",
    "ethers": "^5.0.0",
    "hardhat": "^2.6.8",
    "requirejs": "^2.3.6"
  },
  "scripts": {
    "type": "module",
    "build": "esbuild app/javascript/*.* --bundle --platform=node --outdir=app/assets/builds --banner:js='import { createRequire as topLevelCreateRequire } from \"module\";\n const require = topLevelCreateRequire(import.meta.url);'"
  }
}
hyrious commented 2 years ago

To answer your question title, you should know how Node.js treat some file as es modules, either:

  1. in the file's folder (or parent folder), there is package.json with "type": "module"
  2. without 1, let file name end with .mjs

You may know require can not be used in es modules context natively from #946. But both banner or inject won't save you perfectly because once a file is bundled, require() in it can not resolve relative paths correctly, and those files not resolved won't be included in outdir, so you got require(something_wont_be_found).

If your dependencies (not the "dependencies" field in package.json, but libraries your code really depends on) include these dynamic require calls, the best way for esbuild would be you edit these code to "correct" one (not using dynamic require).

evanw commented 2 years ago

What you are trying to do is not going to work. Passing --platform=node tells esbuild that you're going to be running the result in node, but then you are running the result in the browser instead. This may break things in a lot of ways. You should be using --platform=browser instead if you are going to be running it in the browser.

ReferenceError: fs is not defined

It sounds like you are trying to use packages that aren't designed to work on the web. There may not be much you can do about that here. If a library needs fs to work, there's no simple fix because there's no equivalent file system on the web. You could try polyfilling it but that sounds pretty complicated. I'm not sure what polyfill to recommend but when you have one, you can try intercepting fs with an esbuild plugin to substitute the path of your polyfill instead: https://esbuild.github.io/plugins/#resolve-callbacks. There's some links about fs polyfills here: https://github.com/webpack/node-libs-browser/issues/72. But you may be better off using some other similar package that is actually intended to be used on the web, or maybe even some other bundler that attempts to polyfill fs for you.

Uncaught SyntaxError: Cannot use import statement outside a module

You can read about how import statements work here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import. Specifically you need <script type="module"> to load code containing import in the browser. That doesn't help you in this case because it sounds like you're trying to import node APIs in the browser, which won't work. But if you did need to load code containing import in the browser, that's how you would do it.

evanw commented 2 years ago

Closing due to age.

Jaikant commented 2 years ago

I faced a similar error. Adding type=module resolved it. But what I do not understand is, the code did not give the error for a pure "JS" app, which did the import. When I moved the code into React, the error popped up. (Which went away when I added type=module).

If anyone knows why?