webpack not bundling scss when using import instead of require - css

Simple webpack setup, when I use import to load my scss it is completely missing from the bundle. The line where the import should be is simply missing. When I use require instead, it works.
optimization: {usedExports: true} is not the problem, I tried with and without
mini-css-extract-plugin also did not work.
when I put a typo in the scss it complains, so it is parsed but simply not bundled in the end?
index.js
require("./scss/style.scss");
//import "./scss/style.scss" <--- not working
import { createApp } from 'vue';
import App from './components/App.vue';
const el = document.getElementById('app');
createApp(App).mount(el);
webpack config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const { DefinePlugin } = require('webpack');
const dist = path.resolve(__dirname, "dist");
module.exports = env => {
const mode = env.production == true ? "production" : "development";
const devtool = mode == "production" ? false : "inline-source-map";
return {
mode: mode,
entry: './web/index.js',
output: {
filename: 'bundle.js',
path: dist
},
optimization: {
usedExports: true,
},
devServer: {
static: {
directory: dist
},
port: 8888
},
module: {
rules: [{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
}, {
test: /\.(ttf|eot|woff|woff2|svg)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
},
},
}, {
test: /\.vue$/,
loader: 'vue-loader'
}]
},
plugins: [
new CleanWebpackPlugin(),
new DefinePlugin({
__VUE_OPTIONS_API__: false,
__VUE_PROD_DEVTOOLS__: false,
}),
new HtmlWebpackPlugin({
template: path.resolve("./web/index.html")
}),
new VueLoaderPlugin()
],
resolve: {
extensions: ['.js'],
alias: {
"#": path.resolve(__dirname, 'web')
}
},
devtool
};
};

I found the problem but I don't understand why webpack drops it.
Quote from https://webpack.js.org/guides/tree-shaking/
Note that any imported file is subject to tree shaking. This means if you use something like css-loader in your project and import a CSS file, it needs to be added to the side effect list so it will not be unintentionally dropped in production mode:
In my package.json I put
"sideEffects": false,
to be able to use treeshaking.
But I had to disable it in the loader rule
{
test: /\.(sa|sc|c)ss$/,
use: ['style-loader','css-loader','sass-loader'],
sideEffects: true <----
}

Related

Webpack configuration does not create CSS files from SCSS/LESS

I'm using the following WordPress starter plugin with a full working Vue configuration implemented: https://github.com/tareq1988/vue-wp-starter
This plugin works in general. But seems that there is a Webpack issue which is responsible to generate some css files.
This is the Webpack configuration:
const webpack = require("webpack");
const path = require("path");
const package = require("./package.json");
const { VueLoaderPlugin } = require("vue-loader");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const BrowserSyncPlugin = require("browser-sync-webpack-plugin");
const TerserJSPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const config = require("./config.json");
const devMode = process.env.NODE_ENV !== "production";
// Naming and path settings
var appName = "app";
var entryPoint = {
frontend: "./src/frontend/main.js",
admin: "./src/admin/main.js",
style: ['./assets/less/style.less', './assets/scss/style.scss'],
};
var exportPath = path.resolve(__dirname, "./assets/js");
// Enviroment flag
var plugins = [];
// extract css into its own file
plugins.push(
new MiniCssExtractPlugin({
filename: "../css/[name].css",
ignoreOrder: true
})
);
plugins.push(
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: false,
__VUE_PROD_DEVTOOLS__: false,
}),
)
// enable live reload with browser-sync
// set your WordPress site URL in config.json
// file and uncomment the snippet below.
// --------------------------------------
// plugins.push(new BrowserSyncPlugin( {
// proxy: {
// target: config.proxyURL
// },
// files: [
// '**/*.php'
// ],
// cors: true,
// reloadDelay: 0
// } ));
plugins.push(new VueLoaderPlugin());
// Differ settings based on production flag
if (devMode) {
appName = "[name].js";
} else {
appName = "[name].min.js";
}
module.exports = {
entry: entryPoint,
mode: devMode ? "development" : "production",
output: {
path: exportPath,
filename: appName,
},
resolve: {
alias: {
vue$: "vue/dist/vue.esm-bundler.js",
"#": path.resolve("./src/"),
frontend: path.resolve("./src/frontend/"),
admin: path.resolve("./src/admin/"),
},
modules: [
path.resolve("./node_modules"),
path.resolve(path.join(__dirname, "src/")),
],
},
optimization: {
runtimeChunk: "single",
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\\/]node_modules[\\\/]/,
name: "vendors",
chunks: "all",
},
},
},
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
plugins,
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/,
},
{
test: /\.less$/,
use: ['vue-style-loader', "css-loader", "less-loader"],
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.png$/,
use: [
{
loader: "url-loader",
options: {
mimetype: "image/png",
},
},
],
},
{
test: /\.svg$/,
use: "file-loader",
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: (resourcePath, context) => {
return path.relative(path.dirname(resourcePath), context) + "/";
},
hmr: process.env.NODE_ENV === "development",
},
},
"css-loader",
],
},
],
},
};
This is the folder structure for the relevant part:
Starting the app with npm run dev I get no error messages. But I expect the files /assets/css/admin.css and /assets/css/frontend.css to automatically be created. This is what the developer says here:
Also the paths are included in the related PHP files for WordPress (includes/Assets.php):
So I get the following 404 error:
....plugin-root/assets/css/admin.css?ver=0.1.0 net::ERR_ABORTED 404 (Not Found)
Long description in short: The configurator for this starter plugin should generate two css files which are missing. What could be the reason for this? Do I miss something?

