Extract multiple css files to different directories - css

I'm trying to compile some sass files and extract each one to multiple paths.
Example:
resources/home/a.scss -> public/home/a.css
resources/home/layout/b.scss -> public/home/layout/b.scss
resources/login/c.scss -> public/login/c.css
At the moment I'm using this config and it bundles every scss file:
const path = require("path")
const webpack = require("webpack")
const ExtractTextPlugin = require("extract-text-webpack-plugin")
module.exports = {
// Context is an absolute path to the directory where webpack will be
// looking for our entry points.
context: path.resolve(process.cwd(), 'resources/assets'),
entry:
[
'./sass/app/home/a.scss',
'./sass/app/home/layout/b.scss',
'./sass/app/login/c.scss',
],
output: {
path: __dirname,
filename: '[name].js'
},
module: {
rules: [
{
test: /\.(scss)$/,
loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
}
]
},
plugins: [
new ExtractTextPlugin({ filename: '[name].css', allChunks: true })
]
}

I think the problem is you are using an Array as the entry, which means that all of the dependencies (in this case the different css files, are added into the same output file).
You should change it to:
entry: {
a: './sass/app/home/a.scss',
b: './sass/app/home/layout/b.scss',
c: './sass/app/login/c.scss'
}
You can read more here https://webpack.js.org/concepts/entry-points/#object-syntax

Related

Multiple Tailwind CSS classes having multiple Webpack entry points

Problem statement
So I have a React project setup with webpack and tailwind CSS.
In my webpack config I have multiple entry point in order to generate different CSS and JS for each entry point.
The problem arises when I use the tailwind classes in my React components.
Let's suppose if I use a tailwind class bg-red-600 only in Component1(or entry point 1).
So after building the files through webpack the bg-red-600 will be present in all the entry point's generated CSS files(keep in mind I have just used this class in first entry point component only).
What it should be doing is only have bg-red-600 class in first component CSS file instead it is preset in all the CSS files even though I have not used it in any other place other than first component.
Hope I was able to made my point.
Thanks.
Webpack's entry points:
entry: {
app1: path.resolve(
__dirname,
'src/Component1'
),
app2: path.resolve(
__dirname,
'src/Component2'
),
},
Here is my solution:
/config folder with custom tailwind-xxx.config file for each entry js
eg. /config/tailwind-ConfirmButton.config.js:
module.exports = {
content: [
'./src/common/ConfirmButton/ConfirmButton.jsx',
],
// plugins: [require('#tailwindcss/forms')],
}
webpack.config.js
const postcssOpts = { postcssOptions: env => {
// here is the point
const component = env._module.context.slice(env._module.context.lastIndexOf('/') + 1)
return {
plugins: [
['tailwindcss', {
config: `./config/tailwind-${component}.config.js`,
}],
autoprefixer
]
}
}
}
...
entry: {
confirm: path.resolve(__dirname, './src/widgets/confirmButton.js'),
},
target: ['web', 'es5'], // <=== can be omitted as default is 'web'
output: {
filename: '[name]/tag.js',
path: path.resolve(__dirname, 'dist/exp'),
publicPath: './',
},
...
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: postcssOpts,
},
],
},
entry widget js
eg. /src/widgets/customButton.js:
...
render(
<ConfirmButton
expId={expId}
content={content}
confirmBtn={confirmBtn}
cancelBtn={cancelBtn}
field={field}
/>,
container
)
finally run weppack --mode=production

Webpack 4 Multiple file types per entry does not output css files

I have a situation when I have multiple themes in a project and every theme has it's [theme-name].scss file and [theme-name].js file which has to be compiled into [theme-name]/dist folder. So I have multiple entries with multiple outputs.
I thought I've found the simplified solution on Webpack's page here: https://v4.webpack.js.org/guides/entry-advanced/ , however only 2 js files are built into dist folder and no css. Maybe someone could tell me why it's so? Thank you.
My exact config:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: process.env.NODE_ENV,
entry: {
home: ['./src/js/home.js', './src/scss/home.scss'],
account: ['./src/js/account.js', './src/scss/account.scss'],
},
output: {
filename: '[name].js',
},
module: {
rules: [
{
test: /\.scss$/,
use: [
// fallback to style-loader in development
process.env.NODE_ENV !== 'production' ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css'
}),
],
};
file structure

Compile CSS and JS in difference files / WEBPACK

