webpack loader to extract HTML and CSS from the bundle.
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.
npm install extract-loader
##
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
},
]
}
};
};
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>
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
}
}
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:
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.
npm test
to run the unit testsnpm run coverage
to check the test coverage (using istanbul)Unlicense