Module parse failed: Unexpected character '#' (1:0) You may need an appropriate loader to handle this file type

I'm having some issues. I keep getting this error
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
#import url("https://cloud.typography.com/7374818/6819812/css/fonts.css");
I've tried some of the suggested solutions I've found online and none seems to work.
This is what my Webpack config looks like
const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production';
module.exports = {
entry: {
main: path.resolve(__dirname, './src/App.tsx'),
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'public'),
sourceMapFilename: "[name].js.map"
},
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
include: [
path.resolve(__dirname, './src'),
],
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react', '#babel/preset-typescript'],
},
},
{
test: /\.(scss|css)$/,
include: path.resolve(__dirname, './src'),
use: [
MiniCssExtractPlugin.loader,
'style-loader',
'css-loader',
{
loader: "postcss-loader",
options: {
plugins: () => [
require("autoprefixer")()
],
},
},
'sass-loader',
],
},
{
test: /\.(png|woff|woff2|eot|ttf|otf|svg)$/,
loader: 'url-loader',
options: {
limit: 100000,
}
},
],
},
resolve: {
extensions: ['.js', '.jsx', 'json', 'ts', 'tsx', '.scss'],
alias: {
src: path.resolve(__dirname, './src/'),
},
},
devServer: {
historyApiFallback: true,
contentBase: './public',
port: 3030,
open: true,
compress: true,
hot: true,
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
plugins: [
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin(),
],
};
The component looks like this. uilib is a local dependency I'm linking using yarn link
import React from 'react';
import ReactDOM from 'react-dom';
import { Header, Footer } from 'uilib';
import 'uilib/index.css';
export const App: React.FC<{}> = () => (
<>
<Header/>
<div>Hello in oct</div>
<Footer/>
</>
);
ReactDOM.render(
<App />,
document.getElementById('root'),
);
The uilib/index.css looks like this
#import url("https://cloud.typography.com/7374818/6819812/css/fonts.css");
.header {
height: 70px;
}
I feel that ulib folder is perhaps not inside src directory.
Can you try changing your webpack loader config for scss|css to be like so:
{
test: /\.(scss|css)$/,
include: path.resolve(__dirname) // <---- leave it __dirname or remove the prop completely
....
....
}
I am hoping this will let webpack look at project's root which can help it parse the file accordingly.
Also, unless ulib is in node_modules, you can exclude it from loader with exclude: 'node_modules'

Is it possible for Webpack (with plugins) to minify my CSS files, place it in my dist folder, and add a tag to index.html

