Webpack 4 - Loader options based on entry - javascript

Is it possible to create a css bundle with different loader options, based on the entry?
module.exports = {
entry: {
bundle1: './app.scss',
bundle2: './app.scss'
},
module: {
rules: {
test: /\.(css|scss)$/,
use: [
...,
{
loader: "sass-loader",
options: {
prependData: (entry) => `$value: ${entry.name === bundle1 ? 'foo' : 'bar'};`,
},
},
],
}
},
...
}
So with this setup basically I will create 2 bundle.css files, a bundle1.css and a bundle2.css, but I want to pass down different variables to my scss files based on each entry points. So the ideal would be something similar I wrote in the prependData, but I don't know if it's possible somehow.

Related

Webpack-compiled CSS file is including Javascript variables and functions

We use a simple application of webpack/mix:
mix.js('resources/js/app.js', 'public/js')
.js('resources/js/cpg.js', 'public/js')
.js('resources/js/editor.js', 'public/js')
.copy('resources/js/ckeditor/dist', 'public/js/editor')
.sass('resources/sass/app.scss', 'public/css')
.sass('resources/sass/cpg.scss', 'public/css')
With webpack.config.js:
module.exports = {
resolve: {
alias: {
'#': path.resolve('resources/js'),
},
},
// https://webpack.js.org/configuration/entry-context/
entry: './resources/js/editor.js',
// https://webpack.js.org/configuration/output/
output: {
path: path.resolve(__dirname, 'public/js/editor'),
filename: 'editor.js'
},
module: {
rules: [
{
test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
use: ['raw-loader']
},
{
test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
use: [
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag',
attributes: {
'data-cke': true
}
}
},
{
loader: 'postcss-loader',
options: styles.getPostCssConfig({
themeImporter: {
themePath: require.resolve('#ckeditor/ckeditor5-theme-lark')
},
minify: true
})
}
]
}
]
},
// Useful for debugging.
devtool: 'source-map',
// By default webpack logs warnings if the bundle is bigger than 200kb.
performance: { hints: false }
}
Prior to the addition of ckeditor, we had no troubles. But now that ckeditor has been added, the following JS now appears in our compiled cpg.css file:
function webpackContext(req) {
var id = webpackContextResolve(req);
return __webpack_require__(id);
}
function webpackContextResolve(req) {
if(!__webpack_require__.o(map, req)) {
var e = new Error("Cannot find module '" + req + "'");
e.code = 'MODULE_NOT_FOUND';
throw e;
}
return map[req];
}
webpackContext.keys = function webpackContextKeys() {
return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = "./node_modules/moment/locale sync recursive ^\\.\\/.*$";
Obviously, this is a problem. JS code does not belong in CSS files, and it trips up our SonarCloud quality gate (for good reason) so we can't deploy anything that's been compiled unless we manually edit the compiled files. Which mostly defeats the purpose of having them.
Further backstory: the section of our project that uses CKEditor was completed by a contractor. So, all of this was merged into our project before we saw that compiled files were improper. The contractor is no longer with the company, so I'm trying to debug on my own and getting nowhere. It seems to be an exceedingly rare bug for Webpack to place JS code in a CSS file.
Progress update: Removing the ckeditor references has no impact. The Webpack just seems to be broken now. Comprehensive node_modules re-install had no effect. It's just broken.
Issue appears to be a copy of https://github.com/laravel-mix/laravel-mix/issues/1976. Upgrading to Mix 6 creates an absolutely absurd amount of problems for my project, so this will just go unresolved.
Followed the instructions here: https://github.com/laravel-mix/laravel-mix/issues/2633#issuecomment-802023077 I was able to resolve the problem.
Might be related to this https://github.com/ckeditor/ckeditor5/issues/8112
A solution there suggests this change
use: [
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag',
attributes: {
'data-cke': true
}
}
},
'css-loader', // added this line
{
loader: 'postcss-loader',
options: styles.getPostCssConfig({
themeImporter: {
themePath: require.resolve('#ckeditor/ckeditor5-theme-lark')
},
minify: true
})
}
]

