I've been trying to get SCSS global variables to work using webpack, but it doesn't seem to resolve the paths correctly in any attempt I make.
The config looks as following (using electron-webpack, extending the default config)
const path = require('path');
const merge = require('webpack-merge');
const webpackRenderer = require('electron-webpack/webpack.renderer.config.js');
const cssnano = require('cssnano');
const { extend, find, flow, has } = require('lodash');
const isProd = process.env.NODE_ENV === 'production';
function replaceLoader(config, test, callback) {
const rule = find(config.module.rules, (r) => String(r.test) === String(test));
if (rule) {
extend(rule, callback(rule));
}
return config;
}
// Since we want to uses SCSS modules, we need to replace the
// default SCSS loader config that ships with electron-webpack.
function replaceSassLoader(config) {
const minimize = isProd
? [
{
loader: 'postcss-loader',
options: {
plugins: [cssnano({ preset: 'default' })]
}
}
]
: [];
return replaceLoader(config, /\.scss$/, () => ({
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]__[local]___[hash:base64:5]'
},
sourceMap: !isProd,
importLoaders: 1
}
},
...minimize,
{
loader: 'sass-loader',
options: {
data:
'#import "~common/stylesheets/variables"; #import "~common/stylesheets/mixins"; #import "~common/stylesheets/themes";',
includePaths: [path.resolve(__dirname, '..', 'src/common')],
sourceMap: !isProd
}
}
]
}));
}
module.exports = async (env) => {
const config = await webpackRenderer(env);
const newConfig = merge.smart(config, {
resolve: {
alias: {
common: path.resolve(__dirname, '..', 'src/common'),
}
}
});
return flow(replaceSassLoader)(newConfig);
};
Used versions:
"electron-webpack": "2.7.4",
"node-sass": "4.13.0",
"sass-loader": "8.0.0",
"webpack": "4.41.2",
"webpack-cli": "3.3.10",
"webpack-dev-server": "3.9.0",
"webpack-merge": "4.2.2"
Whatever attempt/tweaks I do to the path resolving/config I continue to receive the same errors like these:
[start:renderer] # multi css-hot-loader/hotModuleReplacement ./src/renderer/index.js
[start:renderer] Child html-webpack-plugin for "index.html":
[start:renderer] 1 asset
[start:renderer] Entrypoint undefined = index.html
[start:renderer] [./node_modules/html-loader/dist/cjs.js?minimize=false&url=false!./dist/.renderer-index-template.html] 360 bytes {0} [built]
[start:renderer] Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-2!node_modules/rc-tooltip/assets/bootstrap.css:
[start:renderer] Entrypoint mini-css-extract-plugin = *
[start:renderer] [./node_modules/css-loader/dist/cjs.js?!./node_modules/rc-tooltip/assets/bootstrap.css] ./node_modules/css-loader/dist/cjs.js??ref--6-2!./node_modules/rc-tooltip/assets/bootstrap.css 4.22 KiB {mini-css-extract-plugin} [built]
[start:renderer] [./node_modules/css-loader/dist/runtime/api.js] 2.61 KiB {mini-css-extract-plugin} [built]
[start:renderer] Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--8-2!node_modules/sass-loader/dist/cjs.js!src/renderer/account/components/Account/Account.scss:
[start:renderer] Entrypoint mini-css-extract-plugin = *
[start:renderer] [./node_modules/css-loader/dist/cjs.js?!./node_modules/sass-loader/dist/cjs.js!./src/renderer/account/components/Account/Account.scss] ./node_modules/css-loader/dist/cjs.js??ref--8-2!./node_modules/sass-loader/dist/cjs.js!./src/renderer/account/components/Account/Account.scss 368 bytes {mini-css-extract-plugin} [built] [failed] [1 error]
[start:renderer]
[start:renderer] ERROR in ./src/renderer/account/components/Account/Account.scss (./node_modules/css-loader/dist/cjs.js??ref--8-2!./node_modules/sass-loader/dist/cjs.js!./src/renderer/account/components/Account/Account.scss)
[start:renderer] Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
[start:renderer] SassError: Undefined variable: "$responsive-width".
[start:renderer] on line 18 of /<path_to_project>/src/renderer/account/components/Account/Account.scss
[start:renderer] >> #media only screen and (max-width: $responsive-width) {
[start:renderer]
[start:renderer] ---------------------------------------^
[start:renderer] Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--8-2!node_modules/sass-loader/dist/cjs.js!src/renderer/shared/components/Panel/Panel.scss:
[start:renderer] Entrypoint mini-css-extract-plugin = *
[start:renderer] [./node_modules/css-loader/dist/cjs.js?!./node_modules/sass-loader/dist/cjs.js!./src/renderer/shared/components/Panel/Panel.scss] ./node_modules/css-loader/dist/cjs.js??ref--8-2!./node_modules/sass-loader/dist/cjs.js!./src/renderer/shared/components/Panel/Panel.scss 432 bytes {mini-css-extract-plugin} [built] [failed] [1 error]
[start:renderer]
[start:renderer] ERROR in ./src/renderer/shared/components/Panel/Panel.scss (./node_modules/css-loader/dist/cjs.js??ref--8-2!./node_modules/sass-loader/dist/cjs.js!./src/renderer/shared/components/Panel/Panel.scss)
[start:renderer] Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
[start:renderer] SassError: ".panel" failed to #extend "%panelColor".
[start:renderer] The selector "%panelColor" was not found.
[start:renderer] Use "#extend %panelColor !optional" if the extend should be able to fail.
[start:renderer] on line 2 of D:\Development\<path>\src\renderer\shared\components\Panel\Panel.scss
[start:renderer] >> #extend %panelColor;
[start:renderer] ----------^
It all used to be working with webpack 2/babel 6.
We used module resolving from babel 6, which was removed in babel 7, so I replaced it with the webpack 4 resolution aliases and that was the point where all the above issues arised when upgrading all the webpack/babel related packages
Related
My "older" web application uses Grunt and Webpack, but I am wondering if I can remove Grunt completely and still have the same functionality with just Webpack? Or should I replace Grunt with another technology? I've noticed grunt development slow to a crawl, and as new security warnings arise, I am having difficulty upgrading packages.
Below is my grunt file which performs a few basic tasks and allows me to run some very handy commands like "grunt dev" (for packaging dev bundle), "grunt dev-watch" (for packaging and watching for changed files), and "grunt prod" (for production bundle). Additionally, I use maven to build my web application, so I can define these commands from within the pom.xml depending on the build profile (development vs production)
Below: Grunt file, Webpack (common), and Webpack (dev)
gruntfile.js
/**
* Grunt Settings
*
* Contains settings for Grunt tasks that allow for development and production environments
*
* Usage (in bash shell, /webapp directory):
* grunt dev
* grunt dev-watch
* grunt prod
*/
// Define webpack configuration files
const webpackConfig_dev = require('./webpack.dev.js');
const webpackConfig_prod = require('./webpack.prod.js');
module.exports = function(grunt) {
let baseDir = ".";
// Set fileVersion from command line arguments
let fileVersion = grunt.option('fileVersion');
if (typeof fileVersion === 'undefined') {
fileVersion = 'dev';
}
fileVersion = fileVersion.trim();
grunt.initConfig({
// Identify which packages to load
pkg: grunt.file.readJSON('package.json'),
// Remove old files
clean: {
build: {
src: [
// js files
baseDir + '/js/dist',
// css files
baseDir + '/styles/*.css',
baseDir + '/styles/*.map',
]
}
},
// Crunch style files (eg *.scss)
sass: {
options:{
implementation: require('node-sass'),
sourceMap: true,
outputStyle: 'expanded'
},
dist: {
files: [{
src: baseDir + '/styles/main.scss',
dest: baseDir + '/styles/main.' + fileVersion + '.css',
}]
}
},
// Modifications after main.css is created
postcss: {
options: {
map: true,
processors: [
require('autoprefixer')
]
},
dist: {
src: baseDir + '/styles/main.' + fileVersion + '.css'
}
},
// Copy jquery external library to the dist/ folder
copy: {
main: {
files: [
{
expand: true,
cwd: 'node_modules/jquery/dist',
src: 'jquery.min.js',
dest: baseDir + '/js/dist'
},
],
},
},
// Run webpack (depending on development versus production)
webpack: {
dev: webpackConfig_dev(fileVersion),
prod: webpackConfig_prod(fileVersion),
},
// Enable live reloading of js/scss files
watch: {
css: {
files: [baseDir + '/styles/**/*.scss'],
tasks: ['sass']
},
js: {
files: [baseDir + '/js/src/**/*.js', '!' + baseDir + '/js/src/**/*.min.js'],
tasks: ['webpack:dev']
}
}
});
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-webpack');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-clean');
// Available Grunt tasks
// development
grunt.registerTask('default', ['dev']);
grunt.registerTask('dev', ['clean', 'sass', 'postcss', 'copy', 'webpack:dev']);
// development (and watches for changes of .js and .css files)
grunt.registerTask('dev-watch', ['sass', 'postcss', 'webpack:dev', 'watch']);
// production ready
grunt.registerTask('prod', ['clean', 'sass', 'postcss', 'copy', 'webpack:prod']);
};
webpack.common.js
/**
* Webpack settings file :: Common (development & production)
*/
const ESLintPlugin = require('eslint-webpack-plugin');
const baseDir_js = "./js";
module.exports = {
// Define the "source" entry point for the application (or sub apps)
entry: {
main: baseDir_js + '/src/index.js',
admin: baseDir_js + '/src/admin.js',
sysAdmin: baseDir_js + '/src/sysAdmin.js',
},
// Let grunt know we have external libraries available
externals: {
jquery: 'jQuery',
google: 'google',
Stripe: 'Stripe',
},
module: {
rules: [
// process css files
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ],
},
// process images/fonts -> base64
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
type: 'asset/inline',
}
],
},
plugins: [new ESLintPlugin()],
};
webpack.dev.js
/**
* Webpack settings file :: Development
*/
// Import common settings
const {merge} = require('webpack-merge');
const common = require('./webpack.common.js');
const baseDir_js = "./js";
const path = require('path');
// Merge settings with "common"
module.exports = (fileVersion) => merge(common, {
mode: 'development',
// Use full source map
devtool: 'inline-source-map',
// Output entire code base to one file
output: {
filename: '[name].' + fileVersion + '.js',
// store in "dist/" directory
path: path.resolve(baseDir_js, 'dist'),
},
});
I´m trying to setup a uitkit template with
webpack 4
sass loader
MiniCssExtractPlugin
uikit
What I would like to achieve is that a build automatically converts sass to css and that resulting css is injected into src/index.html.
webpack.config.js
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: { minimize: true }
}
]
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true,
sourceMap: true,
importLoader: 4
}
},
"sass-loader"
]}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
]
};
index.js
import UIkit from 'uikit';
import Icons from 'uikit/dist/js/uikit-icons';
import style from "uikit/src/scss/uikit.scss"
UIkit.use(Icons);
Unfortunately a build fails with
ERROR in ./node_modules/uikit/src/scss/uikit.scss (./node_modules/css-loader??ref--6-1!./node_modules/sass-loader/lib/loader.js!./node_modules/uikit/src/scss/uikit.scss)
Module build failed (from ./node_modules/sass-loader/lib/loader.js):
$inverse-base-color: $inverse-global-color !default;
Undefined variable: "$inverse-global-color".
in new_proj/node_modules/uikit/src/scss/components/base.scss (line 607, column 49)
I would be thankful if one could explain What I´m doing wrong and why sass loader cannot find the variable $inverse-global-color.
I´ve found the answer. It´s working like this:
index.js
...
import style from "./main.scss"
...
main.scss
// 1. Your custom variables and variable overwrites.
$global-link-color: #DA7D02;
// 2. Import default variables and available mixins.
#import "uikit/src/scss/variables-theme.scss";
#import "uikit/src/scss/mixins-theme.scss";
// 3. Your custom mixin overwrites.
#mixin hook-card() { color: #000; }
// 4. Import UIkit.
#import "uikit/src/scss/uikit-theme.scss";
Is there a way to reference a process.env.NODE_ENV in a scss file by passing it to the sass-loader in web-pack. If so anyone know how to go about this ?
Here is my webpack module lodaers array.
module: {
loaders: [
{ test: /\.js?$/,
loader: 'babel-loader',
include: path.join(__dirname, '../app'),
exclude: /node_modules/
},
{ test: /\.scss?$/,
loader: 'style-loader!css-loader!sass-loader',
include: path.join(__dirname, '../app', 'styles')
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
include : path.join(__dirname, '../app', 'images'),
loader : 'file-loader?limit=30000&name=[name].[ext]'
},
{
test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
include : path.join(__dirname, '../app', 'fonts'),
loader: 'file-loader?name=fonts/[name].[ext]'
}
]
I even tried
{ test: /\.scss?$/,
loader: 'style-loader!css-loader!sass-loader',
include: path.join(__dirname, '../app', 'styles'),
options: {
data: "$env: " + process.env.NODE_ENV + ";"
}
}
but the above broke the build.
I just want a way to access a url in my scss file depending on the environment.
It doesn't have to be via webpack any ideas would help without hard coding it.
for example:
.contact-transparent{
width: 100%;
height: 100%;
background: url(process.env.NODE_ENV+'/home-background.jpg') left center no-repeat;
}
I think you could get this done with a custom loader that you chain in before the others for your Sass files. In that loader you would check the value of NODE_ENV and do a find/replace in the Sass source, where the replaced value is based on the env. Not fancy but would get the job done. For example:
// url-by-env-loader.js
module.exports = (source) => {
const urlsByEnv = {
dev: "the-dev-host.com",
staging: "the-staging-host.com",
prod: "the-prod-host.com"
};
const urlToReplace = "the-url-in-your-sass-source.com";
const urlToUse = urlsByEnv[process.env.NODE_ENV];
const replaceRegex = new RegExp(urlToReplace, 'g');
return source.replace(replaceRegex, urlToUse);
}
I'm trying something like:
#for $i from 0 through 5 {
.foo-#{$i} {
padding-left: 16px * $i;
}
}
I get the following error:
CssSyntaxError: app/styles.scss:41:11:
Unknown word You tried to parse SCSS with the standard CSS parser;
try again with the postcss-scss parser
39 |
40 | #for $i from 0 through 5 {
> 41 | .foo-#{$i} {
| ^
42 | padding-left: 16px * $i;
43 | }
And this is the relevant excerpt from my webpack.config.js:
{
test: /\.scss$/,
exclude: /node_modules/,
use: ExtractTextPlugin.extract([
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[local]--[hash:base64:6]'
}
},
{
loader: 'sass-loader'
}
])
}
What would it be the correct webpack configuration to be able to use #{$i} for selector names?
I've tried postcss-loader and some other things, but no clue yet. Any help will be very very appreciated. Thanks!
The problem was in runtime compilation process. Nothing to do with webpack or sass-loader.
require('css-modules-require-hook')({
generateScopedName: '[local]--[hash:base64:6]',
extensions: ['.scss'],
preprocessCss: function (data, filename) {
var result = sass.renderSync({
data: data,
file: filename
}).css;
return result;
}
});
The above worked.
I am new to webpack and I'm trying to implement it in my project. I'm also using Compass for my .scss files.
I included all my scss files in my entrypoint (see the webpack configuration down below) and I have this error:
Error line 1: Function sprite-map doesn't support keyword arguments
The scss file that causes the module build to fail:
$icons: sprite-map("icons/icons-1/k/*.png", $spacing: 10px);
#each $i in sprite_names($icons) {
.k-#{$i}{
background-position: sprite-position($icons, $i, 0, 0, true);
}
}
.k-121, .k-122, .k-123, .k-124, .k-125, .k-126, .k-127, .k-128, .k-129, .k-130, .k-131, .k-132, .k-133, {
background-image: $icons;
background-repeat: no-repeat;
}
But looking at the documentation, it is possible to use keyword arguments, isn't it ?
Here is my webpack.config.js :
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, 'scripts/app.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.s?css$/,
loaders: 'sass-loader'
},
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'file-loader?name=/dist/img/[name].[ext]'
},
{
test: /\.(eot|otf|ttf|woff|woff2)$/,
loader: 'file-loader?name=dist/fonts/[name].[ext]'
}
]
},
devtool: 'source-map',
plugins: [
new HtmlWebpackPlugin()
]
};
Thanks for your help !