Gatsby: mapboxgl / Webpack error: self is undefined

Created on 22 Aug 2018  Â·  6Comments  Â·  Source: gatsbyjs/gatsby

Hi guys, I'm working on a site which uses the mapbox-gl module, development mode works fine but it breaks in build stages. I am aware of the "(window/whatever) is undefined at build time" issue but I am unsure how to implement the solution. Almost all solutions I found revolve around Webpack configuration which is where I struggle in combination with Gatsby.

To be specific: Here seems to be the solution:
https://github.com/mapbox/mapbox-gl-js/issues/4359#issuecomment-288001933

but I don't know how to implement that in Gatsby. Do I have to put this in the Gatsby-node.js file? I have tried but without success. I'd be happy if someone could advise.

Here's my Gatsby-node.js:

`
const makeRequest = (graphql, request) =>
new Promise((resolve, reject) => {
// Query for nodes to use in creating pages.
resolve(
graphql(request).then(result => {
if (result.errors) {
reject(result.errors);
}

            return result;
        })
    );
});

exports.createPages = ({ boundActionCreators, graphql }) => {
const { createPage } = boundActionCreators;

const getPage = makeRequest(
    graphql,
    `
{
  allStrapiReferenz {
    edges {
      node {
        id
        Headline
        Ref_Category
      }
    }
  },
  allStrapiArbeitsgebiete{
      edges{
          node{
            id
            Arbeitsgebiet_Titel
          }
      }
  }
}
`
).then(result => {
    result.data.allStrapiReferenz.edges.forEach(({ node }) => {
        createPage({
            path: `/referenz/${slugify(node.Ref_Category)}/${slugify(node.Headline)}`,
            component: path.resolve(`src/templates/referenz.js`),
            context: {
                id: node.id,
            },
        });
    });
    result.data.allStrapiArbeitsgebiete.edges.forEach(({ node }) => {
        createPage({
            path: `/arbeitsgebiete/${slugify(node.Arbeitsgebiet_Titel)}`,
            component: path.resolve(`src/templates/arbeitsgebiete.js`),
            context: {
                id: node.id,
            },
        });
    });



});

// Query for articles nodes to use in creating pages.
return getPage;

};`

help wanted question or discussion

Most helpful comment

Hi @stemmlerjs – thank you! Similar to your solution, I was able to solve this by using class extends like that:

class RefMap extends React.Component {
  createMap() {
    const iconSize = 1
    const map = new mapboxgl.Map({
      center: this.props.center,
      container: 'map',
      style: 'mapbox://styles/mapbox/light-v9',
      trackResize: 'true',
      zoom: this.props.zoom,
      pitch: 50,
      detectRetina: 'true'
    })
    var el = document.createElement('div');
    el.className = 'marker';
    el.style.backgroundImage = 'url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTIwMCIgaGVpZ2h0PSIxMjAwIiB2aWV3Qm94PSIwIDAgMTIwMCAxMjAwIj48Zz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZmZmZmZmIj48L3JlY3Q+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjAwIDYwMCkgc2NhbGUoMC42OSAwLjY5KSByb3RhdGUoMCkgdHJhbnNsYXRlKC02MDAgLTYwMCkiIHN0eWxlPSJmaWxsOiNmNDdmMjQ7Ij48c3ZnIGZpbGw9IiNmNDdmMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAgMTAwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBkPSJNNTAsMjVjNi44OTYsMCwxMi41LDUuNjA0LDEyLjUsMTIuNVM1Ni44OTYsNTAsNTAsNTBzLTEyLjUtNS42MDQtMTIuNS0xMi41UzQzLjEwNCwyNSw1MCwyNXogTTUwLDAgIEMyOS4xNjcsMCwxMi41LDE2LjY2NywxMi41LDM3LjVTMjkuMTY3LDc1LDUwLDEwMGMyMC44MzMtMjUsMzcuNS00MS42NjcsMzcuNS02Mi41UzcwLjgzMywwLDUwLDB6Ij48L3BhdGg+PC9zdmc+PC9nPjwvZz48L3N2Zz4=);)'

    map.on('load', () => {

      new mapboxgl.Marker(el)
        .setLngLat(this.props.center)
        .addTo(map);

      (map) => { map.resize() }

    })
  }

  componentDidMount() {
    mapboxgl.accessToken = '[TOKEN]'
    this.createMap()
  }

  render() {
    return (

      <div id={'map'} style={{ width: '100%', height: '100%' }} />

    )
  }

And in my gatsby-node.js:

exports.modifyWebpackConfig = ({ config, stage }) => {
    if (stage === "build-html") {
        config.loader("null", 
        {
            test: /isotope\-|fizzy\-ui\-utils|desandro\-|masonry|outlayer|get\-size|doc\-ready|eventie|eventemitter|react-mapbox-gl|react-mapbox-gl|react-map-gl|(mapbox-gl)\.js$/,
            loader: "null-loader",
        },
    );
    }
};

note that I had to exclude isotope.js as well.

Things are working now, thank you everyone! Hopefully this thread will be useful for somebody else as well.

All 6 comments

I should add I have tried the steps here (under fixing 3rd party modules):

https://www.gatsbyjs.org/docs/debugging-html-builds/

but that also threw an error:

WebpackError: (0 , _reactMapboxGl2.default) is not a function

so I guess the solution is not to skip the module but to skip the uglification of the module ...

I had the same problem, was hard to debug and hard to solve.
End up with something like this on gatsby-node.js

exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
  const config = getConfig();

