Storybook UI with CSS modules and Less - reactjs

Is it possible to use Storybook UI with React, CSS modules and Less? Have anyone managed to configure this kind of setup?

Adding .storybook/webpack.config.js helped me fix the issue, with
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader'],
}, {
test: /\.css$/,
use: {
loader: "css-loader",
options: {
modules: true,
}
}
}
],
},
}

I had same case. Resolved by adding webpackFinal to .storybook/main.js :
module.exports = {
stories: ['../src/**/*.stories.[tj]s'],
webpackFinal: async (config, { configType }) => {
config.module.rules.push(
{
test: /\.less$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
modules: true,
importLoaders: 1,
localIdentName: '[name]__[local]___[hash:base64:5]'
},
},
require.resolve('less-loader')
]
},
);
return config;
},
};

With sass as example:
Step 1: configure webpack in main.js. You can find documentation here: https://storybook.js.org/docs/configurations/custom-webpack-config/
const path = require('path');
module.exports = {
webpackFinal: async (config, { configType }) => {
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.
// Make whatever fine-grained changes you need
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
});
// Return the altered config
return config;
},
stories: ['../stories/**/*.stories.js'],
};
Step 2: Install sass-loader
https://webpack.js.org/loaders/sass-loader/
Step 3: Create your scss files
#import "../stories/main.scss";
h1{color:$mediumBlue}

Modify your .storybook.main.js
module.exports = {
stories: ['../src/**/*.stories.js'],
addons: [
'#storybook/preset-create-react-app',
'#storybook/addon-actions',
'#storybook/addon-links',
],
webpackFinal: async (webpackConfig, { configType }) => {
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.
const { loadCracoConfig } = require('#craco/craco/lib/config');
const { getCraPaths } = require('#craco/craco/lib/cra');
const context = {env: process.env.NODE_ENV};
const cracoConfig = loadCracoConfig(context);
context.paths = getCraPaths(cracoConfig);
const {overrideWebpackConfig} = require('#semantic-ui-react/craco-less');
overrideWebpackConfig({
context,
webpackConfig
});
// Return the altered config
return webpackConfig;
},
};
This is taken from node_modules/#craco/craco/scripts/start.js

localIdentName option moved in css-loader configuration so this is the new configuration.
source: https://github.com/rails/webpacker/issues/2197#issuecomment-517234086
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.#(js|jsx|ts|tsx)'],
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.less$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
modules: {
localIdentName: '[name]__[local]___[hash:base64:5]',
},
},
},
require.resolve('less-loader'),
],
});
return config;
},
};

Related

How to resolve aliases in Storybook?