Webpack problem generating sourcemaps -- only generated when a particular plugin is disabled

I'm using my own custom plugin to strip some optional code from a build. This works well, but for some reason it seems to be blocking generation of source maps.
My best guess is that the fact that I'm modifying the index.js output file interferes with the ability to generate a map of for that file. If I comment out the plugin, my source maps come back.
Is there perhaps something I can do to change order of plugin execution that will fix this? Or perhaps a way to strip code from source file input streams (not from the files themselves) rather than from the generated output?
I've tried explicitly adding SourceMapDevToolPlugin to my plugins, but that didn't help.
Here's my webpack.config.cjs file:
const { Compilation, sources } = require('webpack');
const { resolve } = require('path');
module.exports = env => {
const esVersion = env?.esver === '5' ? 'es5' : 'es6';
const dir = env?.esver === '5' ? 'web5' : 'web';
const chromeVersion = env?.esver === '5' ? '23' : '51';
// noinspection JSUnresolvedVariable,JSUnresolvedFunction,JSUnresolvedFunction
return {
mode: env?.dev ? 'development' : 'production',
target: [esVersion, 'web'],
entry: {
index: './dist/index.js'
},
output: {
path: resolve(__dirname, 'dist/' + dir),
filename: `index.js`,
libraryTarget: 'umd',
library: 'tbTime'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: { presets: [['#babel/preset-env', { targets: { chrome: chromeVersion } }]] }
},
resolve: { fullySpecified: false }
}
]
},
resolve: {
mainFields: ['esm2015', 'es2015', 'module', 'main', 'browser']
},
externals: { 'by-request': 'by-request' },
devtool: 'source-map',
plugins: [
new class OutputMonitor {
// noinspection JSUnusedGlobalSymbols
apply(compiler) {
compiler.hooks.thisCompilation.tap('OutputMonitor', (compilation) => {
compilation.hooks.processAssets.tap(
{ name: 'OutputMonitor', stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE },
() => {
const file = compilation.getAsset('index.js');
let contents = file.source.source();
// Strip out dynamic import() so it doesn't generate warnings.
contents = contents.replace(/return import\(.*?\/\* webpackIgnore: true \*\/.*?tseuqer-yb.*?\.join\(''\)\)/s, 'return null');
// Strip out large and large-alt timezone definitions from this build.
contents = contents.replace(/\/\* trim-file-start \*\/.*?\/\* trim-file-end \*\//sg, 'null');
compilation.updateAsset('index.js', new sources.RawSource(contents));
}
);
});
}
}()
]
};
};
Full project source can be found here: https://github.com/kshetline/tubular_time/tree/development
I think using RawSource would disable the source map. The right one for devtool is supposed to be SourceMapSource so the idea looks like following:
const file = compilation.getAsset('index.js');
const {devtool} = compiler.options;
let contents = file.source.source();
const {map} = file.source.sourceAndMap();
// your replace work
// ...
compilation.updateAsset(
'index.js',
devtool
// for devtool we have to pass map file but this the original one
// it would be wrong since you have already changed the content
? new sources.SourceMapSource(contents, 'index.js', map)
: new sources.RawSource(contents)
);

How to handle dynamic image paths with webpack

