JHipster - How to compile SCSS files directly with angular 7 - jhipster

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

Related

Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema

im installing a laravel x vue template (https://pixinvent.com/demo/vuexy-vuejs-admin-dashboard-template/landing/) and while i run npm dev i get this error:
Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.- configuration.module.rules[12].rules should be an array:
My webpack.mix.js looks like this:
const mix = require('laravel-mix')
const path = require('path');
mix
.js('resources/js/app.js', 'public/js')
.webpackConfig({
resolve: {
alias: {
'#resources': path.resolve(__dirname, 'resources/'),
'#': path.resolve(__dirname, 'resources/js/src/'),
'#themeConfig': path.resolve(__dirname, 'resources/js/themeConfig.js'),
'#core': path.resolve(__dirname, 'resources/js/src/#core'),
'#validations': path.resolve(__dirname, 'resources/js/src/#core/utils/validations/validations.js'),
'#axios': path.resolve(__dirname, 'resources/js/src/libs/axios'),
},
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
rules: [
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths: ['node_modules', 'resources/assets'],
},
},
},
],
},
{
enforce: "pre",
test: /(\.(png|jpe?g|gif|webp)$|^((?!font).)*\.svg$)/,
rules: {
loader: 'file-loader',
options: {
name: 'images/[path][name].[ext]',
context: '../vuexy-vuejs-bootstrap-vue-template/src/assets/images',
// context: 'frontend/src/assets/images'
},
},
},
],
},
})
.sass('resources/scss/core.scss', 'public/css')
.options({
postCss: [require('autoprefixer'), require('postcss-rtl')],
})
mix.copy('resources/css/loader.css', 'public/css')
if (mix.inProduction()) {
mix.version()
mix.webpackConfig({
output: {
publicPath: '/demo/vuexy-vuejs-laravel-admin-template/demo-1/',
chunkFilename: 'js/chunks/[name].[chunkhash].js'
}
})
mix.setResourceRoot('/demo/vuexy-vuejs-laravel-admin-template/demo-1/')
}
*/
mix.webpackConfig({
output: {
chunkFilename: 'js/chunks/[name].[chunkhash].js',
},
})
mix.browserSync('http://127.0.0.1:8000/')

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

Undefined .env variables when webpack goes into production mode