I would like to end up with a CSS file that is a merged and minified version of my development environments standard CSS files. With a hashed name based on the content of the file which is automatically added as a tag in my resulting index.html file in my distribution folder.
Is this possible? I assume it is since I've gotten Webpack 4 to do just about everything else I've needed it to do. If it is, how would I go about it?
Mind you, it has to be set up this way. I'm not allowed to use import statements or the like in my JS.
I have tried playing with the Mini Css Extract plugin and Optimize CSS Assets Webpack plugin to see if they could do what I want, but I'm likely doing something wrong as I never get any output file ( I know it wouldn't give me a modified index.html, but having the CSS file would be a start at least).
My current file structure is basically:
'mainFolder'/public/css <- the src css folder,
'mainFolder'/dist/css <- the dist folder where I would like to put the minified file
My config is currently set up like thus:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: [
'./public/js/app.js',
'./public/js/filters/weekday.js',
'./public/js/filters/dial-in.js',
'./public/js/filters/time.js',
'./public/js/filters/group-member.js',
'./public/js/filters/bool-to-icon.js',
'./public/js/filters/date-format.js',
'./public/js/filters/time-format.js',
'./public/js/services/main.js',
'./public/js/configs/config.js',
'./public/js/controllers/main.js',
'./public/js/pages/calendar/controller.js',
'./public/js/pages/company/view/controller.js',
'./public/js/pages/company/edit/controller.js',
'./public/js/pages/group-meeting/create/controller.js',
'./public/js/pages/group-meeting/detail/controller.js',
'./public/js/pages/group-meeting/view/controller.js',
'./public/js/pages/group-meeting-calendar/controller.js',
'./public/js/pages/login/controller.js',
'./public/js/pages/meeting-link/view/controller.js',
'./public/js/pages/meeting-link/edit/controller.js',
'./public/js/pages/meeting-link/detail/controller.js',
'./public/js/pages/user/view/controller.js',
'./public/js/pages/user/edit/controller.js',
'./public/js/lib/angular-semantic-ui.min.js'
],
output: {
filename: 'js/[contenthash].js',
path: path.resolve(__dirname, 'dist'),
publicPath: 'dist'
},
devtool: 'source-map ',
plugins: [
new CleanWebpackPlugin(['dist'], {
}),
new HtmlWebpackPlugin({
title: 'Zenvite',
template: './public/templates/index.html',
filename: 'templates/index.html'
}),
new CopyWebpackPlugin([
//{ from: 'public/css', to:'css'},
{ from: 'public/media', to:'media'},
{ from: 'public/js/pages', to: 'js/pages', ignore: [ '*.js' ]},
{ from: 'public/templates/app.html', to: 'templates'}
]),
new MiniCssExtractPlugin({
filename: 'app.css',
chunkFilename: '[contenthash].css',
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// you can specify a publicPath here
// by default it use publicPath in webpackOptions.output
publicPath: '../public'
}
},
"css-loader"
]
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
},
};
Again the optimal result would be having something like [contenthash].css with merged and minified versions of my standard CSS and if possible an added tag in my index.html file.
So far I've got nothing of the sort (except copying the standard files over to the dist directory)
Thanks in advance for any help and if I can answer any questions, feel free to ask.
In the end, when my supervisor realized the import statement was stripped out during compile they allowed it. So we went using that route to achieve what we want. Bellow is ultimately what we went with
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
entry: [
'./public/js/app.js',
'./public/js/filters/weekday.js',
'./public/js/filters/dial-in.js',
'./public/js/filters/time.js',
'./public/js/filters/group-member.js',
'./public/js/filters/bool-to-icon.js',
'./public/js/filters/date-format.js',
'./public/js/filters/time-format.js',
'./public/js/services/main.js',
'./public/js/configs/config.js',
'./public/js/controllers/main.js',
'./public/js/pages/calendar/controller.js',
'./public/js/pages/company/view/controller.js',
'./public/js/pages/company/edit/controller.js',
'./public/js/pages/group-meeting/create/controller.js',
'./public/js/pages/group-meeting/detail/controller.js',
'./public/js/pages/group-meeting/view/controller.js',
'./public/js/pages/group-meeting-calendar/controller.js',
'./public/js/pages/login/controller.js',
'./public/js/pages/meeting-link/view/controller.js',
'./public/js/pages/meeting-link/edit/controller.js',
'./public/js/pages/meeting-link/detail/controller.js',
'./public/js/pages/user/view/controller.js',
'./public/js/pages/user/edit/controller.js',
'./public/js/lib/angular-semantic-ui.min.js'
],
output: {
filename: 'js/app.[contenthash].js',
path: path.resolve(__dirname, 'dist'),
publicPath: 'dist'
},
devtool: 'source-map ',
plugins: [
new CleanWebpackPlugin(['dist'], {
}),
new HtmlWebpackPlugin({
title: 'Zenvite',
template: './public/templates/index.html',
filename: 'templates/index.html'
}),
new CopyWebpackPlugin([
//{ from: 'public/css', to:'css'},
{ from: 'public/media', to:'media'},
{ from: 'public/js/pages', to: 'js/pages', ignore: [ '*.js' ]},
{ from: 'public/templates/app.html', to: 'templates'}
]),
new MiniCssExtractPlugin({
filename: 'css/app.[contenthash].css'
})
],
module: {
rules: [{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
],
}]
},
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // Must be set to true if using source-maps in production
}),
new OptimizeCSSAssetsPlugin({})
]
}
};

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
}
}
]

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