1

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>
glennsl
  • 28,186
  • 12
  • 57
  • 75
Peteris
  • 3,548
  • 4
  • 28
  • 44
  • Thanks @Rob - fixed. – Peteris Aug 16 '19 at 19:44
  • 1
    You should include the generated javascript. – glennsl Aug 16 '19 at 20:58
  • Done now have the Reason -> React -> Webpack generated JS. – Peteris Aug 16 '19 at 21:44
  • 1
    Just an educated guess, but you're mixing es6 and commonjs. `import` is es6, `require` is commonjs. You can either get bucklescript to emit es6, or change the `import` to `require`. You'll then probably also have to set the `esModule` option to `false`. – glennsl Aug 16 '19 at 23:02
  • 1
    You're also not assigning the imported svg to any identifier. Not sure how that's supposed to work then. – glennsl Aug 16 '19 at 23:02
  • At the risk of sounding like a Luddite, I'm not totally sure what this plugin is or how it works. Could you provide some background info? Then perhaps we can point you to a more idiomatic Reason way of doing it. – Yawar Aug 17 '19 at 01:53
  • Not assigning the svg to an identifier is how it's meant to be used in the react tutorial. Thanks a lot for pointing out the commonjs/es6 dilemma, converting now to es6-global! The purpose of this plug-in is to allow one to maintain an svg file of sprites (icons) and then be able to switch between them just by changing the id of the local svg component. – Peteris Aug 17 '19 at 08:57
  • Changing to `es6-global` in `bsconfig` fixed things - do you mind submitting so I can accept as correct answer? Thank you! – Peteris Aug 17 '19 at 09:33
  • Unfortunately right now the SVG path is loaded and present in the DOM but not visible at all. – Peteris Aug 17 '19 at 22:23
  • I've updated the question with this detail. – Peteris Aug 18 '19 at 10:47
  • 1
    That's a different question, unrelated to most of the existing tags. You should post it as a separate question instead. Doing so will also get you a lot more attention. I'll roll your last edit back and formulate an answer from the comments here. – glennsl Aug 18 '19 at 13:51
  • Posted new question here: https://stackoverflow.com/questions/57546347/svg-not-visible-svg-sprite-loader-webpack-reason-ml. – Peteris Aug 18 '19 at 16:19

1 Answers1

2

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.

glennsl
  • 28,186
  • 12
  • 57
  • 75