Webpack and React BrowserRouter - reactjs

im working on a project that was made in react with hashrouter, i want to change to browserouter but the project already has a webpack config and im kind of new to it, i know i should make webpack to take all calls to index (since im getting Cannot get on all routes) but i cant find any info on this kind of setup:
This is the current webpack config
const path = require('path');
const webpack = require('webpack');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
devtool: 'cheap-module-source-map',
entry: {
app: [
'webpack-hot-middleware/client',
'react-hot-loader/patch',
'./src/app'
]
},
resolve: {
modules: ['node_modules'],
extensions: ['.js', '.jsx', '.scss'],
alias: {
'react-native': 'react-native-web'
}
},
output: {
path: path.join(__dirname, 'public/assets'),
publicPath: '/assets/',
filename: '[name].bundle.js'
},
module: {
rules: [{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [{
loader: 'react-hot-loader/webpack'
}, {
loader: 'babel-loader', options: {cacheDirectory: '.babel-cache'}
}]
}, {
// Most react-native libraries include uncompiled ES6 JS.
test: /\.js$/,
include: [
/node_modules\/react-native-/,
/node_modules\/react-router-native/,
/node_modules\/#indec/
],
loader: 'babel-loader',
query: {
presets: ['react-app'],
cacheDirectory: '.babel-cache'
}
}, {
test: /\.scss$/,
loader: [
'css-hot-loader'
].concat(
ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader']
})
)
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader']
}, {
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.json$/,
/\.s?css$/,
/\.(jpg|png)/
],
loader: 'url-loader',
options: {name: '[name].[ext]', limit: 10000}
}, {
test: /\.(jpg|png)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
options: {name: '[name].[ext]'}
}]
},
plugins: [
new webpack.DefinePlugin({
VERSION: JSON.stringify(require('./package.json').version),
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
ENDPOINT: JSON.stringify(require('./config.json').endpoint)
}),
new webpack.optimize.CommonsChunkPlugin('vendor'),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
new FriendlyErrorsWebpackPlugin(),
new ExtractTextPlugin('[name].bundle.css')
],
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
app gets initiated on this index.js file
const path = require('path');
const app = require('connect')();
const config = require('./config.json');
const winston = require('winston');
const PORT = process.env.PORT || config.server.port;
process.env.NODE_ENV = config.mode;
app.use(require('morgan')(config.server.morgan));
app.use(require('compression')());
app.use(require('serve-static')(path.join(__dirname, config.server.static)));
if (config.mode === 'development') {
const config = require('./webpack.config');
const compiler = require('webpack')(config);
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: config.output.publicPath,
}));
app.use(require('webpack-hot-middleware')(compiler));
}
require('http').createServer(app).listen(
PORT,
() => winston.info('Server started at port %s', config.server.port)
);
and my app js
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Aux from 'react-aux';
import Home from '../Home';
import Admin from '../Admin';
import SignIn from '../SignIn';
import Header from './Header';
import Footer from './Footer';
const App = () => (
<BrowserRouter>
<Aux>
<Header/>
<main>
<Switch>
<Route path="/" component={Home}/>
<Route path="/admin" component={Admin}/>
<Route path="/signIn" component={SignIn}/>
</Switch>
</main>
</Aux>
</BrowserRouter>
);
export default App;

I end up finding a easy solution, just created a express app and handled all 404 to index, didn't find how to do it with connect.
const path = require('path');
const express = require('express');
const app = express();
const config = require('./config.json');
const winston = require('winston');
const PORT = process.env.PORT || config.server.port;
process.env.NODE_ENV = config.mode;
app.use(require('morgan')(config.server.morgan));
app.use(require('compression')());
app.use(require('serve-static')(path.join(__dirname, config.server.static)));
if (config.mode === 'development') {
const config = require('./webpack.config');
const compiler = require('webpack')(config);
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: config.output.publicPath,
}));
app.use(require('webpack-hot-middleware')(compiler));
}
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, './public/index.html'), function(err) {
if (err) {
res.status(500).send(err);
}
});
});
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
require('http').createServer(app).listen(
PORT,
() => winston.info('Server started at port %s', config.server.port)
);

You can use historyApiFallback: true in your config to do this.
Docs on that are located here

Related

React router throwing Cannot GET /info

