webpackreasonbucklescriptwebpack-loadersvg-sprite

"exports is not defined" when loading svg icon


I'm using the svg-sprite-loader plug-in when trying to load an icon from an svg sprite file, however, the page fails with webpack error "exports is not defined". What could be happening? How can one debug webpack loaders like this and get more relevant error messages?

I followed this tutorial except adapting for react.

The error I'm getting is:

Icon.bs.js:21 Uncaught ReferenceError: exports is not defined
    at eval (Icon.bs.js:21)
    at Module../src/components/Icon/Icon.bs.js (Index.js:1357)
    at __webpack_require__ (Index.js:20)
    at eval (DebuggerTopBar.bs.js:6)
    at Object../src/components/DebuggerTopBar/DebuggerTopBar.bs.js (Index.js:1345)
    at __webpack_require__ (Index.js:20)
    at eval (Debugger.bs.js:6)
    at Object../src/components/Debugger/Debugger.bs.js (Index.js:1321)
    at __webpack_require__ (Index.js:20)
    at eval (Index.bs.js:15)

Here's all the code:

Component code

[%%raw "import '../../svg/icons.svg'"]; // this is the line that fails

[@react.component]
let make = (~name) =>
  <svg className={{j|icon icon-$name|j}}>
    <use xlinkHref={{j|#icons_$name|j}} />
  </svg>;

Which outputs this react code

// Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
'use strict';

var React = require("react");

import '../../svg/icons.svg'
;

function Icon(Props) {
  var name = Props.name;
  return React.createElement("svg", {
              className: "icon icon-" + (String(name) + "")
            }, React.createElement("use", {
                  xlinkHref: "#icons_" + (String(name) + "")
                }));
}

var make = Icon;

exports.make = make;
/*  Not a pure module */

And the generated JS

__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _svg_icons_svg__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../svg/icons.svg */ "./src/svg/icons.svg");
// Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE


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



function Icon(Props) {
  var name = Props.name;
  return React.createElement("svg", {
              className: "icon icon-" + (String(name) + "")
            }, React.createElement("use", {
                  xlinkHref: "#icons_" + (String(name) + "")
                }));
}

var make = Icon;

exports.make = make;
/*  Not a pure module */


Webpack config

  rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader']
      },
      {  
        test: /\.svg$/i,
        loader: 'svg-sprite-loader'
      },
    ]
  }

Icon sprite file

<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <symbol id="locked" viewBox="0 0 32 32">
      <path d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z"></path>
      <path d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.55 0.45 1 1 1h2v-14h-2z"></path>
      <path class="handles"
        d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.919 0.338 3.759 0.958 5.464-0.609 1.038-0.958 2.246-0.958 3.536 0 3.526 2.608 6.443 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.82 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"></path>
    </symbol>
  </defs>
</svg>

Solution

  • The problem seems to be that you're mixing the es6 and commonjs module systems. import is es6 while require and the exports object is commonjs.

    You'll have to either:

    1. Move everything to es6. It is probably sufficient to just get BuckleScript to emit es6 by setting the package-spec to es6-global in bsconfig.json.

    2. Move everything to commonjs. Change import to require and get svg-sprite-loader to emit commonjs by setting esModule to false in its options in webpack config.