Webpack CSS Output Is Always Minified - css

For my specific project, I need to control the minification of my CSS and only minify certain files. I am very close to a working solution using OptimizeCSSAssetsPlugin where I use the assetNameRegExp option to choose the CSS files I want to minify.
I have spent a while now trying to figure out why all my other CSS files are still being minified. It turns out that sass-loader will always minify your CSS when in production mode whether you want it to or not.
Here is my full webpack.config.js.
const CopyWebpackPlugin = require('copy-webpack-plugin');
const FixStyleOnlyEntriesPlugin = require('webpack-fix-style-only-entries');
const FractalWebpackPlugin = require('fractal-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const path = require('path');
const PrettierPlugin = require('prettier-webpack-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = (env, argv) => {
/**
* Determine if is production mode from the command executed
*/
const isProduction = argv.mode === 'production';
/**
* Common paths
*/
const paths = {
src: 'src',
dev: 'public',
prod: 'public'
};
/**
* Generate the settings for webpack depending on if it is
* development or production mode.
*/
const settings = {
mode: isProduction ? 'production' : 'development',
outputDir: isProduction ? paths.prod : paths.dev,
fractal: {
mode: isProduction ? 'build' : 'server',
sync: isProduction ? false : true
}
};
return {
// Mode is set by --mode property in command
mode: settings.mode,
/**
* 3 entries:
* designSystem: This is Design System UI specific CSS
* website: This is website & component specific CSS
* app: This is the website & component specific JS
*/
entry: {
/**
* Main website and Design System CSS files
*/
designSystem: path.resolve(__dirname, `./${paths.src}/theme/scss/theme.scss`),
website: path.resolve(__dirname, `./${paths.src}/scss/styles.scss`),
/**
* Specific enteries for all comonents to generate a CSS file specific to that component
*/
headings: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/02-headings/headings.scss`),
paragraphs: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/03-paragraphs/paragraphs.scss`),
inlineElements: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/04-inline-elements/inline-elements.scss`),
ordered: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/05-lists/ordered/ordered.scss`),
unordered: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/05-lists/unordered/unordered.scss`),
images: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/06-images/images.scss`),
spacers: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/01-layout/02-spacers/spacers.scss`),
primaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/primary-button/primary-button.scss`),
secondaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/secondary-button/secondary-button.scss`),
tertiaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/tertiary-button/tertiary-button.scss`),
checkboxes: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/checkboxes/checkboxes.scss`),
inputs: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/inputs/inputs.scss`),
labels: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/labels/labels.scss`),
radios: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/radios/radios.scss`),
selects: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/selects/selects.scss`),
textareas: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/textareas/textareas.scss`),
footer: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/04-footer/footer.scss`),
navigation: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/05-navigation/navigation.scss`),
informationPanel: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/06-information-panel/information-panel.scss`),
informationPill: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/07-information-pill/information-pill.scss`),
modal: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/08-modal/modal.scss`),
/**
* Main website and Design System JS files
*/
app: [
'regenerator-runtime/runtime',
'core-js/modules/es6.array.from',
'core-js/modules/es6.array.for-each',
'core-js/modules/es6.object.assign',
'core-js/modules/es6.promise',
path.resolve(__dirname, `./${paths.src}/js/app.js`)
]
},
/**
* JS output goes into the scripts folder and depending on mode will
* either go into the public or the dist folder with it's chunks
*/
output: {
path: path.resolve(__dirname, `./${settings.outputDir}`),
filename: 'scripts/[name].js',
chunkFilename: 'scripts/[name].chunk.js'
},
module: {
rules: [
{
parser: {
amd: false
}
},
{
/**
* Load JS files with Babel Loader and set to transpile code to work
* in IE10 and above.
*/
test: /\.(js)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
configFile: './babel.config.js',
presets: [
[
'#babel/preset-env',
{
useBuiltIns: 'entry',
corejs: '^3.1.4',
targets: {
browsers: ['defaults, ie >= 10']
}
}
]
]
}
},
{
loader: 'eslint-loader',
options: {
configFile: '.eslintrc.json'
}
}
]
},
{
/**
* Load SASS files with 2 loaders
* PostCSS: This converts the SCSS to CSS, adds in polyfills for flexbox,
* auto prefixes and adds in normalise CSS.
* SASS Loader: This generates source maps for CSS.
*/
test: /\.(scss|sass)$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009'
},
stage: 3
}),
require('autoprefixer')()
],
sourceMap: true,
minimize: false
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
minimize: false,
outputStyle: 'uncompressed'
}
}
]
},
{
/**
* This looks for all images and uses the File Loader to move them to
* the output directory. It excludes the fonts directory so there is no
* duplication of SVG files
*/
test: /\.(png|jpg|jpeg|gif|svg)$/,
exclude: /fonts/,
use: [
{
loader: 'file-loader',
options: {
name: '[folder]/[name].[ext]',
outputPath: '/images'
}
}
]
},
{
/**
* This looks for all font files and uses the File Loader to
* move hem to the output directory. It excludes the images directory
* so there is no duplication of SVG files
*/
test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
exclude: /images/,
use: [
{
loader: 'file-loader',
options: {
name: '[folder]/[name].[ext]',
outputPath: '/fonts'
}
}
]
}
]
},
plugins: [
/**
* This prevents webpack from generating a JS file for SCSS entries
*/
new FixStyleOnlyEntriesPlugin(),
/**
* Runs SASS linting
*/
new StyleLintPlugin({
configFile: '.stylelintrc.json',
context: 'src',
files: '**/*.scss',
failOnError: false,
quiet: false,
emitErrors: true
}),
/**
* This outputs SCSS entires into CSS files and thier chunks
*/
new MiniCssExtractPlugin({
filename: 'style/[name].css',
chunkFilename: 'style/[name].chunk.css'
}),
/**
* Runs Fractal in either server mode for dev and build mode for
* production.
*/
new FractalWebpackPlugin({
mode: settings.fractal.mode,
sync: settings.fractal.sync
}),
/**
* Copies images over to the output directory
*/
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, `./${paths.src}/images`),
to: 'images'
}
]),
// new PrettierPlugin()
],
/**
* This only runs when in production mode and will minify JS and CSS
*/
optimization: {
minimize: true,
minimizer: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /style\/(website|designSystem).css/,
cssProcessor: require('cssnano')
}),
new TerserPlugin({
include: /\/js/,
exclude: /\/scss/
})
]
},
/**
* Generates source maps
*/
devtool: 'source-maps'
};
};

I finally figured out my issue and wanted to post the answer so if anyone else in the future comes across this issue they can solve it.
Despite what my question says, to start with I actually didn't know which loader was causing the issue, after doing some research I initially thought css-loader was the culprit. I did some digging into the code and found that there is no minification in css-loader. The next loader to look into was sass-loader, after lots of research I eventually figured out sass-loader was doing the minification. Looking into the sass-loader docs I didn't seem to find any information about minification or how to stop it. After lots of googling, I eventually found the very poorly documented option outputStyle.
From what I can find outputStyle takes 3 options:
outputStyle: 'compressed'
outputStyle: 'uncompressed'
outputStyle: 'expanded'
This was my magic option, while sass-loader seems to take no notice of minimize: false in webpack.config.js it will listen to the outputStyle option. This will turn off the minification for all CSS files. This then allows OptimizeCSSAssetsPlugin to come into play and minify the filed you need.
Here if the new sass-loader code:
{
loader: 'sass-loader',
options: {
sourceMap: true,
minimize: false,
outputStyle: 'expanded'
}
}
Here is the full webpack.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin');
const FixStyleOnlyEntriesPlugin = require('webpack-fix-style-only-entries');
const FractalWebpackPlugin = require('fractal-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const path = require('path');
const PrettierPlugin = require('prettier-webpack-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = (env, argv) => {
/**
* Determine if is production mode from the command executed
*/
const isProduction = argv.mode === 'production';
/**
* Common paths
*/
const paths = {
src: 'src',
dev: 'public',
prod: 'public'
};
/**
* Generate the settings for webpack depending on if it is
* development or production mode.
*/
const settings = {
mode: isProduction ? 'production' : 'development',
outputDir: isProduction ? paths.prod : paths.dev,
fractal: {
mode: isProduction ? 'build' : 'server',
sync: isProduction ? false : true
}
};
return {
// Mode is set by --mode property in command
mode: settings.mode,
/**
* 3 entries:
* designSystem: This is Design System UI specific CSS
* website: This is website & component specific CSS
* app: This is the website & component specific JS
*/
entry: {
/**
* Main website and Design System CSS files
*/
designSystem: path.resolve(__dirname, `./${paths.src}/theme/scss/theme.scss`),
website: path.resolve(__dirname, `./${paths.src}/scss/styles.scss`),
/**
* Specific enteries for all comonents to generate a CSS file specific to that component
*/
headings: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/02-headings/headings.scss`),
paragraphs: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/03-paragraphs/paragraphs.scss`),
inlineElements: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/04-inline-elements/inline-elements.scss`),
ordered: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/05-lists/ordered/ordered.scss`),
unordered: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/05-lists/unordered/unordered.scss`),
images: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/06-images/images.scss`),
spacers: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/01-layout/02-spacers/spacers.scss`),
primaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/primary-button/primary-button.scss`),
secondaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/secondary-button/secondary-button.scss`),
tertiaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/tertiary-button/tertiary-button.scss`),
checkboxes: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/checkboxes/checkboxes.scss`),
inputs: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/inputs/inputs.scss`),
labels: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/labels/labels.scss`),
radios: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/radios/radios.scss`),
selects: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/selects/selects.scss`),
textareas: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/textareas/textareas.scss`),
footer: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/04-footer/footer.scss`),
navigation: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/05-navigation/navigation.scss`),
informationPanel: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/06-information-panel/information-panel.scss`),
informationPill: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/07-information-pill/information-pill.scss`),
modal: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/08-modal/modal.scss`),
/**
* Main website and Design System JS files
*/
app: [
'regenerator-runtime/runtime',
'core-js/modules/es6.array.from',
'core-js/modules/es6.array.for-each',
'core-js/modules/es6.object.assign',
'core-js/modules/es6.promise',
path.resolve(__dirname, `./${paths.src}/js/app.js`)
]
},
/**
* JS output goes into the scripts folder and depending on mode will
* either go into the public or the dist folder with it's chunks
*/
output: {
path: path.resolve(__dirname, `./${settings.outputDir}`),
filename: 'scripts/[name].js',
chunkFilename: 'scripts/[name].chunk.js'
},
module: {
rules: [
{
parser: {
amd: false
}
},
{
/**
* Load JS files with Babel Loader and set to transpile code to work
* in IE10 and above.
*/
test: /\.(js)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
configFile: './babel.config.js',
presets: [
[
'#babel/preset-env',
{
useBuiltIns: 'entry',
corejs: '^3.1.4',
targets: {
browsers: ['defaults, ie >= 10']
}
}
]
]
}
},
{
loader: 'eslint-loader',
options: {
configFile: '.eslintrc.json'
}
}
]
},
{
/**
* Load SASS files with 2 loaders
* PostCSS: This converts the SCSS to CSS, adds in polyfills for flexbox,
* auto prefixes and adds in normalise CSS.
* SASS Loader: This generates source maps for CSS.
*/
test: /\.(scss|sass)$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009'
},
stage: 3
}),
require('autoprefixer')()
],
sourceMap: true,
minimize: false
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
minimize: false,
outputStyle: 'expanded'
}
}
]
},
{
/**
* This looks for all images and uses the File Loader to move them to
* the output directory. It excludes the fonts directory so there is no
* duplication of SVG files
*/
test: /\.(png|jpg|jpeg|gif|svg)$/,
exclude: /fonts/,
use: [
{
loader: 'file-loader',
options: {
name: '[folder]/[name].[ext]',
outputPath: '/images'
}
}
]
},
{
/**
* This looks for all font files and uses the File Loader to
* move hem to the output directory. It excludes the images directory
* so there is no duplication of SVG files
*/
test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
exclude: /images/,
use: [
{
loader: 'file-loader',
options: {
name: '[folder]/[name].[ext]',
outputPath: '/fonts'
}
}
]
}
]
},
plugins: [
/**
* This prevents webpack from generating a JS file for SCSS entries
*/
new FixStyleOnlyEntriesPlugin(),
/**
* Runs SASS linting
*/
new StyleLintPlugin({
configFile: '.stylelintrc.json',
context: 'src',
files: '**/*.scss',
failOnError: false,
quiet: false,
emitErrors: true
}),
/**
* This outputs SCSS entires into CSS files and thier chunks
*/
new MiniCssExtractPlugin({
filename: 'style/[name].css',
chunkFilename: 'style/[name].chunk.css'
}),
/**
* Runs Fractal in either server mode for dev and build mode for
* production.
*/
new FractalWebpackPlugin({
mode: settings.fractal.mode,
sync: settings.fractal.sync
}),
/**
* Copies images over to the output directory
*/
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, `./${paths.src}/images`),
to: 'images'
}
]),
// new PrettierPlugin()
],
/**
* This only runs when in production mode and will minify JS and CSS
*/
optimization: {
minimize: true,
minimizer: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /style\/(website|designSystem).css/,
cssProcessor: require('cssnano')
}),
new TerserPlugin({
include: /\/js/,
exclude: /\/scss/
})
]
},
/**
* Generates source maps
*/
devtool: 'source-maps'
};
};