  let newConfig = {
    ...config,
    module: {
      ...config.module,
      noParse: /(mapbox-gl)\.js$/,
    },
  };

  if (stage === 'build-html') {
    newConfig = {
      ...newConfig,
      module: {
        ...newConfig.module,
        rules: [
          ...newConfig.module.rules,
          {
            test: /(mapbox-gl)\.js$/,
            loader: 'null-loader',
          },
        ],
      },
    };
  }

  actions.replaceWebpackConfig(newConfig);
};

@porfirioribeiro Thank you and sorry for the late reply. Unfortunately it did not work. Do I have to adjust anything in the Webpack config you pasted?

Hey @dwehrmann. I hope you found a solution to this. I was able to get mine working with some logic inside the component to create a mock Map instance when it's in build. I'm also using react-mapbox-gl.


let mapboxgl
let ReactMapboxGl = {}

if (typeof window !== `undefined`) {
  mapboxgl = require('mapbox-gl')
  ReactMapboxGl = require('react-mapbox-gl')
} else {
  ReactMapboxGl.Map = () => {
    return class Mock extends React.Component {
      constructor() {
        super()
      }
      render() {
        return <div />
      }
    }
  }
}

const Map = ReactMapboxGl.Map({
  accessToken:  '[your-token-here]',
})

class DirectoryMap extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <div className="directory-map">
        <Map
          ref={e => {
            this.map = e
          }}
          zoom={[11]}
          speed={[0.6]}
        >
        </Map>
      </div>
    )
  }
}

Hi @stemmlerjs – thank you! Similar to your solution, I was able to solve this by using class extends like that:

class RefMap extends React.Component {
  createMap() {
    const iconSize = 1
    const map = new mapboxgl.Map({
      center: this.props.center,
      container: 'map',
      style: 'mapbox://styles/mapbox/light-v9',
      trackResize: 'true',
      zoom: this.props.zoom,
      pitch: 50,
      detectRetina: 'true'
    })
    var el = document.createElement('div');
    el.className = 'marker';
    el.style.backgroundImage = 'url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTIwMCIgaGVpZ2h0PSIxMjAwIiB2aWV3Qm94PSIwIDAgMTIwMCAxMjAwIj48Zz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZmZmZmZmIj48L3JlY3Q+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjAwIDYwMCkgc2NhbGUoMC42OSAwLjY5KSByb3RhdGUoMCkgdHJhbnNsYXRlKC02MDAgLTYwMCkiIHN0eWxlPSJmaWxsOiNmNDdmMjQ7Ij48c3ZnIGZpbGw9IiNmNDdmMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAgMTAwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBkPSJNNTAsMjVjNi44OTYsMCwxMi41LDUuNjA0LDEyLjUsMTIuNVM1Ni44OTYsNTAsNTAsNTBzLTEyLjUtNS42MDQtMTIuNS0xMi41UzQzLjEwNCwyNSw1MCwyNXogTTUwLDAgIEMyOS4xNjcsMCwxMi41LDE2LjY2NywxMi41LDM3LjVTMjkuMTY3LDc1LDUwLDEwMGMyMC44MzMtMjUsMzcuNS00MS42NjcsMzcuNS02Mi41UzcwLjgzMywwLDUwLDB6Ij48L3BhdGg+PC9zdmc+PC9nPjwvZz48L3N2Zz4=);)'

    map.on('load', () => {

      new mapboxgl.Marker(el)
        .setLngLat(this.props.center)
        .addTo(map);

      (map) => { map.resize() }

    })
  }

  componentDidMount() {
    mapboxgl.accessToken = '[TOKEN]'
    this.createMap()
  }

  render() {
    return (

      <div id={'map'} style={{ width: '100%', height: '100%' }} />

    )
  }

And in my gatsby-node.js:

exports.modifyWebpackConfig = ({ config, stage }) => {
    if (stage === "build-html") {
        config.loader("null", 
        {
            test: /isotope\-|fizzy\-ui\-utils|desandro\-|masonry|outlayer|get\-size|doc\-ready|eventie|eventemitter|react-mapbox-gl|react-mapbox-gl|react-map-gl|(mapbox-gl)\.js$/,
            loader: "null-loader",
        },
    );
    }
};

note that I had to exclude isotope.js as well.

Things are working now, thank you everyone! Hopefully this thread will be useful for somebody else as well.

I know this issue is closed, but @porfirioribeiro's solution worked for me. Just pasted his code in gatsby-node.js

Thank you very much!

Was this page helpful?
0 / 5 - 0 ratings