I want to render a component using React Router, The component responsibility is just to show the JSON object containing build information (commitHash, branch, version, etc).
The problem is whenever I access http://localhost:3000/info to get the build information, it is treated as a backend API call and throws 404 error (
Cannot GET /info
) ,
I am using the express-actuator package in the server.dev.js file, I am guessing something we have to change in the server.dev.js file because it is treating the /info as a server call instead of rendering the component
Below is a code snippet
require('dotenv-flow').config();
const path = require('path');
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const {createProxyMiddleware} = require('http-proxy-middleware');
const actuator = require('express-actuator');
const app = express(),
DIST_DIR = path.join(__dirname, 'dist'),
HTML_FILE = path.join(DIST_DIR, 'index.html');
const config = require('./webpack.dev.config.js');
const compiler = webpack(config);
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", req.headers.origin);
res.header("Access-Control-Allow-Methods", "GET,POST,DELETE,PATCH");
res.header(
"Access-Control-Allow-Headers",
"X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept"
);
next();
});
app.use(
webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
})
);
app.use(
webpackHotMiddleware(compiler, {
publicPath: config.output.publicPath,
})
);
app.use(actuator({ basePath: "/actuator" }));
app.use(express.static(DIST_DIR));
app.get("/", (req, res) => {
res.sendFile(HTML_FILE);
});
const port = process.env.PORT || 9000;
app.listen(port, function (err) {
if (err) {
console.log(`\nError in server setup: ${err}`);
}
console.log(`\nServer started on Port: ${port}...`);
});
and /webpack.dev.config.js fil
const DotenvFlow = require('dotenv-flow-webpack');
const path = require('path');
const webpack = require('webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const { GitRevisionPlugin } = require('git-revision-webpack-plugin');
const gitRevisionPlugin = new GitRevisionPlugin();
module.exports = {
entry: {
main: [
'webpack-hot-middleware/client?reload=true',
'./src/main/index.tsx'
]
},
output: {
path: path.join(__dirname, 'dist'),
publicPath: '/',
filename: 'bundle.js',
globalObject: 'this'
},
mode: 'development',
target: 'web',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
hot: true,
},
module: {
rules: [
{
test: /\.tsx$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}, {
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.html$/,
use: [
{
loader: 'html-loader'
}
]
}, {
test: /(\.css)$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
}
]
}, {
test: /\.s[ac]ss$/i,
exclude: [
/\.module\.s[ac]ss$/,
/\.libs\.s[ac]ss$/,
],
use: [
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag',
}
},
{
loader: 'css-loader',
options: {
modules: false,
}
},
'sass-loader'
],
}, {
test: /\.module\.s[ac]ss$/,
exclude: [
/\.libs\.s[ac]ss$/,
],
use: [
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[local]__[hash:base64:5]',
}
}
},
'sass-loader',
]
}, {
test: /\.libs\.s[ac]ss$/,
use: [
{
loader: 'css-loader',
},
'sass-loader'
]
}, {
test: /\.(png|jpg|jpeg|gif|ico|pdf)$/,
exclude: [
path.resolve(__dirname, './node_modules'),
],
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
}
]
}, {
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
}
}
]
}
]
},
resolve: {
extensions: ['.js', '.tsx']
},
plugins: [
new webpack.DefinePlugin({
'_GIT_VERSION_': JSON.stringify(gitRevisionPlugin.version()),
'_GITSHA_': JSON.stringify(gitRevisionPlugin.commithash()),
'_GIT_BRANCH_': JSON.stringify(gitRevisionPlugin.branch()),
'_COMMIT_TIMESTAMP_': JSON.stringify(gitRevisionPlugin.lastcommitdatetime())
}),
new DotenvFlow(),
new WebpackManifestPlugin({ publicPath: "" }),
new webpack.HotModuleReplacementPlugin(),
new webpack.LoaderOptionsPlugin({
debug: true
}),
gitRevisionPlugin,
new HtmlWebPackPlugin({
template: './src/main/index.html',
filename: './index.html'
})
]
};
my App.tsx file
import React from "react";
import HomeComponent from "./components/HomeComponent";
import {Routes , Route } from "react-router-dom"
import BuildInfoPopper from "./components/BuildInfoPopper";
const App = () => {
return (
<div className="App">
<Routes>
<Route path="/info" element={<BuildInfoPopper/>}/>
</Routes>
</div>
);
};
export default App;
and index.tsx file
import React from "react";
import { render } from "react-dom";
import "./index.css";
import App from "./App";
import BuildInfoPopper from "./components/BuildInfoPopper";
import { BrowserRouter } from "react-router-dom";
const root = document.getElementById("root");
const rootElement = (
<div>
<BrowserRouter>
<App />
</BrowserRouter>
</div>
);
render(rootElement, root);
I have tried keeping the wild card character in server.dev.js but it didn't work