Related

Error importing mobiscroll css styles in webpack

I have a problem with importing mobiscroll css styles in my react project (created by webpack)
Other css files are working well but
This line generates Error:
import '#mobiscroll/react-lite/dist/css/mobiscroll.min.css'
The generated Error:
./node_modules/#mobiscroll/react-lite/dist/css/icons_mobiscroll.ttf?vtxdtu 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
./node_modules/#mobiscroll/react-lite/dist/css/icons_mobiscroll.woff?vtxdtu 1:4
Module parse failed: Unexpected character '�' (1:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
./node_modules/#mobiscroll/react-lite/dist/css/icons_mobiscroll.woff 1:4
Module parse failed: Unexpected character '�' (1:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
and my webpack.config.js:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const path = require('path');
const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CSSModuleLoader = {
loader: 'css-loader',
options: {
modules: {
localIdentName: "[name]__[local]___[hash:base64:5]",
},
sourceMap: true
}
};
const CSSLoader = { loader: 'css-loader' };
const PostCSSLoader = {
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: false, // turned off as causes delay
plugins: () => [
autoprefixer({
browsers: ['>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9']
})
]
}
};
const StyleLoader = {
loader: 'style-loader'
};
const SassLoader = {
loader: 'sass-loader'
};
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
chunkFilename: '[id].js',
publicPath: ''
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}, {
test: /\.html$/,
use: [{loader: 'html-loader'}]
},
{
test: /\.(sa|sc|c)ss$/,
exclude: /\.module\.(sa|sc|c)ss$/,
use: [StyleLoader, CSSLoader, PostCSSLoader, SassLoader]
},
{
test: /\.module\.(sa|sc|c)ss$/,
use: [StyleLoader, CSSModuleLoader, PostCSSLoader, SassLoader]
},
{
test: /\.(svg|png|jpe?g|gif|bmp)$/,
loader: 'url-loader?limit=10000&name=img/[name].[ext]'
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'file?name=[name].[ext]'
}
]
},
plugins: [
new CopyWebpackPlugin({
patterns: [ // for copying the content of 'public/static' folder to 'dist' folder
{ from: path.resolve(__dirname, 'public/static'), to: path.resolve(__dirname, 'dist/static')}
]
}),
new HtmlWebpackPlugin({
template: __dirname + '/public/index.html',
filename: 'index.html',
inject: 'body'
}),
]
};
my webpack configuration is working well for all other css or scss files but can not load that css file. what's the problem?
please help me with this.
I found out my mistake. I haven't defined a loader for font files and that css file was using some fonts.
This solved my Problem:
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
// exclude: /node_modules/,
loader: 'file-loader',
options: {
limit: 1024,
name: '[name].[ext]',
publicPath: 'dist/assets/',
outputPath: 'dist/assets/'
}
}

