Upgrade of Webpack 1 to 4 got so complicated - node.js

I have been stuck with this upgrade since 2 days and I have made some progress.
It is an old project and I have to take it a bit forward.
This is my webpack.config.js file:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
//const validate = require('webpack-validator');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const PATHS = {
app: path.join(__dirname, 'src/js'),
build: path.join(__dirname, 'build'),
public: path.join(__dirname, 'src/public'),
assets: path.join(__dirname, 'src/assets'),
styles: [
path.join(__dirname, 'src/assets/css/bootstrap.min.css'),
path.join(__dirname, 'src/assets/css/font-awesome.min.css'),
path.join(__dirname, 'src/assets/css/bootstrap-grid-rtl.css'),
path.join(__dirname, 'src/assets/css/main.css'),
path.join(__dirname, 'src/assets/css/PrintStyle.css'),
path.join(__dirname, 'node_modules/react-dates/lib/css/_datepicker.css'),
path.join(__dirname, 'node_modules/flag-icon-css/css/flag-icon.css'),
path.join(__dirname, 'src/assets/css/react-datepicker.css'),
path.join(__dirname, 'src/assets/css/tracker.css'),
]
};
const isDebug = !process.argv.includes('--release');
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
uglifyOptions: {
compress: false,
ecma: 6,
mangle: true
},
sourceMap: true
})
],
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
}
},
entry: {
app: ['babel-polyfill',PATHS.app],
style: PATHS.styles
},
output: {
path: PATHS.build,
filename: 'js/[name].js',
},
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
use:
{
loader: 'babel-loader'
}
,
include: [
path.resolve(__dirname, PATHS.app),
],
query: {
cacheDirectory: isDebug,
babelrc: true,
presets: ['latest', 'react',
...isDebug ? [] : [
'react-optimize',
],
],
plugins: [
'transform-object-rest-spread',
'transform-object-assign',
[
'react-intl', {
'messagesDir': './build/messages',
'enforceDescriptions': false
}
]
]
},
},
{
test: /\.css$/,
use:
{
loader: ExtractTextPlugin.extract(
'style-loader',
`css-loader?${JSON.stringify({
importLoaders: 1,
sourceMap: true,
modules: true,
localIdentName: isDebug ? '[name]-[local]-[hash:base64:5]' : '[hash:base64:5]',
discardComments: { removeAll: true },
})}`,
'resolve-url-loader',
'postcss-loader?pack=default'
)
}
,
exclude: PATHS.styles,
},
{
test: /\.css$/,
use: [
{
loader:
ExtractTextPlugin.extract(
'style-loader',
`css-loader?${JSON.stringify({
localIdentName: isDebug ? '[name]-[local]-[hash:base64:5]' : '[hash:base64:5]',
minimize: true,
discardComments: { removeAll: true },
})}`,
'resolve-url-loader'
)
}
],
exclude: PATHS.app,
include: PATHS.styles
},
{
test: /\.sss$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
}
]
},
{
test: /\.json$/,
use: [
{
loader: 'json-loader'
}
]
},
{
test: /\.(eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
use: [
{
loader:
`file-loader?${JSON.stringify({
name: isDebug ? '/[path][name].[ext]?[hash:8]' : '/fonts/[hash:8].[ext]',
})}`
}
]
},
{
test: /\.(ico|jpg|jpeg|png|gif)(\?.*)?$/,
use: [
{
loader:
`file-loader?${JSON.stringify({
name: isDebug ? '/[path][name].[ext]?[hash:8]' : '/images/[hash:8].[ext]'
})}`
}
]
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
'process.env.BROWSER': true,
__DEV__: isDebug,
}),
new ExtractTextPlugin('[name].css', {allChunks: true}),
new OptimizeCssAssetsPlugin({
cssProcessorOptions: { discardComments: {removeAll: true } },
}),
new HtmlWebpackPlugin({
title: 'GACA Portal',
template: path.join(PATHS.public, 'index.ejs'),
favicon: path.join(PATHS.assets, 'images/favicon.ico'),
}),
...isDebug? [
new webpack.HotModuleReplacementPlugin({
multiStep: true
}),
] : [
new CleanWebpackPlugin(PATHS.build, {
root: process.cwd()
}),
new webpack.optimize.OccurrenceOrderPlugin(true),
new webpack.optimize.DedupePlugin()
] //else ends
],
// Don't attempt to continue if there are any errors.
bail: !isDebug,
cache: false,
stats: {
colors: true,
reasons: isDebug,
timings: true,
},
devtool: isDebug ? 'cheap-module-source-map' : false,
devServer: {
historyApiFallback: true,
hot: true,
inline: true,
stats: 'errors-only',
port: 3000,
host: '0.0.0.0',
publicPath: '/',
contentBase: PATHS.build,
proxy: {
'/api/**': {
target: 'http://localhost:8080',
secure: false
}
}
},
/*postcss: [
require('postcss-partial-import')(),
require('postcss-url')(),
require('postcss-custom-properties')(),
require('postcss-custom-media')(),
require('postcss-media-minmax')(),
require('postcss-custom-selectors')(),
require('autoprefixer')({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
})
]*/
};
Now I am trying to npm start but I still getting this error:
× 「wds」: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.module.rules[2].use should be one of these:
non-empty string | function | object { ident?, loader?, options?, query? } | function | [non-empty string | function | object { ident?, loader?, options?, query? }]
-> Modifiers applied to the module when rule is matched
Details:
* configuration.module.rules[1].use should be a string.
* configuration.module.rules[1].use should be an instance of function
* configuration.module.rules[1].use.loader should be a string.
* configuration.module.rules[1].use should be an instance of function
* configuration.module.rules[1].use should be an array:
[non-empty string | function | object { ident?, loader?, options?, query? }]
* configuration.module.rules[2].use should be a string.
* configuration.module.rules[2].use should be an instance of function
* configuration.module.rules[2].use should be an object.
* configuration.module.rules[2].use should be an instance of function
* configuration.module.rules[2].use[0] should be a string.
* configuration.module.rules[2].use[0] should be an instance of function
* configuration.module.rules[2].use[0].loader should be a string.
- configuration.resolve.extensions[0] should not be empty.
Any help would be very helpful