I have a React/Typescript project with Storybook. Storybook works great, but as soon as I start importing files with aliases, it crashes.
Example:
import Foo from "#components/foo" => crash
import Foo from "../../components/foo" => ok
The app works fine with the aliases. The issue is only related to Storybook.
Here is my storybook config:
module.exports = {
stories: ["../**/stories.tsx"],
webpackFinal: (config) => {
return {
...config,
module: {
...config.module,
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: { loader: "babel-loader" },
},
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{ test: /\.(png|jpg|gif)$/, use: ["file-loader"] },
{
test: /\.svg$/,
use: [
{
loader: "babel-loader",
},
{
loader: "react-svg-loader",
options: {
jsx: true,
},
},
],
},
],
},
};
},
typescript: {
check: false,
checkOptions: {},
reactDocgen: "react-docgen-typescript",
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) =>
prop.parent ? !/node_modules/.test(prop.parent.fileName) : true,
},
},
};
My webpack config:
/* eslint-env node */
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const Dotenv = require("dotenv-webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const isProductionMode = (mode) => mode === "production";
module.exports = () => {
const env = require("dotenv").config({ path: __dirname + "/.env" });
const nodeEnv = env.parsed.NODE_ENV;
return {
mode: "development",
entry: "./src/index.tsx",
output: {
path: path.join(__dirname, "./dist"),
filename: "[name].[contenthash].bundle.js",
publicPath: "/",
},
resolve: {
extensions: [".ts", ".tsx", ".js", "jsx", ".json"],
alias: {
"#api": path.resolve(__dirname, "src/api/"),
"#assets": path.resolve(__dirname, "src/assets/"),
"#components": path.resolve(__dirname, "src/components/"),
"#containers": path.resolve(__dirname, "src/containers/"),
"#data": path.resolve(__dirname, "src/data/"),
"#i18n": path.resolve(__dirname, "src/i18n/"),
"#models": path.resolve(__dirname, "src/models/"),
"#pages": path.resolve(__dirname, "src/pages/"),
"#src": path.resolve(__dirname, "src/"),
"#stores": path.resolve(__dirname, "src/stores/"),
"#utils": path.resolve(__dirname, "src/utils/"),
},
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: { loader: "babel-loader" },
},
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{ test: /\.(png|jpg|jpeg|gif)$/, use: ["file-loader"] },
{
test: /\.svg$/,
use: [
{
loader: "babel-loader",
},
{
loader: "react-svg-loader",
options: {
jsx: true,
},
},
],
},
],
},
devServer: {
historyApiFallback: true,
port: 3000,
inline: true,
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
new Dotenv(),
],
optimization: {
minimize: isProductionMode(nodeEnv),
minimizer: isProductionMode(nodeEnv) ? [new TerserPlugin()] : [],
splitChunks: { chunks: "all" },
},
};
};
How to fix this? I am on webpack 5.24.2 and storybook 6.1.20, so these are the latest versions.
Just add this in your .storybook/main.js
const path = require('path');
module.exports = {
"stories": [
"../components/**/*.stories.mdx",
"../components/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials",
'#storybook/preset-scss',
],
webpackFinal: async (config, { configType }) => {
config.resolve.alias = {
...config.resolve.alias,
'#/interfaces': path.resolve(__dirname, "../interfaces"),
};
return config;
}
}
here interface is folder at my project root
It works For Me
This worked for me when I had the same problem:
Install a package in dev deps yarn add -D tsconfig-paths-webpack-plugin.
Then adjust your ./storybook/main.js config:
... // other imports
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
...
webpackFinal: (config) => {
config.resolve.plugins = config.resolve.plugins || [];
config.resolve.plugins.push(
new TsconfigPathsPlugin({
configFile: path.resolve(__dirname, "../tsconfig.json"),
})
);
return { ... }
}
...
From the docs:
// .storybook/main.js
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
webpackFinal: async (config) => {
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve.extensions,
}),
];
return config;
},
};
Link
My React/TypeScript Storybook project uses Vite rather than Webpack.
The readme for storybook-builder-vite clarifies "The builder will not read your vite.config.js file by default," so anything that you specified in there may be having no influence whatsoever on the Storybook build; instead, you have to customise the Storybook-specific Vite config via the viteFinal option in .storybook/main.js.
Here's how I went about introducing vite-tsconfig-paths into the Storybook Vite config to resolve tsconfig path aliases:
// .storybook/main.js
const path = require("path");
const tsconfigPaths = require("vite-tsconfig-paths").default;
module.exports = {
"stories": [
"../frontend/**/*.stories.mdx",
"../frontend/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials",
"#storybook/addon-interactions"
],
"framework": "#storybook/react",
"core": {
"builder": "storybook-builder-vite"
},
/**
* A option exposed by storybook-builder-vite for customising the Vite config.
* #see https://github.com/eirslett/storybook-builder-vite#customize-vite-config
* #param {import("vite").UserConfig} config
* #see https://vitejs.dev/config/
*/
viteFinal: async (config) => {
config.plugins.push(
/** #see https://github.com/aleclarson/vite-tsconfig-paths */
tsconfigPaths({
// My tsconfig.json isn't simply in viteConfig.root,
// so I've passed an explicit path to it:
projects: [path.resolve(path.dirname(__dirname), "frontend", "tsconfig.json")],
})
);
return config;
},
}
In case you use #storybook/vite-builder. This neat config works for me
const tsconfigPaths = require("vite-tsconfig-paths");
...
module.exports = {
...
async viteFinal(config) {
return {
...config,
plugins: [...config.plugins, tsconfigPaths.default()],
};
},
};
If you're using webpack 5 you'll need to specify that webpack5 should be used by also adding the following in addition to the previous answers:
core: {
builder: "webpack5",
},
Final storybook/main.js would then resemble:
// .storybook/main.js
const path = require('path');
const appWebpack = require(path.join(process.cwd(), 'webpack.config.js'));
module.exports = {
stories: ['../src/**/*.stories.#(tsx|mdx)'],
addons: [
"#storybook/addon-links",
"#storybook/addon-essentials",
'#storybook/preset-scss'
],
core: {
builder: "webpack5",
},
webpackFinal: async (config) => {
config.resolve.modules = [
...(config.resolve.modules || []),
...[path.resolve(process.cwd(), "src")],
];
config.resolve.alias = {
...(config.resolve.alias || {}),
...appWebpack().resolve.alias,
};
return config;
},
};
This will allow both absolute paths as well as aliases (as long as those aliases are properly set up in your main webpack.config.js and jsconfig.json/tsconfig.json of course)
Edited
Having trouble after the fact specifically with aliases, I took another trip down the webpack rocky-road.
I've updated the original 'final' for the .storybook/main.js above, explicitly merging in the alias as well as the modules nodes.
Edit 2
Be aware, eslint is going to squawk over using an alias within global decorators you create (and add to .storybook/preview.js). You can safely ignore this - they still work. If/when I figure out how to correct this as well, I'll come back and add a 3rd edit.
We're using Vite and typescript project references, for us adding the following to the storybook main.cjs worked;
viteFinal: async (config) => {
config.resolve.alias = {
...config.resolve.alias,
'#some-alias': path.resolve(__dirname, '../../some/ts/project/reference'),
};
return config;
}
As an alternative to Jamie Birch's excellent answer, if you're using vite and don't want to install vite-tsconfig-paths, you can just edit .storybook/main.js and add viteFinal to the config, like this:
const path = require('path');
module.exports = {
// ... whatever you already have here
viteFinal: async (config) => {
if (config.resolve.alias) {
config.resolve.alias.push({ find: '#', replacement: path.resolve(__dirname, '../src') + '/' });
} else {
config.resolve.alias = [{ find: '#', replacement: path.resolve(__dirname, '../src') + '/' }];
}
return config;
}
}

