How to provide `babel-preset-react-app` env variables? - reactjs

I am working on an App which connects create-react-app with an express server( using server rendering). I am referring this tutorial for it.To render the file from server, the code is
bootstrap.js
require('ignore-styles');
require('babel-register')({
ignore:[/(node-modules)/],
presets:['es2015','react-app']
});
require('./index');
index.js
import express from 'express';
// we'll talk about this in a minute:
import serverRenderer from './middleware/renderer';
const PORT = 3000;
const path = require('path');
// initialize the application and create the routes
const app = express();
const router = express.Router();
// root (/) should always serve our server rendered page
router.use('^/$', serverRenderer);
// other static resources should just be served as they are
router.use(express.static(
path.resolve(__dirname, '..', 'build'),
{ maxAge: '30d' },
));
// tell the app to use the above rules
app.use(router);
// start the app
app.listen(PORT, (error) => {
if (error) {
return console.log('something bad happened', error);
}
console.log("listening on " + PORT + "...");
});
While running the command
node bootstrap.js
I am getting error that
Error: Using babel-preset-react-app requires that you specify NODE_ENV or BABEL_ENV environment variables. Valid values are "development", "test", and "production".

There are a few options here. I will describe the most easy options.
The most easy one is to run your node bootstrap.js like this:
NODE_ENV=production BABEL_ENV=production node bootstrap.js
But that is just too long to remember every time, so you can use package.json scripts.
If you open up your package.json file, you should see a scripts section (if not, see the doc). In that scripts section you can create your own scripts.
I mostly use 2 scripts, one for development and one for production. So in your case something like:
"scripts": {
"start": "NODE_ENV=development BABEL_ENV=development node bootstrap.js",
"serve": "NODE_ENV=production BABEL_ENV=production node bootstrap.js"
}
Now you can run your node app like this:
In development
node run start or node start (because node start is an alias for node run start)
and in production
node run serve (no shorthands here)
If you still think your package.json becomes too large, you can abstract that away to some .js files. And change your scripts accordingly to something like:
"scripts": {
"start": "node scripts/start.js"
"serve": "node scripts/serve.js"
}
In those script files you can define both of those environment variables before running your app.

For solving the next error
0:0 error Parsing error: [BABEL] {some-path}: Using `babel-preset-react-app` requires that you specify `NODE_ENV` or `BABEL_ENV` environment variables. Valid values are "development", "test", and "production". Instead, received: undefined. (While processing: "{project-path}\\node_modules\\babel-preset-react-app\\index.js")
It was useful for me to add
"parser": "#typescript-eslint/parser",
into "eslintConfig" block in the package.json of my React app.

Error: Using babel-preset-react-app requires that you specify NODE_ENV or BABEL_ENV
Answer for CRA(create react app):
#origin post https://github.com/facebook/create-react-app/issues/2377
#Error:
0:0 error Parsing error: [BABEL] {some-path}: Using `babel-preset-react-app` requires
that you specify `NODE_ENV` or `BABEL_ENV` environment variables. Valid values are "development", "test", and "production". Instead, received: undefined. (While processing: "{project-path}\\node_modules\\babel-preset-react-app\\index.js")
#Description:
Problem occured after added .babelrc file to CRA using #rescripts/cli and #rescripts/rescript-use-babel-config.
For fix error you should:
Typescript:
Added "parser": "#typescript-eslint/parser", to your eslint.
Javascript:
Added "parser": "#babel/eslint-parser", to your eslint.
Eslint docs: https://eslint.org/docs/user-guide/configuring/plugins#specifying-parser
Example of typescript eslint config in package.json for CRA:
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest",
"eslint:recommended"
],
"globals": {
"NodeJS": true
},
"parser": "#typescript-eslint/parser",
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"rules": {
"no-undef": "off",
"no-unused-vars": "off",
"#typescript-eslint/no-unused-vars": ["error"]
}
}
],
"rules": {
"no-empty": [
"error",
{
"allowEmptyCatch": true
}
]
}
}

Related

Nrwl/React - Upload source maps to sentry

