I'm using scss modules so I'm using styleName
in my react components and everything works in dev
. The elements in prod
looks like <div class="row" stylename="table"></div>
but no styles have been applied to table
.
The webpack configs are split up into multiple files (webpack.profile.js
, webpack.base.js
, webpack.dev.js
, webpack.prod.js
)
Side note: I'm also noticing that certain jenkins job can't process @
variables, not sure if it's related to webpack/sass-loader.
webpack.profile.js
const path = require('path')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CompressionPlugin = require('compression-webpack-plugin')
const VisualizerPlugin = require('webpack-visualizer-plugin')
module.exports = require('./webpack.base')({
mode: 'production',
devServer: {
port: 3000,
contentBase: path.join(process.cwd(), 'dist/')
},
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true
}),
new OptimizeCSSAssetsPlugin()
]
},
module: {
rules: [
{
test: /.(sc|sa|c)ss$/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{
loader: "css-loader",
options: {
modules: true,
sourceMap: true,
localIdentName: "[name]_[local]_[hash:base64:5]"
}
}
]
}
]
},
plugins: [
new CompressionPlugin({
test: /.(js|css|html)$/
}),
new VisualizerPlugin({
filename: '../stats/bundleStats.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css'
})
]
})
webpack.base.js
const path = require('path')
const Dotenv = require('dotenv-webpack')
const HtmlWebPackPlugin = require('html-webpack-plugin')
module.exports = options => {
let envPath = '.env'
process.argv.forEach(val => {
if (val.includes('--env=')) {
const curEnv = val.slice(6)
if (['dev', 'stg'].includes(curEnv)) {
envPath = `.env.${curEnv}`
}
}
})
return {
mode: options.mode,
devServer: options.devServer,
entry: [path.join(process.cwd(), 'src/main.js')],
output: {
path: path.join(__dirname, '../dist/'),
publicPath: '/',
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].js'
},
resolve: {
extensions: ['.js', '.jsx'],
modules: [
path.join(__dirname, '../src'),
path.join(__dirname, '../node_modules')
],
alias: {
'@constants': path.join(__dirname, '../src/constants'),
'@c': path.join(__dirname, '../src/components'),
'@C': path.join(__dirname, '../src/containers')
}
},
plugins: options.plugins.concat([
new Dotenv({
path: path.join(process.cwd(), envPath)
}),
new HtmlWebPackPlugin({
inject: true,
template: path.join(__dirname, '../src/static/index.html'),
favicon: path.join(__dirname, '../src/static/favicon.ico')
})
]),
module: {
rules: options.module.rules.concat([
// {
// enforce: 'pre',
// test: /.jsx?$/,
// exclude: /node_modules/,
// use: [
// {
// loader: 'eslint-loader',
// options: {
// quiet: true
// }
// }
// ]
// },
{
test: /.(woff|woff2|eot|ttf|otf)$/,
exclude: [ /.scss$/ ],
use: [
{
loader: 'file-loader',
options: {
name: '[hash].[ext]',
outputPath: 'fonts'
}
}
]
},
{
test: /.svg$/,
loader: 'svg-react-loader'
},
{
test: /.(gif|png|jpe?g)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 10 * 1024,
name: '[hash].[ext]',
outputPath: 'assets'
}
},
{
loader: 'image-webpack-loader',
options: {
disable: options.mode === 'development'
}
}
]
}
])
}
}
}
webpack.dev.js
const webpack = require('webpack')
const BrowserSyncPlugin = require('browser-sync-webpack-plugin')
module.exports = require('./webpack.base')({
mode: 'development',
devServer: {
hot: true,
port: 3000,
historyApiFallback: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new BrowserSyncPlugin(
{ proxy: 'http://localhost:3000/', open: false },
{ reload: false }
)
],
module: {
rules: [
{
test: /.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
plugins: [
[
'react-css-modules',
{
"filetypes": {
".scss": { "syntax": "postcss-scss" }
},
"generateScopedName": '[name]_[local]_[hash:base64:5]'
}
],
],
},
},
resolve: {
extensions: ['.js', '.jsx']
}
},
{
test: /.(sa|sc)ss$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
localIdentName: '[name]_[local]_[hash:base64:5]'
}
},
{
loader: 'sass-loader'
}
]
},
{
test: /.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
]
}
})
webpack.prod.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = require('./webpack.base')({
mode: 'production',
devServer: {
port: 3000,
contentBase: path.join(process.cwd(), 'dist/')
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
cache: true,
parallel: true
}),
new OptimizeCSSAssetsPlugin()
]
},
module: {
rules: [
{
test: /.jsx?$/,
exclude: /node_modules/@babel/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
'targets': {
'ie': '11'
},
'loose': true,
'forceAllTransforms': true
}
],
'@babel/preset-react'
],
sourceType: 'unambiguous',
plugins: [
[
'@babel/plugin-proposal-decorators',
{
'legacy': true
}
],
'@babel/plugin-transform-runtime',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-proposal-function-bind',
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-export-default-from',
'@babel/plugin-proposal-export-namespace-from'
]
}
},
resolve: {
extensions: ['.js', '.jsx']
}
},
{
test: /.(sc|sa)ss$/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{
loader: "css-loader",
options: {
modules: true,
sourceMap: true,
localIdentName: "[name]_[local]_[hash:base64:5]"
}
},
{ loader: "postcss-loader" },
{ loader: "sass-loader" }
]
},
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin([path.join(process.cwd(), '/dist')], {
allowExternal: true
}),
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css'
}),
new CompressionPlugin({
test: /.(js|css)$/,
filename: asset => asset.file
})
]
})
See Q