Styling differences in Webpack Dev versus Prod - css
Short Summary:
I've noticed that there are small styling discrepancies between my dev/prod configurations of webpack when testing locally. It seems to have to do with the sass-loader, but it might expand outside of that? I can't be sure because it's very difficult to track down the bug.
(Code at bottom, images just to highlight certain elements)
The Clue
The majority of styles that are missing on dev or overwritten come from my Sass file. Therefore I imagine the culprit is buried somewhere in how I process Sass files on prod.config.js versus dev.config.js. Specifically, I've noticed that variables in Sass like $main-font are not interpreted on dev, but do seem ok on prod config. I've also noticed some weird overrides happening where dev might have a border applied from a set of classes, but production doesn't. Perhaps Minicssextract is doing something differently for prod?
The Problem
In the image below, the left is my production configuration. As you can see it's extracting text into a CSS file which is called in index.html. It's loading my SCSS variables just fine. On the right, I don't use minicssextractloader and all my styles get put into tags and injected. It's switching the font-family over to something that I haven't defined anywhere in my code?
The Loader
On the left is dev, on the right is prod for Sass.
Here's how this font is defined in Sass:
Ideas and tests:
Style-loader: I tried removing postcss, sass-loader, and style-loader on dev form the scss test. Didn't seem to fix the problem.
Is there a chance webpack is doing something funky between dev/production modes?
Perhaps my css compression on prod is making things strange?
Dev.config
const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
require('babel-polyfill').default;
const PATHS = {
app: path.join(__dirname, '../src'),
build: path.join(__dirname, '../dist'),
};
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: ['webpack-hot-middleware/client', './src/index'],
mode: 'development',
output: {
publicPath: '/dist/',
filename: 'bundle.js',
},
resolve: {
extensions: ['.jsx', '.js', '.json', '.scss', '.less'],
modules: ['node_modules', PATHS.app],
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
'postcss-loader',
],
},
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
'sass-loader',
],
},
{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
'less-loader',
},
],
},
{
test: /bootstrap-sass\/assets\/javascripts\//,
use: [
{
loader: 'imports-loader',
options: {
jQuery: 'jquery',
},
},
],
},
{
test: require.resolve('jquery'),
use: [
{
loader: 'expose-loader',
options: '$',
},
{
loader: 'expose-loader',
options: 'jQuery',
},
],
},
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 50000,
mimetype: 'application/font-woff',
},
},
],
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream',
},
},
],
},
{
test: /\.otf(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-otf',
},
},
],
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
},
],
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'image/svg+xml',
},
},
],
},
{
test: /\.png$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
{
test: /\.jpg$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
{
test: /\.ico$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"',
},
__DEVELOPMENT__: true,
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.ProvidePlugin({
jQuery: 'jquery',
}),
],
};
Prod.config
const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
require('babel-polyfill').default;
// const PurifyCSSPlugin = require('purifycss-webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const cssnano = require('cssnano');
const PATHS = {
app: path.join(__dirname, '../src'),
build: path.join(__dirname, '../dist'),
};
const pathsToClean = [
'../dist/*.{js,css,eot,woff,woff2,svg,ttf,jpg,map}',
'../dist/index.html',
];
const cleanOptions = {
root: PATHS.build,
exclude: [
'sitemap.xml',
],
dry: false,
};
module.exports = {
// devtool: 'source-map',
entry: ['./src/index'],
mode: 'production',
output: {
publicPath: '/dist/',
chunkFilename: '[name].[chunkhash:4].js',
filename: '[name].[chunkhash:4].js',
},
resolve: {
extensions: ['.jsx', '.js', '.json', '.scss', '.less'],
modules: ['node_modules', PATHS.app],
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
'postcss-loader',
],
},
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
'sass-loader',
],
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
'less-loader',
},
],
},
{
test: /bootstrap-sass\/assets\/javascripts\//,
use: [
{
loader: 'imports-loader',
options: {
jQuery: 'jquery',
},
},
],
},
{
test: require.resolve('jquery'),
use: [
{
loader: 'expose-loader',
options: '$',
},
{
loader: 'expose-loader',
options: 'jQuery',
},
],
},
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
// test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 50000,
mimetype: 'application/font-woff',
},
},
],
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream',
},
},
],
},
{
test: /\.otf(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-otf',
},
},
],
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
},
],
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'image/svg+xml',
},
},
],
},
{
test: /\.png$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
{
test: /\.jpg$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
{
test: /\.ico$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"',
},
__DEVELOPMENT__: false,
}),
new CleanWebpackPlugin(pathsToClean, cleanOptions),
new MiniCssExtractPlugin({
filename: '[name].[contenthash:4].css',
}),
new OptimizeCSSAssetsPlugin({
cssProcessor: cssnano,
cssProcessorOptions: {
options: {
discardComments: {
removeAll: true,
},
// Run cssnano in safe mode to avoid
// potentially unsafe transformations.
safe: true,
},
},
canPrint: false,
}),
new StatsPlugin('stats.json', {
chunkModules: true,
exclude: [/node_modules[\\/]react/],
}),
// new PurifyCSSPlugin({
// paths: glob.sync(PATHS.app),
// }),
],
optimization: {
minimizer: [
new UglifyJsPlugin({
sourceMap: true,
}),
],
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial',
},
},
},
runtimeChunk: {
name: 'manifest',
},
},
};
Some of the Sass that seems to be having trouble?
/** 03. Typography **/
$font-source: 'Source Sans Pro', 'Helvetica', 'Arial', sans-serif;
body {
font-size: 1em;
line-height: 1.85em;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: $font-source;
color: $color-secondary;
font-weight: 400;
}
Found this old q while looking into why I had a similar problem.
In my case, there was iffy css in a sass file (an opacity value set as a percentage instead of a decimal)
Dev coped with that but prod didn't. I'm still not sure why beyond "prod is more strict" - but correcting the css syntax made it work on prod (of course)
Related
Bootstrap not applying CSS using Webpack
I'm using Bootstrap with webpack, but when I run my app, no Bootstrap and no CSS is applied. This is my webpack.config.js : const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); var OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { // mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'build'), publicPath: '', filename: 'bundle.js' }, devServer: { contentBase: path.join(__dirname, './build'), compress: true, port: 3000 }, performance: { maxEntrypointSize: 512000, maxAssetSize: 512000 }, module: { rules: [ { test: /\.s?css$/, use: [ { loader: "file-loader", options: { name: "[name].css" } }, { loader: "extract-loader" }, { loader: "css-loader", }, { loader: "sass-loader" } ] }, { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader" } }, { test: /\.(png|svg|jpg|gif)$/, use: ["file-loader"] }, { test: /\.(woff(2)?|ttf|otf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, use: [ { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'fonts/' } } ] } ] }, //remove comments from JS files optimization: { minimizer: [ new UglifyJsPlugin({ uglifyOptions: { output: { comments: false, }, }, }), new OptimizeCSSAssetsPlugin({ cssProcessorPluginOptions: { preset: ['default', { discardComments: { removeAll: true } }], } }) ], }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].css" }), new ManifestPlugin(), new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: path.resolve('./public/index.html'), }), ] }; I have this kind of error in brower console : Uncaught Error: Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): ModuleBuildError: Module build failed (from ./node_modules/css-loader/dist/cjs.js): CssSyntaxError (192:1) Unknown word I don't have an idea where this error comes from.
Below two versions of dev/prod and all the code you can see here webpack-boilerplate Dev: { test: /\.(css|sass|scss)$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2, sourceMap: true }, }, { loader: 'sass-loader', options: { sourceMap: true, }, } ], }, Prod: { test: /\.(css|sass|scss)$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true, importLoaders: 2 }, }, { loader: 'sass-loader', options: { sourceMap: true, }, }, ], },
Webpack Image Path
I coded my site using Webpack. Everything works except the image's path. I'm used to write ../img/test.png for it work. I've researched other questions but none work. My files are organized inside the dist folder of the wordpress theme. Here's my webpack.config.js : module.exports = { mode: 'development', entry: [ './src/index.js' ], devtool: "source-map", // any "source-map"-like devtool is possible devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, port: 9000 }, module:{ rules:[ { test: /\.js$/, exclude: /(node_modules)/, use: { loader: 'babel-loader', options: { presets: ['env'] } } }, { test:/\.(s*)css$/, use: [{ loader: "style-loader", options: { sourceMap: true } }, { loader: "css-loader", options: { sourceMap: true } }, { loader: "resolve-url-loader", //resolve-url-loader needs to come *BEFORE* sass-loader options: { sourceMap: true } },{ loader: "sass-loader", options: { sourceMap: true } }] } ] }, plugins: [ new CopyWebpackPlugin([ {from:'assets/images',to:'images'} ]) ], watch: true, output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' } }; I've tried other url-loaders but nothing seems to work. Thanks in advance.
I figured it out. The public and output path were the most important things I had to learn. Since this was with Wordpress, the other answers weren't sufficient. The new config looks like: const webpack = require('webpack'); const path = require('path'); module.exports = { mode: 'development', entry: [ './src/index.js' ], devtool: "source-map", // any "source-map"-like devtool is possible devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, port: 9000 }, module:{ rules:[ { test: /\.js$/, exclude: /(node_modules)/, use: { loader: 'babel-loader', options: { presets: ['env'] } } }, { test: /\.(png|jpe?g|gif)$/, use: [ { loader: 'file-loader', options: { outputPath: 'images', publicPath: 'wp-content/themes/{{ THEME NAME }}/dist/images', }, }, ], }, { test:/\.(s*)css$/, use: [{ loader: "style-loader", options: { sourceMap: true } }, { loader: "css-loader", options: { sourceMap: true } }, { loader: "sass-loader", options: { sourceMap: true } }] } ] }, watch: true, output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' } }; Hope this helps for anyone who is working through this.
Can't get webpack to output bundled css?
i'm new to webpack and following a course. But one thing they don't really talk about is how to compile different sass to css. I've given it a try here, but am getting an error. I'm trying to get it to output to styles.css for use with wordpress const path = require('path'), settings = require('./settings'); module.exports = { entry: { home: [ settings.themeLocation + "js/scripts.js" ], style: [ settings.themeLocation +'styles/main.sass' ] App: settings.themeLocation + "js/scripts.js" }, output: { home: [ path: path.resolve(__dirname, settings.themeLocation + "js"), filename: "scripts-bundled.js" ] style:[ path: path.resolve(__dirname, settings.themeLocation + "styles"), filename: "styles-bundled.css" ] }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['#babel/preset-env'] } } }, { test: /\.css$/, use: 'css-loader' } { test: /\.sass$/, use: 'sass-loader' } ] }, mode: 'development' }
Here is a snippet from my webpack config You need to install style-loader, css-loader and sass-loader The following snippet is for development part { test: /\.css$/, exclude: /node_modules/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', options: { sourceMap: true, }, }, ], }, { test: /\.scss$/, exclude: /node_modules/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', options: { sourceMap: true, }, }, { loader: 'sass-loader', options: { sourceMap: true, }, }, ], }, For production { test: /\.css$/, exclude: /node_modules/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { minimize: true, importLoaders: 2, }, }, ], }, { test: /\.scss$/, exclude: /node_modules/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { minimize: true, importLoaders: 3, }, }, 'sass-loader', ], }, & add the folling to plugin section new MiniCssExtractPlugin({ filename: '[name].[chunkhash:8].css', }), Note that the above snippets may not work directly for you, Its is just to give you idea how webpack can handle css $ scss
Webpack dev server w/ Wordpress
I'm using Webpack for my WP theme development and I really would like to use the HMR. So, based on my usual webpack conf (using BrowserSync plugin to perform a live reload), I want to modify it in order to use webpack-dev-server with the hot option properly. Here's what I've done so far: const webpack = require('webpack'); const merge = require('webpack-merge'); const common = require('./webpack.common.js'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const PostCSSCSSNext = require('postcss-cssnext')(); const WPThemeConfig = require('./wp.config'); // const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); const extractSass = new ExtractTextPlugin(`css/${WPThemeConfig.cssFileName}.min.css`); module.exports = merge(common, { mode: 'development', plugins: [ extractSass, new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', }), // new BrowserSyncPlugin({ // host: 'localhost', // port: 3000, // proxy: 'http://localhost:8888/' // }) ], devServer: { open: true, hotOnly: true, hot: true, port: 3000, proxy: { '/': { target: 'http://localhost:8888', secure: false, changeOrigin: true, autoRewrite: true, headers: { 'X-ProxiedBy-Webpack': true, }, }, }, publicPath: "/wp-content/themes/my-theme/assets/", }, module: { rules: [ { test: /\.scss$/, exclude: /node_modules/, use: extractSass.extract({ fallback: 'style-loader', use: [ 'css-loader', { loader: 'postcss-loader', options: { plugins: [ PostCSSCSSNext, ], }, }, 'sass-loader', ], }), }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['#babel/preset-env'] } } }, { test: /\.css$/, exclude: /node_modules/, use: extractSass.extract({ fallback: 'style-loader', use: [ 'css-loader', { loader: 'postcss-loader', options: { plugins: [ PostCSSCSSNext, ], }, }, ], }), }, { test: /\.(png|jpg)$/, use: 'url-loader?limit=65000&mimetype=image/png&name=../img/[name].[ext]' }, { test: /\.svg$/, loader: 'url-loader?limit=65000&mimetype=image/svg+xml&name=public/fonts/[name].[ext]' }, { test: /\.woff$/, loader: 'url-loader?limit=650000&mimetype=application/font-woff&name=public/fonts/[name].[ext]' }, { test: /\.woff2$/, loader: 'url-loader?limit=65000&mimetype=application/font-woff2&name=public/fonts/[name].[ext]' }, { test: /\.[ot]tf$/, loader: 'url-loader?limit=650000&mimetype=application/octet-stream&name=public/fonts/[name].[ext]' }, { test: /\.eot$/, loader: 'url-loader?limit=650000&mimetype=application/vnd.ms-fontobject&name=public/fonts/[name].[ext]' }, ], }, }); Running this command: webpack-dev-server --config webpack.dev.js This doesn't work. But I really would to know why, and how to debug it (e.g. how to access CSS/JS used by webpack-dev-server)?
Getting webpack style loader to let me import css within my components
I'm currently messing around with https://github.com/sahat/megaboilerplate and trying to get it such that I can import css from within my components. Here's my config: const path = require('path'); const webpack = require('webpack'); var config = { devtool: 'cheap-module-eval-source-map', entry: [ 'webpack-hot-middleware/client', './app/main' ], output: { path: path.join(__dirname, 'public', 'js'), filename: 'bundle.js', publicPath: '/js' }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin(), new webpack.optimize.OccurrenceOrderPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }), ], module: { loaders: [ { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, ], }, { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { plugins: [ ['react-transform', { transforms: [ { transform: 'react-transform-hmr', imports: ['react'], locals: ['module'] }, { transform: 'react-transform-catch-errors', imports: ['react', 'redbox-react'] } ] }] ] } } ] } }; if (process.env.NODE_ENV === 'production') { config.plugins.push( new webpack.optimize.UglifyJsPlugin({ compressor: { screw_ie8: true, warnings: false } }) ); } module.exports = config; As you can see from the original config: https://github.com/sahat/megaboilerplate/blob/master/webpack.config.js I've added: test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, ], But when I run the server, I get: /megaboiler/node_modules/spectre.css/dist/spectre.min.css:1 (function (exports, require, module, __filename, __dirname) { /*! Spectre.css | MIT License | github.com/picturepan2/spectre */html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}hr{box-sizing:content-box;height:0;overflow:visible}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input SyntaxError: Unexpected token { What am I missing?
Your test: /\.css$/ is now. use: [ { loader: "style-loader" }, { loader: "css-loader" }, ], Seems that you may miss some loaders which can handle your files correctly, remember each loader has it's limits what it can process. use: [ 'raw-loader', 'style-loader', 'css-loader', 'postcss-loader', 'resolve-url-loader', ]