gatsby-node.js doesn't allow ES6 import

Created on 2 Sep 2018  ·  39Comments  ·  Source: gatsbyjs/gatsby

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

Most helpful comment

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:
    javascript 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 :)

All 39 comments

That file is run by node.js so it supports whatever the version of node you're using supports. Es6 module support in node is still a wip https://medium.com/@giltayar/native-es-modules-in-nodejs-status-and-future-directions-part-i-ee5ea3001f71

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.

What would be the steps to enable import/export support in gatsby-node.js? The Medium post linked to mentions using .mjs files but I don't think that will work with Gatsby?

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

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:
    javascript 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 :)

This pattern definitely works @nikoladev! Not sure where it'd go in the docs though. Any ideas @gatsbyjs/docs?

Here's how I did it:

require('babel-register')({
  presets: [ 'env' ]
})
require('babel-polyfill')
module.exports = require(`./gatsby-node.mjs`)

It seems to work well enough but I wonder if it might not mess with the cache? I think the cache is supposed to reset when you modify gatsby-node.js but I've been running into issues with that, although I don't know if they're related.

@KyleAMathews How about under Sourcing Content and Data? At least that's the reason I used it for.

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.

@KyleAMathews, is there a way to require a local ES6 file? For example, I have a src/utils/article.js file that looks like the following:

// src/utils/article.js
import { format } from 'date-fns'

export const createArticleUrl = (a) => (
  `/${format(a.publishDate, 'YYYY')}` +
  `/${format(a.publishDate, 'MM')}` +
  `/${format(a.publishDate, 'DD')}` +
  `/${a.category.urlSlug}` +
  `/${a.urlSlug}`
)

and the my gatsby-node.js file:

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

And I get the error:

  Error: .../src/utils/article.js:1
  (function (exports, require, module, __filename, __dirname) { import { format } from 'date-fns'
                                                                ^^^^^^
  SyntaxError: Unexpected token import

Any ideas? I use the function to create the url for our articles/posts and would like to import it like I do in my React components. Thanks!

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'.

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:
    js require = require('esm')(module) module.exports = require('./gatsby-node.esm.js')
  5. Use import in gatsby-node.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 :)

Is the 5th "Use import in gatsby-node.esm.js all you want"?

@WeZZard You're totally right! I'll fix it in my post.

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

@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?

@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

Thanks, I have installed esm. the problem is that netlify doesn't have access to npx, says command failed. I specified my node version but that didn't help.

@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\""
  },

@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 will do. thanks for all the help.

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.

esm doesn't seem to work when using yarn workspaces. It says it can't find the workspace module.

Actually it seems like it only works for the first level import in gatsby-node.ems.js, not for anything that the imported component also imports.

For example,

./gatsby-node.ems.js

import foo from "./foo";
const fooText = foo + " more text";

./foo

import bar from "bar";
const foo = bar("whatever");
export default foo;

Throws an error:

Error in "C:\...\gatsby-node.js": Cannot find module 'bar'

Since Node modules are almost there, I wanted to use node with --experimental-modules instead of esm.

I changed scripts.start to "node --experimental-modules ./node_modules/.bin/gatsby develop", renamed gatsby-node.js to gatsby-node.mjs, but I get

Error in "~/website/gatsby-node.mjs": Must use import to load ES Module: ~/website/gatsby-node.mjs

Error: [ERR_REQUIRE_ESM]: Must use import to load ES Module: ~/website/gatsby-node.mjs

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.

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

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.

Has anyone found a solution using babel? It'd be great if I could drop my babel config in a gatsby project, and have it all work.

Using gatsby, but then having to go back to module.exports really feels like a massive step back.

Modern web tech without the headache -> not so much :(

npm i esm

and then modify your commands to look like this:

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

@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```

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

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?

@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!

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",

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!

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 :)

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 😔

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.

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?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rossPatton picture rossPatton  ·  3Comments

theduke picture theduke  ·  3Comments

dustinhorton picture dustinhorton  ·  3Comments

3CordGuy picture 3CordGuy  ·  3Comments

mikestopcontinues picture mikestopcontinues  ·  3Comments