gatsby: gatsby-node.js doesn't allow ES6 import

Description

gatsby-node.js doesn’t allow ES6 javascript.

Steps to reproduce

gatsby-node.js:

import myOnCreatePage from './gatsby/node/onCreatePage';
export const onCreatePage = myOnCreatePage;

Expected result

gatsby-node.js should be transpiled and allow ES6 like gatsby-ssr.js or gatsby-browser.js.

Actual result

Error

Error: <root>/gatsby-node.js:1
SyntaxError: Unexpected token import

Environment

  System:
    OS: macOS High Sierra 10.13.6
    CPU: x64 Intel(R) Core(TM) i7-6567U CPU @ 3.30GHz
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 8.10.0 - ~/.nvm/versions/node/v8.10.0/bin/node
    Yarn: 1.9.4 - /usr/local/bin/yarn
    npm: 6.4.1 - ~/.nvm/versions/node/v8.10.0/bin/npm
  Browsers:
    Chrome: 68.0.3440.106
    Firefox: 61.0.2
    Safari: 11.1.2
  npmPackages:
    gatsby: next => 2.0.0-rc.5 
    gatsby-source-filesystem: next => 2.0.1-rc.1 
  npmGlobalPackages:
    gatsby-cli: 1.1.58

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 13
  • Comments: 52 (23 by maintainers)

Commits related to this issue

Most upvoted comments

I use esm for this and it works so far. Here’s what I did:

  1. Install esm (npm i esm)
  2. Create a file called gatsby-node.esm.js in your root folder (the same folder that contains gatsby-node.js)
  3. Move all your code from from gatsby-node.js to gatsby-node.esm.js
  4. Replace all the code in gatsby-node.js with the following:
    require = require('esm')(module)
    module.exports = require('./gatsby-node.esm.js')
    
  5. Use import in gatsby-node.esm.js all you want 🎉

@KyleAMathews Is there anything dangerous about doing it this way? Because if it’s safe I could add it to the docs 😃

It’s kind of a different debate, but any chance using import could be supported out of the box? I feel like for larger Gatsby projects, what happens in gatsby-node.js is almost as important as your actual front-end and it sucks not to be able to code in a modern way with import, async/await, etc. without extra hacks.

Now that ES modules are no longer experimental, should this issue be reopened?

npm i esm

and then modify your commands to look like this:

https://github.com/wesbos/awesome-uses/blob/master/package.json#L39-L42

I was able to resolve my issue by using ES5 in my src/utils/article.js file, like so:

// src/utils/article.js
const { format } = require('date-fns')

var createArticleUrl = function (a) {
  return (
    `/${format(a.publishDate, 'YYYY')}` +
    `/${format(a.publishDate, 'MM')}` +
    `/${format(a.publishDate, 'DD')}` +
    `/${a.category.urlSlug}` +
    `/${a.urlSlug}`
  )
}

module.exports.createArticleUrl = createArticleUrl

and then gatsby-node.js, like this:

// gatsby-node.js
...
const { createArticleUrl } = require(`./src/utils/article`)
...

I can also import createArticleUrl like normal in ES6 files, import { createArticleUrl } from '../utils/article'.

They are still experimental and not quite finished. Just the flag has been removed

however the implementation remains experimental and subject to change

I dove deep into this, and imo it’s not worth the pain yet. Wait for loaders to be implemented or use esm package.

I would strongly consider adding that esm approach to documentation, since Gatsby is indicating es6 modules should work inside gatsby-node.js file. This error message is printed, when you mix modules:

error This plugin file is using both CommonJS and ES6 module systems together which we don't support. You'll need to edit the file to use just one or the other.

It clearly indicates, you CAN use es6 modules.

Can someone provide a final solution and lock this thread?

Hate doing this dance of skimming all comments in really important but seemingly closed issues where conversation is still happening and trying to sift through all comments to see which one has the most thumbs ups and is likely to be the best solution 😔

As an alternative, you may want to use TypeScript for gatsby-* files.

Is there a way to use babel-node instead of node?

Man, what a welcoming community Gatsby has. A user asks for something basic (like support for the JS language) and this is the response they get.

Sure, but asking a fully working ESM-using app to rename all its files just to use modern JS syntax is complete idiocy.

