I've been ask to take on a react project by a client and I'm struggling to understand how to prepare the files for hosting. I've used npm / webpack before, and I can see in the package.json the following:
"main": "index.js",
"scripts": {
"dev": "webpack --mode development --config server.config.js && webpack --mode development && nodemon server.js",
"watch": "webpack --mode development --watch",
"start": "webpack --mode production --config server.config.js && node server.js",
"heroku-postbuild": "webpack --mode production"
},
Also within the webpack.config.js file I have this:
output: {
path: path.join(__dirname, "dist"),
filename: "bundle.js"
},
I've run 'npm run heroku-postbuild'. This has led to a dist folder that gets created, which I expected would create the necessary compiled files to host on the server, however when I look in that directory all I get is a single 'bundle.js' file along with fonts and image assets.
What's confusing me is that there are zero generated HTML files.
Since I've not hosted a react site before, I'm not sure if this is what should be expected and if so, how do I set up hosting for this?
Thanks
By default, webpack does not create an index.html file for you. There are two main routes you can go:
Create your own index.html by hand, which loads your bundle.js with a <script> tag. Place this in a folder called public (or static -- the name isn't important), and then copy this into your dist folder at build time. This is easier in the short term.
Use the HtmlWebpackPlugin, which will generate an index.html file for you automatically, as part of the webpack build process. This is probably easier in the long run.
I have simple running react Server Side App .
GitHub Link for the repo, in the local env i have a build script that runs using npm start which starts the client and server builds and then i have a watcher which listen's to changes in file. This set up works really well locally.
"scripts": {
"start": "npm run dev",
"dev": "npm-run-all --parallel dev:*",
"dev:server": "nodemon --watch build --exec \"node build/bundle.js\"",
"dev:build-server": "webpack --config webpack.server.js --watch",
"dev:build-client": "webpack --config webpack.client.js --watch"
},
I tried to deploy these changes as a prod app to heroku here for some odd reason it never works and returns 503 . I have not added any changes to the build scripts in the package.json and tried deploying as is.
I thought it should work as the build log dosent give any errors on heroku but while trying to access the app it shows error and asks me to check app log but i am not using heroku cli and not planning on using it , and
thinking of auto deployment from github.
I am quite new to script/ largely build scripts trying to learn more on them.
How can we make sure this small React SSR git repo deploys and i am
able to access the home page .
I am not using heroku cli and not planning on using it
You should and I really recommend, as it's the best way to see errors that occurs after the build, why your app crashed and see the full logs. Even though you can see the logs from your heroku dashbord (More -> View logs), this only gives you the tail of the logs.
How can we make sure this small React SSR git repo deploys and i am able to access the home page.
Make sure the server is listening on the right port
by using process.env.PORT as heroku expose a dynamic port.
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log('Listening on Port ' + port);
})
On heroku the NODE_ENV environment variable is set toproduction by default,
which means heroku will prune thedevDependencies after the build,
but in your case, you still need these dependencies to start your app with webpack.
You can fix this in two ways :
Customize your build process by adding a build script:
From your github
repo
:
"start": "webpack --config webpack.client.js; webpack --config webpack.server.js; node build/bundle.js"
to
"scripts": {
"start": "node build/bundle.js",
"build": "webpack --config webpack.client.js & webpack --config webpack.server.js",
"build:start": "webpack --config webpack.client.js && webpack --config webpack.server.js && node build/bundle.js",
}
Set the NODE_ENV to any other value to skip pruning your
devDependencies
heroku config:set NODE_ENV=development //(Another reason to use the CLI)
Check this link for more details
So I am having this error on Heroku. I just woke up this morning and found it after adding a new feature. It says my www.js file is not found. This file is not on GitHub. I am writing typescript so I'm guessing Heroku will build my project and create a "./dist" folder which will include the /bin/www.js file. The application was working before and still works locally. The application still builds successfully. I have tried adding a Procfile and also used a preinstall script but nothing seems to work. Is it possible to know if the "./dist" folder was successfully created?
Error on Heroku:
Folder Structure:
Scripts:
"scripts": {
"test": "SET NODE_ENV=test&& mocha --exit -r ts-node/register tests/**/*.ts",
"dev": "nodemon src/bin/www.ts",
"start": "nodemon dist/bin/www.js",
"build": "tsc -p .",
"lint": "eslint . --ext .ts",
"precommit": "npm run lint&& npm test",
"preinstall": "npm run build"
}`
The Procfile just has: web: npm start
I found the problem. Apparently, the new build created another www.js file in "dist/src/bin/www.js" and deleted "dist/bin/www.js" on Heroku. I have no idea why this happened. So I changed my start script to "nodemon dist/src/bin/www.js".
I want to setup nodemon to run the "prestart" script when server restarts, the reason, I'm using webpack and I want to build bundle.js every time a file changes, I'm not using webpack-dev-server cause I don't know how to set it up with an existing node app running, I got my backend on node.js, help on that will be appreciated.
The way I've been working is: run npm start every time I make a change on the code, but that too much effort.
Here's the scripts object at package.json so that you have an idea of what's going on:
"scripts": {
"bundle": "webpack --config webpack.config.js",
"prestart": "npm run bundle",
"start": "node server.js"
}
The way I've accomplished to do that is by creating a nodemon.json file with the exec property:
{
// ... Other conf (see an example in https://github.com/remy/nodemon/blob/master/doc/sample-nodemon.md)
"watch": [
"app" // Set your directories/files to watch
],
"exec": "npm run prestart && node server/index.js"
}
Thanks to an excellent answer by #McMath I now have webpack compiling both my client and my server. I'm now on to trying to make webpack --watch be useful. Ideally I'd like to have it spawn something like nodemon for my server process when that bundle changes, and some flavor of browsersync for when my client changes.
I realize it's a bundler/loader and not really a task runner, but is there some way to accomplish this? A lack of google results seems to indicate I'm trying something new, but this must have been done already..
I can always have webpack package to another directory and use gulp to watch it/copy it/browsersync-ify it, but that seems like a hack.. Is there a better way?
Install the following dependencies:
npm install npm-run-all webpack nodemon
Configure your package.json file to something as seen below:
package.json
{
...
"scripts": {
"start" : "npm-run-all --parallel watch:server watch:build",
"watch:build" : "webpack --watch",
"watch:server" : "nodemon \"./dist/index.js\" --watch \"./dist\""
},
...
}
After doing so, you can easily run your project by using npm start.
Don't forget config WatchIgnorePlugin for webpack to ignore ./dist folder.
Dependencies
npm-run-all - A CLI tool to run multiple npm-scripts in parallel or sequential.
webpack - webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.
nodemon - Simple monitor script for use during development of a node.js app.
Faced the same problem and found the next solution - webpack-shell-plugin.
It
allows you to run any shell commands before or after webpack builds
So, thats my scripts in package.json:
"scripts": {
"clean": "rimraf build",
"prestart": "npm run clean",
"start": "webpack --config webpack.client.config.js",
"poststart": "webpack --watch --config webpack.server.config.js",
}
If I run 'start' script it launches next script sequence: clean -> start -> poststart.
And there is part of 'webpack.server.config.js':
var WebpackShellPlugin = require('webpack-shell-plugin');
...
if (process.env.NODE_ENV !== 'production') {
config.plugins.push(new WebpackShellPlugin({onBuildEnd: ['nodemon build/server.js --watch build']}));
}
...
"onBuildEnd" event fires only once after first build, rebuilds are not trigger "onBuildEnd", so nodemon works as intended
I like the simplicity of nodemon-webpack-plugin
webpack.config.js
const NodemonPlugin = require('nodemon-webpack-plugin')
module.exports = {
plugins: [new NodemonPlugin()]
}
then just run webpack with the watch flag
webpack --watch
In addition to #Ling's good answer:
If you want to build your project once, before you watch it with nodemon, you can use a webpack compiler hook. The plugin's code triggers nodemon in the done hook once after webpack has finished its compilation (see also this helpful post).
const { spawn } = require("child_process")
function OnFirstBuildDonePlugin() {
let isInitialBuild = true
return {
apply: compiler => {
compiler.hooks.done.tap("OnFirstBuildDonePlugin", compilation => {
if (isInitialBuild) {
isInitialBuild = false
spawn("nodemon dist/index.js --watch dist", {
stdio: "inherit",
shell: true
})
}
})
}
}
}
webpack.config.js:
module.exports = {
...
plugins: [
...
OnFirstBuildDonePlugin()
]
})
package.json:
"scripts": {
"dev" : "webpack --watch"
},
Hope, it helps.
There's no need to use plugins here. You could try running multiple nodemon instances like below. Try modifying the following script for your use case, and see if it works for you:
"scripts": {
"start": "nodemon --ignore './public/' ./bin/www & nodemon --ignore './public/' --exec 'yarn webpack'",
"webpack": "webpack --config frontend/webpack.config.js"
}
You don't need any plugins to use webpack and nodemon, just use this scripts on your package.json
"scripts": {
"start": "nodemon --ignore './client/dist' -e js,ejs,html,css --exec 'npm run watch'",
"watch": "npm run build && node ./server/index.js",
"build": "rimraf ./client/dist && webpack --bail --progress --profile"
},
#Ling has an answer very close to being correct. But it errors the first time somebody runs watch. You'll need to modify the solution as so to prevent errors.
Run npm install npm-run-all webpack nodemon
Create a file called watch-shim.js in your root. Add the following contents, which will create a dummy file and directory if they're missing.
var fs = require('fs');
if (!fs.existsSync('./dist')) {
fs.mkdir('./dist');
fs.writeFileSync('./dist/bundle.js', '');
}
Setup your scripts as so in package.json. This will only run watch if the watch-shim.js file runs successfully. Thereby preventing Nodemon from crashing due to missing files on the first run.
{
...
"scripts": {
"start": "npm run watch",
"watch": "node watch-shim.js && npm-run-all --parallel watch:server watch:build",
"watch:build": "webpack --progress --colors --watch",
"watch:server": "nodemon \"./dist/bundle.js\" --watch \"./dist/*\""
}
...
},
Assuming nodemon server.js touch the server.js file afterEmit:
// webpack.config.js
module.exports = {
// ...
plugins: [
// ...,
// 👇
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
require('child_process').execSync('touch server.js') // $ touch server.js
});
}
]
}
I tried most of the solution provided above. I believe the best one is to use nodemon-webpack-plugin .
It is very simple to use i.e. just add
const NodemonPlugin = require('nodemon-webpack-plugin')
to webpack file with
new NodemonPlugin() as your plugin.
Below are the scripts to use it:
"scripts": {
"watch:webpack-build-dev": "webpack --watch --mode development",
"clean-db": "rm -rf ./db && mkdir -p ./db",
"local-dev": "npm run clean-db && npm run watch:webpack-build-dev"
...
}
After this you can simply run npm run local-dev.
Adding a module to development is usually not as bad as adding to a production one. And mostly you will be using it for the development anyway.
This also doesn't require any additional package like nodemon or npm-run-all etc.
Also nodemon-webpack-plugin only works in watch mode.