React Router - Route not working on Refresh in webpack production build

I am currently trying to deploy my react app created with Webpack. I am using react router for routing and everything works fine in dev mode, but when I am creating the production version of my web app routing works too at first but when I refresh the page I get the following
Not found
I also don't know if this is caused by webpack.
This is my webpack.common.js (settings that are similar between production and dev version)
const path = require("path");
module.exports = {
context: __dirname,
entry: {
main: "./src/index.js",
vendor: "./src/vendor.js"
},
module: {
rules: [
{
test: /\.html$/,
use: ["html-loader"]
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.(jpg|png)$/,
use: {
loader: "file-loader",
options:{
name: "[name].[hash].[ext]",
outputPath:"imgs"
}
},
},
{
test:/\.svg$/,
use: ['#svgr/webpack']
},
{
test: /\.(ttf|eot|woff|woff2)$/,
exclude: /node_modules/,
use: {
loader: "url-loader",
},
},
],
},
};
This is my webpack.prod.js (production version of webpack):
const path = require("path");
const common = require("./webpack.common");
const { merge } = require("webpack-merge");
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require("terser-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = merge(common, {
context: __dirname,
mode: "production",
output: {
path: path.resolve(__dirname, "dist"),
publicPath: "/",
filename: "bundle.[contentHash].js",
},
optimization: {
minimizer:[
new OptimizeCssAssetsPlugin(), new TerserPlugin()
]
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
],
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "public/template.html"),
// favicon: "./src/App/assets/Logo.png",
filename: "index.html",
minify:{
removeAttributeQuotes: true,
collapseWhiteSpaces: true,
removeComments: true,
}
}),
],
})
This is the main react file that provides the routes:
import Projects from "./pages/Projects/projects.jsx";
import ContentSwitch from "./pages/Content/ContentSwitch/ContentSwitch.jsx";
import Contact from "./pages/Contact/contact.jsx";
import Foyer from "./pages/Foyer/foyer.jsx";
import Login from "./pages/Login/login.jsx";
import Error from "./pages/Error/error.js";
import UgBar from "./shared/bar/bar.jsx";
import "./scss/App.scss";
import React, {
useRef,
useState,
useCallback,
useLayoutEffect,
useContext,
} from "react";
import { Route, Switch } from "react-router-dom";
import { AnimatePresence } from "framer-motion";
import { SearchProvider } from "./context/SearchContext.js";
import { BlurProvider } from "./context/BlurContext.js";
function App() {
return (
<div>
<SearchProvider>
<BlurProvider>
<UgBar />
<AnimatePresence>
<Switch className="ug-switch">
<Route exact path="/" component={Foyer}></Route>
<Route path="/projects" component={Projects}></Route>
<Route path="/content" component={ContentSwitch}></Route>
<Route path="/contact" component={Contact}></Route>
<Route path="/login" component={Login}></Route>
<Route path="*" exact={true} component={Error}></Route>
</Switch>
</AnimatePresence>
</BlurProvider>
</SearchProvider>
</div>
);
}
export default App;
Thank you and stay safe!
What kind of server are you using?
I had this problem using nginx. In the sites-available file the line I had to fix was
try_files $uri $uri/ /index.html =404;
in the server block. Now it works.

Lazy loading react-router-dom, webpack not working