I'm working on a React application inside my NX Workspace.
Now I want to add sentry to my project. I already have a deploy configuration in my project.json. But I'm struggling with adding the step to upload the source maps.
Here is my project.json
"deploy": {
"executor": "nx:run-commands",
"options": {
"parallel": false,
"commands": [
{
"command": "nx run my-app:build:{args.target}",
"forwardAllArgs": true
},
{
"command": "echo Run {args.target} deployment on {args.server}",
"forwardAllArgs": true
},
{
"command": "rsync -avz --progress --delete dist/apps/my-app/ {args.user}#{args.server}:{args.path}",
"forwardAllArgs": true
},
{
"command": "echo my-app deployed to {args.target}",
"forwardAllArgs": true
}
]
},
"configurations": {
"production": {
"args": "--target=production --user=user --server=myserver.com --path=path/to/app"
}
}
}
Is there any example of how to perform the upload of the source maps using a nx workspace? Or do I have to create a custom script that handles everything and put it into my project.json as the second command (after build, before deploy).
Also, I'm not sure how to handle the version number of my application as NX does not provide a way to define version numbers for each application inside the workspace.
You'll have to create a custom webpack config on the Nx project: https://nx.dev/recipes/other/customize-webpack
and then follow the instructions on how to setup it on Sentry using webpack:
https://docs.sentry.io/platforms/javascript/guides/react/sourcemaps/uploading/webpack/
Your custom webpack should look like something like this:
const {merge} = require('webpack-merge')
const SentryWebpackPlugin = require('#sentry/webpack-plugin')
const nrwlConfig = require('#nrwl/react/plugins/webpack.js')
module.exports = (config) => {
// merge config from #nrwl/react first
nrwlConfig(config)
return merge(config, {
devtool: 'source-map', // Source map generation must be turned on
plugins: [
new SentryWebpackPlugin({
org: 'orgId',
project: 'projectId',
// Specify the directory containing build artifacts
include: './build',
// Auth tokens can be obtained from https://sentry.io/settings/account/api/auth-tokens/
// and needs the `project:releases` and `org:read` scopes
authToken: process.env.NX_SENTRY_AUTH_TOKEN,
// Optionally uncomment the line below to override automatic release name detection
// release: process.env.RELEASE,
}),
],
})
}
Please notice that unfortunately this is not a final answer, because with this I can upload the artifact, but the source maps are still not working on Sentry and I don't know why yet - but maybe this can be helpful somehow to you.

How to resolve "Cannot use import statement outside a module" in jest