Here is a step by step guide from Webpack.
https://webpack.js.org/migrate/
If you have plugin which is not provided by webpack, please go and check on the repo.

I'd recommend first upgrading to version 2 or 3 https://webpack.js.org/migrate/3/ , you will have clear documentation and you will find great help in Google ...
And only then continue to version 4 https://webpack.js.org/migrate/4/

I just left it away and create a new project with create app cli and copied the content and formatted the structure

Related

Webpack does not transpile TailwindCss used in SCSS

I am trying to use scss with tailwindcss, but I cannot get webpack to transpile the tailwind code into destination site.css.
This is my scss used.
_base.scss
#import "tailwindcss/base";
#import "tailwindcss/components";
#import "tailwindcss/utilities";
// Also tried below
#import '~tailwindcss/base.css';
#import '~tailwindcss/components.css';
#import '~tailwindcss/utilities.css';
Once transpiled, I expected the file to have bunch of tailwind styling, like below:
site.css
from-green-500{--gradient-from-color:#48bb78;--gradient-color-stops:var(--gradient-from-color),var(--gradient-to-color,rgba(72,187,120,0))}.from-green-600{--gradient-from-color:#38a169;--gradient-color-stops:var(--gradient-from-color),var(--gradient-to-color,rgba(56,161,105,0))}...
But instead, I got the raw import statements below.
#tailwind base;#tailwind components;#tailwind utilities; ...
My webpack.common.js is below. Does anyone have suggestions on how to properly get the actual transpiled the css content into site.css?
webpack.common.js
'use strict';
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TSConfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');
const SOURCE_ROOT = __dirname + '/src/main/webpack';
const resolve = {
extensions: ['.js', '.ts'],
plugins: [new TSConfigPathsPlugin({
configFile: './tsconfig.json'
})]
};
module.exports = {
resolve: resolve,
entry: {
site: SOURCE_ROOT + '/site/main.ts'
},
output: {
filename: (chunkData) => {
return chunkData.chunk.name === 'dependencies' ? 'clientlib-dependencies/[name].js' : 'clientlib-site/[name].js';
},
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
},
{
loader: 'glob-import-loader',
options: {
resolve: resolve
}
}
]
},
{
test: /\.(sc|sa|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: false
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('autoprefixer')]
}
}
},
{
loader: 'sass-loader',
},
{
loader: 'glob-import-loader',
options: {
resolve: resolve
}
}
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new ESLintPlugin({
extensions: ['js', 'ts', 'tsx']
}),
new MiniCssExtractPlugin({
filename: 'clientlib-[name]/[name].css'
}),
new CopyWebpackPlugin({
patterns: [
{ from: path.resolve(__dirname, SOURCE_ROOT + '/resources'), to: './clientlib-site/' }
]
})
],
stats: {
assetsSort: 'chunks',
builtAt: true,
children: false,
chunkGroups: true,
chunkOrigins: true,
colors: false,
errors: true,
errorDetails: true,
env: true,
modules: false,
performance: true,
providedExports: false,
source: false,
warnings: true
}
};