I try to lazy load routes using react-router-dom but it doesn't work. Webpack should automatically split chunks on import() but it doesn't, I always end up with one main.hash.js file instead of multiple chunks.
Is there something I'm missing ?
App Component:
import * as React from 'react';
import { Route, BrowserRouter, Link } from 'react-router-dom';
const Todos = React.lazy(() => import('routes/Todos'))
class App extends React.Component<{}, {}> {
render() {
return (
<>
<BrowserRouter>
<React.Suspense fallback={<div>loading...</div>}>
<Route exact path="/" render={() => <Link to="/todos">Todos</Link>} />
<Route exact path="/todos" component={Todos} />
</React.Suspense>
</BrowserRouter>
</>
)
}
}
export default App;
Here is the webpack config in case it may be related to some plugins or missing config on this side:
webpack common config:
const webpack = require('webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
// clean folder (dist in this case)
const CleanWebpackPlugin = require('clean-webpack-plugin');
// copy files
const CopyPlugin = require('copy-webpack-plugin');
const path = require('path');
module.exports = {
entry: path.resolve(__dirname, 'src', 'index.tsx'),
resolve: {
extensions: ['.js', '.ts', '.tsx', '.scss'],
alias: {
'src': path.resolve(__dirname, 'src/'),
'components': path.resolve(__dirname, 'src/components/'),
'routes': path.resolve(__dirname, 'src/routes/'),
}
},
plugins: [
new CleanWebpackPlugin(['dist'], {}),
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "index.html"
}),
new CopyPlugin([
{ from: 'assets', to: 'assets' },
]),
]
};
webpack prod config:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');
// split css per js file
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// optimize js
const TerserPlugin = require('terser-webpack-plugin');
// optimize css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// service-worker
const Workbox = require('workbox-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
module: {
rules: [
{
test: /\.ts|\.tsx$/,
loader: "ts-loader",
include: path.resolve(__dirname, 'src')
},
{
test: /\.scss$/,
loader: MiniCssExtractPlugin.loader
},
{
test: /\.scss$/,
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[hash:base64:5]'
}
},
{
test: /\.scss$/,
loader: 'postcss-loader',
},
{
test: /\.scss$/,
loader: 'sass-loader',
}
],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css',
}),
new OptimizeCssAssetsPlugin({}),
new Workbox.GenerateSW({
clientsClaim: true,
skipWaiting: true,
exclude: [/\.(?:png|jpg|jpeg|svg)$/],
runtimeCaching: [
{
urlPattern: /https?:\/\/.+/,
handler: 'StaleWhileRevalidate',
options: {
cacheableResponse: {
statuses: [0, 200]
}
}
}, {
urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
handler: 'CacheFirst',
}],
})
],
optimization: {
minimizer: [new TerserPlugin()],
},
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist')
}
});
From: https://github.com/webpack/webpack/issues/5703#issuecomment-357512412
compilerOptions.module has to be set to esnext in order for webpack to split dynamic imports.

electron + react + webpack dev middleware + hmr - refreshes entire page instead of hot reloading

For the life of me, I can't seem to get hot reloading to work. Does anyone have any ideas? What I do get in the console is "[HMR] connected", but when i make a change to a react component, the entire page refreshes.
I understand that I should something along the lines of:
[HMR] bundle rebuilding
[HMR] bundle rebuilt in 5269ms
.....
But I don't see any of this.
Here are my configs:
server.js
var path = require('path')
var webpack = require('webpack')
var express = require('express')
var config = require('./webpack.config')
var app = express()
var compiler = webpack(config)
app.use(
require('webpack-dev-middleware')(compiler, {
publicPath: config.output.publicPath
})
)
app.use(require('webpack-hot-middleware')(compiler, {
heartbeat: 10 * 1000
}))
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'index.html'))
})
const PORT = 8080
app.listen(PORT, function(err) {
if (err) {
return console.error(err)
}
console.log(`Listening at http://localhost:${PORT}/`)
})
webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'development',
entry: [
'react-hot-loader/patch',
'webpack-hot-middleware/client?path=http://localhost:8080/__webpack_hmr',
'babel-polyfill',
'./src/index.js'
],
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
filename: 'bundle.js'
},
target: 'electron-main',
plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.ExternalsPlugin('commonjs', ['electron']) ],
module: {
rules: [
{
test: /\.(js|jsx)$/, // include .js files
exclude: /node_modules/, // exclude any and all files in the node_modules folder
loader: require.resolve('babel-loader'),
include: path.join(__dirname, 'src')
},
{
test: /\.css$/,
use: [{loader: 'style-loader'}, {loader: 'css-loader'}]
}
]
},
externals: ['electron', 'fs']
}
src/index.js
import React from 'react'
import ReactDOM from "react-dom"
import {AppContainer} from 'react-hot-loader'
import App from './app'
const renderApp = Component => {
ReactDOM.render(
<AppContainer>
<App />
</AppContainer>,
document.getElementById("root")
)
}
renderApp(App)
if (module.hot) {
module.hot.accept('./app.js', () => {
const nextRootContainer = require('./app').default
renderApp(nextRootContainer)
})
}
.babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react", "#babel/preset-flow"],
"plugins": [
["transform-class-properties", { "loose": true }],
"react-hot-loader/babel"
]
}
any suggestions welcome!!