I have a React application (not using Create React App) built using TypeScript, Jest, Webpack, and Babel. When trying to run yarn jest, I get the following error:
I have tried removing all packages and re-adding them. It does not resolve this. I have looked at similar questions and documentation and I am still misunderstanding something. I went so far as to follow another guide for setting up this environment from scratch and still received this issue with my code.
Dependencies include...
"dependencies": {
"#babel/plugin-transform-runtime": "^7.6.2",
"#babel/polyfill": "^7.6.0",
"babel-jest": "^24.9.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-test-renderer": "^16.11.0",
"source-map-loader": "^0.2.4"
},
"devDependencies": {
"#babel/core": "^7.6.0",
"#babel/preset-env": "^7.6.0",
"#babel/preset-react": "^7.0.0",
"#types/enzyme": "^3.9.2",
"#types/enzyme-adapter-react-16": "^1.0.5",
"#types/jest": "^24.0.13",
The component's import lines...
import * as React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/pages";
import {
Footer,
Header,
Navigation,
} from "./components/shared";
The test file....
import * as React from "react";
import * as renderer from "react-test-renderer";
import App from "../App";
it("Renders the Footer correctly", () => {
const tree = renderer
.create(<App />)
.toJSON();
expect(tree).toMatchSnapshot();
});
I expected to be able to use named imports in my components without my tests blowing up. It appears to fix the issue if I only use default imports through my solution, but I would prefer to not go that route.
Also using Babel, Typescript and Jest. Had the same failure, driving me crazy for hours.
Ended up creating a new babel.config.js file specifically for the tests. Had a large .babelrc that wasn't getting picked up by jest no matter what i did to it. Main app still uses the .babelrc as this overrides babel.config.js files.
Install jest, ts-jest and babel-jest:
npm i jest ts-jest babel-jest
babel.config.js (only used by jest)
module.exports = {presets: ['#babel/preset-env']}
jest.config.js
module.exports = {
preset: 'ts-jest',
transform: {
'^.+\\.(ts|tsx)?$': 'ts-jest',
"^.+\\.(js|jsx)$": "babel-jest",
}
};
package.json
"scripts": {
"test": "jest"
Use Babel to transpile those JS Modules and you'll be able to write your tests with es6.
Install Babel/preset-env
npm i -D #babel/preset-env
Create a babel configuration file with the preset
//babel.config.js
module.exports = {presets: ['#babel/preset-env']}
I solved this by migrating the .babelrc file to babel.config.js! Shocker.
For future references,
I solved the problem by using below jest config, after reading Logan Shoemaker's answer.
module.exports = {
verbose: true,
setupFilesAfterEnv: ["<rootDir>src/setupTests.ts"],
moduleFileExtensions: ["js", "jsx", "ts", "tsx"],
moduleDirectories: ["node_modules", "src"],
moduleNameMapper: {
"\\.(css|less|scss)$": "identity-obj-proxy"
},
transform: {
'^.+\\.(ts|tsx)?$': 'ts-jest',
"^.+\\.(js|jsx)$": "babel-jest",
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/file.js",
}
};
try this thing if you are using babel 6
Adding #babel/plugin-transform-modules-commonjs in the plugin section of babel.config.js
or
For my case import issue was due to the react file drop by adding that to transformIgnorePatterns
"transformIgnorePatterns": ["/node_modules/(?!react-file-drop)"]
I fixed it by simply appending the pattern after the run statement in package.json runner
{
"scripts": {
...
"test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!my-library-dir)/'"
...
Then, just run npm test
Solution: my named imports were coming from index.js files and I believe ts-jest needed them as index.ts files (I'm using Typescript). If anyone else runs into this error, couldn't hurt to check if you derped your file extensions.
I wasted a lot of time on this, unfortunately, but I learned a lot about webpack configurations and Babel.
Add your test script in package.json with Node experimental feature: --experimental-vm-modules
In this way you won't require babel or other dependencies.
Examples:
"test": "NODE_OPTIONS='--experimental-vm-modules --experimental-specifier-resolution=node' jest"
If you get this error: zsh: command not found: jest, try with node passing jest.js like this:
"test": "NODE_OPTIONS='--experimental-vm-modules --experimental-specifier-resolution=node --trace-warnings' node node_modules/jest/bin/jest.js --detectOpenHandles"
I'm surprised that none of the answers does not give an elegant solution:
jest.config.js
module.exports = {
...,
globals: {
"ts-jest": {
isolatedModules: true,
},
},
};
This compiles each file separately therefore avoiding the no exports issue.
Create .babelrc on the main directory and add this code and install these packages
#babel/core, #babel/preset-env and #babel/preset-react
{
"presets": [
[
"#babel/preset-env",
{
"modules": "commonjs"
}
],
"#babel/preset-react"
]
}
Matching file extensions:
I importing a file named Todo.jsx in the root as ./src/Todo/. Whenever I changed it to Todo.js the problem went away.
Disclaimer: I'm not sure what the requirement is for having your file extension as jsx vs js for your components. It did not effect me at all, but I could imagine it could mess with intellisense or snippets.
For me renaming file to babel.config.js worked.
Here is my config file an NX project using next with Typescript along with Twin-macro
// .babelrc.js >> babel.config.js
module.exports = {
presets: [
[
"#nrwl/react/babel",
{
"runtime": "automatic",
"targets": {
"browsers": [">0.25%", "not dead"]
},
"preset-react": {
runtime: "automatic",
importSource: "#emotion/react",
},
}
],
'#babel/preset-env',
'#emotion/babel-preset-css-prop',
'#babel/preset-typescript'
],
plugins: ['#emotion', 'macros', '#babel/plugin-transform-runtime', 'react-docgen'],
}
Also, please note even updating package.json works,
https://kulshekhar.github.io/ts-jest/docs/getting-started/presets/#basic-usage
// package.json
"jest": {
// Replace `ts-jest` with the preset you want to use
// from the above list
"preset": "ts-jest"
}
I encountered the same problem with Typescript, Jest, and VueJS/VueCli 3. The normal build has no problem. only happens for Jest. I struggled for hours by searching. But no answer actually works. In my case, I have a dependency on my own typescript package which I specific "target": "es6" in the tsconfig.json. That's the root cause. So the solution is simply to change the dependent's (Not the same project) back to es5 tsconfig.json:
{
"compilerOptions": {
"target": "es5",
...
},
...
}
Personnaly I followed #ajwl setup but discovered that jsdom-worker inside setupFiles: section of jest.config.js was triggering that same error. Once removed, my tests were passing.
P.S. my babel.config.js is a bit different, since I have a Vuejs (v2) SPA (bundled with Vitejs):
module.exports = {
plugins: ['#babel/plugin-transform-modules-commonjs'],
presets: [['#babel/preset-env', { targets: { node: 'current' } }]]
}
The problem is likely that jest doesn't support esmodules natively. Which can cause problems if youre typescript target is es6 or greater.
If you are testing the built typescript output, you could simply add a module=commonjs flag while transpiling. That way, your code can run with es6 or greater and still work with Jest.
"scripts": {
"test": tsc --module commonjs && jest {your-output-folder}/
}
What's great about this is that I didn't have to add any additional babel dependencies or special jest runners :)
I solved it by changing my tsconfig.json to a compatible native output
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
It is not ideal in every scenario but you might be okay with this.
All I had to do, was simply updating the package #babel/preset-env in the dev dependencies to the latest version
// package.json
"#babel/preset-env": "^7.18.6"
None of the answers helped me, what did help me was making sure my NODE_ENV was set to test, since babel config is per NODE_ENV using the wrong NODE_ENV by accident that is not configured in babel config will mean you wont be using babel and the typescript files will not be transformed.
It took me couple of hours to figure this one out so i hope it will save someone else the time it took me.
Don't know why and how but how I solved the problem was really interesting.
Just add __mocks__ folder in your src folder and create an empty file inside __mocks__ named axios.js
I discovered that this error might be triggered when you try to load a dependency that is made for the browser and, thus, cannot work with jest (node).
I had a lot of trouble solving this issue for #zip.js/zip.js lib. But I could do it like that:
Here is my jest.config.js. Adapt it to your need. The trick here is the moduleNameMapper that will make all imports to zip.js point to the file __mocks__/#zip.js/zip.js I created in my root folder.
export default {
preset: 'ts-jest',
testEnvironment: 'node',
moduleNameMapper: {
'#zip.js/zip.js': '<rootDir>/__mocks__/#zip.js/zip.js',
},
}
And here is what I have in <rootDir>/__mocks__/#zip.js/zip.js file:
module.exports = {}
Too late for this answer :)
After trying all the possible solutions, this worked for me:
The solution, that works for me:
create a file named jest/mocks/#react-native-firebase/crashlytics.js
export default () => ({ log: jest.fn(), recordError: jest.fn(), });
create a file named jest/jestSetupFile.js
import mockFirebaseCrashlytics from './mocks/#react-native-firebase/crashlytics';
jest.mock('#react-native-firebase/crashlytics', () => mockFirebaseCrashlytics);
in package.json add
"jest": { "setupFiles": ["./jest/jestSetupFile.js"] },
I needed to do a couple things to get this to work for me
Rename my .babelrc to babel.config.js and make a little change:
// .babelrc
{
"presets": [
[
"#babel/preset-env",
{
"corejs": "3.26",
"useBuiltIns": "usage"
}
],
"#babel/preset-react"
],
...
}
// babel.config.js - This still works fine with webpack
module.exports = {
"presets": [
[
"#babel/preset-env",
{
"corejs": "3.26",
"useBuiltIns": "usage"
}
],
"#babel/preset-react"
],
...
}
Add the following to my jest config file:
{
...
"transformIgnorePatterns": [
"node_modules/(?!(react-leaflet-custom-control)/)"
],
...
}
Where react-leaflet-custom-control was the package causing issues for me.
If you're using TypeScript, and you have a tsconfig.json file, try removing "module": "esnext" if you're using it
Running npm ci fixed this problem for me.

Webpack+SemanticUI+React: process is not defined

I have found numerous posts about the Webpack error:
Uncaught ReferenceError: process is not defined
most of which suggest adding a plugin to the webpack.config.js:
plugins: [
// ...
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development')
}
}),
// ...
]
however this does not seem to do the trick in my case.
To make things easy, I have created a repo with the bare minimum to setup SemanticUI-React with Webpack, which should be straightforward to navigate. My config in webpack.config.js is heavily inspired from this recent tutorial which seems to have a lot of positive comments.
To reproduce the error, just clone the repo on your machine (I use yarn, but this should work with npm too):
git clone https://github.com/sheljohn/minimal-semantic-react
cd minimal-semantic-react/
yarn install
yarn run serve
which opens at localhost:3000, and the error can be seen in the developer console.
As far as I understand, it seems that when React loads, it is looking to determine whether production or development mode is set, using the variable process.env.NODE_ENV, which is undefined in the browser.
This might be related to the target field in the Webpack config (set to web by default); but since React is loaded from CDN, prior to the bundle, I guess it doesn't know about what WebPack is doing, which makes me perplex as to why adding a plugin in the config would change anything...
Hence my question: is it possible to use semantic-ui-react by declaring the big libs (React, ReactDOM, semantic) as externals? Everything works fine if I bundle them, but the bundle ends up around 4MB, which is quite big.
Additional Details
Error as seen in Chrome (OSX High Sierra, v66.0.3359.181, dev console):
react.development.js:14 Uncaught ReferenceError: process is not defined
at react.development.js:14
(anonymous) # react.development.js:14
and code excerpt at line 14:
if (process.env.NODE_ENV !== "production") {
File webpack.config.js
const path = require("path");
const webpack = require("webpack");
const publicFolder = path.resolve(__dirname, "public");
module.exports = {
entry: "./src/index.jsx",
target: "web",
output: {
path: publicFolder,
filename: "bundle.js"
},
devServer: {
contentBase: publicFolder,
port: 3000
},
externals: {
'jquery': 'jQuery',
'lodash': '_',
'react': 'React',
'react-dom': 'ReactDOM',
'semantic-ui-react': 'semantic-ui-react'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development')
}
}),
new webpack.HotModuleReplacementPlugin()
]
};
File .babelrc
{
"presets": ["env", "react"]
}
I think I finally solved this:
Mistake #1: I was using cjs versions of the React libs from cdnjs, when I should have been using umd instead. Although UMD style is ugly, it seems to work fine within browsers, whereas CommonJS uses require for example. See this post for a comparison of AMD / CommonJS / UMD.
Mistake #2: in webpack.config.js, the "name" for the external semantic-ui-react should be semanticUIReact (case sensitive). This is what is defined in the window global when the script is loaded from the CDN (e.g. like jQuery or React).
I updated the repository with these fixes, and you should be able to reproduce that working example on your machine. This repository contains the bare minimum needed to get SemanticUI, React and Webpack working together. This would have saved me a lot of time, so hopefully other people get to benefit from that!
Everything works fine if I bundle them, but the bundle ends up around 4MB, which is quite big.
It's because you bundle them in "development" mode. Try using "production" in your script instead, it will be much smaller.
"build": "webpack --mode production"
If you bundle everything in production, without specifying external, it will be better for a standalone app.