I've managed to process the majority of my icons within a large application but there are still two use cases which don't get caught.
Dynamic paths used within angular templates
<md-icon md-svg-src="{{'/assets/icons/ic-' + $ctrl.icon + '.svg'}}"></md-icon>
Paths used as varibled within components that are passed to angular templates
i.e something along the lines of
class Comp {
this.settingsLinks = [
{name: 'advanced settings', icon: '/assets/icons/ic-settings.svg'}
]
}
and then that gets used within a template like so
<div ng-repeat="setting in $ctrl.settingsLinks">
<md-icon md-svg-src="{{setting.icon}}"></md-icon>
</div>
My webpack config is like so
module.exports = {
module: {
loaders: [
{
test: /\.html$/,
loaders: 'html-loader',
options: {
attrs: ['md-icon:md-svg-src', 'img:ng-src'],
root: path.resolve(__dirname, '../src'),
name: '[name]-[hash].[ext]'
}
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
enforce: 'pre'
},
{
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'file-loader',
options: {
name() {
return '[name]-[hash].[ext]';
}
}
},
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'ng-annotate-loader',
'babel-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: conf.path.src('index.html')
}),
new ExtractTextPlugin('index-[contenthash].css'),
],
output: {
path: path.join(process.cwd(), conf.paths.dist),
filename: '[name]-[hash].js'
},
entry: {
app: [`./${conf.path.src('app/app.module.js')}`],
vendor: Object.keys(pkg.dependencies)
},
};
I've been looking at the webpack.ContextReplacementPlugin as a potential way of handling the dynamic paths does anyone have any insight into what I'm trying to achieve? I want to be able to hash names for cache busting purposes but am struggling to work out how to handle dynamic paths in both js and templates.
https://webpack.js.org/guides/dependency-management/
webpack gives access to require.context which allows telling webpack what the dynamic path could/should be, it removed the uncertainty of what is being required and allows you to return the newly hashed icon name from its original name. It does not require them all having an import cost it merely creates a map between the old and new name if I have understood correctly.
for example, this is saying grab all the file names in the icons folder grab the ones starting with ic- as that's our naming scheme for icons, this creates an object at build time I believe of all the possible icons to be used.
const ICONS = require.context('../../../assets/icons', true, /ic-[a-zA-Z0-9.-]/);
whats returned is a function where you can pass the original icon name and get back the hashed version. You can also use ICONS.keys() to get an array of icons.
here is an example usage I've used to provide some icons.
const ICONS = require.context('../../../assets/icons', true, /ic-[a-zA-Z0-9.-]/);
export function getIconFromPath(path) {
return ICONS(path);
}
function getIconsFromPaths(obj) {
Object.keys(obj).forEach(key => Object.assign(obj, {[key]: ICONS(obj[key])}));
return obj;
}
export default getIconsFromPaths({
ARCHIVED: './ic-status-warning.svg',
CANCELLED: './ic-status-cancelled.svg',
CONFLICT: './ic-status-warning.svg',
DRAFT: './ic-status-draft.svg',
EARLIER: './ic-status-published.svg',
ENDED: './ic-status-ended.svg',
ERROR: './ic-status-published-failure.svg',
FAILURE: './ic-status-failure.svg',
INVALID: './ic-status-warning.svg',
IN_PROGRESS: './ic-content-publish-in-progress.svg',
LATEST: './ic-status-published-latest.svg',
LOCKED: './ic-status-locked.svg',
PUBLISHED: './ic-status-published.svg',
PUBLISHING: './ic-content-publish-in-progress.svg',
SCHEDULED: './ic-status-scheduled.svg',
SCHEDULING: './ic-content-publish-in-progress.svg',
UNLOCKED: './ic-status-unlocked.svg',
UPDATED: './ic-webhook-request-success.svg',
UNPUBLISHING: './ic-content-publish-in-progress.svg',
UNSCHEDULING: './ic-content-publish-in-progress.svg',
VALID: './ic-content-valid-tick.svg',
WARNING: './ic-status-warning.svg'
});
Because webpack now knows what could be returned from here it can now hash the name and you can do all sorts of good stuff like optimizing at build time.
so the example class given in my question would be resolved by
import { getIconFromPath } from '../icons/;
class Comp {
this.settingsLinks = [
{
name: 'advanced settings',
icon: getIconFromPath('./ic-settings.svg')
}
]
}

webpack compile a single entry point into multiple outputs based on DefinePlugin