Uncaught TypeError: Cannot read property 'split' of undefined (Webpack)

I'm making an update to a Chrome Extension (using Create React App) of mine, but suddenly I get this error:
Uncaught TypeError: Cannot read property 'split' of undefined
For the following code:
/* WEBPACK VAR INJECTION */(function(process) {
Object.defineProperty(exports, "__esModule", { value: true });
const defer_to_connect_1 = __webpack_require__(1533);
const nodejsMajorVersion = Number(process.versions.node.split('.')[0]);
The error occurs in the last line for (process.versions.node.split('.')[0] and the code is generated by Webpack.
Here is my Webpack Config:
"use strict";
const autoprefixer = require("autoprefixer");
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ManifestPlugin = require("webpack-manifest-plugin");
const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin");
const SWPrecacheWebpackPlugin = require("sw-precache-webpack-plugin");
const eslintFormatter = require("react-dev-utils/eslintFormatter");
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const paths = require("./paths");
const getClientEnvironment = require("./env");
const publicPath = paths.servedPath;
const shouldUseRelativeAssetPaths = publicPath === "./";
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
const publicUrl = publicPath.slice(0, -1);
const env = getClientEnvironment(publicUrl);
if (env.stringified["process.env"].NODE_ENV !== '"production"') {
throw new Error("Production builds must have NODE_ENV=production.");
}
const cssFilename = "static/css/[name].css";
const extractTextPluginOptions = shouldUseRelativeAssetPaths
? { publicPath: Array(cssFilename.split("/").length).join("../") }
: {};
module.exports = {
mode: "production",
externals: ["dns"],
bail: true,
devtool: shouldUseSourceMap ? "inline-source-map" : false,
entry: {
app: [require.resolve("./polyfills"), paths.appIndexJs],
content: [require.resolve("./polyfills"), "./src/content.js"],
},
optimization: {
minimize: false,
},
output: {
path: paths.appBuild,
filename: "static/js/[name].js",
chunkFilename: "static/js/[name].[chunkhash:8].chunk.js",
publicPath: publicPath,
devtoolModuleFilenameTemplate: (info) =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, "/"),
},
resolve: {
modules: ["node_modules", paths.appNodeModules].concat(
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
extensions: [".web.js", ".mjs", ".js", ".json", ".web.jsx", ".jsx"],
alias: {
"react-native": "react-native-web",
},
plugins: [new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson])],
},
module: {
strictExportPresence: true,
rules: [
{
test: /\.(js|jsx|mjs)$/,
enforce: "pre",
use: [
{
options: {
formatter: eslintFormatter,
eslintPath: require.resolve("eslint"),
},
loader: require.resolve("eslint-loader"),
},
],
include: paths.appSrc,
},
{
oneOf: [
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"),
options: {
limit: 10000,
name: "static/media/[name].[hash:8].[ext]",
},
},
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
loader: require.resolve("babel-loader"),
options: {
compact: true,
},
},
{
test: /\.css$/,
use: [{ loader: MiniCssExtractPlugin.loader }, "css-loader"],
},
{
loader: require.resolve("file-loader"),
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
options: {
name: "static/media/[name].[hash:8].[ext]",
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
new webpack.DefinePlugin(env.stringified),
new MiniCssExtractPlugin({
filename: cssFilename,
}),
new ManifestPlugin({
fileName: "asset-manifest.json",
}),
new SWPrecacheWebpackPlugin({
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: "service-worker.js",
logger(message) {
if (message.indexOf("Total precache size is") === 0) {
return;
}
if (message.indexOf("Skipping static resource") === 0) {
return;
}
console.log(message);
},
minify: true,
navigateFallback: publicUrl + "/index.html",
navigateFallbackWhitelist: [/^(?!\/__).*/],
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
node: {
dgram: "empty",
fs: "empty",
net: "empty",
tls: "empty",
child_process: "empty",
},
};
I've already tried updating the dependencies, but with little success. What could be the problem?

Much older version of my Website shows when rebuilding Production Bundle with Webpack

I've noticed that when I rebuild my production bundle of my node.js app using webpack, the site in the browser reverts to a way older version (300+ git commits later). I use pm2 to run my website on my VPS.
How to keep the oldest version live until the new production build is done?
This is my webpack setup:
const { resolve } = require('path');
const webpack = require('webpack');
const CompressionPlugin = require('compression-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const cssOutputLocation = process.env.NODE_ENV === 'production' ?
'public/stylesheets/style-prod.css' :
'stylesheets/style.css';
const jsProdOutput = {
filename: 'public/javascripts/build-prod.js',
path: resolve(__dirname),
publicPath: '/',
};
const jsDevOutput = {
filename: 'javascripts/build.js',
path: '/',
publicPath: '/',
};
const jsOutputLocation = process.env.NODE_ENV === 'production' ? jsProdOutput : jsDevOutput;
module.exports = {
context: resolve(__dirname, 'src'),
entry: [
'./index.jsx',
],
output: jsOutputLocation,
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components|public\/)/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'es2017'],
},
},
{
test: /\.js?$/,
exclude: /(node_modules|bower_components|public\/)/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'es2017'],
},
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader',
}),
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
},
],
fallback: 'style-loader',
}),
},
],
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new ExtractTextPlugin(cssOutputLocation),
new BundleAnalyzerPlugin(),
new webpack.ContextReplacementPlugin(/^\.\/locale$/, (context) => {
if (!/\/moment\//.test(context.context)) { return; }
// context needs to be modified in place
Object.assign(context, {
// include only CJK
regExp: /^\.\/(nl|en-gb)/,
// point to the locale data folder relative to moment's src/lib/locale
request: '../../locale',
});
}),
],
};
if (process.env.NODE_ENV === 'production') {
module.exports.plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
screw_ie8: true,
conditionals: true,
unused: true,
comparisons: true,
sequences: true,
dead_code: true,
evaluate: true,
if_return: true,
join_vars: true,
},
output: {
comments: false,
},
}));
module.exports.plugins.push(new webpack.HashedModuleIdsPlugin());
module.exports.plugins.push(new webpack.optimize.ModuleConcatenationPlugin());
module.exports.plugins.push(
new webpack.DefinePlugin(
{ 'process.env.NODE_ENV': JSON.stringify('production') },
),
);
module.exports.plugins.push(
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.js$|\.css$|\.html$|\.eot?.+$|\.ttf?.+$|\.woff?.+$|\.svg?.+$/,
threshold: 10240,
minRatio: 0.8,
}),
);
}
if (process.env.NODE_ENV !== 'production') {
module.exports.entry.unshift(
'react-hot-loader/patch',
'react-hot-loader/babel',
'webpack-hot-middleware/client',
);
module.exports.plugins.unshift(new webpack.HotModuleReplacementPlugin());
}
You can use the hash placeholder in the filename prop of your output config.
const jsProdOutput = {
filename: 'public/javascripts/build-prod-[hash].js',
path: resolve(__dirname),
publicPath: '/',
};
With this your generated files will be unique on every build and will not be overridden.

