Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static file path returned instead of file content on require('./file.svg') #5708

Closed
VanDoornP opened this issue Feb 21, 2019 · 18 comments
Closed

Comments

@VanDoornP
Copy link

VanDoornP commented Feb 21, 2019

I'm building a component which loads SVG files. But when I use icon = import() or icon = require(), the value returned is a static-file-path instead of the content of the file.

this.prop.iconName = 'cart' (propType.oneOf)
const icon require(`../assets/svg/${iconName}.svg`)
console.log(icon)

Yields

static/media/cart.67bd7202.svg

The issue #1776 is about a similar issue, except that I'm using the full control mode, and I did reset the server. I tried no custom loaders, svg-url-loader, url-loader and file-loader.

My current .storybook/webpack.config.js:

const path = require('path');

module.exports = (baseConfig, env, defaultConfig) => {
    // Extend defaultConfig as you need.

    // For example, add scss loader:
    defaultConfig.module.rules.push({
        test: /\.scss$/,
        loaders: ['style-loader', 'css-loader', 'sass-loader'],
        include: path.resolve(__dirname,'../')
    });
    defaultConfig.resolve.extensions.push('.scss');

    // For example, add SVG loader:
    defaultConfig.module.rules.push({
        test: /\.svg$/,
        loaders: ['svg-url-loader'],
        include: path.resolve(__dirname,'../')
    });
    defaultConfig.resolve.extensions.push('.svg');

    return defaultConfig;
};

Project webpack.config.js:

{
    test: /\.svg$/,
    use: [
        {
            loader: 'svg-url-loader',
            options: {},
         }
    ]
}

"webpack": "^4.27.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.14"

Command to run storybook locally: "start-storybook -p 9001 -s .storybook/static -c .storybook"

Can anyone help me in the right direction?

@VanDoornP
Copy link
Author

VanDoornP commented Feb 26, 2019

Okay, it took some time, but I was finally able to track down the problem.

I tried several loaders in a clean react app install without storybook, and found out that the loader I needed was "raw-loader" (I wanted the content of the file). The next problem was that I had already tried this loader in storybook and, like the others, it yielded the static file path instead of the content. I looked into various potential solutions, and at some point I stumbled on...:

require(`-!raw-loader!../assets/svg/${iconName}.svg`);

For anyone who doesn't know (I didn't), these are loader prefixes which allow you to override the order of the preloaders.

Problem is they're deprecated, but it did give a clear clue as to where to look next: storybook's default webpack config, which I'm extending using the "Full Control + default" mode. This config contains:

{
  test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
  loader: '[long path]/file-loader/dist/cjs.js',
  query: { name: 'static/media/[name].[hash:8].[ext]' }
}

I added a few lines to my .storybook/webpack.config.js file, so it now looks like:

const path = require('path');

module.exports = (baseConfig, env, defaultConfig) => {
    defaultConfig.module.rules.push({
        test: /\.scss$/,
        loaders: ['style-loader', 'css-loader', 'sass-loader'],
        include: path.resolve(__dirname,'../')
    });
    defaultConfig.resolve.extensions.push('.scss');

    defaultConfig.module.rules.push({
        test: /\.svg$/,
        loader: 'raw-loader',
        include: path.resolve(__dirname,'../')
    });
    defaultConfig.resolve.extensions.push('.svg');

    defaultConfig.module.rules.forEach(function(data, key) {
        if (data.test.toString().indexOf('svg|') >= 0) {
            defaultConfig.module.rules[key].test = /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/;
            return false;
        }
    });

    return defaultConfig;
};

Which technically resolves my problem. I like the fact that I can still use the default settings, but it's a pretty ugly hack. Not using the defaults feels even worse though... I have been unable to find clear instructions on how I'd build up all the necessary rules myself, and it feels like re-inventing the wheel.

Suggestions?

@jesprider
Copy link

The same problem here with svg-inline-loader. It worked well with v4.1.1. After v5.0.0 it returns a string (path to an svg file) instead of an svg tag.

{
    test: /\.svg$/,
    loader: 'svg-inline-loader',
},

What I found so far is that the loader is completely ignored by storybook in webpack extend mode.

@jesprider
Copy link

jesprider commented Mar 5, 2019

Update: it is exactly what @VanDoornP says in a previous comment. Used the same hack for now.

The only thing that there is no 3rd argument in a module.exports function:

module.exports = (baseConfig, env, defaultConfig) => {...}
// should be
module.exports = ({ config }) => {
    config.module.rules.push(...);
    ...
    return config;
}

@Pixelatex
Copy link

@shilman any eta on this thing? 😄

@shilman
Copy link
Member

shilman commented Mar 6, 2019

Played around with this today and came up with this partial workaround. Issue is still WIP: #5924

@Pixelatex
Copy link

@shilman the weird thing is that this used to work with my webpack setup, (not full control) but it seems to just ignore my loaders now 😕seems like either something undocumented changed in the webpack api or something is broken..

@shilman shilman self-assigned this Mar 7, 2019
@Pixelatex
Copy link