I'm using the dot-env NPM package in order to pass simple variables to my webpack/express application.
When I run in PRODUCTION mode for webpack, all my variables from .env become undefined.
I'm building up a development and production webpack config files and currently have the following setup.
Any advise on the mistakes I'm making and why my .env variables are being dropped would be greatly appreciated.
package.json (Scripts)
"buildDev": "rm -rf dist && webpack --mode development --config webpack.server.config.js && webpack --mode development --config webpack.dev.config.js",
"buildProd": "rm -rf dist && webpack --mode production --config webpack.server.config.js && webpack --mode production --config webpack.prod.config.js",
webpack.dev
const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const paths = require("./config/paths");
const isDevelopment = false;
const Dotenv = require('dotenv-webpack');
require('dotenv').config()
module.exports = {
entry: {
main: [
"webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000",
paths.appIndexJs,
],
},
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "[name].js",
},
devServer: {
historyApiFallback: true,
},
mode: "production",
target: "web",
devtool: "#source-map",
module: {
rules: [
{
enforce: "pre",
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "eslint-loader",
options: {
emitWarning: true,
failOnError: false,
failOnWarning: false,
},
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.resolve(paths.appSrc),
loader: "babel-loader",
},
{
// Loads the javacript into html template provided.
// Entry point is set below in HtmlWebPackPlugin in Plugins
test: /\.html$/,
include: path.resolve(paths.appSrc),
use: [
{
loader: "html-loader",
},
],
},
{
test: /\.css$/,
include: path.resolve(paths.appSrc),
use: ["style-loader", "css-loader"],
},
{
test: /\.css$/,
include: /node_modules/,
use: ["style-loader", "css-loader"],
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.(png|svg|jpg|gif|eot|woff|woff2|ttf)$/,
use: ["file-loader"],
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
plugins: [
new HtmlWebPackPlugin({
favicon: "./src/assets/img/favicons/favicon.ico",
template: "./src/html/index.html",
filename: "./index.html",
excludeChunks: ["server"],
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new Dotenv()
],
};
webpack.prod
const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const paths = require("./config/paths");
const isDevelopment = false;
const Dotenv = require('dotenv-webpack');
require('dotenv').config()
module.exports = {
entry: {
main: [
"webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000",
paths.appIndexJs,
],
},
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "[name].js",
},
devServer: {
historyApiFallback: true,
},
mode: "production",
target: "web",
devtool: "#source-map",
module: {
rules: [
{
enforce: "pre",
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "eslint-loader",
options: {
emitWarning: true,
failOnError: false,
failOnWarning: false,
},
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.resolve(paths.appSrc),
loader: "babel-loader",
},
{
// Loads the javacript into html template provided.
// Entry point is set below in HtmlWebPackPlugin in Plugins
test: /\.html$/,
include: path.resolve(paths.appSrc),
use: [
{
loader: "html-loader",
},
],
},
{
test: /\.css$/,
include: path.resolve(paths.appSrc),
use: ["style-loader", "css-loader"],
},
{
test: /\.css$/,
include: /node_modules/,
use: ["style-loader", "css-loader"],
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
/*{
test: /\.scss$/,
use: [
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true }
},
]
},*/
/*{
test: /\.css$/,
include: path.resolve(paths.appSrc),
use: ["style-loader", "css-loader"],
},
{
test: /\.sass$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
]
},
{
test: /\.css$/,
include: /node_modules/,
loader: [
isDevelopment ? "style-loader" : MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true,
sourceMap: isDevelopment,
},
},
],
},
{
test: /\.(scss)$/,
use: [
MiniCssExtractPlugin.loader, { loader: 'css-loader',
options: { url: false, sourceMap:
true } }, { loader: 'sass-loader', options: { sourceMap: true } },
]
},*/
{
test: /\.(png|svg|jpg|gif|eot|woff|woff2|ttf)$/,
//include: path.resolve(paths.appSrc),
use: ["file-loader"],
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
plugins: [
new webpack.DefinePlugin({
'process.env': { 'NODE_ENV': JSON.stringify('production') }
}),
new HtmlWebPackPlugin({
favicon: "./src/assets/img/favicons/favicon.ico",
template: "./src/html/index.html",
filename: "./index.html",
excludeChunks: ["server"],
}),
new webpack.NoEmitOnErrorsPlugin(),
new Dotenv()
],
};
webpack.server
const path = require("path");
const webpack = require("webpack");
const nodeExternals = require("webpack-node-externals");
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = (env, argv) => {
const SERVER_PATH =
argv.mode === "production" ? "./src/server/server-prod.js" : "./src/server/server-dev.js";
return {
entry: {
server: SERVER_PATH,
},
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "[name].js",
},
mode: argv.mode,
target: "node",
node: {
// Need this when working with express, otherwise the build fails
__dirname: false, // if you don't put this is, __dirname
__filename: false, // and __filename return blank or /
},
externals: [nodeExternals()], // Need this to avoid error when working with Express
devServer: {
historyApiFallback: true,
},
module: {
rules: [
{
// Transpiles ES6-8 into ES5
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
}
],
},
};
};
If you look at yow webpack.prod in its plugins section you have it like.
new webpack.DefinePlugin({
'process.env': { 'NODE_ENV': JSON.stringify('production') }
So basically you is setting process.env to be equals to What Eva is there
solution
first you need to install two modules
dotenv-expand
dotenv
$ npm i -D dotenv-expand dotenv
Next
create a new file called env.js, I mean the name is up to you. Place the file same place where yow webpacks are
env.js
'use strict';
const fs = require('fs');
const path = require('path');
const {NODE_ENV} = process.env.NODE_ENV;
const dotenvFile = `.env.${NODE_ENV}`;
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
}));
const appDirectory = fs.realpathSync(process.cwd());
process.env.NODE_PATH = (process.env.NODE_PATH || '')
.split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder))
.map(folder => path.resolve(appDirectory, folder))
.join(path.delimiter);
// set your own prefix
const PREFIX = /^ENETO_/i;
function getEnvironment() {
const raw = Object.keys(process.env)
.filter(key => PREFIX.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key];
return env;
},
{
NODE_ENV: process.env.NODE_ENV || 'development',
PORT: process.env.PORT||3000,
}
);
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
console.log("stringified", stringified);
return { raw, stringified };
}
module.exports = getEnvironment();
NOTE
Make sure to add a prefix so all of them shuld start with a PREFIX
you can set your own prefix
now on each one of yow webpacks add it;
webpack.server.js
/**
webpack server
*/
"use strict";
const path = require("path");
const nodeExternals = require("webpack-node-externals");
const webpack = require("webpack");
/**
* HERE IS IMPORTED ALL THE WAY TO THE BOTTOM YOU'LL SEE IT
*/
const vars = require("./env");
const { NODE_ENV } = process.env;
const entry = [path.join(__dirname, NODE_ENV === "production" ? `../src/prod.ts` : `../src/dev.ts`)];
module.exports = {
target: "node",
name: "server",
externals: [nodeExternals()],
entry: [
require.resolve("#babel/register"),
require.resolve("core-js/proposals"),
require.resolve("core-js"),
require.resolve("#babel/runtime-corejs3/regenerator"),
require.resolve("es6-promise/auto"),
...entry,
],
devtool: false,
mode: process.env.NODE_ENV,
output: {
filename: "[name]-bundle.js",
chunkFilename: "[name].chunk.js",
path: path.resolve(__dirname, "../bundle"),
publicPath: "/",
libraryTarget: "commonjs2",
},
resolve: {
extensions: [".ts", ".tsx", ".js"],
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
},
],
},
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
loader: require.resolve("babel-loader"),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [[require.resolve("#babel/preset-env")]],
cacheDirectory: true,
cacheCompression: false,
sourceMaps: false,
inputSourceMap: false,
},
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "file-loader",
},
],
},
],
},
plugins: [new webpack.DefinePlugin(vars.stringified)],
};
Do the same with yow client and thats it :3
do not need third part deps.
just use env like this:
--node-env=
// in package.json
"build": "webpack --node-env=production --config build/webpack.config.js"
or
module.exports = (env, argv) => {
const isDevelopment = argv.mode !== 'production';
return {
// Config...
}
}
see:
--node-env
argv

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.

Upgrade of Webpack 1 to 4 got so complicated

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

Resources