How to avoid using relative path imports (/../../../redux/action/action1) in create-react-app

I've been using create-react-app package for creating a react website. I was using relative paths throughout my app for importing components, resources, redux etc. eg, import action from '../../../redux/action
I have tried using module-alis npm package but with no success. Is there any plugin that I can use to import based on the folder name or alias i.e. an absolute path?
Eg., import action from '#redux/action' or import action from '#resource/css/style.css'
Create a file called .env in the project root and write there:
NODE_PATH=src
Then restart the development server. You should be able to import anything inside src without relative paths.
Note I would not recommend calling your folder src/redux because now it is confusing whether redux import refers to your app or the library. Instead you can call your folder src/app and import things from app/....
We intentionally don't support custom syntax like #redux because it's not compatible with Node resolution algorithm.
The approach in the accepted answer has now been superseded. Create React App now has a different way to set absolute paths as documented here.
To summarise, you can configure your application to support importing modules using absolute paths by doing the following:
Create/Edit your jsconfig.json/tsconfig.json in the root of your project with the following:
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}
Once you have done this you can then import by specifying subdirectories of "src" (in the following example, components is a subdirectory of src) e.g.
import Button from 'components/Button'
We can use webpack 2 resolve property in the webpack config.
Sample webpack config using resolve :
Here component and utils are independent folder containing React components.
resolve: {
modules: ['src/scripts', 'node_modules'],
extensions: ['.jsx', '.js'],
unsafeCache: true,
alias: {
components: path.resolve(__dirname, 'src', 'scripts', 'components'),
utils: path.resolve(__dirname, 'src', 'scripts', 'utils'),
}
}
After that we can import directly in files :
import UiUtils from 'utils/UiUtils';
import TabContent from 'components/TabContent';
Webpack 2 Resolve Reference
After you try Ben Smith's solution above if you find eslint complains about importing absolute path add the following line to your eslint config:
settings: {
'import/resolver': {
node: {
paths: ['src'],
},
},
},
replace 'src' with your folder if you use your own boilerplate with your folder's name.
Feb 2010
Wasted about an hour on this.
An example is below:
Goal: Import App.css in HomePage.js
myapp\src\App.css
myapp\src\pages\HomePage.js
File: jsconfig.json
{
"compilerOptions": {
"baseUrl": "src"
}
}
File: src\pages\HomePage.js
import "App.css";
The alias solution for craco or rewired create-react-app is react-app-alias for systems as: craco, react-app-rewired, customize-cra
According docs of mentioned systems replace react-scripts in package.json and configure next:
react-app-rewired
// config-overrides.js
const {aliasWebpack, aliasJest} = require('react-app-alias')
const options = {} // default is empty for most cases
module.exports = aliasWebpack(options)
module.exports.jest = aliasJest(options)
craco
// craco.config.js
const {CracoAliasPlugin} = require('react-app-alias')
module.exports = {
plugins: [
{
plugin: CracoAliasPlugin,
options: {}
}
]
}
all
Configure aliases in json like this:
// tsconfig.paths.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"example/*": ["example/src/*"],
"#library/*": ["library/src/*"]
}
}
}
And add this file in extends section of main typescript config file:
// tsconfig.json
{
"extends": "./tsconfig.paths.json",
// ...
}
I am using babel-plugin-module-resolver for my project to resolve that problem.
babel-plugin-module-resolver also is the same as module-alis. So I think you should just resolve using module-alis problem.
Because you didn't tell us why using module-alis was fail? So i cant show you how to fix it.
Dont give up your solution while you dont know the reason!
in package.json file,
eject this code in the scripts object like this..
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom",
"eject": "NODE_PATH=src/ react-scripts eject"
},
this will enable the absolute path imports in your app
None of the answers worked for me. Some didn't work at all and others worked but the import was already inside src, for example:
import something from 'path/to/file'.
Whereas I wanted to be able to do:
import something from 'src/path/to/file'
Here is how I solved it:
tsconfig.json
{
"compilerOptions": {
// ...
"baseUrl": ".",
"rootDirs": [
"src"
]
},
"include": [
"src"
]
}