pre-v5, this config worked for me without issue, post-v5 it doesn't do a thing, it just ignores it.

const path = require('path');

module.exports = {
    module: {
        rules: [
            {
                test: /\.scss$/,
                loaders: ['style-loader', 'css-loader', 'sass-loader'],
                include: path.resolve(__dirname, '../'),
            },
            {
                test: /\.svg$/,
                loader: 'svg-inline-loader',
            },
            {
                test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/,
                loader: require.resolve('file-loader'),
                query: {
                name: 'static/media/[name].[hash:8].[ext]'
                },
            },

        ],
    },
};

Looking at the docs, this should still be the correct way of using them but not a single rule seems to be used right now, it's not just the svg's that get frapped.

@shilman
Copy link
Member

shilman commented Mar 16, 2019

Boo-yah!! I just released https://github.com/storybooks/storybook/releases/tag/v5.1.0-alpha.7 containing PR #6104 that references this issue. Upgrade today to try it out!

Because it's a pre-release you can find it on the @next NPM tag.

Closing this issue. Please re-open if you think there's still more to do.

@shilman shilman closed this as completed Mar 16, 2019
@shilman
Copy link
Member

shilman commented Mar 16, 2019

@Pixelatex 5.1.0-alpha.7 should fix your problem since it restores 4.x "extend mode" webpack behavior. @VanDoornP has a slightly different problem since he's already using full-control mode. I'll patch it back into 5.0.x once a few people have verified the fix. Please LMK!

@ng-hai
Copy link

ng-hai commented Mar 17, 2019

Tested with svg-react-loader and still get this error

image

Here is my webpack config

const path = require('path')

// Export a function. Accept the base config as the only param.
module.exports = async ({ config, mode }) => {
  // `mode` has a value of 'DEVELOPMENT' or 'PRODUCTION'
  // You can change the configuration based on that.
  // 'PRODUCTION' is used when building the static version of storybook.

  // Make whatever fine-grained changes you need
  config.module.rules.push({
    test: /\.scss$/,
    loaders: [
      'style-loader',
      {
        loader: 'css-loader',
        options: { modules: true },
      },
      'sass-loader',
    ],
    include: path.resolve(__dirname, '../'),
  })

  config.module.rules.push({
    test: /\.svg$/,
    include: path.resolve(__dirname, '../src/icons'),
    exclude: /node_module/,
    use: {
      loader: 'svg-react-loader',
      options: {
        name: 'Icon'
      }
    }
  })

  // Return the altered config
  return config
}

The component still render well when the development running with Gatsby, outside of Storybook.

image

@shilman
Copy link
Member

shilman commented Mar 17, 2019

@ng-hai You'll probably need to remove the existing svg loader and add yours in -- there's a conflict right now. You can look at the combined webpack config with the --debug-webpack command-line option that was added in 5.0.2

@ng-hai
Copy link

ng-hai commented Mar 17, 2019

It works @shilman, it works 😄

@shilman
Copy link
Member

shilman commented Mar 18, 2019

cc @elie222

@radum
Copy link

radum commented Mar 25, 2019

I have the same error as @ng-hai , removing the existing SVG rules like @VanDoornP did solves that problem.

But now, another problem I have is that if I used babel with corejs 3 it will fail with missing polyfills. If I use corejs2 works just fine.

I think this should also be addressed because storybook does't specify the corejs version with babel and that also triggers an warning.

@shilman
Copy link
Member

shilman commented Mar 25, 2019

@radum Mind filing a separate issue about the corejs version?

@radum
Copy link

radum commented Mar 26, 2019

@shilman Raised here #6294

@iambumblehead
Copy link

iambumblehead commented May 3, 2019

I'm using @storybook/react 5.0.11 and found this config to work,

module.exports = async ({ config }) => {
    config.resolve.extensions.push('.svg');

    config.module.rules = config.module.rules.map( data => {
        if (/svg\|/.test( String( data.test ) ))
            data.test = /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/;

        return data;
    });

    config.module.rules.push({
        test: /\.svg$/,
        use: [{ loader: require.resolve('babel-loader') },
              { loader: require.resolve('react-svg-loader') }]
    });

    return config;
};

@adrienjoly
Copy link

adrienjoly commented Jul 26, 2019

FYI, just adding the following parts in .storybook/webpack.config.js did the trick:

  config.module.rules = config.module.rules.map( data => {
    if (/svg\|/.test( String( data.test ) ))
      data.test = /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/;
    return data;
  });

... and:

  config.module.rules.push({
      test: /\.svg$/,
      use: [
        { loader: 'svg-inline-loader' }
      ]
  });

=> I did not need to add .svg in config.resolve.extensions.

Thanks for your help!

MethodenMann pushed a commit to tocco/tocco-client that referenced this issue Nov 22, 2019
MethodenMann pushed a commit to tocco/tocco-client that referenced this issue Nov 22, 2019
MethodenMann pushed a commit to tocco/tocco-client that referenced this issue Nov 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants