I have a page that works fine in gatsby develop, but fails gatsby build. I would like to simply publish this page as a React client-side app, and not pre-render it like the other pages.
The page in question uses the Monaco Editor. This project has its own custom loader, and expects to be in the browser. Using monaco-editor-webpack-plugin, I am able to import the editor into my page and use it from React. However, gatsby build fails on building static HTML pages with "ReferenceError: self is not defined". I've used imports-loader to alias self, which gets past this error, but runs into the next one (navigator is not defined). Continuing down this path proved fruitless.
The page does not currently use any GraphQL queries. Ideally, I would like it to, but I can live without that to make it run entirely in the browser.
MacOS
System:
OS: macOS 10.14.2
CPU: (8) x64 Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 11.1.0 - /usr/local/bin/node
npm: 6.5.0 - /usr/local/bin/npm
Browsers:
Chrome: 71.0.3578.98
Safari: 12.0.2
npmPackages:
gatsby: ^2.0.53 => 2.0.73
gatsby-image: ^2.0.20 => 2.0.25
gatsby-plugin-manifest: ^2.0.9 => 2.0.12
gatsby-plugin-offline: ^2.0.16 => 2.0.20
gatsby-plugin-react-helmet: ^3.0.2 => 3.0.4
gatsby-plugin-sass: ^2.0.7 => 2.0.7
gatsby-plugin-sharp: ^2.0.14 => 2.0.15
gatsby-remark-copy-linked-files: ^2.0.8 => 2.0.8
gatsby-remark-graphviz: ^1.0.5 => 1.0.5
gatsby-remark-prismjs: ^3.1.4 => 3.1.4
gatsby-source-filesystem: ^2.0.12 => 2.0.12
gatsby-transformer-remark: ^2.1.17 => 2.1.17
gatsby-transformer-sharp: ^2.1.8 => 2.1.9
npmGlobalPackages:
gatsby-cli: 2.4.7
gatsby-node.js:
const MonacoWebpackPlugin = require(`monaco-editor-webpack-plugin`);
exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
plugins: [
new MonacoWebpackPlugin()
]
});
};
'tryit.js':
//import { graphql, StaticQuery } from 'gatsby';
import React, { Component } from 'react';
import Layout from '../components/layout';
import SEO from '../components/seo';
import * as monaco from "monaco-editor";
class TryItPage extends Component {
render() {
return (
<Layout className="body-container">
<SEO title="Try It" keywords={[`jinaga`, `node`, `typescript`, `javascript`]} />
<div className="page-content">
<h1>Try it!</h1>
<div id="container" style={{width:800,height:600,border:"1px solid #ccc"}}></div>
</div>
</Layout>
);
}
componentDidMount() {
monaco.editor.create(document.getElementById('container'), {
value: [
'function x() {',
'\tconsole.log("Hello world!");',
'}'
].join('\n'),
language: 'javascript'
});
}
}
export default TryItPage;
I may have answered my own question: gatsby-plugin-create-client-paths
Yes, sounds like gatsby-plugin-create-client-paths is what you're looking for. Glad you already found it yourself 馃憤
Feel free to close this issue if that resolves your question, or follow up if you need anything else.
gatsby build is still trying to load the references from this page. I'll keep working down this path. If I get it resolved, I'll close the issue.
Thanks!
Did you try using the editor without the webpack plugin? Going by the official webpack example that is certainly possible, and might solve the SSR incompatibility.
@michaellperry On a separate note, if you'd just like to be able to use monaco on a separate page then you can also create a static html file in /static and follow the instructions at https://github.com/Microsoft/monaco-editor/blob/master/docs/integrate-amd-cross.md
That way Gatsby's webpack won't run on it at all
The static file will of course be available on the url that is its file name
The problem wasn't server-side routing, it was server-side importing. Therefore the solution wasn't client-only routing, but conditional importing.
Instead of adding the import statement for monaco-editor to the top of the file, I added it to componentDidMount, which is only called within the browser.
//import { graphql, StaticQuery } from 'gatsby';
import React, { Component } from 'react';
import Layout from '../components/layout';
import SEO from '../components/seo';
//import * as monaco from "monaco-editor"; NOT HERE!
class TryItPage extends Component {
render() {
return (
<Layout className="body-container">
<SEO title="Try It" keywords={[`jinaga`, `node`, `typescript`, `javascript`]} />
<div className="page-content">
<h1>Try it!</h1>
<div id="container" style={{width:800,height:600,border:"1px solid #ccc"}}></div>
</div>
</Layout>
);
}
componentDidMount() {
import("monaco-editor").then(monaco => { // HERE!!
monaco.editor.create(document.getElementById('container'), {
value: [
'function x() {',
'\tconsole.log("Hello world!");',
'}'
].join('\n'),
language: 'javascript'
});
});
}
}
export default TryItPage;
As a bonus, I get to use GraphQL, as all of the rendering is still happening on the server.
In answer to the above suggestions:
I was able to bundle monaco-editor without the plugin. The plugin adds a global variable that Monaco needs in order to dynamically load language parsers. Without the plugin, I just had to set that variable myself. This prevented the first "self is not defined" error, but not the remaining errors, like "navigator is not defined".
I tried loading Monaco on a separate page by including it in \static. I ran into a problem where Chrome was refusing to load the CSS files that Monaco fetches because they did not have the correct MIME type. I'm sure I could have continued pushing in that direction and gotten it to work, but I had already given up all the benefits of Gatsby on that page (reuse of Layout component, React, GraphQL, ...)
I'm much happier with this solution. I'll be writing it up in more detail for those who run into the same issue.
Thank you!
Thank you for posting how you solved this @michaellperry
Glad this is resolved 馃檪
Posted: https://medium.com/@michaellperry/gatsby-pages-with-client-only-script-85a7c5664a27
thank you for posting an answer @michaellperry :)
Most helpful comment
Posted: https://medium.com/@michaellperry/gatsby-pages-with-client-only-script-85a7c5664a27