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 use import statement outside a module #490

Open
arpitdalal opened this issue Oct 14, 2023 · 30 comments · May be fixed by #527
Open

Cannot use import statement outside a module #490

arpitdalal opened this issue Oct 14, 2023 · 30 comments · May be fixed by #527

Comments

@arpitdalal
Copy link

Describe the bug
I'm getting Cannot use import statement outside a module when building it for prod, dev just works fine. I've type: module in my package.json and here's my tsconfig

"esModuleInterop": true,
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",

And when importing Cropper like this import Cropper from 'react-easy-crop' gives this error when using it

JSX element type 'Cropper' does not have any construct or call signatures.ts(2604)

This project did build successfully before with these exact configs but after adding react-easy-crop, it doesn't

Full error:

(node:406) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
SyntaxError: Cannot use import statement outside a module
/myapp/node_modules/react-easy-crop/index.module.js:1
import { __assign, __extends } from 'tslib';
^^^^^^
SyntaxError: Cannot use import statement outside a module

Expected behavior
It should build successfully with these configs and shouldn't throw an error when using the Cropper component

@ValentinH
Copy link
Owner

Thanks for sharing this issue. However, could you please share a repository with a minimal reproducing example so that I can easily investigate?

@arpitdalal
Copy link
Author

@ValentinH Thanks for looking into this
Here's minimal reproducing example, clone it, run cp .env.example .env, run npm run setup and then npm run dev to run the dev server. Everything will run with flying colors. You can go to localhost:3000 to see the app running locally, you can log in with kody/kodylovesyou (username/password) and then go to localhost:3000/settings/profile/photo, then click Change button to change the image and select any image then you'll see the Cropper component to crop the picture that you've selected, you can click the save button and it'll actually crop the image and save it in the db. Functionally everything works!!
The only issue is when you build the app. Run npm run build to build the app > run npm start to start in prod mode and then you'll see the error on your console, this is what I get:

(node:52258) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
SyntaxError: Cannot use import statement outside a module
/mre-react-easy-crop/node_modules/react-easy-crop/index.module.js:1
import { __assign, __extends } from 'tslib';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at internalCompileFunction (node:internal/vm:73:18)
    at wrapSafe (node:internal/modules/cjs/loader:1178:20)
    at Module._compile (node:internal/modules/cjs/loader:1220:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
    at Module.load (node:internal/modules/cjs/loader:1119:32)
    at Function.Module._load (node:internal/modules/cjs/loader:960:12)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:169:29)
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)

I believe it has to do with the module.js extension maybe 🤷‍♂️

@arpitdalal
Copy link
Author

Also, in the editor if you go to /app/routes/settings+/profile.photo.tsx file, you'll see the TS error that I mentioned in the issue:

JSX element type 'Cropper' does not have any construct or call signatures.ts(2604)

@arpitdalal
Copy link
Author

temp fix with patch-package:

diff --git a/node_modules/react-easy-crop/index.module.js b/node_modules/react-easy-crop/index.module.js
index 11cf90c..f50b7dd 100644
--- a/node_modules/react-easy-crop/index.module.js
+++ b/node_modules/react-easy-crop/index.module.js
@@ -1,7 +1,30 @@
-import { __assign, __extends } from 'tslib';
 import React from 'react';
 import normalizeWheel from 'normalize-wheel';
 