Importing SVG image results in Warning: Prop `src` did not match. Server:

I installed file-loader in my next.js project and configured my next.config.js to be like this:
module.exports = {
entry: './src/index.js',
webpack: config => {
const env = Object.keys(process.env).reduce((acc, curr) => {
acc[`process.env.${curr}`] = JSON.stringify(process.env[curr]);
return acc;
}, {});
config.plugins.push(new webpack.DefinePlugin(env));
config.module.rules.push({
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'images/[hash]-[name].[ext]',
},
},
],
});
return config;
}
};
I then have an image in /public/images/book-reading.svg
So I tried to import the image like this in a component I have within /src/components:
import BookReading from '../../public/images/book-reading.svg';
And using it like this:
<img src={BookReading} />
However the image does not show and I get this warning:
Warning: Prop src did not match. Server:
"images/364068d183bb962a8423031f65bab6ad-book-reading.svg" Client:
"/_next/images/364068d183bb962a8423031f65bab6ad-book-reading.svg"
Any ideas?
You need to add the publicPath and the outputPath to file-loader's options.
module.exports = {
webpack: config => {
config.module.rules.push({
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'images/[hash]-[name].[ext]',
publicPath: `/_next/static/images/`,
outputPath: 'static/images',
},
},
],
});
return config;
}
};
This is not your case but for the sake of completeness: if you had used a different basePath, you'd have needed to add it at the beginning of your publicPath.
Source