Again, the Node org provided options for a reason, and my only concern was making sure Gatsby supports those options (or better yet, supports the esm package … but that doesn’t sound like it’s happening). And again, I only said anything because you yourself carved out the possibility of not doing so:

we should be able to at least use .mjs

(which clearly implies not supporting the other option)

Another way I found this to work was by updated your package.json with the following:

  "scripts": {
    "build": "npx --node-arg '-r esm' gatsby build",
    "develop": "npx --node-arg '-r esm' gatsby develop",
    "start": "npx --node-arg '-r esm' npm run develop",
    "serve": "npx --node-arg '-r esm' gatsby serve",
    "test": "echo \"Write tests! -> https://gatsby.app/unit-testing\""
  },

No need to create new files

ERROR #10123 CONFIG

We encountered an error while trying to load your site’s gatsby-config. Please fix the error and try again.

Error: /project/gatsby-config.js:1 (function (exports, require, module, __filename, __dirname) { import urlJoin from “url-join”; ^^^^^^ SyntaxError: Cannot use import statement outside a module```

Just got bitten by this but in gatsby-node.js. I am not a javascript developer so I don’t care much about ideology, but the ESM import syntax seemed so natural to me (I’m more experienced in Python, which has a very close import syntax) that I thought it was native to modern JS. I just want to extend Gatsby to my needs, and it feels very weird when you can use ESM everywhere in Gatsby (it’s even the format used in the documentation) but not in some specific files at the root.

Whatever is your ideology, I think we can agree consistency is always a good practice. And here the import format is inconsistent within gatsby.

we should be able to at least use .mjs

The .mjs extension is hot garbage. Please don’t force every Gatsby developer to use it: the Node org added the other options precisely because of all of the fallback from the Node’ orgs misguided attempt to make everyone use “Michael Jackson Script”.

P.S. A few relevant details that you might not be aware of …

  • This is a two-line fix. Unless Gatsby has multiple “entry points”, the entire PR to fix this will be three lines: the package.json entry for the esm package, and the lines:
require = require("esm")(module/*, options*/)
module.exports = require("./main.js")
  • This module is already widely used in major libraries like Knex. There are literally 135 … thousand libraries depending on it already!

  • It was written by the creator of Lodash, so this is not some Junior Engineer’s first NPM project: it’s a serious library written by a seasoned professional

  • It’s completely backwards-compatible: unless someone uses import or export in their non-ES Module code (and I’m pretty sure those have been JS banned keywords since the dawn of time) all existing code will continue working the same

  • if there was any kind of performance concern it’s trivially easy to “gate keep” this feature with a command line argument (knex opted to go this route, for instance)

So it’s “gain modern Javascript language features with three lines of code” or … fight it for ? benefit.

If there’s interest, I’d be more than happy to submit a PR 😃

Wow, I understand change is hard, but ES Modules are Javascript now, so it’s really disappointing to see (for whatever totally valid reasons) the maintainers fighting against supporting Javascript … especially when JDalton and his esm package makes it so easy!

mjs is going to be needed in some cases where you have a massive commonjs project. You can’t just snap your fingers and have the thing swich over. you need to do it bit by bit, and opting into esm on a per file basis is a good solution until you can get there.

I realize it’s an ugly extension (?) and it’s funny to say michael jackson script, but that is a good option going forward

@wesbos @reaktivo are you able to use that fix still with the latest gatsby (2.22.17 in my case)?

I’ve had it working without any issues using that solution, but updated gatsby today and started getting the import errors again:

> npx --node-arg '-r esm' gatsby develop

ERROR #10123  CONFIG

We encountered an error while trying to load your site's gatsby-config. Please fix the error and try again.

Error: /project/gatsby-config.js:1
(function (exports, require, module, __filename, __dirname) { import urlJoin from "url-join";
                                                              ^^^^^^
SyntaxError: Cannot use import statement outside a module```

I’m also running into this. I need to use gatsby-mdx/mdx-renderer, and even if I require it, the required file itself uses ES6 module syntax and breaks. Is there a way to change configuration to have gatsby-node.js go through babel? It would be cool to be able to use JSX inside it as well, though less urgent for me.

It’s not worth implementing the esm module into Gatsby - that is a great solution for now, but the future is using native esm in Node by either:

  1. Supporting "type": "module" in package.json, allowing up to use the import syntax in gatsby-node
  2. Support .mjs for gatsby-node.js

We will have some growing pains as node moves to esm and we wait for lots of packages to catch up, but as long as authors know that as well at the node version limitations, we should be able to at least use .mjs

Like I said, the hard part is not making the esm module work: it’s designed to make things extremely simple. I would submit a PR myself if I knew anything about the Gatsby architecture (and again, to see another major library use it just look at Knex).

The hard part is just getting a maintainer with that knowledge to care 😦

You’d think that even if every maintainer had a personal hated for modern JS module syntax, they still could at least appreciate their users’ desire for it … but the refusal to even re-open this issue, let alone fix it (again, possibly with only two lines of code) suggests otherwise. Given how great this team has been with other issues, it’s honestly confusing to me.

Any word from maintainers on this subject, please?

Just a heads up that I struggled with this for an hour and finally figured out that no matter what I put in my package.json for build, the Netlify UI configuration was taking precedence. It wasn’t until I added a netlify.toml that this became apparent. Moral of the story, edit your Netlify UI build settings, or clear them out and toss it in the config. I prefer the latter. 😄

[build]
  command = "npm run build"
  publish = "public"

It’s worth noting that yes, this worked for me:

"build": "NODE_OPTIONS='-r esm' gatsby build",

Please have in mind that we don’t officially support it and the workarounds mentioned here can only go so far. Maybe some of our packages are not yet compatible with this experimental node feature or it just won’t work. We can revisit this topic when things are stable.

@rotexhawk Just pushed an example and it worked fine:

https://github.com/reaktivo/gatsby-esm/ https://gatsby-esm-example.netlify.com/

Make sure you’re running npm install --save-dev esm before and that your build config runs npm run build instead of gatsby build

Check out this commit: https://github.com/reaktivo/gatsby-esm/commit/cf620259ac8b118dea38b99409963cb26bf1b240

It’s worth noting that yes, this worked for me:

"build": "NODE_OPTIONS='-r esm' gatsby build",

this also worked for me!

make sure to install the package first: yarn add esm

package.json

"scripts": {
	"start": "NODE_OPTIONS='-r esm' gatsby develop -p 9988 -o",
	"build": "NODE_OPTIONS='-r esm' gatsby build",
	"serve": "NODE_OPTIONS='-r esm' gatsby serve -p 9500 -o"
},

gatsby-config

import { config } from './content/meta/config'
import emoji from 'remark-emoji'

gatsby-node

import { createFilePath } from 'gatsby-source-filesystem'
import path from 'path'

Like I said, the hard part is not making the esm module work: it’s designed to make things extremely simple. I would submit a PR myself if I knew anything about the Gatsby architecture (and again, to see another major library use it just look at Knex).

The hard part is just getting a maintainer with that knowledge to care 😦

You’d think that even if every maintainer had a personal hated for modern JS module syntax, they still could at least appreciate their users’ desire for it … but the refusal to even re-open this issue, let alone fix it (again, possibly with only two lines of code) suggests otherwise. Given how great this team has been with other issues, it’s honestly confusing to me.

@rotexhawk That’s really weird, considering the project that I sent you was deployed to Netlify… anyway, if you need to avoid npx, the following might work:

  "scripts": {
    "build": "node -r esm ./node_modules/bin/gatsby build",
    "develop": "node -r esm ./node_modules/bin/gatsby develop",
    "start": "npm run develop",
    "serve": "node -r esm ./node_modules/bin/gatsby serve",
    "test": "echo \"Write tests! -> https://gatsby.app/unit-testing\""
  },

oops @alishaevn did you mean yarn add esm? It’s cool that I learned about https://www.npmjs.com/package/ems though.

@wesbos I ended up reverting back to es5 require for now 😦

EDIT: I just noticed your latest fix here https://github.com/gatsbyjs/gatsby/issues/24925 … I’ll give that a try!

my esm trick above just stopped working. I even rolled back gatsby and node version and it persists.

Detailed it here if anyone has the same issue: https://github.com/reaktivo/gatsby-esm/issues/1

@caycecollins did you find a fix?

@rotexhawk Got in touch with the Netlify team, you might also need to specify the NPM_VERSION, see https://www.netlify.com/docs/build-settings/#node-npm-and-yarn

@reaktivo your scripts work locally but I wasn’t able to get it to work with netlify. Are u able to deploy your site to netlify with npx?