Combining tailwind css with sass using webpack

I'm struggling a bit getting Tailwind CSS to work with SASS and Webpack. It seems like the postcss configuration for tailwind doesn't really do anything in terms of processing #tailwind preflight, #tailwind components and #tailwind utilities
My set up is as follows:
layout.scss
#import "~tailwindcss/preflight.css";
#import "~tailwindcss/components.css";
.my-class {
#apply text-blue;
#apply border-red;
}
#import "~tailwindcss/utilities.css";
entry.js
import '../css/src/layout.scss';
postcss.config.js
const tailwindcss = require('tailwindcss');
const purgecss = require('#fullhuman/postcss-purgecss');
const cssnano = require('cssnano');
const autoprefixer = require('autoprefixer');
module.exports = {
plugins: [
tailwindcss('./tailwind.js'),
cssnano({
preset: 'default',
}),
purgecss({
content: ['./views/**/*.cshtml']
}),
autoprefixer
]
}
webpack.config.js
// NPM plugins
const autoprefixer = require('autoprefixer');
const WebpackNotifierPlugin = require('webpack-notifier');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
entry: {
main: './scripts/entry.js'
},
output: {
filename: '[name].bundle.js',
publicPath: './'
},
watch: false,
externals: {
jquery: 'jQuery'
},
mode: 'development',
plugins: [
// Notify when build succeeds
new WebpackNotifierPlugin({ alwaysNotify: true }),
// Extract any CSS from any javascript file to process it as LESS/SASS using a loader
new MiniCssExtractPlugin({
fileame: "[name].bundle.css"
}),
// Minify CSS assets
new OptimizeCSSAssetsPlugin({}),
// Use BrowserSync plugin for file changes. I.e. if a CSS/SASS/LESS file changes, the changes will be injected directly in the browser with no page load
new BrowserSyncPlugin({
proxy: 'mysite.local',
open: 'external',
host: 'mysite.local',
port: 3000,
files: ['./dist/main.css', './views', './tailwind.js']
},
{
// disable reload from the webpack plugin since browser-sync will handle CSS injections and JS reloads
reload: false
})
],
module: {
rules: [
{
// Transpile ES6 scripts for browser support
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
use: [
{
loader: 'file-loader'
}
]
},
{
// Extract any SCSS content and minimize
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: {
plugins: () => [autoprefixer()]
}
},
{
loader: 'sass-loader',
options: {
plugins: () => [autoprefixer()]
}
}
]
},
{
// Extract any CSS content and minimize
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{ loader: 'postcss-loader' }
]
}
]
}
};
When I run Webpack, everything runs just fine, but the content of /dist/main.css is:
#tailwind preflight;#tailwind components;#tailwind utilities;.my-class{#apply text-blue;#apply border-red}
I suspect it's related to the (order of) loaders, but I can't seem to figure out why it's not getting processed correctly.
Does anyone know what I'm doing wrong here? :-)
Thanks in advance.
Wow, so after fiddling around with the loaders even more, I made it work :-) For future reference:
I added options: { importLoaders: 1 } to the css-loader for SCSS files and removed: plugins: () => [autoprefixer()] from the postcss-loader in my webpack.config.js file.
Full, updated webpack.config.js file:
// NPM plugins
const autoprefixer = require('autoprefixer');
const WebpackNotifierPlugin = require('webpack-notifier');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
entry: {
main: './scripts/entry.js'
},
output: {
filename: '[name].bundle.js',
publicPath: './'
},
watch: false,
externals: {
jquery: 'jQuery'
},
mode: 'development',
plugins: [
// Notify when build succeeds
new WebpackNotifierPlugin({ alwaysNotify: true }),
// Extract any CSS from any javascript file to process it as LESS/SASS using a loader
new MiniCssExtractPlugin({
fileame: "[name].bundle.css"
}),
// Minify CSS assets
new OptimizeCSSAssetsPlugin({}),
// Use BrowserSync plugin for file changes. I.e. if a CSS/SASS/LESS file changes, the changes will be injected directly in the browser with no page load
new BrowserSyncPlugin({
proxy: 'mysite.local',
open: 'external',
host: 'mysite.local',
port: 3000,
files: ['./dist/main.css', './views', './tailwind.js']
},
{
// disable reload from the webpack plugin since browser-sync will handle CSS injections and JS reloads
reload: false
})
],
module: {
rules: [
{
// Transpile ES6 scripts for browser support
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
use: [
{
loader: 'file-loader'
}
]
},
{
// Extract any SCSS content and minimize
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{
loader: 'postcss-loader'
},
{
loader: 'sass-loader',
options: {
plugins: () => [autoprefixer()]
}
}
]
},
{
// Extract any CSS content and minimize
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{ loader: 'postcss-loader' }
]
}
]
}
};
There is an extension called tailwindcss-transpiler which compiles your layout.tailwind.scss files into pure CSS files.It also optimizing features.I hope it will be useful.
For VS Code
https://marketplace.visualstudio.com/items?itemName=sudoaugustin.tailwindcss-transpiler
For Atom
https://atom.io/packages/tailwindcss-transpiler