Obfuscate React classNames in existing codebase

Hi guys
I am working on a big React application with an existing code-base (100+ components). Currently, we are using the traditional styling method, example:
JSX:
<div className="div" />
SCSS:
.div {
/* ... */
}
We are using webpack with these loaders:
/* ... */
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
},
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: { implementation: sass }
}
]
/* ... */
Is there any way to obfuscate these classNames from both JSX and SCSS without rewriting the whole thing? Is it also possible to rewrite only some components to be obfuscated or do we have to rewrite it all?
Thanks.
css-loader has a modules option which when set to true obfuscates class names. Unfortunately, this will obfuscate all of the class names and will require you to change all usages of <div className="div" /> to:
import styles from <stylesheet>;
<div className={styles.div} />
Edit:
The only way I can think of avoiding having to change all of the usages at once is by splitting your css config in webpack with two patterns, for example:
{
test: /.*dirA\/.*\.css/,
use: [{ loader: 'style-loader' }, { loader: 'css-loader', options: { modules: true } }, ...]
},
{
test: /.*dirB\/.*\.css/,
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, ...]
}
Solution for ejected Create-react-app
Example classNames: a_a, a_b, a_c .... etc.
Eject app
Install incstr
Create getScopedName.js at the config folder.
const incstr = require('incstr');
const createUniqueIdGenerator = () => {
const uniqIds = {};
const generateNextId = incstr.idGenerator({
alphabet: 'abcefghijklmnopqrstuvwxyzABCEFGHJKLMNOPQRSTUVWXYZ',
});
return (name) => {
if (!uniqIds[name]) {
uniqIds[name] = generateNextId();
}
return uniqIds[name];
};
};
const localNameIdGenerator = createUniqueIdGenerator();
const componentNameIdGenerator = createUniqueIdGenerator();
module.exports = (localName, resourcePath) => {
const componentName = resourcePath
.split('/')
.slice(-2, -1)[0];
const localId = localNameIdGenerator(localName);
const componentId = componentNameIdGenerator(componentName);
return `${componentId}_${localId}`;
};
Now...
Open the webpack.config.js and add:
const getScopedName = require('./getScopedName')
Find the ~445-460 rows and replace with:
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
...(isEnvDevelopment ? {
localIdentName: '[path]_[name]_[local]',
} : {
getLocalIdent: (context, localIdentName, localName) => (
getScopedName(localName, context.resourcePath)
),
})
},
}),
}
It's all. :)

What's best way to config React components library with Webpack, typescript, css modules and SASS

