Search code examples
javascriptgatsbyp5.jsnetlifyml5.js

WebpackError: TypeError: p5__WEBPACK_IMPORTED_MODULE_4___default.a is not a constructor


This server is running fine in develop mode but throws an error during production build

I'm using Gatsby.js Framework and currently, my codes are deployed on netlify server.

Here is my gatsby-node.js config file:

exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
  if (stage === "build-html") {
    actions.setWebpackConfig({
      module: {
        rules: [
          {
            test: /ml5/,
            use: loaders.null(),
          },
          {
            test: /p5/,
            use: loaders.null(),
          },
        ],
      },
    })
  }
}

**Error Output: ** output

Here are the codes in the experiments.js file

import React from "react"
import MetaData from "../components/MetaData"
import ml5 from "ml5"
import "../components/styles.css"
import p5 from "p5"

export default function Experiments() {
  let label = ""
  let probability = ""
  let classifier
  let video

  const gotResult = (err, result) => {
    if (err) {
      console.error(err)
    } else {
      label = result[0].label.toUpperCase()

      probability = (result[0].confidence * 100).toFixed(2)
      classifier.classify(gotResult)
    }
  }

  const modelOnLoaded = () => {
    console.log("Model is Successfully Loaded!")
  }

  const videoReady = () => {
    classifier.classify(gotResult)
  }

  function sketch(p) {
    p.setup = () => {
      p.createCanvas(640, 480)

      video.hide()
      p.background(0)
    }
    p.draw = () => {
      p.background(0)
      p.image(video, 0, 0)
      p.fill(0)
      p.textSize(32)
      p.text(label, 10, p.height - 100)
      p.text(probability, 10, p.height - 30)
    }
    p.preload = () => {
      video = p.createCapture(p.VIDEO, videoReady)

      classifier = ml5.imageClassifier("MobileNet", video, modelOnLoaded)
    }
  }

  new p5(sketch)

  return (
    <div>
      <MetaData />
      <h1 className="display-1">Image Classifier</h1>

      <div></div>
    </div>
  )
}

Machine Specification:

System:
    OS: Linux 5.4 Ubuntu 20.04.1 LTS (Focal Fossa)
    CPU: (4) x64 Intel(R) Core(TM) i3-4005U CPU @ 1.70GHz
    Shell: 5.8 - /usr/bin/zsh
  Binaries:
    Node: 12.20.0 - /usr/bin/node
    Yarn: 1.22.10 - /usr/bin/yarn
    npm: 6.14.10 - /usr/bin/npm
  Browsers:
    Chrome: 87.0.4280.88
  npmPackages:
    gatsby: ^2.26.1 => 2.26.1
    gatsby-plugin-load-script: ^1.1.0 => 1.1.0
    gatsby-plugin-manifest: ^2.9.0 => 2.9.0
    gatsby-plugin-react-helmet: ^3.7.0 => 3.7.0
    gatsby-source-github-api: ^0.2.1 => 0.2.1
    gatsby-source-graphql: ^2.11.0 => 2.11.0
  npmGlobalPackages:
    gatsby-cli: 2.16.2

Solution

  • Sometimes the offending module is not the dependency itself (p5 and ml5 in this case) and is one of the dev-dependencies of that libraries. For example, in a project of mine, I'm showing a map and the map library is outputting the same as your issue, I had to add a null loader to /canvg/ in my case, a library that is using my map to print it.

    Another solution from the Gatsby docs that may fit for you is using loadable-components, basically, instead of adding a null loader in the build-time, it loads the dependency on the client-side (and not on the server-side). In your case:

    import loadable from '@loadable/component'
    
    const YourFunctionToUseMl5 = loadable(() => import ml5 from "ml5")
    const YourFunctionToUseP5 = loadable(() => import p5 from "p5")
    
    
    function MyComponent() {
     useEffect(()=>{
       // use safely ml5 and p5
     }, [])
    
      return (
        <div>
         Hello loadable
        </div>
      )
    }
    

    Use it carefully because despite being a low-size dependency, you are using a non-native approach (as null loader is) and it may increase the bundled size of the application.