Webpack4 - CSS files imported from less files aren't being minified

I'm putting together a project with webpack 4. Virtually all of it working as it should, I'm just having trouble with minifying CSS.
If in my entrypoint i include a.css or .less file then everything works correctly. It also works correctly if i use #include './myfile.less'. However if I use #include './myfile.css' that section is imported without being minified.
How do I get imported CSS files to work?
webpack.config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: {
'app': './src/app/app.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.html/,
use: 'html-loader'
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"less-loader"
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader?name=assets/images/[path][name].[ext]?[hash]']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader?name=assets/fonts/[name].[ext]?[hash]'
]
}
],
},
plugins: [
new HtmlWebpackPlugin({ template: './src/public/index.html' }),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "[name].bundle.css",
chunkFilename: "[id].css"
}),
]
};
While webpack 5 is likely to come with a CSS minimizer built-in, with webpack 4 you need to bring your own. To minify the output, use a plugin like optimize-css-assets-webpack-plugin. Setting optimization.minimizer overrides the defaults provided by webpack, so make sure to also specify a JS minimizer
webpack.config.js
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
}
}
Reference
I found notes on MiniCssExtractPlugin production usage.
Following these fixed the issue:
specifically adding:
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
...
module.exports = {
...
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({})
]
},
}
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
minimize: true
}
}
]