Sass relative import with webpack

I'm trying to import the SCSS files relative to the component in the same folder but ex:
import './ngg.scss';
The strange thing is when I change the path of this SCSS file or using dev server it's working fine but when I run it for the production it doesn't work as if the SCSS file doesn't exist, I'm using web-pack 4, I can't see the issue really because there are some components use relative import and it's working fine, please help!!
module.exports = function makeWebpackConfig(options) {
var BUILD = options.environment === 'prod';
var DEV_SERVER = process.env.WEBPACK_MODE === 'watch';
var config = {
mode: BUILD ? 'production' : 'development',
optimization: {
chunkIds: 'named',
moduleIds: 'hashed',
namedModules: true,
noEmitOnErrors: true,
usedExports: true,
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
},
desktop: {
test: /Resources\/desktop\/assets\/js/,
name: 'common-desktop',
chunks: 'initial',
minChunks: 5
},
mobile: {
test: /Resources\/mobile\/assets\/js/,
name: 'common-mobile',
chunks: 'initial',
minChunks: 5
}
}
},
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true
})
]
},
entry: options.entry,
resolve: {
alias: options.alias,
extensions: ['.js', '.jsx'],
modules: ['node_modules']
},
output: {
path: options.parameters.path ? options.parameters.path : __dirname + '/../../web/compiled/',
publicPath: publicPath,
filename: BUILD ? '[name].[contenthash].js' : '[name].[hash].js',
chunkFilename: BUILD ? '[name].[contenthash].js' : '[name].[hash].js'
},
devServer: {
disableHostCheck: true,
overlay: {
warnings: true,
errors: true
},
headers: {
'Access-Control-Allow-Origin': '*'
}
}
};
config.module = {
rules: [
{
test: /\.jsx?$/i,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
minified: !DEV_SERVER,
cacheDirectory: DEV_SERVER,
presets: ['es2015', 'react']
}
},
{
test: /\.(gif|png|jpe?g)(\?.*)?$/i,
enforce: 'pre',
loader: 'image-webpack-loader',
options: options.parameters.image_loader_options || !DEV_SERVER ? {
optipng: {
optimizationLevel: 7,
progressive: true
}
} : {
disable: true
}
},
{
test: /\.(png|jpg|jpeg|gif|svg)(\?.*)?$/i,
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]'
}
},
{
test: /\.(eot|ttf|woff|woff2)$/,
loader: 'url-loader',
},
{
test: /\.html$/i,
loader: 'raw-loader'
},
{
test: /\.(css|scss)$/i,
use: [
DEV_SERVER ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
minimize: false,
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
plugins: function () {
return [
autoprefixer({
browsers: ['last 2 version']
})
];
}
}
},
]
},
{
test: /\.scss$/i,
loader: 'sass-loader?sourceMap',
options: {
enforce: 'pre'
}
}
]
};
config.plugins = [
new MiniCssExtractPlugin({
filename: BUILD ? '[name].[id].[chunkhash].css' : '[name].[hash].css'
}),
new PostCssPipelineWebpackPlugin({
suffix: 'critical',
processor: postcss([
criticalSplit({
output: criticalSplit.output_types.CRITICAL_CSS
})
])
}),
new PostCssPipelineWebpackPlugin({
suffix: 'rest',
processor: postcss([
criticalSplit({
output: criticalSplit.output_types.REST_CSS
})
])
}),
new PostCssPipelineWebpackPlugin({
suffix: 'min',
processor: postcss([
csso({
restructure: false,
comments: false
})
]),
map: {
inline: false
}
}),
new AssetsPlugin({
filename: path.basename(options.manifest_path),
path: path.dirname(options.manifest_path)
}),
new webpack.ProvidePlugin({
React: "react",
ReactDOM: "react-dom",
$: "jquery",
jQuery: "jquery"
})
];
if (BUILD) {
config.devtool = 'nosources-source-map';
} else {
config.devtool = 'source-map';
}
return config;
};
Below is my webpack config
module: {
rules: [{
test: /\.scss$/,
use: [
'style-loader',
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
minimize: true,
sourceMap: true
}
},
{
loader: "sass-loader"
}
]
}
]
}
Then in my js I only need to import like this
import "css/Client/client.scss";
You can view my full config here