How to use CSS Modules with webpack in React isomorphic app?

I am build an isomorphic app using react, react-router, express and webpack. Now I want to use css modules to import css.
I use import './index.css' in index.jsx, it works fine on client, but doesn't work on server rendering. The error is Error: Cannot find module './index.css'.
components/index.jsx
import React, {Component, PropTypes} from 'react';
import style from './index.css';
class App extends Component {
constructor(props, context) {
super(props, context);
}
render() {
return (
<div id="login">
// ...
</div>
);
}
};
export default App;
server/router/index.js
import url from 'url';
import express from 'express';
import swig from 'swig';
import React from 'react';
import {renderToString} from 'react-dom/server';
import {match, RouterContext} from 'react-router';
import routes from '../../client/routes/routes';
import DataWrapper from '../../client/container/DataWrapper';
import data from '../module/data';
const router = express.Router();
router.get('*', async(req, res) => {
match({
routes,
location: req.url
}, async(error, redirectLocation, props) => {
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.status(302).redirect(redirectLocation.pathname + redirectLocation.search);
} else if (props) {
let content = renderToString(
<DataWrapper data={data}><RouterContext {...props}/></DataWrapper>
);
let html = swig.renderFile('views/index.html', {
content,
env: process.env.NODE_ENV
});
res.status(200).send(html);
} else {
res.status(404).send('Not found');
}
});
});
export default router;
webpack.config.dev.js(for webpack-dev-server)
var webpack = require('webpack');
var config = require('./config');
module.exports = {
devtool: 'inline-source-map',
entry: [
'webpack-dev-server/client?http://localhost:' + config.webpackPort,
'webpack/hot/only-dev-server',
'./src/client/entry',
],
output: {
path: __dirname + '/public/js',
filename: 'app.js',
publicPath: 'http://localhost:' + config.webpackPort + '/public/js',
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify('development')
}
})
],
resolve: {
extensions: ['', '.js', '.jsx', '.css']
},
module: {
loaders: [{
test: /\.jsx?$/,
loader: 'react-hot',
exclude: /node_modules/
}, {
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules',
exclude: /node_modules/
}, {
test: /\.(png|woff|woff2|svg|ttf|eot)$/,
loader: 'url-loader',
exclude: /node_modules/
}]
}
}
I'd recommend using webpack to compile UI code for both client and server side in that case. Just set target: "node" in webpack config to produce bundle which can executed in Node environment.
That article might help for compiling your server side code with Webpack: http://jlongster.com/Backend-Apps-with-Webpack--Part-I
Especially on how to exclude node_modules with the externals key.
A very bare config might look like:
'use strict';
const path = require('path');
const fs = require('fs');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const rootDir = path.resolve(__dirname, '..');
const distDir = path.join(rootDir, 'dist');
const srcDir = path.join(rootDir, 'src');
const localStyles = new ExtractTextPlugin('local.css', { allChunks: true });
const nodeModules = fs.readdirSync('node_modules')
.filter(dir => !dir.startsWith('.'))
.reduce((acc, prop) => {
acc[prop] = 'commonjs ' + prop;
return acc;
}, {});
const loaders = [
{
test: /\.(js|jsx)$/,
include: srcDir,
exclude: /node_modules/,
loader: 'babel',
query: {
cacheDirectory: true,
},
},
{
test: /\.css$/,
include: srcDir,
loader: localStyles.extract(
'style',
'css?modules&localIdentName=[name]-[local]_[hash:base64:5]'
),
},
{
test: /\.json$/,
loader: 'json',
},
];
module.exports = {
target: 'node',
entry: {
server: ['server/index'],
},
output: {
path: distDir,
filename: '[name].bundle.js',
},
externals: nodeModules,
module: {
loaders,
},
plugins: [
localStyles,
],
};
Another solution (Webpack free) could be to use babel-plugin-css-modules-transform
.

Resources