Window is not defined (Server-side rendering with Asp.Net SPA Services)

Thank you guys for reading my question. Really hoping for a solution to this, I've been trying to find a fix for days to no avail.
Here's the rundown: My goal is to render a React application on the server-side using .NET Core. I haven't even started with the react part yet, right now I'm simply trying to render an h1 tag with the ASP.NET Javascript Services Prerendering functionality.
On my first iteration, I wrote my boot-server.js file with es5 and it worked perfectly. However, I quickly realized I was going to need to compile the file through webpack in order for it to understand my React code.
As soon as I piped that file through webpack though, I got a "window is not defined" error which I haven't been able to fix. I understand that part of my application should not be aware of the window object since it lives in the server but setting the webpack config's target field to node does not seem to fix the issue. Below are all the files involved.
Here is my boot-server.js file:
import { createServerRenderer } from 'aspnet-prerendering';
module.exports = createServerRenderer((params) => {
return new Promise((resolve, reject) => {
var html = '<h1>Hello world!</h1>';
resolve({
html: html
});
});
});
Here is my cshtml view:
#addTagHelper "*, Microsoft.AspNetCore.SpaServices"
<app id="root" asp-prerender-module="wwwroot/client/boot-server.bundle">Loading...</app>
Here is my webpack.config.js file:
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const path = require("path");
const clientConfig = {
entry: './FrontEnd/index.js',
output: {
path: path.resolve(__dirname, "wwwroot/client"),
filename: "client.min.js",
publicPath: "/client/"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["es2015", "react", "stage-0"],
plugins: ["transform-class-properties", "transform-decorators-legacy", "react-html-attrs"]
}
}
},
{
test: /\.scss$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "sass-loader"
}]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader?name=[name]_[hash:8].[ext]'
]
}
]
},
mode: 'development',
devServer: {
contentBase: './wwwroot/client',
hot: true
}
};
const bootServerConfig = {
stats: { modules: false },
resolve: { extensions: ['.js'] },
output: {
filename: '[name].js',
publicPath: '/wwwroot/client/', // Webpack dev middleware, if enabled, handles requests for this URL prefix
libraryTarget: 'commonjs'
},
entry: {
'main-server': './FrontEnd/boot-server.js'
},
module: {
rules: [
{
test: /\.js$/,
include: /FrontEnd/,
use: [
{
loader: "babel-loader",
options: {
presets: ["es2015", "react", "stage-0"],
plugins: ["transform-class-properties", "transform-decorators-legacy", "react-html-attrs"]
}
}
]
},
{
test: /\.svg$/,
use: {
loader: 'url-loader',
options: { limit: 25000 } //?limit=100000'
}
}
]
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, './wwwroot/client')
},
target: 'node'
}
module.exports = [clientConfig, bootServerConfig];
Here is a screenshot of the error page:

Webpack source-map does not resolve sass imports

I have webpack configured to transpile scss -> css, but sourcemap generated by webpack does not resolve scss #imports.
webpack.config.js:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const outputPath = path.join(__dirname, 'dist');
module.exports = {
devtool: 'source-map',
entry: ['./src/main.scss'],
target: 'web',
output: {
filename: 'js/[name].bundle.js',
path: outputPath
},
module: {
rules: [
{ // sass / scss loader for webpack
test: /\.(sass|scss)$/,
loader: ExtractTextPlugin.extract([
{
loader: 'css-loader',
options: {
url: false,
import: true,
minimize: true,
sourceMap: true,
}
},
'sass-loader'
])
},
]
},
plugins: [
new ExtractTextPlugin({ // define where to save the file
filename: 'css/[name].bundle.css',
allChunks: true,
})
]
};
main.scss:
#import 'foo';
_foo.scss:
h1 { color: red; }
However, in Chrome dev tools, I see a reference to main.scss where I expect reference to _foo.scss - see the screenshot below:
Compiled demo: http://store.amniverse.net/webpacktest/
You should not use extractTextPlugin when you are in dev mode.
Please make extra configs for dev and production mode. In production the use of extractTextPlugin is fine but in dev mode it is not necessary and can lead to other features not working. So instead use the style-loader.
Also - I am not sure if that fixes your problem - try to use importLoaders prop on the css loader. Look here for more info:
https://github.com/webpack-contrib/css-loader#importloaders
const path = require('path');
const outputPath = path.join(__dirname, 'dist');
module.exports = {
devtool: 'source-map',
entry: ['./src/main.scss'],
target: 'web',
output: {
filename: 'js/[name].bundle.js',
path: outputPath
},
module: {
rules: [
{ // sass / scss loader for webpack
test: /\.(sass|scss)$/,
loader: [
{
loader: 'style-loader',
options: {
sourceMap: true
}
},
{
loader: 'css-loader',
options: {
url: false,
import: true,
minimize: true,
sourceMap: true,
importLoaders: 1,
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
]
}
};
You have sass-loader there, switch it with:
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
And that would work.
There's nothing wrong with ExtractTextPlugin in dev mode, and what #Omri Aharon posted is correct. However, what you should consider is having source-map enabled only in dev mode.
To build webpack using its default production settings (which uglifies and applies OccurrenceOrderPlugin plugin by default in webpack 2.0+), run the command webpack -p, and then in your webpack.config.js, you can determine if you're in dev mode or not by doing:
const DEBUG = !process.argv.includes('-p');
Add the function
function cssConfig(modules) {
return {
sourceMap: DEBUG,
modules,
localIdentName: DEBUG ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]',
minimize: !DEBUG
};
}
in your webpack.config.js, making your scss loader appear as so:
test: /\.(sass|scss)$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: cssConfig(true)
},
{
loader: 'sass-loader',
options: { sourceMap: DEBUG }
}
]
})
},
and my plugins section has
new ExtractTextPlugin('[name].css?[contenthash]'),

Resources