I'm config a React components library for reuses in other our projects. I want to use these:
Typescript for components
Css modules
Sass
Webpack for bundle
So this is my webpack config:
const childProcess = require('child_process');
const path = require('path');
const url = require('url');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const loadersConf = require('./webpack.loaders');
const NPM_TARGET = process.env.npm_lifecycle_event; //eslint-disable-line no-process-env
const targetIsRun = NPM_TARGET === 'run';
const targetIsTest = NPM_TARGET === 'test';
const targetIsStats = NPM_TARGET === 'stats';
const targetIsDevServer = NPM_TARGET === 'dev-server';
const DEV = targetIsRun || targetIsStats || targetIsDevServer;
const STANDARD_EXCLUDE = [
path.join(__dirname, 'node_modules'),
];
// These files will be imported in every sass file
const sassResourcesPaths = [
path.resolve(__dirname, 'src/styles/abstracts/_variables.sass'),
path.resolve(__dirname, 'src/styles/abstracts/_mixins.sass'),
];
const config = {
module: {
rules: loadersConf,
},
resolve: {
modules: [
'node_modules',
path.resolve(__dirname),
],
extensions: ['.js', '.jsx', '.ts', '.tsx', '.css', '.scss'],
},
performance: {
hints: 'warning',
},
target: 'web',
plugins: [
new webpack.DefinePlugin({
COMMIT_HASH: JSON.stringify(childProcess.execSync('git rev-parse HEAD || echo dev').toString()),
}),
new MiniCssExtractPlugin({
filename: '[name].[contentHash].css',
chunkFilename: '[name].[contentHash].css',
}),
],
};
if (DEV) {
config.mode = 'development';
config.devtool = 'source-map';
}
const env = {};
if (DEV) {
} else {
env.NODE_ENV = JSON.stringify('production');
}
config.plugins.push(new webpack.DefinePlugin({
'process.env': env,
}));
module.exports = config;
And this is loaders for webpack:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const STANDARD_EXCLUDE = [
path.join(__dirname, 'node_modules'),
];
const NPM_TARGET = process.env.npm_lifecycle_event; //eslint-disable-line no-process-env
const targetIsRun = NPM_TARGET === 'run';
const targetIsTest = NPM_TARGET === 'test';
const targetIsStats = NPM_TARGET === 'stats';
const targetIsDevServer = NPM_TARGET === 'dev-server';
const DEV = targetIsRun || targetIsStats || targetIsDevServer;
// noinspection WebpackConfigHighlighting
module.exports = [
{
test: /\.(js|jsx|ts|tsx)?$/,
exclude: STANDARD_EXCLUDE,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// Babel configuration is in .babelrc because jest requires it to be there.
},
},
},
{
type: 'javascript/auto',
test: /\.json$/,
include: [
path.resolve(__dirname, 'i18n'),
],
exclude: [/en\.json$/],
use: [
{
loader: 'file-loader?name=i18n/[name].[hash].[ext]',
},
],
},
// ==========
// = Styles =
// ==========
// Global CSS (from node_modules)
// ==============================
{
test: /\.css/,
include: path.resolve(__dirname, "node_modules"),
use: [
MiniCssExtractPlugin.loader,
{
loader: "style-loader"
},
{
loader: 'css-loader'
}
]
},
{
test: /\.(sc|sa|c)ss$/,
exclude: /\.st.css$/, //This must appear before the "oneOf" property
use: [
MiniCssExtractPlugin.loader,
'style-loader',
'css-modules-typescript-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true,
camelCase: "dashes",
localIdentName: DEV
? '[name]__[local]___[hash:base64:5]'
: '_[hash:base64:5]',
},
},
{
loader: "postcss-loader",
options: {
sourceMap: "inline",
extract: true,
}
},
"sass-loader",
],
},
{
test: /\.(png|eot|tiff|svg|woff2|woff|ttf|gif|mp3|jpg)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'files/[hash].[ext]',
},
},
{
loader: 'image-webpack-loader',
options: {},
},
],
},
];
Babel config:
const config = {
presets: [
['#babel/preset-env', {
targets: {
chrome: 66,
firefox: 60,
edge: 42,
safari: 12,
},
modules: false,
corejs: 3,
useBuiltIns: 'usage',
shippedProposals: true,
}],
['#babel/preset-react', {
useBuiltIns: true,
}],
['#babel/typescript', {
allExtensions: true,
isTSX: true,
}],
],
plugins: [
'#babel/plugin-transform-runtime',
'#babel/plugin-syntax-dynamic-import',
'#babel/proposal-class-properties',
'#babel/proposal-object-rest-spread',
'#babel/plugin-proposal-optional-chaining',
['module-resolver', {
root: ['./src', './test'],
}],
],
};
// Jest needs module transformation
config.env = {
test: {
presets: config.presets,
plugins: config.plugins,
},
};
config.env.test.presets[0][1].modules = 'auto';
module.exports = config;
This is a demo component of this library:
import React from 'react';
const styles = require('./style.scss');
type Props = {
children?: React.ReactNode;
openLeft?: boolean;
openUp?: boolean;
id?: string;
ariaLabel: string;
customStyles?: object;
}
export default class Button extends React.PureComponent<Props> {
public render() {
return (
<button className={styles.test}>
{this.props.children}
</button>
);
}
}
So, this is develop build command:
"build": "cross-env NODE_ENV=production webpack --display-error-details --verbose",
"run": "webpack --progress --watch",
when I using this library:
import ExampleComponent from 'mylibrary';
When I run BUILD or RUN command, the javascript is built, but not SCSS. So in my other project, the build throw an error:
can not find module './style.scss'
This error occur in ExampleComponent
Please tell me how to resolve it. Thanks you very much!
This is my webpack config for the styles part:
{
test: /\.s?css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader'
},
{
loader: 'postcss-loader',
options: {
config: {
path: 'postcss.config.js'
}
}
},
{
loader: 'sass-loader'
},
]
}
and in my postcss.config.js:
module.exports = {
plugins: [
require('autoprefixer')
]
}

