Closed sam1git closed 8 months ago
Hello @sam1git,
thanks for the question.
I try to clarify your question. There is the source HTML template:
<html>
<head>
<link href="../styles/main.css" rel="stylesheet" />
<script src="../scripts/main.js" defer="defer"></script>
</head>
<body>
<img src="../images/image.webp" />
</body>
</html>l
The plugin generate the HTML:
<html>
<head>
<link href="/static/main-e2ac2b4a.css" rel="stylesheet" />
<script src="/static/main-5317c1f6.js" defer="defer"></script>
</head>
<body>
<img src="/static/file1-1234abcd.png" />
</body>
</html>l
But you want to have the output paths of assets w/o the /static
leading path:
<html>
<head>
<link href="/main-e2ac2b4a.css" rel="stylesheet" />
<script src="/main-5317c1f6.js" defer="defer"></script>
</head>
<body>
<img src="/file1-1234abcd.png" />
</body>
</html>l
I can have express.static serve the static folder with all static files
This means that the static
prefix must be removed in the CSS
files too.
E.g. there is the source CSS:
.img {
width: 160px;
height: 130px;
background-image: url(../images/file1.png);
}
defaults will be generated
.img {
width: 160px;
height: 130px;
background-image: url(/static/file1-1234abcd.png);
}
but you want to have the following output path:
.img {
width: 160px;
height: 130px;
background-image: url(/file1-1234abcd.png);
}
Is it right?
Because the generated /static/main-e2ac2b4a.cs
output path is correct URL relative by build/
directory, is no right way to customise the publicPath
different from the path where will be saved generated assets.
But if you will have incorrect
(in terms of Webpack config) URLs in the generated HTML, then you can force transform the generated HTML before it will be saved. See please the beforeEmit callback.
For example:
const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'dist/'),
publicPath: '/',
},
plugins: [
new HtmlBundlerPlugin({
entry: {
home: './src/views/home.html',
},
outputPath: 'html/', // html path relative by `dist/`
js: {
filename: 'static/[name]-[contenthash:8].js',
},
css: {
filename: 'static/[name]-[contenthash:8].css',
},
// you can modify the generated HTML content here
beforeEmit: (content, { assetFile }, compilation) => {
console.log(compilation.assets); // => all assets used in the current entry template
// remove the `/static/` prefix in generated HTML
return content.replaceAll('="/static/', '="/');
},
}),
],
module: {
rules: [
{
test: /\.(css)$/,
use: ['css-loader'],
},
{
test: /\.(png|jpe?g|webp|ico|svg)$/,
type: 'asset/resource',
generator: {
filename: 'static/[name].[hash:8][ext]',
},
},
],
},
};
If you want remove the prefix in the CSS files, then you can modify the compilation.assets
object:
beforeEmit: (content, { assetFile }, compilation) => {
const { RawSource } = compilation.compiler.webpack.sources;
// remove the prefix in CSS files used in the HTML
for (let filename in compilation.assets) {
// skip not css files
if (!/\.(css)$/.test(filename)) continue;
let assetContent = compilation.assets[filename].source();
assetContent = assetContent.replaceAll('/static/', '/');
compilation.updateAsset(filename, new RawSource(assetContent));
//console.log('>>: ', { filename }, assetContent);
}
// remove the prefix in the generated HTML
return content.replaceAll('="/static/', '="/');
},
@webdiscus thank you for the answer. your understanding of the questions is correct and went one step ahead. I wasn't thinking of resolving paths correctly (or should I say incorrectly from webpacks perspective) in the .css output asset files. Suggested solution works like a charm. This is what my config file looks like now:
const path = require('path');
const HtmlBundlerWebpackPlugin = require('html-bundler-webpack-plugin');
module.exports = {
mode: "production",
output: {
path: path.resolve(__dirname, 'build'),
publicPath: '/',
assetModuleFilename: "static/[name]-[contenthash][ext]",
clean: true,
},
plugins: [
new HtmlBundlerWebpackPlugin({
entry: './src/templates/',
outputPath: path.join(__dirname, 'build/html/'),
minify: {
collapseWhitespace: true,
keepClosingSlash: true,
removeComments: true,
removeRedundantAttributes: false,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
minifyCSS: true,
minifyJS: true,
},
js: {
filename: 'static/[name]-[contenthash].js',
},
css: {
filename: 'static/[name]-[contenthash].css',
},
beforeEmit: (content, {assetFile}, compilation) => {
const { RawSource } = compilation.compiler.webpack.sources;
for (let filename in compilation.assets) {
if (!/\.(css)$/.test(filename)) continue;
let assetContent = compilation.assets[filename].source();
assetContent = assetContent.replaceAll('/static/', '/');
compilation.updateAsset(filename, new RawSource(assetContent));
}
return content.replaceAll('="/static/', '="/');
}
})
],
module: {
rules: [
{
test: /\.js$/i,
exclude: [/node_modules/],
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: "asset/resource",
},
{
test: /\.css$/i,
use: [
'css-loader'
],
}
],
},
};
Unrelated to the question in the heading and seems unrelated to applied code changes but for some reason CSS files are not being minified.
package.json:
"devDependencies": {
"@babel/preset-env": "^7.23.6",
"babel-loader": "^9.1.3",
"css-loader": "^6.8.1",
"dotenv": "^16.3.1",
"html-bundler-webpack-plugin": "^3.4.7",
"nodemon": "^3.0.1",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
@sam1git
to minify CSS, add the sass-loader
:
npm i sass-loader sass --save-dev
modify your webpack config:
{
test: /\.css$/i,
use: ['css-loader', 'sass-loader'],
}
The sass-loader
can minify CSS.
Note: the minify.minifyCSS
and minify.minifyJS
options minify inlined CSS and JS code in HTML, not in separate files.
Note: the
minify.minifyCSS
andminify.minifyJS
options minify inlined CSS and JS code in HTML, not in separate files.
ah, i see. thank you.
I have directory structure as shown below. My webpack.prod.js file uses html-bundler-webpack-plugin as shown below.
The output as expected gives all .css and .js files inside
build/static
directory and all .html files insidebuild/html
directory. The output html files have<link>
tags that point to css like sohref=/static/<filename>.css
Can I specify a custom path so that href looks like
href=/filename.css
while still preserving the output directory structure shown below and having all .css files insidebuild/static
.Why I want to do this? When I serve the output html files using node js, the path inside
<link>
is interpreted as relative to the url in the web browser. This way, I can have express.static serve the static folder with all static .css files inside it while controlling access to the html files rather than having all html files and static files inside one directory.