I currently have webpack setup to compile using babel-loadera single entry point into a single output bundle. Something like
entry.js
import { A } from "a.js"
import { B } from "b.js"
...
if (TEST) {
console.log("this is a test");
}
webpack.config.js
module.exports = {
entry: {
entry: "entry.js"
},
output: {
filename: "[name].bundle.js",
path: __dirname + "/output"
},
module: {
rules: [
{
test: /\.js$/
use: {
loader: "babel-loader"
}
}]
},
plugins: [
new webpack.DefinePlugin({
TEST: JSON.stringify(true)
})
]
}
currently this all works fine. What I want though is the ability to create two versions of the entry.bundle.js. Effectively a version where TEST is true and a version where it is false: entry.bundle.js and entry.test.bundle.js
What do i need to change to achieve that? Ideally I would prefer not to have to have multiple webpack config files
If you have the boolean in this file, why not just if/else the file name and pass that same boolean down to the plugin?
let TEST = true; // assume this is passed in somehow, perhaps via cli arguments
module.exports = {
entry: {
entry: 'entry.js'
},
output: {
filename: TEST ? '[name].test.bundle.js' : '[name].bundle.js'
path: __dirname + '/output'
},
module: {
rules: [{
test: /\.js$/,
use: {
loader: 'babel-loader'
}
}]
},
plugins: [
new webpack.DefinePlugin({
TEST: JSON.stringify(TEST) // variable, will be consistent with filename
})
]
}
I do understand what you wish to do, but I am unaware of a slicker way to do this through any webpack specific techniques.

Can I use webpack to generate CSS and JS separately?

