4

I'm using React component as an NPM Package. in the component, I have SCSS file with url(../iamges/img..) path, but actually, the images folder located in the Dist folder, how can I point Webpack to take the relative path from node_modules and serve it from images folder located in the Dist?

located in node_modules =>
background: url('../images/some-icon.svg') no-repeat center center; 

Webpack config:


const webpack = require('webpack');
const path = require('path');

module.exports = {
    entry: './src/index.js',
    devtool: 'inline-module-source-map',
    output: {
        path: path.resolve(__dirname, '/dist'),
        filename: 'bundle.js',
        publicPath: '/',
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                },
            },
            {
                test: /\.scss$/,
                use: [
                    { loader: 'style-loader' },
                    { loader: 'css-loader' },
                    {
                        loader: 'resolve-url-loader',
                        // options: {
                        //  debug: true,
                        //  root: path.join(__dirname, './dist/images'),
                        //  includeRoot: true,
                        //  absolute: true,
                        // },
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: true,
                            sourceMapContents: false,
                        },
                    },
                ],
            },
            {
                test: /\.css$/,
                loaders: ['style-loader', 'css-loader'],
            },
            {
                test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
                use: {
                    loader: 'url-loader?name=/images/[name].[ext]',
                    options: {
                        limit: 10000,
                    },
                },
            },
        ],
    },
    resolve: {
        extensions: ['.js', '.jsx'],
        // modules: [path.resolve(__dirname, '/images'), 'node_modules'],
        alias: {
            'react-redux': path.resolve('./node_modules/react-redux'),
        },
    },
    plugins: [new webpack.HotModuleReplacementPlugin()],
    devServer: {
        hot: true,
        publicPath: '/dist/',
    },
};

babel.config.js

module.exports = {
    // presets: ['@babel/preset-env', '@babel/preset-react'],
    plugins: [
        '@babel/plugin-proposal-class-properties',
        '@babel/plugin-proposal-export-default-from',
        '@babel/transform-runtime',
    ],
    sourceType: 'unambiguous',
    presets: [
        [
            '@babel/preset-env',
            {
                targets: {
                    node: 'current',
                },
            },
        ],
        '@babel/preset-react',
    ],
};


dist
   -- images
   -- index.html

ERROR:

ERROR in ./node_modules/comp/src/styles/details.scss (./node_modules/css-loader!./node_modules/resolve-url-loader!./node_modules/sass-loader/lib/loader.js??ref--5-3!./node_modules/compdetails/src/styles/details.scss)
Module not found: Error: Can't resolve '../images/icon.svg'
Edi Afremov
  • 77
  • 1
  • 2
  • 8

1 Answers1

3

Anything referred through url('...') in css will be computed with reference to the path of deployed application (scss will not compute the path unless variable or function is not being used):

For example: If your referred component SCSS module is having background: url('../images/some-icon.svg') no-repeat center center;

The final CSS compilation will be same (it is also because the component is not using any SCSS variables or functions to compute the final path).

So your application will always try to find that image as:

Example: http://localhost:3000/../images/some-icon.svg which is a problem. (.. is referred as parent directory)

If you try to run your app with some sub-url (also known as sub context) as http://localhost:3000/sub-url/ and you keep your images parallel to sub-url folder it will automatically work.

-- /
   |
   |-- sub-url
         |
         |-- index.html
   |-- images
         |
         |-- some-icon.svg

Another option can be override the component SCSS with yours.

You already found the solution to use resolve-url-loader, but in this case you need to import the component's scss file into you scss.

so your webpack config should look like:

const webpack = require('webpack');
const path = require('path');

module.exports = {
    entry: './src/index.js',
    devtool: 'inline-module-source-map',
    output: {
        path: path.resolve(__dirname, '/dist'),
        filename: 'bundle.js',
        publicPath: '/',
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                },
            },
            {
                test: /\.scss$/,
                use: [
                    { loader: 'style-loader' },
                    { loader: 'css-loader' },
                    // CHANGE HERE
                    {
                        loader: 'resolve-url-loader',
                        options: {
                          root: '/images', // considering all your images are placed in specified folder. Note: this is just a string that will get as prefix to image path
                          includeRoot: true,
                          absolute: true,
                        },
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: true,
                            sourceMapContents: false,
                        },
                    },

                ],
            },
            {
                test: /\.css$/,
                loaders: ['style-loader', 'css-loader'],
            },
            {
                test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
                use: {
                    loader: 'url-loader?name=/images/[name].[ext]',
                    options: {
                        limit: 10000,
                    },
                },
            },
        ],
    },
    resolve: {
        extensions: ['.js', '.jsx'],
        // modules: [path.resolve(__dirname, '/images'), 'node_modules'],
        alias: {
            'react-redux': path.resolve('./node_modules/react-redux'),
        },
    },
    plugins: [new webpack.HotModuleReplacementPlugin()],
    devServer: {
        hot: true,
        publicPath: '/dist/',
    },
};

I hope it helps.

Sudhir
  • 125
  • 7