extract-loader

extract-loader

webpack loader to extract HTML and CSS from the bundle.

Dependency Status Build Status Coverage Status

The extract-loader evaluates the given source code on the fly and returns the result as string. Its main use-case is to resolve urls within HTML and CSS coming from their respective loaders. Use the file-loader to emit the extract-loader's result as separate file.

import stylesheetUrl from "file-loader!extract-loader!css-loader!main.css";
// stylesheetUrl will now be the hashed url to the final stylesheet

The extract-loader works similar to the extract-text-webpack-plugin and the mini-css-extract-plugin and is meant as a lean alternative to it. When evaluating the source code, it provides a fake context which was especially designed to cope with the code generated by the html- or the css-loader. Thus it might not work in other situations.


Installation

npm install extract-loader


Examples

##

Bundling CSS with webpack has some nice advantages like referencing images and fonts with hashed urls or hot module replacement in development. In production, on the other hand, it's not a good idea to apply your stylesheets depending on JS execution. Rendering may be delayed or even a FOUC might be visible. Thus it's still better to have them as separate files in your final production build.

With the extract-loader, you are able to reference your main.css as regular entry. The following webpack.config.js shows how to load your styles with the style-loader in development and as separate file in production.

module.exports = ({ mode }) => {
    const pathToMainCss = require.resolve("./app/main.css");
    const loaders = [{
        loader: "css-loader",
        options: {
            sourceMap: true
        }
    }];

    if (mode === "production") {
        loaders.unshift(
            "file-loader",
            "extract-loader"
        );
    } else {
        loaders.unshift("style-loader");
    }

    return {
        mode,
        entry: pathToMainCss,
        module: {
            rules: [
                {
                    test: pathToMainCss,
                    loaders: loaders
                },
            ]
        }
    };
};

Extracting the index.html Extracting the index.html" class="icon-link" href="#extracting-the-index-html">

You can even add your index.html as entry and just reference your stylesheets from there. You just need to tell the html-loader to also pick up link:href:

module.exports = ({ mode }) => {
    const pathToMainJs = require.resolve("./app/main.js");
    const pathToIndexHtml = require.resolve("./app/index.html");

    return {
        mode,
        entry: [
            pathToMainJs,
            pathToIndexHtml
        ],
        module: {
            rules: [
                {
                    test: pathToIndexHtml,
                    use: [
                        "file-loader",
                        "extract-loader",
                        {
                            loader: "html-loader",
                            options: {
                                attrs: ["img:src", "link:href"]
                            }
                        }
                    ]
                },
                {
                    test: /\.css$/,
                    use: [
                        "file-loader",
                        "extract-loader",
                        {
                            loader: "css-loader",
                            options: {
                                sourceMap: true
                            }
                        }
                    ]
                },
                {
                    test: /\.jpg$/,
                    use: "file-loader"
                }
            ]
        }
    };
}

turns

<html>
<head>
    <link href="main.css" type="text/css" rel="stylesheet">
</head>
<body>
    <img src="hi.jpg">
</body>
</html>

into

<html>
<head>
    <link href="7c57758b88216530ef48069c2a4c685a.css" type="text/css" rel="stylesheet">
</head>
<body>
    <img src="6ac05174ae9b62257ff3aa8be43cf828.jpg">
</body>
</html>


Source Maps

If you want source maps in your extracted CSS files, you need to set the sourceMap option of the css-loader:

    {
        loader: "css-loader",
        options: {
            sourceMap: true
        }
    }


Options

There is currently exactly one option: publicPath. If you are using a relative publicPath in webpack's output options and extracting to a file with the file-loader, you might need this to account for the location of your extracted file. publicPath may be defined as a string or a function that accepts current loader context as single argument.

Example with publicPath option as a string:

module.exports = {
    output: {
        path: path.resolve("./dist"),
        publicPath: "dist/"
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "file-loader",
                        options: {
                            name: "assets/[name].[ext]",
                        },
                    },
                    {
                        loader: "extract-loader",
                        options: {
                            publicPath: "../",
                        }
                    },
                    {
                        loader: "css-loader",
                    },
                ],
            }
        ]
    }
};

Example with publicPath option as a function:

module.exports = {
    output: {
        path: path.resolve("./dist"),
        publicPath: "dist/"
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "file-loader",
                        options: {
                            name: "assets/[name].[ext]",
                        },
                    },
                    {
                        loader: "extract-loader",
                        options: {
                            // dynamically return a relative publicPath based on how deep in directory structure the loaded file is in /src/ directory
                            publicPath: (context) => '../'.repeat(path.relative(path.resolve('src'), context.context).split('/').length),
                        }
                    },
                    {
                        loader: "css-loader",
                    },
                ],
            }
        ]
    }
};

You need another option? Then you should think about:


Contributing

From opening a bug report to creating a pull request: every contribution is appreciated and welcome. If you're planing to implement a new feature or change the api please create an issue first. This way we can ensure that your precious work is not in vain.

All pull requests should have 100% test coverage (with notable exceptions) and need to pass all tests.

  • Call npm test to run the unit tests
  • Call npm run coverage to check the test coverage (using istanbul)


License

Unlicense

Sponsors