Webpack gzip bundle - Uncaught SyntaxError: Unexpected token <

I am working on a react project using webpack for bundling. I wanted to reduce my bundle size so decided to use a compression plugin to serve a gzip file to get a nice small bundle size. The project builds fine and I get a nice small bundle size but here's my issues..when I go to serve my current project here is the error i get:
Looking into I realized that for whatever reason instead of serving the contents of main.js or vendor.js it's returning my index.html file
I am fairly certain my apache server is configured to handle gzip encoding this as I can see it in the response header:
Here is the webpack config I am using:
const appConstants = function() {
switch (process.env.NODE_ENV) {
case 'local':
const localConfig = require('./config/local');
return localConfig.config();
case 'development':
const devConfig = require('./config/development');
return devConfig.config();
case 'production':
default:
const prodConfig = require('./config/production');
return prodConfig.config();
}
};
const HtmlWebPackPlugin = require("html-webpack-plugin");
const webpack = require('webpack');
const CompressionPlugin = require('compression-webpack-plugin');
const htmlWebpackPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html",
hash: true
});
const compressionPlugin = new CompressionPlugin({
filename: "[path].gz[query]",
test: /\.(js|css)$/,
algorithm: 'gzip',
deleteOriginalAssets: true
});
let webpackConfig = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
exclude: [ /assets/, /node_modules/ ],
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true,
importLoaders: 1,
localIdentName: "[name]_[local]_[hash:base64]",
sourceMap: true,
minimize: true
}
}
]
},
{
test: /\.(pdf|jpg|png|gif|svg|ico)$/,
exclude: [/node_modules/],
use: [
{
loader: 'file-loader'
},
]
},
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
exclude: [/node_modules/],
use: {
loader: 'url-loader?limit100000'
}
}
]
},
entry: [ "#babel/polyfill", "./src/index.js"],
output: {
publicPath: appConstants().DS_BASENAME ? JSON.parse(appConstants().DS_BASENAME) : '/',
chunkFilename: '[name].[chunkhash].js'
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
plugins: [
htmlWebpackPlugin,
compressionPlugin,
new webpack.DefinePlugin({
'process.env': appConstants()
}),
new webpack.EnvironmentPlugin(['NODE_ENV']),
],
devServer: {
historyApiFallback: true
}
};
// configure source map per-env
if (process.env.NODE_ENV === 'local') {
webpackConfig.devtool = 'source-map';
} else {
webpackConfig.devtool = 'hidden-source-map';
}
module.exports = webpackConfig;
I cannot figure out why the gzip is not being read/recognized by the browser. I've tried several post with similar issues but no solutions.
You need to add middleware function to handle .js and .css file with suffix .gz
Like that
const app = express()
app.get('*.js', function(req, res, next) {
req.url = req.url + '.gz'
res.set('Content-Encoding', 'gzip')
res.set('Content-Type', 'text/javascript')
next()
})
app.get('*.css', function(req, res, next) {
req.url = req.url + '.gz'
res.set('Content-Encoding', 'gzip')
res.set('Content-Type', 'text/css')
next()
})
app.use('/dist', serve('./dist', true))
app.use(express.static('./dist'));
You have to add middleware function before express.static
Good luck!

Resources