JHipster - How to compile SCSS files directly with angular 7

Normaly it is really easy to add SCSS styles to components with angular components (just create the scss file and import it in the component.ts), however this styles are not rendered to normal css files, the styles are embeded (from what i understand, i am very new with angular).
This creates a problem, i am trying to use a theme with some dinamic skins with a "customization panel", but this component needs the path to my compiled css indepedently.
To achieve this, in the vendor basic app, i can see they added this to the angular.js:
{
...
"projects": {
"angular-starter": {
...,
"architect": {
"build": {
"builder": "#angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/angular-starter",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss",
{ "input": "src/vendor/styles/appwork.scss", "bundleName": "vendor/styles/appwork", "lazy": true },
{ "input": "src/vendor/styles/appwork-material.scss", "bundleName": "vendor/styles/appwork-material", "lazy": true },
{ "input": "src/vendor/styles/bootstrap.scss", "bundleName": "vendor/styles/bootstrap", "lazy": true },
{ "input": "src/vendor/styles/bootstrap-material.scss", "bundleName": "vendor/styles/bootstrap-material", "lazy": true },
// More styles like this
],
...
And then it can be referenced directly as css file, as the index shows:
<html lang="en" class="default-style">
<head>
...
<!-- Here it references the compiled scss as css directly -->
<link rel="stylesheet" href="vendor/styles/bootstrap.css" class="theme-settings-bootstrap-css">
<link rel="stylesheet" href="vendor/styles/appwork.css" class="theme-settings-appwork-css">
<link rel="stylesheet" href="vendor/styles/theme-corporate.css" class="theme-settings-theme-css">
<link rel="stylesheet" href="vendor/styles/colors.css" class="theme-settings-colors-css">
<link rel="stylesheet" href="vendor/styles/uikit.css">
...
<script>
// Here uses the path of the compiled css as parameter,
// this way the skin selector changes the css used in the page
window.themeSettings = new ThemeSettings({
cssPath: 'vendor/styles/',
themesPath: 'vendor/styles/'
});
</script>
</head>
...
</html>
However
Cheking the angular.js generated from jhipster, i can see the architect part is empty:
{
"$schema": "./node_modules/#angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"consuldent": {
"root": "",
"sourceRoot": "src/main/webapp",
"projectType": "application",
"architect": {}
}
},
I do not know if this is because in the example code it uses ng serve to run the demo page, using a node.js server, where jhipster uses spring directly, i tried adding the styles part to the jhipster's angular.js file, but i can not find any route where the css could be loaded, so im guessing it is just ignoring the code that i added
In angular, the view is encapsulated with each unique attribute.
add encapsulation: ViewEncapsulation.None in main component, hence the css will be shared by all components
import { Component, ViewEncapsulation } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
name = 'Angular app';
}
Jhipster uses webpack to pack the angular modules. You want to bundle the css hence
copy your css into /src/main/webapp/vendor/styles/appwork.css'
add an entry in webpack.common.js
new CopyWebpackPlugin([
{ from: './node_modules/swagger-ui/dist/css', to: 'swagger-ui/dist/css' },
{ from: './node_modules/swagger-ui/dist/lib', to: 'swagger-ui/dist/lib' },
{ from: './node_modules/swagger-ui/dist/swagger-ui.min.js', to: 'swagger-ui/dist/swagger-ui.min.js' },
{ from: './src/main/webapp/swagger-ui/', to: 'swagger-ui' },
{ from: './src/main/webapp/content/', to: 'content' },
{ from: './src/main/webapp/vendor/styles/appwork.css', to: 'content/vendor/styles/appwork.css' },
{ from: './src/main/webapp/favicon.ico', to: 'favicon.ico' },
{ from: './src/main/webapp/manifest.webapp', to: 'manifest.webapp' },
// jhipster-needle-add-assets-to-webpack - JHipster will add/remove third-party resources in this array
{ from: './src/main/webapp/robots.txt', to: 'robots.txt' }
]),
To compile custom sass
Add the entry in webpack.prod.js , for example myfile and myfile2. Then change the sass compiler to include our myfile.scss, myfile2.scss, finally comment the MiniCssExtractPlugin
entry: {
polyfills: './src/main/webapp/app/polyfills',
global: './src/main/webapp/content/scss/global.scss',
myfile: './src/main/webapp/content/scss/myfile.scss',
myfile2: './src/main/webapp/content/scss/myfile2.scss',
main: './src/main/webapp/app/app.main'
},
.
.
.
.
exclude: /(vendor\.scss|global\.scss|myfile\.scss|myfile2\.scss)/
.
.
.
.
test: /(vendor\.scss|global\.scss|myfile\.scss|myfile2\.scss)/
.
.
.
.
exclude: /(vendor\.css|global\.css|myfile\.css|myfile2\.css)/
.
.
.
.
test: /(vendor\.css|global\.css|myfile\.css|myfile2\.css)/,
.
.
.
.
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
// filename: '[name].[contenthash].css',
// chunkFilename: '[id].css'
}),
Your webpack.prod.js file look like this
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const Visualizer = require('webpack-visualizer-plugin');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
const AngularCompilerPlugin = require('#ngtools/webpack').AngularCompilerPlugin;
const path = require('path');
const utils = require('./utils.js');
const commonConfig = require('./webpack.common.js');
const ENV = 'production';
const sass = require('sass');
module.exports = webpackMerge(commonConfig({ env: ENV }), {
// Enable source maps. Please note that this will slow down the build.
// You have to enable it in UglifyJSPlugin config below and in tsconfig-aot.json as well
// devtool: 'source-map',
entry: {
polyfills: './src/main/webapp/app/polyfills',
global: './src/main/webapp/content/scss/global.scss',
myfile: './src/main/webapp/content/scss/myfile.scss',
myfile2: './src/main/webapp/content/scss/myfile2.scss',
main: './src/main/webapp/app/app.main'
},
output: {
path: utils.root('build/www'),
filename: 'app/[name].[hash].bundle.js',
chunkFilename: 'app/[id].[hash].chunk.js'
},
module: {
rules: [{
test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
loader: '#ngtools/webpack'
},
{
test: /\.scss$/,
use: ['to-string-loader', 'css-loader', {
loader: 'sass-loader',
options: { implementation: sass }
}],
exclude: /(vendor\.scss|global\.scss|myfile\.scss|myfile2\.scss)/
},
{
test: /(vendor\.scss|global\.scss|myfile\.scss|myfile2\.scss)/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: { implementation: sass }
}
]
},
{
test: /\.css$/,
use: ['to-string-loader', 'css-loader'],
exclude: /(vendor\.css|global\.css|myfile\.css|myfile2\.css)/
},
{
test: /(vendor\.css|global\.css|myfile\.css|myfile2\.css)/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
}]
},
optimization: {
runtimeChunk: false,
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
},
minimizer: [
new TerserPlugin({
parallel: true,
cache: true,
terserOptions: {
ie8: false,
// sourceMap: true, // Enable source maps. Please note that this will slow down the build
compress: {
dead_code: true,
warnings: false,
properties: true,
drop_debugger: true,
conditionals: true,
booleans: true,
loops: true,
unused: true,
toplevel: true,
if_return: true,
inline: true,
join_vars: true
},
output: {
comments: false,
beautify: false,
indent_level: 2
}
}
}),
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
// filename: '[name].[contenthash].css',
// chunkFilename: '[id].css'
}),
new MomentLocalesPlugin({
localesToKeep: [
'en',
'es'
// jhipster-needle-i18n-language-moment-webpack - JHipster will add/remove languages in this array
]
}),
new Visualizer({
// Webpack statistics in target folder
filename: '../stats.html'
}),
new AngularCompilerPlugin({
mainPath: utils.root('src/main/webapp/app/app.main.ts'),
tsConfigPath: utils.root('tsconfig-aot.json'),
sourceMap: true
}),
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true,
})
],
mode: 'production'
});
similarly do the same step for webpack.dev.js
const webpack = require('webpack');
const writeFilePlugin = require('write-file-webpack-plugin');
const webpackMerge = require('webpack-merge');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const SimpleProgressWebpackPlugin = require('simple-progress-webpack-plugin');
const WebpackNotifierPlugin = require('webpack-notifier');
const path = require('path');
const sass = require('sass');
const utils = require('./utils.js');
const commonConfig = require('./webpack.common.js');
const ENV = 'development';
module.exports = (options) => webpackMerge(commonConfig({ env: ENV }), {
devtool: 'eval-source-map',
devServer: {
contentBase: './build/www',
proxy: [{
context: [
/* jhipster-needle-add-entity-to-webpack - JHipster will add entity api paths here */
'/api',
'/management',
'/swagger-resources',
'/v2/api-docs',
'/h2-console',
'/auth'
],
target: `http${options.tls ? 's' : ''}://127.0.0.1:8080`,
secure: false,
changeOrigin: options.tls,
headers: { host: 'localhost:9000' }
}],
stats: options.stats,
watchOptions: {
ignored: /node_modules/
}
},
entry: {
polyfills: './src/main/webapp/app/polyfills',
global: './src/main/webapp/content/scss/global.scss',
myfile: './src/main/webapp/content/scss/myfile.scss',
myfile2: './src/main/webapp/content/scss/myfile2.scss',
main: './src/main/webapp/app/app.main'
},
output: {
path: utils.root('build/www'),
filename: 'app/[name].bundle.js',
chunkFilename: 'app/[id].chunk.js'
},
module: {
rules: [{
test: /\.ts$/,
enforce: 'pre',
loader: 'tslint-loader',
exclude: [/(node_modules)/, new RegExp('reflect-metadata\\' + path.sep + 'Reflect\\.ts')]
},
{
test: /\.ts$/,
use: [
'angular2-template-loader',
{
loader: 'cache-loader',
options: {
cacheDirectory: path.resolve('build/cache-loader')
}
},
{
loader: 'thread-loader',
options: {
// there should be 1 cpu for the fork-ts-checker-webpack-plugin
workers: require('os').cpus().length - 1
}
},
{
loader: 'ts-loader',
options: {
transpileOnly: true,
happyPackMode: true
}
},
'angular-router-loader'
],
exclude: /(node_modules)/
},
{
test: /\.scss$/,
use: ['to-string-loader', 'css-loader', {
loader: 'sass-loader',
options: { implementation: sass }
}],
exclude: /(vendor\.scss|global\.scss|myfile\.scss|myfile2\.scss)/
},
{
test: /(vendor\.scss|global\.scss|myfile\.scss|myfile2\.scss)/,
use: ['style-loader', 'css-loader', 'postcss-loader', {
loader: 'sass-loader',
options: { implementation: sass }
}]
},
{
test: /\.css$/,
use: ['to-string-loader', 'css-loader'],
exclude: /(vendor\.css|global\.css|myfile\.css|myfile2\.css)/
},
{
test: /(vendor\.css|global\.css|myfile\.css|myfile2\.css)/,
use: ['style-loader', 'css-loader']
}]
},
stats: process.env.JHI_DISABLE_WEBPACK_LOGS ? 'none' : options.stats,
plugins: [
process.env.JHI_DISABLE_WEBPACK_LOGS
? null
: new SimpleProgressWebpackPlugin({
format: options.stats === 'minimal' ? 'compact' : 'expanded'
}),
new FriendlyErrorsWebpackPlugin(),
new ForkTsCheckerWebpackPlugin(),
new BrowserSyncPlugin({
host: 'localhost',
port: 9000,
proxy: {
target: 'http://localhost:9060'
},
socket: {
clients: {
heartbeatTimeout: 60000
}
}
}, {
reload: false
}),
new webpack.ContextReplacementPlugin(
/angular(\\|\/)core(\\|\/)/,
path.resolve(__dirname, './src/main/webapp')
),
new writeFilePlugin(),
new webpack.WatchIgnorePlugin([
utils.root('src/test'),
]),
new WebpackNotifierPlugin({
title: 'JHipster',
contentImage: path.join(__dirname, 'logo-jhipster.png')
})
].filter(Boolean),
mode: 'development'
});
Then if you want the css to be include in your index.html then change the webpack.common.js
new HtmlWebpackPlugin({
template: './src/main/webapp/index.html',
chunks: ['vendors', 'polyfills', 'main', 'global','myfile','myfile2'],
chunksSortMode: 'manual',
inject: 'body'
})
Add your custom styles to src/main/webapp/content/scss/global.scss

Resources