For 2 days I have been trying to compile the js and css file to a separate file because now everything is together. Does anyone have any idea how this can be solved?
I would be very grateful for your help.
There is my code webpack.config.js
const path = require('path');
const webpack = require('webpack');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'src/dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.scss$/,
use: [
"style-loader", // creates style nodes from JS strings
{
loader: "css-loader",
options: {
url: false
}
},
"sass-loader" // compiles Sass to CSS, using Node Sass by default
]
},
]
},
plugins: [
new BrowserSyncPlugin({
// browse to http://localhost:3000/ during development,
// ./public directory is being served
host: 'localhost',
port: 3000,
files: ['./src/*.html'],
server: { baseDir: ['src'] }
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
]
};
I think MiniCssExtractPlugin is what you are looking for.
It takes the output of css-loader and create .css bundles. It takes care of downloading them in the browser (by pushing a section of code in webpack runtime code), and also yeah, it minifies the .css :).
Simple usage:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
};
Yes you are right. Style-loader creates javascript snippets that later in runtime creates .css rules and push them to the browser global css scope.

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({})
]
}
};

Webpack concatenate two CSS files and an SCSS file into one CSS file

I'm struggling to get webpack to do what I want here. Basically, I'm trying to feed in two CSS files and an SCSS file, and output one CSS file from this.
My code so far is:
'use strict';
var webpack = require('webpack');
var webpackDir = __dirname;
let path = require('path');
let nodeModulesPath = path.join(webpackDir, 'node_modules');
//Process the SCSS
var ExtractTextPlugin = require('extract-text-webpack-plugin');
let extractCSS = new ExtractTextPlugin('../css/[name].css',{allChunks: true});
function isDev() {
var isDev = true;
if (process.env.NODE_ENV === 'production') {
isDev = false;
}
return isDev;
}
var definePlugin = new webpack.DefinePlugin({
__DEV__: isDev()
});
module.exports = {
resolve: {
modulesDirectories: [webpackDir + '/node_modules'],
alias: {
rootReducers: path.join(webpackDir, '../../../../../themes/myProject-base/lib/myProject-core/register/myProject-redux-base/base-reducers'),
rootActions: path.join(webpackDir, '../../../../../themes/myProject-base/lib/myProject-core/register/myProject-redux-base/base-actions'),
rootUtils: path.join(webpackDir, '../../../../../themes/myProject-base/lib/myProject-core/register/myProject-redux-base/base-utils'),
lunchMenusReducers: path.join(webpackDir, '/src/lunch-menus-common-js/reducers/lunch-menus-reducers'),
lunchMenusActions: path.join(webpackDir, '/src/lunch-menus-common-js/actions/lunch-menus-actions'),
lunchMenusConfigureStore: path.join(webpackDir, '/src/lunch-menus-common-js/configureStore')
}
},
resolveLoader: {
root: nodeModulesPath
},
entry: {
backend: path.resolve(webpackDir + '/src/components/index.js'),
widgetfrontend: path.join(webpackDir, '../../../includes/widget/public-js/scripts/main.js'),
widgetbackend: path.join(webpackDir, '../../../includes/widget/js/scripts/main.js'),
myProjectLunchMenusAdmin: [
path.join(webpackDir, '../../scss/myProject-lunch-menus-admin.scss'),
path.join(nodeModulesPath + 'react-datepicker/dist/react-datepicker.min.css'),
path.join(nodeModulesPath + 'node_modules/quill/dist/*.css')
]
},
output: {
path: path.resolve(webpackDir + '/../'),
filename: '[name].js',
devtoolLineToLine: true
},
plugins: isDev() !== true ? [
extractCSS,
new webpack.optimize.UglifyJsPlugin({minimize: true}),
definePlugin
] : [
extractCSS,
definePlugin
],
module: {
loaders: [
{
test: /(src|myProject-base|widget)\/.+.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: [
require.resolve('babel-preset-es2015'),
require.resolve('babel-preset-react'),
require.resolve('babel-preset-stage-0')
]
}
},
{
test: /\.scss$/i,
loader: extractCSS.extract(['css','sass'])
},
{
test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$/,
exclude: /node_modules/,
loader: 'file'
}
]
}
}
The area of focus is myProjectLunchMenusAdmin - as it's not taking in the CSS + SCSS files and outputting one CSS file.
The error(s) I get are:
ERROR in multi myProjectLunchMenusAdmin
Module not found: Error: Cannot resolve 'file' or 'directory' /Users/joneslloyd/Documents/MAMP/myProjectv2/wp-content/plugins/myProject-lunch-menus/admin/js/scripts/node_modules/node_modules/quill/dist/*.css in /Users/joneslloyd/Documents/MAMP/myProjectv2/wp-content/plugins/myProject-lunch-menus/admin/js/scripts
# multi myProjectLunchMenusAdmin
ERROR in ./~/react-datepicker/dist/react-datepicker.min.css
Module parse failed: /Users/joneslloyd/Documents/MAMP/myProjectv2/wp-content/plugins/myProject-lunch-menus/admin/js/scripts/node_modules/react-datepicker/dist/react-datepicker.min.css Line 1: Unexpected token .
You may need an appropriate loader to handle this file type.
At this stage, even a pointer/suggestion would be really useful!
There are several issues with this config.
modulesDirectories should be an array of directory names that will be resolved like node_modules (that is: traversing the directory tree and looking for a node_modules folder). Do not put actual paths in this array. This is one of the most common errors I see in webpack configs. Since npm is the most prominent package manager, you usually don't need to set this option as it already defaults to node_modules.
The named chunk myProjectLunchMenusAdmin references CSS files, but you did not activate the css-loader for the .css extension. That's basically what the Module parse failed error is trying to say.
The named chunk myProjectLunchMenusAdmin references the glob pattern node_modules/quill/dist/*.css. Webpack does not understand glob patterns. In your case, it tries to include a file actually named *.css which is what the Module not found error is trying to tell. You need to pass webpack only one file and webpack will figure out the dependency graph. For example: If file main.css imports some-other-file.css, it will include that file too and so on. By doing that, webpack will only include files that are actually needed because they are referenced in your program. (Btw: node_modules/node_modules does not look correct)
Using a relative path for the first argument of the ExtractTextPlugin is probably invalid. I don't know if that actually works. You should just give a filename and webpack will emit the extracted and unified CSS into a file with that name in the specified output.path. The output.path is usually a flat folder without sub-directories. If you need to move the bundled files around afterwards, you should separate that from your webpack build. That's just another build step.
resolveLoader.root does not need to be modified as long as you're installing your loaders via npm (which I strongly recommend).
I've tried to fix the given config. Since I don't know your project, there is not guarantee that this will work.
'use strict';
let webpack = require('webpack');
let path = require('path');
let nodeModulesPath = path.join(__dirname, 'node_modules');
//Process the SCSS
let ExtractTextPlugin = require('extract-text-webpack-plugin');
let extractCSS = new ExtractTextPlugin('[name].css',{allChunks: true});
let isDev = process.env.NODE_ENV !== 'production';
var definePlugin = new webpack.DefinePlugin({
__DEV__: isDev
});
module.exports = {
resolve: {
alias: {
rootReducers: path.join(__dirname, '../../../../../themes/myProject-base/lib/myProject-core/register/myProject-redux-base/base-reducers'),
rootActions: path.join(__dirname, '../../../../../themes/myProject-base/lib/myProject-core/register/myProject-redux-base/base-actions'),
rootUtils: path.join(__dirname, '../../../../../themes/myProject-base/lib/myProject-core/register/myProject-redux-base/base-utils'),
lunchMenusReducers: path.join(__dirname, 'src/lunch-menus-common-js/reducers/lunch-menus-reducers'),
lunchMenusActions: path.join(__dirname, 'src/lunch-menus-common-js/actions/lunch-menus-actions'),
lunchMenusConfigureStore: path.join(__dirname, 'src/lunch-menus-common-js/configureStore')
}
},
entry: {
backend: path.join(__dirname, 'src/components/index.js'),
widgetfrontend: path.join(__dirname, '../../../includes/widget/public-js/scripts/main.js'),
widgetbackend: path.join(__dirname, '../../../includes/widget/js/scripts/main.js'),
myProjectLunchMenusAdmin: [
path.join(__dirname, '../../scss/myProject-lunch-menus-admin.scss'),
path.join(nodeModulesPath, 'react-datepicker/dist/react-datepicker.min.css'),
path.join(nodeModulesPath, 'quill/dist/quill.base.css'),
path.join(nodeModulesPath, 'quill/dist/quill.snow.css')
]
},
output: {
path: path.resolve(__dirname, '..'),
filename: '[name].js',
devtoolLineToLine: true
},
plugins: [
extractCSS,
definePlugin
].concat(isDev ? [
] : [
new webpack.optimize.UglifyJsPlugin({minimize: true})
]),
module: {
loaders: [
{
test: /(src|myProject-base|widget)\/.+.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: [
require.resolve('babel-preset-es2015'),
require.resolve('babel-preset-react'),
require.resolve('babel-preset-stage-0')
]
}
},
{
test: /\.css$/i,
loader: extractCSS.extract(['css'])
},
{
test: /\.scss$/i,
loader: extractCSS.extract(['css','sass'])
},
{
test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$/,
loader: 'file'
}
]
}
}

Resources