gatsby: v2: Local gatsby-ssr.js is not run anymore on a static build.

Not sure what the deal here is, but trying my onRenderBody hook is no longer run when doing a gatsby build

About this issue

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

Commits related to this issue

Most upvoted comments

Oh ok, so es6 is already working. Tried this and it worked fine:

import React from "react"

export const onRenderBody = ({ setPreBodyComponents }) => {
  setPreBodyComponents(<h1>LA LA LA</h1>)
}

Faced this issue several weeks ago, it was a problem that export with default functions doesn’t work, but arrow function work. (I prefer normal functions when I can).

export function ... - didn’t work export const ... = () => {} - worked

Ok spent some time tracking down the cause of this with @pieh (thanks!). Using this gatsby-ssr.js file as an example:

// gatsby-ssr.js
import React from "react"
exports.onRenderBody = ({ setPreBodyComponents }) => {
    setPreBodyComponents(<h1>LA LA LA</h1>)
}

The bug is caused by using an ES6 module import with a CommonJS exports (@jquense pointed this out already), which causes the exports to be ignored by webpack.

Why this happens is - using import means the module is treated as an ES6 module (import / export) instead of a CommonJS module (require / modules.exports / exports).

As gatsby-ssr.js is treated as an ES6 module, exports doesn’t do anything and should be undefined. However, this transformed module ends up in a bundle render-page.js, which gets run in a Node environment.

When running in a Node environment, exports defaults to {}:

echo "console.log(exports)" > test.js && node test.js
{}

Which means that the onRenderBody definition exports.onRenderBody = does not error (exports is defined), and also does not get exported (exports is not an export). It just silently gets defined and then sits there doing nothing.

So onRenderBody does not get exported, meaning it doesn’t get called by the apiRunner.

One “fix” is to not use import with exports (related: https://github.com/webpack/webpack/issues/4039#issuecomment-273804003), but we can’t really control that.

I’ve opened a PR that hacks in a very basic fix, just prepending exports=null to the beginning of the webpack bundle.

This will throw:

error UNHANDLED EXCEPTION


  TypeError: Cannot set property 'onRenderBody' of null

  - render-page.js:661 Module../gatsby-ssr.js

if you try and use exports in an ES6 module. Which is one step better than silent failure.

I think throwing a helpful error describing how to update your file is the best way to resolve this.

We could do it with eslint? But people can override that. Carry on down the static analysis path we’re already using to load plugins? Something else?

Example inputs / outputs for `gatsby-ssr.js`

ES6 Module import, CommonJS exports

This silently fails.

// gatsby-ssr.js
import React from "react"

exports.onRenderBody = ({ setPreBodyComponents }) => {
    setPreBodyComponents(<h1>LA LA LA</h1>)
}
// webpack bundle render-page.js 
// ...more code
/***/ "./gatsby-ssr.js":
/*!***********************!*\
  !*** ./gatsby-ssr.js ***!
  \***********************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "./node_modules/react/index.js");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);


exports.onRenderBody = ({
  setPreBodyComponents
}) => {
  setPreBodyComponents(react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", null, "LA LA LA"));
};
// ...more code

note the /*! no exports provided */ and __webpack_exports__ being passed in.

Full CommonJS

This works.

// gatsby-ssr.js
const React = require("react")

exports.onRenderBody = ({ setPreBodyComponents }) => {
    setPreBodyComponents(<h1>LA LA LA</h1>)
}
// webpack bundle render-page.js 
// ...more code
/***/ "./gatsby-ssr.js":
/*!***********************!*\
  !*** ./gatsby-ssr.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const React = __webpack_require__(/*! react */ "./node_modules/react/index.js"); 

exports.onRenderBody = ({
  setPreBodyComponents
}) => {
  setPreBodyComponents(React.createElement("h1", null, "LA LA LA"));
};

/***/ }),
// ...more code

Here it’s /*! no static exports found */ and exports is passed in instead of __webpack_exports__

This information needs to be added to the docs as its vital to getting gatsby-ssr.js file to work.

I can only get this to work with mixing commonjs imports, and es6 exports.

const React = require("react")

export const onRenderBody = ({ setHeadComponents }, pluginOptions) => {
  if (process.env.NODE_ENV !== `production`) {
    return null
  }
  setHeadComponents([
    <link
      rel="preconnect dns-prefetch"
      key="gatsby-plugin-myplugin-preconnect"
      href="https://mymyplugin.cloudfront.net/"
    />,
  ])
}

If I use commonjs exports, like exports.onRenderBody, it will “fail” “silently”.