I have:
JS files that I want to bundle.
LESS files that I want to compile down to CSS (resolving #imports into a single bundle).
I was hoping to specify these as two separate inputs and have two separate outputs (likely via extract-text-webpack-plugin). Webpack has all the proper plugins/loaders to do compilation, but it doesn't seem to like the separation.
I've seen examples of people requiring their LESS files directly from JS, such as require('./app.less');, for no other reason than to tell webpack to include those files into the bundle. This allows you to only have a single entry point, but it seems really wrong to me -- why would I require LESS in my JS when it has nothing to do with my JS code?
I tried using multiple entry points, handing both the entry JS and main LESS file in, but when using multiple entry points, webpack generates a bundle that doesn't execute the JS on load -- it bundles it all, but doesn't know what should be executed on startup.
Am I just using webpack wrong? Should I run separate instances of webpack for these separate modules? Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?
Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?
Maybe not. Webpack is definitely js-centric, with the implicit assumption that what you're building is a js application. Its implementation of require() allows you to treat everything as a module (including Sass/LESS partials, JSON, pretty much anything), and automatically does your dependency management for you (everything that you require is bundled, and nothing else).
why would I require LESS in my JS when it has nothing to do with my JS code?
People do this because they're defining a piece of their application (e.g. a React component, a Backbone View) with js. That piece of the application has CSS that goes with it. Depending on some external CSS resource that's built separately and not directly referenced from the js module is fragile, harder to work with, and can lead to styles getting out of date, etc. Webpack encourages you to keep everything modular, so you have a CSS (Sass, whatever) partial that goes with that js component, and the js component require()s it to make the dependency clear (to you and to the build tool, which never builds styles you don't need).
I don't know if you could use webpack to bundle CSS on its own (when the CSS files aren't referenced from any js). I'm sure you could wire something up with plugins, etc., but not sure it's possible out of the box. If you do reference the CSS files from your js, you can easily bundle the CSS into a separate file with the Extract Text plugin, as you say.
webpack 4 solution with mini-css-extract plugin
the webpack team recommends using mini-css-extract over the extract text plugin
this solution allows you to create a separate chunk containing only your css entries:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
function recursiveIssuer(m) {
if (m.issuer) {
return recursiveIssuer(m.issuer);
} else if (m.name) {
return m.name;
} else {
return false;
}
}
module.exports = {
entry: {
foo: path.resolve(__dirname, 'src/foo'),
bar: path.resolve(__dirname, 'src/bar'),
},
optimization: {
splitChunks: {
cacheGroups: {
fooStyles: {
name: 'foo',
test: (m, c, entry = 'foo') =>
m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
chunks: 'all',
enforce: true,
},
barStyles: {
name: 'bar',
test: (m, c, entry = 'bar') =>
m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
chunks: 'all',
enforce: true,
},
},
},
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
};
Here is a more contrived example using mutliple entries from one of my personal projects:
const ManifestPlugin = require('webpack-manifest-plugin')
const webpack = require('webpack')
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const VENDOR = path.join(__dirname, 'node_modules')
const LOCAL_JS = path.join(__dirname, 'app/assets/js')
const LOCAL_SCSS = path.join(__dirname, 'app/assets/scss')
const BUILD_DIR = path.join(__dirname, 'public/dist')
const EXTERNAL = path.join(__dirname, 'public/external')
function recursiveIssuer(m) {
if (m.issuer) {
return recursiveIssuer(m.issuer);
} else if (m.name) {
return m.name;
} else {
return false;
}
}
module.exports = {
entry: {
vendor: [
`${VENDOR}/jquery/dist/jquery.js`,
`${VENDOR}/codemirror/lib/codemirror.js`,
`${VENDOR}/codemirror/mode/javascript/javascript.js`,
`${VENDOR}/codemirror/mode/yaml/yaml.js`,
`${VENDOR}/zeroclipboard/dist/ZeroClipboard.js`,
],
app: [
`${LOCAL_JS}/utils.js`,
`${LOCAL_JS}/editor.js`,
`${LOCAL_JS}/clipboard.js`,
`${LOCAL_JS}/fixtures.js`,
`${LOCAL_JS}/ui.js`,
`${LOCAL_JS}/data.js`,
`${LOCAL_JS}/application.js`,
`${LOCAL_JS}/google.js`
],
'appStyles': [
`${EXTERNAL}/montserrat.css`,
`${EXTERNAL}/icons.css`,
`${VENDOR}/purecss/pure-min.css`,
`${VENDOR}/purecss/grids-core-min.css`,
`${VENDOR}/purecss/grids-responsive-min.css`,
`${VENDOR}/codemirror/lib/codemirror.css`,
`${VENDOR}/codemirror/theme/monokai.css`,
]
},
optimization: {
splitChunks: {
cacheGroups: {
appStyles: {
name: 'appStyles',
test: (m, c, entry = 'appStyles') =>
m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
chunks: 'all',
enforce: true,
},
},
},
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [ 'script-loader'],
},
{
test: /\.(scss|css)$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
],
},
],
},
mode: 'development',
resolve: {
extensions: ['.js', '.css', '.scss']
},
output: {
path: BUILD_DIR,
filename: "[name].[chunkhash].js",
},
plugins: [
new ManifestPlugin(),
new MiniCssExtractPlugin({
filename: '[name].css'
}),
]
};
I realize this approach is not very modular, but it should give you a foundation to build from and is an excellent strategy for adopting webpack in projects where you do not wish to inter-mix javascript and css.
The downside to this approach is that css-loader still generates an additional javascript file (whether you choose to use it or not), this will supposedly be fixed in webpack 5.
Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?
I don't see anything wrong with this but ultimately it depends on your tolerance for managing multiple build systems. To me this feels like overkill, so my preference is to remain in the webpack ecosystem.
For more information on the strategies outlined above, please see https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry
A separate CSS bundle can be generated without using require('main/less) in any of your JS, but as Brendan pointed out in the first part of his answer Webpack isn't designed for a global CSS bundle to go alongside modular JS, however there are a couple of options.
The first is to add an extra entry point for main.less, then use the Extract Text plugin to create the CSS bundle:
var webpack = require('webpack'),
ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: {
home: [
'js/common',
'js/homepage'
],
style: [
'styles/main.less'
]
},
output: {
path: 'dist',
filename: "[name].min.js"
},
resolve: {
extensions: ["", ".js"]
},
module: {
loaders: [{
test: /\.less$/,
loader: ExtractTextPlugin.extract("style", "css", "less")
}]
},
plugins: [
new ExtractTextPlugin("[name].min.css", {
allChunks: true
})
]
};
The problem with this method is you also generate an unwanted JS file as well as the bundle, in this example: style.js which is just an empty Webpack module.
Another option is to add the main less file to an existing Webpack entry point:
var webpack = require('webpack'),
ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: {
home: [
'js/common',
'js/homepage',
'styles/main.less'
],
},
output: {
path: 'dist',
filename: "[name].min.js"
},
resolve: {
extensions: ["", ".js"]
},
module: {
loaders: [{
test: /\.less$/,
loader: ExtractTextPlugin.extract("style", "css", "less")
}]
},
plugins: [
new ExtractTextPlugin("[name].min.css", {
allChunks: true
})
]
};
This is ideal if you have only 1 entry point, but if you have more, then your Webpack config will look a bit odd as you'll have to arbitrarily choose which entry point to add the main less file to.
To further clarify bdmason's former answer - it seems the desirable configuration would be to create a JS and CSS bundle for each page, like so:
entry: {
Home: ["./path/to/home.js", "./path/to/home.less"],
About: ["./path/to/about.js", "./path/to/about.less"]
}
And then use the [name] switch:
output: {
path: "path/to/generated/bundles",
filename: "[name].js"
},
plugins: new ExtractTextPlugin("[name].css")
Full configuration - with some additions not connected to the question (we're actually using SASS instead of LESS):
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
require('babel-polyfill');
module.exports = [{
devtool: debug ? "inline-sourcemap" : null,
entry: {
Home: ['babel-polyfill', "./home.js","path/to/HomeRootStyle.scss"],
SearchResults: ['babel-polyfill', "./searchResults.js","path/to/SearchResultsRootStyle.scss"]
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015'],
plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy']
}
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract("style-loader","css-raw-loader!sass-loader")
}
]
},
output: {
path: "./res/generated",
filename: "[name].js"
},
plugins: debug ? [new ExtractTextPlugin("[name].css")] : [
new ExtractTextPlugin("[name].css"),
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin({
compress:{
warnings: true
}
})
]
}
];
Yes, this is possible but like others said you will need additional packages to do so (see devDependencies under package.json). here is the sample code that I used to compile my bootstrap SCSS --> CSS and Bootstrap JS --> JS.
webpack.config.js:
module.exports = {
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
entry: ['./src/app.js', './src/scss/app.scss'],
output: {
path: path.resolve(__dirname, 'lib/modules/theme/public'),
filename: 'js/bootstrap.js'
},
module: {
rules: [
{
test: /\.scss$/,
use: [
{
loader: 'file-loader',
options: {
name: 'css/bootstrap.css',
}
},
{
loader: 'extract-loader'
},
{
loader: 'css-loader?-url'
},
{
loader: 'postcss-loader'
},
{
loader: 'sass-loader'
}
]
}
]
}
};
additional postcss.config.js file:
module.exports = {
plugins: {
'autoprefixer': {}
}
}
package.json:
{
"main": "app.js",
"scripts": {
"build": "webpack",
"start": "node app.js"
},
"author": "P'unk Avenue",
"license": "MIT",
"dependencies": {
"bootstrap": "^4.1.3",
},
"devDependencies": {
"autoprefixer": "^9.3.1",
"css-loader": "^1.0.1",
"exports-loader": "^0.7.0",
"extract-loader": "^3.1.0",
"file-loader": "^2.0.0",
"node-sass": "^4.10.0",
"popper.js": "^1.14.6",
"postcss-cli": "^6.0.1",
"postcss-loader": "^3.0.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"webpack": "^4.26.1",
"webpack-cli": "^3.1.2"
}
}
See the tutorial here: https://florianbrinkmann.com/en/4240/sass-webpack
Like others mentioned you can use a plugin.
ExtractTextPlugin is deprecated.
You can use the currently recommended MiniCssExtractPlugin in your webpack configuration:
module.exports = {
entry: {
home: ['index.js', 'index.less']
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
}),
]
}
You can also put your Less require statements in your entry JS file also:
in body.js
// CSS
require('css/_variable.scss')
require('css/_npm.scss')
require('css/_library.scss')
require('css/_lib.scss')
Then in webpack
entry: {
body: [
Path.join(__dirname, '/source/assets/javascripts/_body.js')
]
},
const extractSass = new ExtractTextPlugin({
filename: 'assets/stylesheets/all.bundle.css',
disable: process.env.NODE_ENV === 'development',
allChunks: true
})

Categories

Resources