How to tell webpack dev server to serve index.html for any route

React router allows react apps to handle /arbitrary/route. In order this to work, I need my server to send the React app on any matched route.
But webpack dev server doesn't handle arbitrary end points.
There is a solution here using additional express server.
How to allow for webpack-dev-server to allow entry points from react-router
But I don't want to fire up another express server to allow route matching. I just want to tell webpack dev server to match any url and send me my react app. please.
I found the easiest solution to include a small config:
devServer: {
port: 3000,
historyApiFallback: {
index: 'index.html'
}
}
I found this by visiting: PUSHSTATE WITH WEBPACK-DEV-SERVER.
historyApiFallback option on official documentation for webpack-dev-server explains clearly how you can achieve either by using
historyApiFallback: true
which simply falls back to index.html when the route is not found
or
// output.publicPath: '/foo-app/'
historyApiFallback: {
index: '/foo-app/'
}
Adding public path to config helps webpack to understand real root (/) even when you are on subroutes, eg. /article/uuid
So modify your webpack config and add following:
output: {
publicPath: "/"
}
devServer: {
historyApiFallback: true
}
Without publicPath resources might not be loaded properly, only index.html.
Tested on Webpack 4.6
Larger part of config (just to have better picture):
entry: "./main.js",
output: {
publicPath: "/",
path: path.join(__dirname, "public"),
filename: "bundle-[hash].js"
},
devServer: {
host: "domain.local",
https: true,
port: 123,
hot: true,
contentBase: "./public",
inline: true,
disableHostCheck: true,
historyApiFallback: true
}
Works for me like this
devServer: {
contentBase: "./src",
hot: true,
port: 3000,
historyApiFallback: true
},
Working on riot app
My situation was a little different, since I am using the angular CLI with webpack and the 'eject' option after running the ng eject command. I modified the ejected npm script for 'npm start' in the package.json to pass in the --history-api-fallback flag
"start": "webpack-dev-server --port=4200 --history-api-fallback"
"scripts": {
"ng": "ng",
"start": "webpack-dev-server --port=4200 --history-api-fallback",
"build": "webpack",
"test": "karma start ./karma.conf.js",
"lint": "ng lint",
"e2e": "protractor ./protractor.conf.js",
"prepree2e": "npm start",
"pree2e": "webdriver-manager update --standalone false --gecko false --quiet",
"startold": "webpack-dev-server --inline --progress --port 8080",
"testold": "karma start",
"buildold": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"},
I agree with the majority of existing answers.
One key thing I wanted to mention is if you hit issues when manually reloading pages on deeper paths where it keeps the all but the last section of the path and tacks on the name of your js bundle file you probably need an extra setting (specifically the publicPath setting).
For example, if I have a path /foo/bar and my bundler file is called bundle.js. When I try to manually refresh the page I get a 404 saying /foo/bundle.js cannot be found. Interestingly if you try reloading from the path /foo you see no issues (this is because the fallback handles it).
Try using the below in conjunction with your existing webpack config to fix the issue. output.publicPath is the key piece!
output: {
filename: 'bundle.js',
publicPath: '/',
path: path.resolve(__dirname, 'public')
},
...
devServer: {
historyApiFallback: true
}
If you choose to use webpack-dev-server, you should not use it to serve your entire React app. You should use it to serve your bundle.js file as well as the static dependencies. In this case, you would have to start 2 servers, one for the Node.js entry points, that are actually going to process routes and serve the HTML, and another one for the bundle and static resources.
If you really want a single server, you have to stop using the webpack-dev-server and start using the webpack-dev-middleware within your app-server. It will process bundles "on the fly" (I think it supports caching and hot module replacements) and make sure your calls to bundle.js are always up to date.
For me I had dots "." in my path e.g. /orgs.csv so I had to put this in my webpack confg.
devServer: {
historyApiFallback: {
disableDotRule: true,
},
},
You can enable historyApiFallback to serve the index.html instead of an 404 error when no other resource has been found at this location.
let devServer = new WebpackDevServer(compiler, {
historyApiFallback: true,
});
If you want to serve different files for different URIs, you can add basic rewriting rules to this option. The index.html will still be served for other paths.
let devServer = new WebpackDevServer(compiler, {
historyApiFallback: {
rewrites: [
{ from: /^\/page1/, to: '/page1.html' },
{ from: /^\/page2/, to: '/page2.html' },
{ from: /^\/page3/, to: '/page3.html' },
]
},
});
I know this question is for webpack-dev-server, but for anyone who uses webpack-serve 2.0. with webpack 4.16.5; webpack-serve allows add-ons.You'll need to create serve.config.js:
const serve = require('webpack-serve');
const argv = {};
const config = require('./webpack.config.js');
const history = require('connect-history-api-fallback');
const convert = require('koa-connect');
serve(argv, { config }).then((result) => {
server.on('listening', ({ server, options }) => {
options.add: (app, middleware, options) => {
// HistoryApiFallback
const historyOptions = {
// ... configure options
};
app.use(convert(history(historyOptions)));
}
});
});
Reference
You will need to change the dev script from webpack-serve to node serve.config.js.

Resources