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
Related
I need to load a local SVG calling as a string './MySvg.svg', but it just works if i call it as a component, like <MySvg />.
I followed this tutorial https://blog.logrocket.com/how-to-use-svgs-in-react/, installed file-loader but the problem persists.
I'm trying to put a custom svg on the nodes of a graph with react-d3-graph, but it needs the svg locally as string and will not accept as a component.
Svgteste just exports the svg tag of svgFile.svg
An example with a component
import * as React from 'react';
import { PageSection, Title } from '#patternfly/react-core';
import { Svgteste } from './Svgteste'
import { Test } from './svgFile.svg'
const Dashboard: React.FunctionComponent = () => {
return(
<PageSection>
<Svgteste /> {/*this works*/}
<img src={'./svgFile.svg'} /> {/*this won't work*/}
<img src={Test} /> {/*neither this*/}
</PageSection>
)
}
export { Dashboard };
My webpack
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const BG_IMAGES_DIRNAME = 'bgimages';
module.exports = env => {
return {
entry: {
app: path.resolve(__dirname, 'src', 'index.tsx')
},
module: {
rules: [
{
test: /\.(tsx|ts|jsx)?$/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true,
experimentalWatchApi: true,
}
}
]
},
{
test: /\.(svg|ttf|eot|woff|woff2)$/,
// only process modules with this loader
// if they live under a 'fonts' or 'pficon' directory
use: {
loader: 'file-loader',
options: {
// Limit at 50k. larger files emited into separate files
limit: 5000,
outputPath: 'fonts',
name: '[name].[ext]',
}
}
},
{
test: /\.svg$/,
include: input => input.indexOf('background-filter.svg') > 1,
use: [
{
loader: 'url-loader',
options: {
limit: 5000,
outputPath: 'svgs',
name: '[name].[ext]',
}
}
]
},
{
test: /\.svg$/,
// only process SVG modules with this loader if they live under a 'bgimages' directory
// this is primarily useful when applying a CSS background using an SVG
include: input => input.indexOf(BG_IMAGES_DIRNAME) > -1,
use: {
loader: 'svg-url-loader',
options: {}
}
},
{
test: /\.svg$/,
// only process SVG modules with this loader when they don't live under a 'bgimages',
// 'fonts', or 'pficon' directory, those are handled with other loaders
include: input => (
(input.indexOf(BG_IMAGES_DIRNAME) === -1) &&
(input.indexOf('fonts') === -1) &&
(input.indexOf('background-filter') === -1) &&
(input.indexOf('pficon') === -1)
),
use: {
loader: 'raw-loader',
options: {}
}
},
{
test: /\.(jpg|jpeg|png|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 5000,
outputPath: 'images',
name: '[name].[ext]',
}
}
]
},
{
test: /\.svg$/,
use: ['#svgr/webpack'],
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
//name: 'images/[hash]-[name].[ext]',
},
},
],
},
]
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src', 'index.html')
}),
new Dotenv({
systemvars: true,
silent: true
})
],
resolve: {
extensions: ['.js', '.ts', '.tsx', '.jsx'],
plugins: [
new TsconfigPathsPlugin({
configFile: path.resolve(__dirname, './tsconfig.json')
})
],
symlinks: false,
cacheWithContext: false
}
}
};
I think, you have to remove the curly braces in the import. The curly braces indicate that you want to import a component that is exported with the given name, which is not the case when you import an svg.
import Test from './svgFile.svg'
I'm trying to load the 3D model with the .gltf extension in React with Typescript. The files in folder with 3D model are .gltf, .png and .bin files. The tools used for this task are webpack and useGLTFLoader from drei library. I've tried different tools. Mainly from three.js library with no effect. Error is showing that the 3D model is not found 404 (shown below) and nothing appears in place where 3D model should be placed.
GET http://localhost:3000/assets/models/Duck/glTF/Duck.gltf 404 (Not Found)
My component for rendering the 3D model is shown below:
import React, { Suspense } from 'react';
import { Canvas } from 'react-three-fiber';
import { useGLTFLoader } from 'drei';
const DuckModel = () => {
const gltf = useGLTFLoader('../../assets/models/Duck/glTF/Duck.gltf', true);
return <primitive object={gltf.scene} dispose={null} />;
};
export const ThreeDimensionComponent = () => {
return (
<>
<Canvas camera={{ position: [0, 0, 10], fov: 70 }}>
<Suspense fallback={null}>
<mesh position={[0, 250, 0]}>
<DuckModel />
</mesh>
</Suspense>
</Canvas>
</>
);
};
And below I share my webpack config.
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const root = __dirname;
const gsapPath = '/node_modules/gsap/src/uncompressed/';
module.exports = {
devtool: 'source-map',
mode: 'development',
entry: path.join(__dirname, 'src', 'index.tsx'),
watch: true,
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
sourceMapFilename: '[name].js.map'
},
module: {
rules: [
{
test: /\.(tsx|ts)$/,
use: ['babel-loader', 'ts-loader', 'tslint-loader']
},
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')()],
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8000,
sourceMap: true
}
}
]
},
{
test: /\.(ttf|eot|woff|woff2)$/,
use: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]',
sourceMap: true
}
}
},
{
test: /\.(glb|gltf)$/,
use: [
{
loader: 'file-loader'
// options: {
// outputPath: 'assets/models'
// }
}
]
},
{
test: /\.(bin)$/,
use: [
{
loader: 'file-loader'
}
]
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
modules: ['node_modules', path.resolve(__dirname, 'src')],
alias: {
TweenLite: 'gsap',
CSSPlugin: 'gsap',
Draggable: path.join(root, gsapPath + 'utils/Draggable.js'),
ScrollToPlugin: path.join(root, gsapPath + 'plugins/ScrollToPlugin.js')
}
},
devServer: {
historyApiFallback: true,
contentBase: './dist',
inline: true,
host: 'localhost',
port: 3000
},
plugins: [
new CleanWebpackPlugin({ verbose: true }),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src', 'index.html')
}),
new webpack.ProvidePlugin({
TweenMax: 'gsap'
}),
new CopyWebpackPlugin({
patterns: [{ from: 'src/assets' }]
})
]
};
My webpack.config.js file is in the root directory for the project. The assets folder is in the src folder. Lastly the file with React code is in src/components/ThreeDimensionComponent/ThreeDimensionComponent.tsx (so path for it is correct).
You either need to import the model and use the url you get from that (url-loader), or put it into the public folder. Your path points nowhere in the bundled output.
One more thing, it's #react-three/drei and useGLTF(url).
Here is a working example with a loaded 3D model in case anyone needed it. I marked an answer from hpalu as correct because it helped me to solve this problem.
I needed to use Suspense with a fallback that isn't an HTML element but instead is a component from react-three-fiber.
The post that also has helped me to solve the bug:
https://spectrum.chat/react-three-fiber/general/hello-im-trying-to-use-the-texture-loader-with-suspense~a9671405-3b8a-4486-a319-cad820347ddc?m=MTU4NDExMzIyMTMwOA==
Here is React component:
import { useGLTF } from '#react-three/drei';
import React, { Suspense } from 'react';
import { Canvas } from 'react-three-fiber';
const DuckModel = () => {
const gltf = useGLTF('./models/Duck/glTF/Duck.gltf', true);
return <primitive object={gltf.scene} dispose={null} />;
};
export const ThreeDimensionComponent = () => {
return (
<>
<Canvas camera={{ position: [0, 0, 3], fov: 80 }}>
<ambientLight intensity={0.3} />
<Suspense
fallback={
<mesh>
<boxBufferGeometry args={[1, 1, 1]} />
<meshStandardMaterial />
</mesh>
}
>
<DuckModel />
</Suspense>
</Canvas>
</>
);
};
And here is the webpack config for this example:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const gsapPath = '/node_modules/gsap/src/uncompressed/';
module.exports = {
devtool: 'source-map',
mode: 'development',
entry: path.join(__dirname, 'src', 'index.tsx'),
watch: true,
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
sourceMapFilename: '[name].js.map',
publicPath: '/'
},
module: {
rules: [
{
test: /\.(tsx|ts)$/,
use: ['babel-loader', 'ts-loader', 'tslint-loader']
},
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')()],
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8000,
sourceMap: true
}
}
]
},
{
test: /\.(ttf|eot|woff|woff2)$/,
use: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]'
// sourceMap: true
}
}
},
{
test: /\.(glb|gltf)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'assets/models',
sourceMap: true
}
}
]
},
{
test: /\.(bin)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'assets/models',
sourceMap: true
}
}
]
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
modules: ['node_modules', path.resolve(__dirname, 'src')],
alias: {
TweenLite: 'gsap',
CSSPlugin: 'gsap',
Draggable: path.join(__dirname, gsapPath + 'utils/Draggable.js'),
ScrollToPlugin: path.join(__dirname, gsapPath + 'plugins/ScrollToPlugin.js')
}
},
devServer: {
historyApiFallback: true,
contentBase: './dist',
inline: true,
host: 'localhost',
port: 3000
},
plugins: [
new CleanWebpackPlugin({ verbose: true }),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src', 'index.html')
}),
new webpack.ProvidePlugin({
TweenMax: 'gsap'
}),
new CopyWebpackPlugin({
patterns: [{ from: 'src/assets' }]
})
]
};
Im trying to get my images to appear in my build version of my react code.
```import reportIcon from "../../../../../src/img/reportmanager.svg";
<img src={reportIcon } className="img-icons-list" />```
this code works when I am in build version. My reportmanager icon shows up, but when I navigate to www.mywebsite.com/reports/user -- the icon disappears
import reportIcon from "/src/img/reportmanager.svg";
does not work either. here is my webpack.config.js file
```const HtmlWebPackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
var path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname + '/public'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env',
'#babel/react', {
'plugins': ['#babel/plugin-proposal-class-properties']
}]
},
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
},
{
test: /\.(eot|ttf|woff|woff2)$/,
loader: 'file-loader?name=./font/[name]_[hash:7].[ext]'
},
{
test: /\.(jpg|png|svg)$/,
loader: 'file-loader?name=./img/[name]_[hash:7].[ext]'
}
]
},
devServer: {
historyApiFallback: {
index: "/",
}
},
plugins: [
new ExtractTextPlugin("css/style.css"),
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
function () {
this.plugin("done", function (stats) {
if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') == -1) {
console.log(stats.compilation.errors);
process.exit(1); // or throw new Error('webpack build failed.');
}
// ...
});
}
]
};
I needed to put
<base href="/">
in the index.html of my react project.
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
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
.