jhamlet / svg-react-loader

Webpack SVG to React Component Loader
MIT License
559 stars 82 forks source link

SVGs referenced by style using `url()` syntax result in javascript in compiled css #50

Closed nicgordon closed 7 years ago

nicgordon commented 7 years ago

Hello, I have a tricky situation: we are requiring SVG files and would like them to be converted to react components which this library is working for, but then we also use some styles from a third party library which requires an SVG file, and when that conversion is taking place this loader injects JS into our compiled CSS file.

Our loaders config looks like this:

      {
        test: /\.styl$/,
        loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[path][name]---[local]---[hash:base64:5]&-autoprefixer!postcss!stylus'),
      },
      {
        test: /\.svg$/,
        loader: 'babel!svg-react',
      }

The result looks something like this:

background-image:url(function (props, context, updater) {
          // This constructor gets overridden by mocks. The argument is used
          // by mocks to assert on what gets mounted.

          if (false) {
            process.env.NODE_ENV !== 'production' ? warning(this instanceof Constructor, 'Something is calling a React component directly. Use a factory or ' + 'JSX instead. See: https://fb.me/react-legacyfactory') : void 0;
          }

          // Wire up auto-binding
          if (this.__reactAutoBindPairs.length) {
            bindAutoBindMethods(this);
          }

          this.props = props;
          this.context = context;
          this.refs = emptyObject;
          this.updater = updater || ReactNoopUpdateQueue;

          this.state = null;

          // ReactClasses doesn't have constructors. Instead, they use the
          // getInitialState and componentWillMount methods for initialization.

          var initialState = this.getInitialState ? this.getInitialState() : null;
          if (false) {
            // We allow auto-mocks to proceed as if they're returning null.
            if (initialState === undefined && this.getInitialState._isMockFunction) {
              // This is probably bad practice. Consider warning here and
              // deprecating this convenience.
              initialState = null;
            }
          }
          !(typeof initialState === 'object' && !Array.isArray(initialState)) ?  false ? invariant(false, '%s.getInitialState(): must return an object or null', Constructor.displayName || 'ReactCompositeComponent') : _prodInvariant('82', Constructor.displayName || 'ReactCompositeComponent') : void 0;

          this.state = initialState;
        })

which obviously isn't valid CSS 😩

Is there a way of differentiating between SVGs that are loaded via require() and ones that are loaded via url() inside CSS? If there is then I might be able to set a separate loader for those SVGs. Thank you in advance.

nemzes commented 7 years ago

Can confirm; having the same problem.

agurtovoy commented 7 years ago

Same here. Don't know enough about webpack yet to suggest a proper solution, but one possible workaround is to adopt a specific naming convention for .svgs used in React components, e.g. .inline.svg, and adjust your test: regex-es correspondingly.

jhamlet commented 7 years ago

Besides naming conventions, you can use the loader configuration's include, and exclude properties to define regular expressions to only include, or exclude, certain paths/files.

For example my current company ran across an issue when using an SVG font. Here was the solution:

{
  test: /\.svg$/,
  exclude: [ /node_modules/, /fonts?/ ],
  loader: 'svg-react'
},
{
  test: /\.woff|\.woff2|\.eot|\.ttf|\.svg/,
  include: /\/fonts?\//,
  loader: 'url-loader?limit=100000&name=[name]-[sha1:hash:hex:7].[ext]'
}

Note: We are using svg-react-loader@next which does not require a babel-loader

bigkrp commented 4 years ago

Both cases doesn't resolve the problem. If i want use same svg file for styles and for rendering as react component, i must duplicate it in another folder or with another name.

vyprichenko commented 2 years ago

This problem can be quite easily resolved with Webpack Rule.issuer and (optionally) Rule.oneOf for more precise configuration. No images duplication is necessary.

Example config:

{
    test: /\.(png|jpg|gif|svg)$/,
    // As opposed to Webpack loaders order (from last to first),
    // only the first matching Rule will be used here
    oneOf: [
        {
            test: /\.svg$/,
            // Use this loader for SVG-into-JavaScript imports only
            issuer: /\.(js|jsx)$/,
            loader: 'svg-react-loader'
        },
        {
            // Use file-loader for all other kinds of images
            loader: 'file-loader'
        }
    ]
}