1

I am building a gatsby site where each page is generated from a template, but each page requires a different module import.

Specifically, each page contains an observablehq notebook, and these are exported from observable as modules. See here.

The pages are dynamically generated from a json file like:

[
    {   "path": "page1",
        "modulename": "gatsby-test"
    },
    {   
        "path": "page2",
        "modulename": "gatsby-test-2"
    }
]

This is done using createPages in gatsby-node.js.

I have already yarn added each notebook the project, e.g. yarn add https://api.observablehq.com/@robinl/gatsby-test.tgz.

I'm trying to write a component which takes the module name as a property, and then loads the right module from the contents of this.props.

An outline of my component looks like:

class ImportedNotebook extends Component {

    componentDidMount() {
        var promise = import(this.props.modulename).then(function(themodule) {
            // do stuff here
        })   
    }

    render() {
        return (<div>blah</div>);
    }
}

This does not work - I get an Unhandled Rejection (Error): Cannot find module 'gatsby-test-2' error when visiting the page on localhost using gatsby develop.

However, if I replace this.props.modulename with "gatsby-test-2" it works fine.

Is there a way of dynamically loading modules from a variable? Or perhaps a different approach that would be more suitable to solve this problem?

RobinL
  • 11,009
  • 8
  • 48
  • 68
  • I feel like [this](https://stackoverflow.com/questions/42797313/webpack-dynamic-module-loader-by-require) may be relevant. – RobinL Sep 19 '19 at 13:25

2 Answers2

1

I have become increasingly convinced that it's not possible to do this using this approach. Essentially this is because webpack requires explicit import. If you attempt to make these 'dynamic' it has no way of knowing what code to bundle.

The solution I came up with instead was: - Develop a component that takes as an input a notebook imported from an observable module - Have a minimal individual page for each notebook that imports the notebook, and then passes it to my component

For each page, the code looks like this:

import React from "react"
import notebook from "gatsby-test-2"
import ObservablePage from "../components/obs_page"

export default ({ data }) => (
    ObservablePage(notebook)
)
RobinL
  • 11,009
  • 8
  • 48
  • 68
1

If I understood your page building process correctly, you should be able to pass the observable modulename into the context of your dynamically generated pages when calling the createPage within exports.createPages:

exports.createPages = function({ actions }) {
  // ...
  actions.createPage({
    path: pagePath,
    component: require.resolve("./src/templates/pageTemplate.js"), // your page template file
    context: {
      modulename: modulename, // pass in your modulename URL here
    },
  })

Then within your page template ("./src/templates/pageTemplate.js") you can access the modulename variable like this:

this.props.pageContext.modulename

Finally, you can use that variable to dynamically import each of your components.

Harry Theo
  • 784
  • 1
  • 8
  • 28