+function extendStatics(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+function __assign () {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    }
+    return __assign.apply(this, arguments);
+}
+
 /**
  * Compute the dimension of the crop area based on media size,
  * aspect ratio and optionally rotation
diff --git a/node_modules/react-easy-crop/package.json b/node_modules/react-easy-crop/package.json
index c807ad1..059d843 100644
--- a/node_modules/react-easy-crop/package.json
+++ b/node_modules/react-easy-crop/package.json
@@ -1,6 +1,7 @@
 {
   "name": "react-easy-crop",
   "version": "5.0.2",
+  "type": "module",
   "description": "A React component to crop images/videos with easy interactions",
   "homepage": "https://ValentinH.github.io/react-easy-crop/",
   "keywords": [

Essentially what is changes is that I added type: module to the package.json and then removed the import from tslib in index.module.js and declared the relevant functions in the file itself. This fix works for me and my use case, I HAVEN'T tested every function and every way you can use react-easy-crop, so test this solution for your use case

@ValentinH
Copy link
Owner

ValentinH commented Oct 19, 2023

Thanks for sharing this. I'm sorry I haven't found the time to look into this so far. To be honest, these kinds of issues are pretty complex to deal with as a lib maintainer because there are so many frameworks that behave differently in the way they consume packages. I might fix it for your use case and break it for others...

@arpitdalal
Copy link
Author

It's not just my use case, it doesn't work with Remix at all, here's a minimal application of remix and react-easy-crop: https://stackblitz.com/edit/remix-run-remix-mryppq?file=app%2Froutes%2F_index.tsx

@ValentinH
Copy link
Owner

Thanks for submitting this minimal reproduction.

I had a look and so far I don't know what should I change to have this working on Remix 🙈

@gajus
Copy link

gajus commented Dec 11, 2023

This could be solved by providing an ESM build.

@ValentinH
Copy link
Owner

This is already the case.

@gajus
Copy link

gajus commented Dec 12, 2023

Ah, I looked at the wrong dist file.

@lww
Copy link

lww commented Apr 3, 2024

You have a misconfiguration in the package.json. Please consider fixing this small bug that makes the library hard to use with some frameworks.

Please have a look at the lint error/warnings for the library here
https://publint.dev/[email protected] and here https://arethetypeswrong.github.io/?p=react-easy-crop%405.0.6

./index.module.js is written in ESM, but is interpreted as CJS. Consider using the .mjs extension, e.g. ./index.module.mjs ([More info](https://publint.dev/rules.html#file_invalid_format)) The types is not exported. Consider adding pkg.exports["."].import.types to be compatible with TypeScript's "moduleResolution": "bundler" compiler option. Note that you cannot use "./index.d.ts" because it has a mismatching format. Instead, you can duplicate the file and use the .mts extension, e.g. pkg.exports["."].import.types: "./index.d.mts" ([More info](https://publint.dev/rules.html#types_not_exported))

[Resolution failed](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md) Import failed to resolve to type declarations or JavaScript files.

For anyone using Remix V2 with Vite you can fix this bug by adding the ssr.noExternal option in the vite.config.js (https://remix.run/docs/en/main/future/vite#esm--cjs):

import { vitePlugin as remix } from "@remix-run/dev";
import { installGlobals } from "@remix-run/node";
import { defineConfig } from "vite";
import path from "path"
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

installGlobals();

export default defineConfig({
  ssr: {
    noExternal: ['react-easy-crop', 'tslib']
  },
  plugins: [
    remix(),
  ],
  resolve: {
    alias: {
      "~": path.resolve(__dirname, "./app")
    }
  }
});

@arpitdalal
Copy link
Author

And when importing Cropper like this import Cropper from 'react-easy-crop' gives this error when using it
JSX element type 'Cropper' does not have any construct or call signatures.ts(2604)

This is fixed with the PR #523


Thank you @lww for digging into the root cause and proposing the solution ❤️

@ValentinH
Copy link
Owner

That's great news! I like fixing bugs without wanting to 🤣

@arpitdalal
Copy link
Author

Oh sorry, I didn't communicate clearly. The part of this issue is fixed. The one that would give an error in the code editor is fixed, but not the terminal error when starting the app in prod mode.
@lww's suggestion is correct here, .mjs extension should be used instead of .js

@ValentinH ValentinH reopened this Apr 3, 2024
@ValentinH
Copy link
Owner

Alright I'll try this then

@ValentinH ValentinH linked a pull request Apr 3, 2024 that will close this issue
@ValentinH
Copy link
Owner

ValentinH commented Apr 3, 2024

Could you please try 5.0.7--canary.2940c18.0 and let me know if it solves your issues? (I'm not really confident that it does)

@arpitdalal
Copy link
Author

Just tried it, didn't fix the issue.
This is the console error:

SyntaxError: Named export '__assign' not found. The requested module 'tslib' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'tslib';
const { __assign, __extends } = pkg;

file:///Users/arpit.dalal/personal/xman/node_modules/react-easy-crop/index.module.mjs:1
import { __assign, __extends } from 'tslib';
         ^^^^^^^^
SyntaxError: Named export '__assign' not found. The requested module 'tslib' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'tslib';
const { __assign, __extends } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at getBuild (file:///Users/arpit.dalal/personal/xman/server/index.ts:204:4)
    at file:///Users/arpit.dalal/personal/xman/server/index.ts:218:34

I tried the suggested solution in the error message by importing tslib as import tslib from "tslib" and import * as tslib from "tslib" but none of these worked. I got TypeError: tslib.__extends is not a function with these both alternatives.

Thanks for working on this @ValentinH. I am not too well versed in building a package for both CJS and ESM, so I am unfortunately out of ideas. Luckily, @lww's workaround of adding tslib and react-easy-crop to the ssr.noExternal option in the vite.config.js works fine.

@lww
Copy link

lww commented Apr 3, 2024

based on the docs https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md the import should be

"import": {
     "types": "./index.d.mts",
    "default": "./index.mjs"
},

notice the missing m in index.d.mts

@ValentinH
Copy link
Owner

I'm actually wondering if we should just get rid of tslib. I don't even recall why it was added in the first place and it's something I haven't seen used elsewhere.

@ValentinH
Copy link
Owner

based on the docs https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md the import should be

"import": {
     "types": "./index.d.mts",
    "default": "./index.mjs"
},

notice the missing m in index.d.mts

I tried that but it requires more changes to the index.m.ts file and the files it imports. However, I couldn't find how to do it with bili, the tool we use to build the lib and that is not maintained anymore.

We need to revamp the build pipeline from scratch to have something we can control. However, the fact that the Cropper component imports some non- js files (css) makes it more tricky. Last but not least, this is the most boring and painful to get right topic.

@arpitdalal
Copy link
Author

Actually, this works

"exports": {
    ".": {
      "import": {
        "types": "./index.module.d.mts", <-- "index.module.d.mts" instead of "index.d.ts"
        "default": "./index.module.mjs"
      },
      "require": {
        "types": "./index.d.ts",
        "default": "./index.js"
      }
    },
    "./react-easy-crop.css": {
      "import": "./react-easy-crop.css",
      "require": "./react-easy-crop.css"
    }
  }

The index.module.d.mts is already being generated in the canary version
So, I believe it's just changing the file name for "imports.types" instead of generating a new file. I am not sure if and how that can be configured in bili

@ValentinH
Copy link
Owner

I tried that but https://arethetypeswrong.github.io/?p=react-easy-crop%405.0.7--canary.527.82deecf.0 is even worse.

Can you try [email protected] please?

@arpitdalal
Copy link
Author

Surprisingly, that worked. I don't see index.d.mts in the build output on npm though. I am not sure how that works.
I think it should be index.module.d.mts since that exists in the build output on npm.

@ValentinH
Copy link
Owner

I'm too tired 😭

Please try [email protected] even if we still have the same result on https://arethetypeswrong.github.io/?p=react-easy-crop%405.0.7--canary.527.1aad632.0:
image

It think it expects ./Cropper to be something like ./Cropper.d.mts

@arpitdalal
Copy link
Author

nvm, sorry, I tried the canary 5.0.7--canary.527.82deecf.0 with ssr.noExternal in the vite config. Without ssr.noExternal in the vite config, it fails on start.
Failing with [email protected] too 😞
Sorry dude, I know it's a hard problem. Thanks for trying to figure this out, I don't have anything else to suggest here.

@ValentinH
Copy link
Owner

I'll try to move the build pipeline to plain Rollup when I get some motivation. 🤞

@confix
Copy link

confix commented Jun 27, 2024

Running into the same issue. Is there anything we can help with to solve it?

@ValentinH
Copy link
Owner

I'm stuck on this topic. Ideally, someone with good knowledge around bundling would jump in to fix this 😅

@binishjoshi
Copy link

binishjoshi commented Jul 18, 2024

I'm getting this in Remix as well.

Edit: This fixed for me: #490 (comment)

@nima9
Copy link

nima9 commented Aug 20, 2024

Same for me while using AstroJs and react

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants