Title was: How do I fix "Cannot use import statement outside a module" error on Heroku without causing a "Must use import to load ES Module" error?
This was before I understood that I was trying to deploy a "static" React "app" and Heroku was running index.js as if it was server code.
I had setup Procfile
to contain:
web: node ./src/index.js
Then I saw this error:
...
2020-03-29T02:34:01.000000+00:00 app[api]: Build succeeded
2020-03-29T02:34:02.637867+00:00 heroku[web.1]: State changed from starting to crashed
2020-03-29T02:34:02.622526+00:00 heroku[web.1]: Process exited with status 1
2020-03-29T02:34:02.583667+00:00 app[web.1]: /app/src/index.js:1
2020-03-29T02:34:02.583689+00:00 app[web.1]: import React from 'react';
2020-03-29T02:34:02.583690+00:00 app[web.1]: ^^^^^^
2020-03-29T02:34:02.583690+00:00 app[web.1]:
2020-03-29T02:34:02.583691+00:00 app[web.1]: SyntaxError: Cannot use import statement outside a module
2020-03-29T02:34:02.583691+00:00 app[web.1]: at wrapSafe (internal/modules/cjs/loader.js:1072:16)
...
I tried to fix it by adding this to package.json
:
"type": "module",
And then I got this error:
...
2020-03-29T03:05:01.000000+00:00 app[api]: Build succeeded
2020-03-29T03:05:06.293573+00:00 heroku[web.1]: Starting process with command `node ./src/index.js`
2020-03-29T03:05:08.744086+00:00 heroku[web.1]: State changed from starting to crashed
2020-03-29T03:05:08.724957+00:00 heroku[web.1]: Process exited with status 1
2020-03-29T03:05:08.650103+00:00 app[web.1]: internal/modules/cjs/loader.js:1174
2020-03-29T03:05:08.650125+00:00 app[web.1]: throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
2020-03-29T03:05:08.650126+00:00 app[web.1]: ^
2020-03-29T03:05:08.650126+00:00 app[web.1]:
2020-03-29T03:05:08.650126+00:00 app[web.1]: Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /app/src/index.js
2020-03-29T03:05:08.650127+00:00 app[web.1]: at Object.Module._extensions..js (internal/modules/cjs/loader.js:1174:13)
2020-03-29T03:05:08.650127+00:00 app[web.1]: at Module.load (internal/modules/cjs/loader.js:1002:32)
...
I should've noted that my React "app" is a front-end only that uses a GraphQL API I've already deployed elsewhere. In other words, there is no "server.js that serves [my] react app". (HMR's comment led me to that realization. I guess the correct terminology would be to call this a "static app".)
This code in server.js
only slightly adapted from Tin Nguyen's answer works:
const http = require('http');
const path = require('path');
const express = require('express');
let wss;
let server;
const app = express();
app.use(express.static(path.join(__dirname, './build')));
server = new http.createServer(app);
server.on('error', err => console.log('Server error:', err));
server.listen(process.env.PORT);
...with this Procfile
:
web: node ./server.js
The only change is the path to the build directory, which I tweaked after snooping around with heroku run bash
.
My site isn't working properly, but I think that is the result of my build not being correct. Perhaps that can be solved with a buildpack. But that belongs in a different question. (And I might not bother figuring it out since I plan to take Tin Nguyen's advice and try hosting on GitHub.)
Update: The problem wasn't with my build, but with client-side routing (as described on https://create-react-app.dev/docs/deployment/). This code (from that page) fixes it (in server.js
):
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(process.env.PORT);
The link in HMR's comment helped me reach this understanding, but Dave Ceddia writes that his article covers how to keep a React app and API server together. Is there any documentation on how to deploy a front-end on Heroku (or elsewhere) with an API server already deployed elsewhere?
(I found this answer where Paras recommends Netlify.)
If I am reading it right you have a static website and you want it to be hosted on Heroku.
Static websites can be served on Netlify and also GitHub pages. They don't require a web server. You can still host it on Heroku by wrapping a web server around it that is then serving your static files.
const http = require('http');
const path = require('path');
const express = require('express');
let wss;
let server;
const app = express();
app.use(express.static(path.join(__dirname, './../build')));
server = new http.createServer(app);
server.on('error', err => console.log('Server error:', err));
server.listen(process.env.PORT);
How do you know if you have a static website?
You should have generated build files somewhere in your project. You can navigate to the folder and open index.html
. The website should work even though you don't have node
or npm
running. That folder is in my example ./../build
.
Personally I would recommend you host static websites on GitHub over Heroku. Heroku is "free" but with a lot of limitations. The dyno hours are limited, websites need to spin up after not being in use for a long time, etc.