Inlined styles link element is not removed from the HTML template
nathanosdev opened this issue · comments
I'm using a very basic config copied from the readme and everything works fine, but the link tag for my CSS file is still present in the HTML template causing a 404 error in the browser.
Can you give me the config and any html template you are using?
I'm getting the same thing. The inlined CSS is not removed as a external CSS file chunk.
const criticalCSS = new ExtractTextPlugin('[name].critical.css');
const entrypointCSS = new ExtractTextPlugin('[name].css');
const appCriticalCssLoader = {
test: /\.critical\.scss$/,
use: criticalCSS.extract(...),
include: path.resolve(ROOT_DIR, 'src')
};
const appCssLoader = {
test: /^(?!.*\.critical).*\.scss$/,
use: entrypointCSS.extract(...),
include: path.resolve(ROOT_DIR, 'src')
};
var config = {
module: {
rules: [
...
appCriticalCssLoader,
appCssLoader,
]
},
entry: {
'client': './client.js',
},
...
plugins: [
// Make App vendor chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
chunks: ['client'],
minChunks: function(module) {
return isVendor(module);
}
}),
// Create app index template
new HtmlWebpackPlugin({
template: 'templates/app.ejs',
filename: '../app.html', // place at root public folder
inject: false,
chunks: [
'vendor',
'client'
],
minify: htmlMinifySettings
}),
// External CSS Files
criticalCSS,
entrypointCSS,
// Inline Critical CSS
new StyleExtHtmlWebpackPlugin({
chunks: ['vendor', 'client'],
cssRegExp: appCriticalCssLoader.test,
minify: true,
position: 'head-top',
}),
]
}
Same problem here. I use pretty simple config:
{
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
},
}),
new HtmlWebpackPlugin({
hash: true,
template: 'src/index.html'
}),
new ExtractTextPlugin("styles.css"),
new StyleExtHtmlWebpackPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new webpack.optimize.AggressiveMergingPlugin(),
],
module: {
rules: [
{
test: /\.html$/,
exclude: /node_modules/,
loader: 'html-loader'
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
{
loader: "css-loader",
options: { importLoaders: 1 }
},
"postcss-loader"
]
})
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.js$/,
include: [
path.resolve(__dirname, "src"),
],
// exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'env'],
plugins: ['transform-runtime']
}
},
}
]
}
};
With webpack 1.12.13 everything was fine, but now we are using webpack 3.8.1 and we have the same problem. 😢
Hi - @medi71, @orenklein, @dictions, @AnimaMundi - can i confirm you are all using the latest version v3.4.3?
Also can I check that none of your html templates have explicit elements for stylesheets?
Yes, we are using the latest version 3.4.3.
Sorry I haven't been much help here, I actually ended up abandoning this approach. I was using the latest version of Webpack and this plugin at the time of writing and did not have any explicit elements for stylesheets.
My index.html looks like this:
<!DOCTYPE html>
<html>
<head>
<base href="/">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta charset="UTF-8">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<title>My Project</title>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
My Webpack config was extremely basic:
new HtmlWebpackPlugin({
template: 'app/index.html'
}),
new ExtractTextWebpackPlugin('styles.css'),
new StyleExtHtmlWebpackPlugin()
That's not my whole Webpack config, just the plugins section
Thanks for the feedback. I'm struggling to reproduce the problem in my unit tests - once I can I'll hopefully be be able to come up with a fix.
@numical I debugged it.
isCssLinkTag function at replaceTag.js is returning false when HtmlWebpackPlugin hash option is set to true because stylePath
is not equal to stylePath?hash
, therefore the link tag is not removed. (https://github.com/numical/style-ext-html-webpack-plugin/blob/master/lib/replaceTag.js#L46)
Thanks @orenklein - I have incorporated a fix in version 3.4.4 for the bug you spotted.
Not closing this issue yet as I still get some failing tests for edge cases (with css regex's) and this cannot be the reason for @dictions issue as he's not using the hash.
@dictions : my failing tests are t0 do with the use of the cssRegExp
option so hopefully I'm onto your problem now...
@numical aha! good find.
@dictions : version 3.4.5 has a fix for my failing tests - to do with having a '?' in the css filename. Don't think it is your issue but could you give it a try?
FWIW, I'm having this exact same issue. Using 3.4.5
.
I'm running into the same issue using 3.4.5
as well, but the problem specific to my setup resides in html-webpack-plugin
. Here's the relevant part of my webpack config.
// In external file, included for simplicity
paths.css = name => path.join('css', `${name}.css`);
const critCssPath = paths.css('critical');
const criticalCss = new ExtractTextPlugin(critCssPath);
const externalCss = new ExtractTextPlugin(paths.css('bundle'));
const cssLoaderOpts = {
use: [{
loader: 'css-loader?minimize'
},
{
loader: 'postcss-loader',
options: {
plugins: [autoprefixer()]
}
},
{
loader: 'sass-loader'
}]
};
export default {
output: {
publicPath: 'build/'
},
module: {
rules: [{
test: /critical\.scss/,
exclude: /node_modules/,
use: criticalCss.extract(cssLoaderOpts)
},
{
test: /styles\.scss/,
exclude: /node_modules/,
use: externalCss.extract(cssLoaderOpts)
}]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
criticalCss,
externalCss,
new StyleExtHtmlWebpackPlugin(critCssPath)
]
};
You can view the whole configuration here.
I ran into this issue at work where my computer runs Windows 7 😒. This plugin wasn't removing the <link>
for criticalCss
.
The first problem was my own fault. I use the path
module for pretty much everything in my webpack config, yet I was setting output.publicPath = 'build/'
which caused this line to return false
. tagDefinition.attributes.href
was 'build/css\critical.css'
while stylePath
was 'build\css\critical.css'
.
So I switched to output.publicPath = path.join('build', '/')
. Except the <link>
still wasn't being removed because of the same condition in isCssLinkTag
. However, this time tagDefinition.attributes.href
was 'build\/css\critical.css'
.
Turns out this is happening because of this conditional. convertToPath
inside replaceTag.js
correctly constructs the path on Windows, while html-webpack-plugin
appends an extra /
.
When I changed this:
if (publicPath.length && publicPath.substr(-1, 1) !== '/') {
publicPath += '/';
}
to this:
var separator = path.join('/');
if (publicPath.length && publicPath.substr(-1, 1) !== separator) {
publicPath += separator;
}
The <link>
for criticalCss
was correctly removed without any changes in this plugin.
This is my first time dabbling with webpack plugin source code and I'm not exactly sure where to go from here. Should I be submitting an issue to html-webpack-plugin
or is using the path
module in webpack config overkill?
@kytrol I tested your solution on my configuration (same issue) and it didn't solve the problem for me.
I'm using Windows 10 64x
I've the same issue, publicPath is set.
Code's in here: https://github.com/kurtextrem/Improved-for-Instagram/blob/master/webpack.config.js
In Windows, the problem, I think, is comparing the href link with filesystem path which differ in slashes (slashes vs back-slashes).
Removing the publicPath (making it an empty string) and changing the name of the css to be in the root (i.e. "styles.css") solved the problem for me.
Though now the js is always relative to the folder i'm in.
I'm still experiencing this with "style-ext-html-webpack-plugin": "^4.1.2"
, here's my config
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const ScriptExtHTMLWebpackPlugin = require('script-ext-html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCSSExtractPlugin = require('mini-css-extract-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const StyleExtHTMLWebpackPlugin = require('style-ext-html-webpack-plugin');
const root = path.join(__dirname, '../');
module.exports = {
performance: {
hints: false
},
entry: path.join(root, 'src/ui/entry.js'),
output: {
path: path.join(root, 'build/public'),
filename: '[hash].js'
},
module: {
strictExportPresence: true,
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
MiniCSSExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader'
]
}
]
},
plugins: [
new MiniCSSExtractPlugin({
filename: '[hash].css'
}),
new HTMLWebpackPlugin({
minify: true,
cache: true,
filename: path.join(root, 'build/index.html'),
template: path.join(root, 'src/index.html')
}),
new StyleExtHTMLWebpackPlugin({
position: 'head-bottom',
minify: true
}),
new ScriptExtHTMLWebpackPlugin({
defaultAttribute: 'defer',
preload: /\.js$/
}),
new CopyWebpackPlugin([
{
from: path.join(root, 'src/server.js'),
to: path.join(root, 'build')
},
{
from: path.join(root, 'src/.env'),
to: path.join(root, 'build')
}
])
]
};
The CSS is being inlined as style
tag in the HTML's head, however the link tag pointing to the now missing CSS file (because it's been inlined so the file wouldn't exist) was not removed.
I also have this problem with 4.1.2
. I am on windows but I don't think its a path problem (not sure exactly how to debug as others above have done)
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PurgecssPlugin = require('purgecss-webpack-plugin')
const StyleExtHtmlWebpackPlugin = require("style-ext-html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const purgecssFromHtml = require('purgecss-from-html');
const path = require("path");
const glob = require('glob');
const PATHS = {
src: path.join(__dirname, 'src')
}
module.exports = {
module: {
rules: [
{
test: /\.scss$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: false
}
},
'sass-loader',
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"]
}
]
},
externals: {
moment: 'moment'
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src", "index.html")
}),
new MiniCssExtractPlugin({
filename: "[name].css"
}),
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/*.*`),
styleExtensions: ['.css'],
whitelist: ['whitelisted'],
extractors: [
{
extractor: purgecssFromHtml,
extensions: ['html']
}
]
}),
new StyleExtHtmlWebpackPlugin({
position: 'head-bottom'
}),
new ScriptExtHtmlWebpackPlugin({
position: 'head-bottom',
defaultAttribute: 'defer',
inline: 'main'
})
]
};
the css is inlined but the <link
tag remains.
the scripts are inlined properly and a <script src="
is not present, so that works at least
OK so you need to have position: 'plugin'
specified in order for it to replace the link
with a style
.
Is this documented anywhere? Either I'm blind/did a crappy job of reading/this isnt easy to come across. Maybe update the docs?