Update current problem :
it seems that the webpack hot loader goes wrong,because when i run the following cmd:webpack,it can be built as usual.but when i run ""dev": "webpack-dev-server --color --hot --progress && node ./server.js"".webpack cannot generate built files for me .
my webpack.config is as follows:
module.exports = {
entry: getEntries(),
.....
function getEntries(){
var routeDir = path.join(SRC_DIR,"javascripts","routes");
var routeNames = routeDir?fs.readdirSync(routeDir):[];
var nameMaps = {};
routeNames.forEach(function(routeName){
var filename = routeName.match(/(.+)\.js$/)[1];
console.log("filename in entry ",filename);
if(filename){
var devEntryPath = [
'webpack-dev-server/client?http://127.0.0.1:3001', // WebpackDevServer host and port
'webpack/hot/only-dev-server',
path.join(routeDir,filename)
];
nameMaps[filename] = devEntryPath;
}
});
return nameMaps;
}
server.js
var server = new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true
}).listen(3001,'localhost',function(err,result){
if(err) console.log(err);
console.log("webpack listening at port 3001");
});
var app = express();
app.get("/monitor/index",function(req,res){
res.sendFile(__dirname+"/src/views/"+"page1.html");
});
app.get("/monitor/category/*",function(req,res){
res.sendFile(__dirname+"/src/views/"+"page2.html");
});
app.use(express.static(__dirname))
.listen(9090, 'localhost', function (err, result) {
if (err) console.log(err);
console.log('Listening at localhost:9090');
});
finally,i found where the problem is,and know the relationship between webpack-dev-server and my express server.
when using hot-loader with webpack-dev-server:
step1:the webpack build the input file to the publicPath (which was designated in "output" of webpack.config.js).
step2,the node server will send html to the front,and search for the related assets(such as js,img etc),but where? we can change the script(related with html) path to the webpack-dev-server.(just generated by step1),so node-server will ask webpack-dev-server for help.
to sum up ,i modified 3 places:
publicPath of webpackDevServer
webpack output(publicPath),equal to above
script path in html.
that's all.and now,my project can run as expected.
Related
I am attempting to implement a service worker for a boilerplate project I'm working on (https://github.com/jonnyasmar/gravity-bp feedback welcome!), but I've hit a snag :(
Problem:
I'm serving the index.html for this boilerplate virtually as an interpreted Twig template via ExpressJS. However, because I'm generating the service worker assets at build time and that is where I'm pointing it to the cacheable static assets, I can't figure out how to tell the service worker that I want it to cache the virtual index.html file served by ExpressJS at runtime.
My most successful attempts successfully cache all static assets (including the asset-manifest.json generated at build time), but will not cache a virtual index.html.
If I convert it to a static html file, the service worker does successfully cache it.
Please be sure to upvote this question to help get some visibility if you are interested in the answer!
Questions:
Is there a way to correct my code to accomplish this?
Is there anything wrong with doing it this way?
If yes to #2, how would you recommend handling this and why?
See the full source on GitHub.
Relevant code:
webpack.config.js:
output: {
filename: '[name].js',
chunkFilename: '[chunkhash].js',
path: path.resolve(__dirname, 'public'),
publicPath: '/'
},
plugins: {
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
new SWPrecacheWebpackPlugin({
cacheId: 'gravity-bp',
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'sw.js',
minify: true,
navigateFallback: 'index.html',
stripPrefix: 'public/',
swFilePath: 'public/sw.js',
staticFileGlobs: [
'public/index.html',
'public/**/!(*map*|*sw*)',
],
})
}
sw.ts:
const swUrl: string = 'sw.js';
export const register = (): void =>{
if(process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator){
const sw: ServiceWorkerContainer = navigator.serviceWorker;
sw.register(swUrl).then(registration =>{
registration.onupdatefound = (): any =>{
const installer: ServiceWorker = registration.installing;
installer.onstatechange = (): any =>{
if(installer.state === 'installed'){
if(sw.controller){
console.log('New content available.');
}else{
console.log('Content cached for offline use.');
}
}
};
};
}).catch((error) =>{
console.error('Failed to register service worker:', error);
});
}
};
export const unregister = (): void =>{
if('serviceWorker' in navigator){
navigator.serviceWorker.ready.then(registration =>{
registration.unregister();
});
}
};
server.ts:
import * as path from 'path';
const twig = require('twig').__express;
const express = require('express');
const compression = require('compression');
const pkg = require('../../package.json');
const version = pkg.version;
let app = express(),
ip = '0.0.0.0',
port = 3000,
views = path.resolve('./src/views');
app.use(compression());
app.use(express.static('public'));
app.set('view engine', 'twig');
app.engine('.twig', twig);
app.set('views', views);
// Routes
app.get("*", function(req: any, res: any, next: any){
// vars
res.locals.version = version;
res.render('index');
});
let server = app.listen(port, ip, function(){
let host = server.address().address;
let port = server.address().port;
console.log('Gravity Boilerplate ready at http://%s:%s', host, port);
});
Within the sw-precache-webpack-plugin documentation it talks about using sw-precache options. The one you should investigate is the dynamicUrlToDependencies setting. See some of these links for more info:
https://github.com/GoogleChromeLabs/sw-precache/issues/156
dynamicUrlToDependencies [Object⟨String,Buffer,Array⟨String⟩⟩]
For example, maybe start with this to test:
dynamicUrlToDependencies: {
'/': 'MAGIC_STRING_HERE'
},
So really, you need to configure the sw-precache WebPack plugin to load a server rendered page as the navigateFallback route.
This is my first time serving react files with express.js. The build has been run and the server is listening, but I cant figure out why the components aren't being injected into the html file. Instead it's rendering just the html template.
Here is a picture of my build folder:
build folder structure
Here is my index.js file from my server folder:
const express = require('express');
const morgan = require('morgan');
const path = require('path');
const app = express();
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method
:url HTTP/:http-version" :status :res[content-length] :response-time
ms'))
app.use(express.static(path.resolve(__dirname, '..', 'build')))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname,'..', 'build', 'index.html'))
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}!`);
});
this what I see in the console:
served page console
Any help would greatly be appreciated.
This question is still unanswered and Im facing the same question. I have deployed the default react app to a build-directory and am hoping to serve the files through Express.
The browser actually received the deployed file BUT it shows me a blank page.
Any ideas?
Using a simple Express server:
const express = require("express");
const path = require("path");
const app = express();
app.use(express.static(".\build"));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, ".", "build", "index.html"));
});
app.listen(8081, () => {
console.log(App listening on port 8081!);
});
See the following link
react-express-boilerplate
Add the following code to index.html file
<script src="src="/js/main.33e13313.js""></script>
main.33e13313.js is a file that packages js with webpack.
Your package.json file will be set as follow:
"scripts": {
"clean": "rm -rf build public/bundle.js",
"build": "babel server --out-dir build && webpack",
"start": "NODE_ENV=production supervisor ./build/main.js",
"development": "NODE_ENV=development node ./build/main.js"
},
You will use npm run build to run.
You can use babel to change es6 to es5. this is build tool.
You can use webpack to package JavaScript files. packagig is bundles JavaScript files into one file. this is packaging tool.
I'm currently building an app with React, which talks to an API on a separate backend. In my server.js I have express listening on one port for WebpackDevServer, and another port for serving a simple index.html. Here is what things look like:
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
watchOptions: {
aggregateTimeout: 300,
poll: 1000
}
})
.listen(3001, '0.0.0.0', function (err, result) {
if (err) {
console.log(err);
}
console.log('Running at http://0.0.0.0:3001');
});
app.use(express.static(__dirname + '/dist'));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'index.html'))
})
app.listen(4000, function() {
console.log('Running at http://0.0.0.4000')
})
How can I conditionally choose which server to run depending on my env? I don't want both ports to have an instance of my front-end running.
What would be a good way to modify the scripts section in package.json to accommodate the changes?
You can split your code into two separate files. And start them depending on your environment.
"scripts": {
"start-dev": "node src/webpack.js",
"start-prod": "node src/server.js"
}
I recently try to make a boilerplate for webpack2 + babel6 + gulp + react-hot-loader project. I started by forking react-hot-loader-minimal-boilerplate. Then I added a gulpfile to it as this branch.
If you read the code, you'll find I've only added the last 1 commit, which added the gulp package, gulp file and add the npm run gulp script. You 'd want to take a look at gulpfile.babel.js, which currently looks like this:
const gulp = require('gulp');
const webpack = require('webpack');
const cfg = require('./webpack.config');
const devServer = require('webpack-dev-server');
const path = require('path');
const util = require('gulp-util');
gulp.task('dev', () => {
cfg.plugins = [
new webpack.HotModuleReplacementPlugin(),
];
cfg.entry = {
'app': [
'babel-polyfill',
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:8080',
'./src/index',
],
};
new devServer(webpack(cfg), {
//contentBase: path.join(__dirname, 'dist'),
hot: true,
historyApiFallback: true,
//publicPath: cfg.output.publicPath,
stats: {
colors: true,
},
}).listen(8080, 'localhost', function (err) {
if(err) throw new gutil.PluginError("webpack-dev-server", err);
util.log(`'${util.colors.cyan('dev:server')}' http://localhost:8080/webpack-dev-server/index.html`);
});
});
Supposedly, the command npm run dev and npm run gulp should have the same effect. But in reality, the gulp command is not working.
If I change my React code, the code in browser should update accordingly.
The console log for code update in npm run dev:
Instead, although the browser did get signal from webpack-dev-server for the update, the DOM is not updated along with the signal.
The console log for code update in npm run gulp:
Any suggestion on how to fix this boilerplate?
I am using react-hot-loader and webpack. I also use webpack-dev-server together with an express backend.
This is my relevant webpack config for development:
var frontendConfig = config({
entry: [
'./src/client/app.js',
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/dev-server'
],
output: {
path: targetDir,
publicPath: PROD ? '/build/assets/' : 'http://localhost:3000/build/assets/' ,
filename: 'app.js'
},
module: {
loaders: [
{test: /\.js$/,
exclude: /node_modules/,
loaders: PROD ? [babelLoader] : ['react-hot', babelLoader] }
]
},
plugins: [
new webpack.HotModuleReplacementPlugin({ quiet: true })
]
});
with this config I start webpack and webpack-dev-server
gulp.task('frontend-watch', function() {
new WebpackDevServer(webpack(frontendConfig), {
publicPath: frontendConfig.output.publicPath,
hot: true,
stats: { colors: true }
}).listen(3000, 'localhost', function (err, result) {
if(err) {
console.log(err);
}
else {
console.log('webpack dev server listening at localhost:3000');
}
});
});
so webpack-dev-server is running at localhost:3000 and receives app.js from webpack watcher (which now is not anymore written to file system).
my express server serves as a backend/api and has the following config:
var express = require('express');
// proxy for react-hot-loader in dev mode
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({
changeOrigin: true,
ws: true
});
var isProduction = process.env.NODE_ENV === 'production';
// It is important to catch any errors from the proxy or the
// server will crash. An example of this is connecting to the
// server when webpack is bundling
proxy.on('error', function(e) {
console.log('Could not connect to proxy, please try again...');
});
module.exports = function (app) {
// We only want to run the workflow when not in production
if (!isProduction) {
console.log('setting up proxy for webpack-dev-server..');
// Any requests to localhost:4200/build is proxied
// to webpack-dev-server
app.all('assets/app.js', function (req, res) {
proxy.web(req, res, {
target: 'http://localhost:3000'
});
console.log('request proxied to webpack-dev!');
});
}
var server = require('http').createServer(app);
app.use(express.static(homeDirectory + '/build'));
app.use(express.static(homeDirectory + '/files'));
server.listen(4200);
};
That's all good so far, the proxying work for app.js and I see successfull hot update messages in the browser console:
Now, while it looks fine it does not work as I expected:
when I change a component's render() method it updates as supposed, but when I change a helper method (that is used in render()) then I don't get any hot update. is that normal?
Another thing that bugs me, if I work like this, and do a 'hard' browser reload at some point, all changes I made are reverted to the point where I started my webpack-dev-server - all the hot updates in between have not been persisted somehow. is that normal as well? I would expect that I loose my state but not any changes I made to the code in the meantime. That has probably something to with my app.js not being written to the file system.
For your question #2, that's not normal, I have a template repo that has HMR working available here and it works just fine https://github.com/briandipalma/wp-r-template
For question #1, usually render methods display or format data, not grab it from somewhere. But if you need to format data, use a function outside of the component
Parent component would call the following once you retrieve the price
<ChildComponent price={this.state.price}
ChildComponent's render function would use props (or better yet a parameter of the function). Remember: the whole point of React is composition and data flow
return (
<div>{this.props.price}</div>
);