Webpack --watch and launching nodemon? - node.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.
Related
debugging webpack issue --config not found
so my webpack has been working fine. I needed a different structure then the default so I placed the webpack files in a build folder. As per documentation I placed the --config option in the package json but I am still not able to access the file in the debugger. Below is my setup: "scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "local": "webpack-dev-server --inline --progress --config build/webpack.local.conf.js", "start": "npm run dev", "build": "node build/build.js", "build_dev": "node build/build.dev.js", "build_stg": "node build/build.stg.js", "build_prod": "node build/build.prod.js", }, When I run the command node-nightly --inspect ./node_modules/webpack/bin/webpack.js I get the error: No configuration file found and no output filename configured via CLI option. A configuration file could be named 'webpack.config.js' in the current directory. In terms of my file structure I have under the root, a folder named build it contains the webpack config files (I was not sure how the structure is written on SO) Version usage: "webpack": "^3.12.0" node 9.3
--config is a property on webpack. You are trying to set a --config property on webpack-dev-server webpack-dev-server You can move your devserver to your webpack.config.js file devServer: { inline: true, progress: true } and then refer to your webpack.config.js file directly in npm like so: webpack --config ./build/yourfile.config.js you can give any name you wish to the config file if you are using --config property else webpack will always try to find webpack.config.js in your projects root directory. ELSE you can create a webpack.config.js in your root and switch between config version on runtime like so: //webpack.config.js const TARGET = process.env.npm_lifecycle_event; if (TARGET === 'build') { module.exports = require('./buildScripts/webpack.config.dev'); } else if (TARGET === 'build:prod') { module.exports = require('./buildScripts/webpack.config.prod'); } and in package.json you need the appropriate targets to match npm_lifecycle_event. //package.json "scripts": { "build": "webpack --watch", "build:prod": "webpack", }
webpack won't set to production. Defaults to development
I'm lost. I've been following a Udemy Webpack 4 tutorial on webpack and I am stuck on having different configs for different modes. I can build from my production config just fine, but when it comes to running the node server, it's not set to production. I have my main.js write this: console.log(`Environment is: ${process.env.NODE_ENV`} and I have only seen it in show as Development. Here's my package.json's script to run production: I run npm run build and then npm run prod "scripts": { "build:dev": "webpack --config=config/webpack.dev.js", "build": "webpack --config=config/webpack.prod.js", "dev": "nodemon --inspect --watch config --watch src/server src/server/main.js", "prod": "cross-env NODE_ENV=production node src/server/main.js" }, It was mentioned that I needed to define NODE_ENV in the webpack prod config in the plugin section. Here is how I have it: new webpack.DefinePlugin({ 'process.env': { NODE_ENV:'"production"' } }), I've tried it in different ways: 'process.env': {'NODE_ENV': JSON.Stringify("production")} and a couple of other ways and I have had no luck. Here is my repository to see my complete config: https://github.com/PepperAddict/webpack-express I would appreciate any help even recommendation on a better way to set this up. Thank you in advance!
Below is mine config using webpack 4 "prod": "webpack -p --mode=production --config webpack.prod.js", "start": "webpack --mode=development --config webpack.dev.js", And if you want to set NODE_ENV you can do like this "prod": "SET NODE_ENV=production& webpack -p --mode=production", Hope that help
How can i include server/app.js in webpack-dev-server?
I'm building a client with react and am running npm start and in package.json my scripts look like this: "scripts": { "start": "webpack-dev-server" } But i've also code other server side code in server/app.js that I want to run and if I run node app.js then that runs but i'd like server/app.js to be included with webpack-dev-server so that when I run npm start server/app.js is also run. Is that possible? I was reading up on the various options and at this stage after the simplest. Thx.
Update your script as follows: "scripts": { "start": "webpack-dev-server && node app.js" }
Run two tasks/scripts that both "hog the terminal" [duplicate]
In my package.json I have these two scripts: "scripts": { "start-watch": "nodemon run-babel index.js", "wp-server": "webpack-dev-server", } I have to run these 2 scripts in parallel everytime I start developing in Node.js. The first thing I thought of was adding a third script like this: "dev": "npm run start-watch && npm run wp-server" ... but that will wait for start-watch to finish before running wp-server. How can I run these in parallel? Please keep in mind that I need to see the output of these commands. Also, if your solution involves a build tool, I'd rather use gulp instead of grunt because I already use it in another project.
Use a package called concurrently. npm i concurrently --save-dev Then setup your npm run dev task as so: "dev": "concurrently --kill-others \"npm run start-watch\" \"npm run wp-server\""
If you're using an UNIX-like environment, just use & as the separator: "dev": "npm run start-watch & npm run wp-server" Otherwise if you're interested on a cross-platform solution, you could use npm-run-all module: "dev": "npm-run-all --parallel start-watch wp-server"
From windows cmd you can use start: "dev": "start npm run start-watch && start npm run wp-server" Every command launched this way starts in its own window.
You should use npm-run-all (or concurrently, parallelshell), because it has more control over starting and killing commands. The operators &, | are bad ideas because you'll need to manually stop it after all tests are finished. This is an example for protractor testing through npm: scripts: { "webdriver-start": "./node_modules/protractor/bin/webdriver-manager update && ./node_modules/protractor/bin/webdriver-manager start", "protractor": "./node_modules/protractor/bin/protractor ./tests/protractor.conf.js", "http-server": "./node_modules/http-server/bin/http-server -a localhost -p 8000", "test": "npm-run-all -p -r webdriver-start http-server protractor" } -p = Run commands in parallel. -r = Kill all commands when one of them finishes with an exit code of zero. Running npm run test will start Selenium driver, start http server (to serve you files) and run protractor tests. Once all tests are finished, it will close the http server and the selenium driver.
I've checked almost all solutions from above and only with npm-run-all I was able to solve all problems. Main advantage over all other solution is an ability to run script with arguments. { "test:static-server": "cross-env NODE_ENV=test node server/testsServer.js", "test:jest": "cross-env NODE_ENV=test jest", "test": "run-p test:static-server \"test:jest -- {*}\" --", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watchAll", } Note run-p is shortcut for npm-run-all --parallel This allows me to run command with arguments like npm run test:watch -- Something. EDIT: There is one more useful option for npm-run-all: -r, --race - - - - - - - Set the flag to kill all tasks when a task finished with zero. This option is valid only with 'parallel' option. Add -r to your npm-run-all script to kill all processes when one finished with code 0. This is especially useful when you run a HTTP server and another script that use the server. "test": "run-p -r test:static-server \"test:jest -- {*}\" --",
I have a crossplatform solution without any additional modules. I was looking for something like a try catch block I could use both in the cmd.exe and in the bash. The solution is command1 || command2 which seems to work in both enviroments same. So the solution for the OP is: "scripts": { "start-watch": "nodemon run-babel index.js", "wp-server": "webpack-dev-server", // first command is for the cmd.exe, second one is for the bash "dev": "(start npm run start-watch && start npm run wp-server) || (npm run start-watch & npm run wp-server)", "start": "npm run dev" } Then simple npm start (and npm run dev) will work on all platforms!
If you replace the double ampersand with a single ampersand, the scripts will run concurrently.
How about forking Another option to run multiple Node scripts is with a single Node script, which can fork many others. Forking is supported natively in Node, so it adds no dependencies and is cross-platform. Minimal example This would just run the scripts as-is and assume they're located in the parent script's directory. // fork-minimal.js - run with: node fork-minimal.js const childProcess = require('child_process'); let scripts = ['some-script.js', 'some-other-script.js']; scripts.forEach(script => childProcess.fork(script)); Verbose example This would run the scripts with arguments and configured by the many available options. // fork-verbose.js - run with: node fork-verbose.js const childProcess = require('child_process'); let scripts = [ { path: 'some-script.js', args: ['-some_arg', '/some_other_arg'], options: {cwd: './', env: {NODE_ENV: 'development'}} }, { path: 'some-other-script.js', args: ['-another_arg', '/yet_other_arg'], options: {cwd: '/some/where/else', env: {NODE_ENV: 'development'}} } ]; let runningScripts= []; scripts.forEach(script => { let runningScript = childProcess.fork(script.path, script.args, script.options); // Optionally attach event listeners to the script runningScript.on('close', () => console.log('Time to die...')) runningScripts.push(runningScript); // Keep a reference to the script for later use }); Communicating with forked scripts Forking also has the added benefit that the parent script can receive events from the forked child processes as well as send back. A common example is for the parent script to kill its forked children. runningScripts.forEach(runningScript => runningScript.kill()); For more available events and methods see the ChildProcess documentation
npm-run-all --parallel task1 task2 edit: You need to have npm-run-all installed beforehand. Also check this page for other usage scenarios.
Quick Solution In this case, I'd say the best bet If this script is for a private module intended to run only on *nix-based machines, you can use the control operator for forking processes, which looks like this: & An example of doing this in a partial package.json file: { "name": "npm-scripts-forking-example", "scripts": { "bundle": "watchify -vd -p browserify-hmr index.js -o bundle.js", "serve": "http-server -c 1 -a localhost", "serve-bundle": "npm run bundle & npm run serve &" } You'd then execute them both in parallel via npm run serve-bundle. You can enhance the scripts to output the pids of the forked process to a file like so: "serve-bundle": "npm run bundle & echo \"$!\" > build/bundle.pid && npm run serve & echo \"$!\" > build/serve.pid && npm run open-browser", Google something like bash control operator for forking to learn more on how it works. I've also provided some further context regarding leveraging Unix techniques in Node projects below: Further Context RE: Unix Tools & Node.js If you're not on Windows, Unix tools/techniques often work well to achieve something with Node scripts because: Much of Node.js lovingly imitates Unix principles You're on *nix (incl. OS X) and NPM is using a shell anyway Modules for system tasks in Nodeland are also often abstractions or approximations of Unix tools, from fs to streams.
step by step guide to run multiple parallel scripts with npm. install npm-run-all package globally npm i -g npm-run-all Now install and save this package within project where your package.json exists npm i npm-run-all --save-dev Now modify scripts in package.json file this way "scripts": { "server": "live-server index.html", "watch": "node-sass scss/style.scss --watch", "all": "npm-run-all --parallel server watch" }, now run this command npm run all more detail about this package in given link npm-run-all
with installing npm install concurrently "scripts": { "start:build": "tsc -w", "start:run": "nodemon build/index.js", "start": "concurrently npm:start:*" },
Use concurrently to run the commands in parallel with a shared output stream. To make it easy to tell which output is from which process, use the shortened command form, such as npm:wp-server. This causes concurrently to prefix each output line with its command name. In package.json, your scripts section will look like this: "scripts": { "start": "concurrently \"npm:start-watch\" \"npm:wp-server\"", "start-watch": "nodemon run-babel index.js", "wp-server": "webpack-dev-server" }
npm install npm-run-all --save-dev package.json: "scripts": { "start-watch": "...", "wp-server": "...", "dev": "npm-run-all --parallel start-watch wp-server" } More info: https://github.com/mysticatea/npm-run-all/blob/master/docs/npm-run-all.md
In a package.json in the parent folder: "dev": "(cd api && start npm run start) & (cd ../client && start npm run start)" this work in windows
Just add this npm script to the package.json file in the root folder. { ... "scripts": { ... "start": "react-scripts start", // or whatever else depends on your project "dev": "(cd server && npm run start) & (cd ../client && npm run start)" } }
... but that will wait for start-watch to finish before running wp-server. For that to work, you will have to use start on your command. Others have already illustrated but this is how it will work, your code below: "dev": "npm run start-watch && npm run wp-server" Should be : "dev": " start npm run start-watch && start npm run wp-server" What this will do is, it will open a separate instance for each command and process them concurrently, which shouldn't be an issue as far as your initial issue is concerned. Why do I say so? It's because these instances both open automatically while you run only 1 statement, which is your initial goal.
I ran into problems with & and |, which exit statuses and error throwing, respectively. Other solutions want to run any task with a given name, like npm-run-all, which wasn't my use case. So I created npm-run-parallel that runs npm scripts asynchronously and reports back when they're done. So, for your scripts, it'd be: npm-run-parallel wp-server start-watch
My solution is similar to Piittis', though I had some problems using Windows. So I had to validate for win32. const { spawn } = require("child_process"); function logData(data) { console.info(`stdout: ${data}`); } function runProcess(target) { let command = "npm"; if (process.platform === "win32") { command = "npm.cmd"; // I shit you not } const myProcess = spawn(command, ["run", target]); // npm run server myProcess.stdout.on("data", logData); myProcess.stderr.on("data", logData); } (() => { runProcess("server"); // package json script runProcess("client"); })();
This worked for me { "start-express": "tsc && nodemon dist/server/server.js", "start-react": "react-scripts start", "start-both": "npm -p -r run start-react && -p -r npm run start-express" } Both client and server are written in typescript. The React app is created with create-react-app with the typescript template and is in the default src directory. Express is in the server directory and the entry file is server.js typescript code and transpiled into js and is put in the dist directory . checkout my project for more info: https://github.com/nickjohngray/staticbackeditor UPDATE: calling npm run dev, to start things off {"server": "tsc-watch --onSuccess \"node ./dist/server/index.js\"", "start-server-dev": "npm run build-server-dev && node src/server/index.js", "client": "webpack-dev-server --mode development --devtool inline-source-map --hot", "dev": "concurrently \"npm run build-server-dev\" \"npm run server\" \"npm run client\""}
You can also use pre and post as prefixes on your specific script. "scripts": { "predev": "nodemon run-babel index.js &", "dev": "webpack-dev-server" } And then run: npm run dev
In my case I have two projects, one was UI and the other was API, and both have their own script in their respective package.json files. So, here is what I did. npm run --prefix react start& npm run --prefix express start&
Simple node script to get you going without too much hassle. Using readline to combine outputs so the lines don't get mangled. const { spawn } = require('child_process'); const readline = require('readline'); [ spawn('npm', ['run', 'start-watch']), spawn('npm', ['run', 'wp-server']) ].forEach(child => { readline.createInterface({ input: child.stdout }).on('line', console.log); readline.createInterface({ input: child.stderr, }).on('line', console.log); });
I have been using npm-run-all for some time, but I never got along with it, because the output of the command in watch mode doesn't work well together. For example, if I start create-react-app and jest in watch mode, I will only be able to see the output from the last command I ran. So most of the time, I was running all my commands manually... This is why, I implement my own lib, run-screen. It still very young project (from yesterday :p ) but it might be worth to look at it, in your case it would be: run-screen "npm run start-watch" "npm run wp-server" Then you press the numeric key 1 to see the output of wp-server and press 0 to see the output of start-watch.
A simple and native way for Windows CMD "start /b npm run bg-task1 && start /b npm run bg-task2 && npm run main-task" (start /b means start in the background)
I think the best way is to use npm-run-all as below: 1- npm install -g npm-run-all <--- will be installed globally 2- npm-run-all --parallel server client
How about a good old fashioned Makefile? This allows you a lot of control including how you manage subshells, dependencies between scripts etc. # run both scripts start: server client # start server and use & to background it server: npm run serve & # start the client client: npm start call this Makefile and then you can just type make start to start everything up. Because the server command is actually running in a child process of the start command when you ctrl-C the server command will also stop - unlike if you just backgrounded it yourself at the shell. Make also gives you command line completion, at least on the shell i'm using. Bonus - the first command will always run so you can actually just type make on it's own here. I always throw a makefile into my projects, just so I can quickly scan later all the common commands and parameters for each project as I flip between them.
Using just shell scripting, on Linux. "scripts": { "cmd": "{ trap 'trap \" \" TERM; kill 0; wait' INT TERM; } && blocking1 & blocking2 & wait" } npm run cmd and then ^C will kill children and wait for clean exit.
As you may need to add more and more to this scripts it will become messy and harder to use. What if you need some conditions to check, variables to use? So I suggest you to look at google/zx that allows to use js to create scripts. Simple usage: install zx: npm i -g zx add package.json commands (optional, you can move everything to scripts): "scripts": { "dev": "zx ./scripts/dev.mjs", // run script "build:dev": "tsc -w", // compile in watch mode "build": "tsc", // compile "start": "node dist/index.js", // run "start:dev": "nodemon dist/index.js", // run in watch mode }, create dev.mjs script file: #!/usr/bin/env zx await $`yarn build`; // prebuild if dist is empty await Promise.all([$`yarn start:dev`, $`yarn build:dev`]); // run in parallel Now every time you want to start a dev server you just run yarn dev or npm run dev. It will first compile ts->js and then run typescrpt compiler and server in watch mode in parallel. When you change your ts file->it's will be recompiled by tsc->nodemon will restart the server. Advanced programmatic usage Load env variables, compile ts in watch mode and rerun server from dist on changes (dev.mjs): #!/usr/bin/env zx import nodemon from "nodemon"; import dotenv from "dotenv"; import path from "path"; import { fileURLToPath } from "url"; // load env variables loadEnvVariables("../env/.env"); await Promise.all([ // compile in watch mode (will recompile on changes in .ts files) $`tsc -w`, // wait for tsc to compile for first time and rerun server on any changes (tsc emited .js files) sleep(4000).then(() => nodemon({ script: "dist/index.js", }) ), ]); function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } function getDirname() { return path.dirname(fileURLToPath(import.meta.url)); } function loadEnvVariables(relativePath) { const { error, parsed } = dotenv.config({ path: path.join(getDirname(), relativePath), }); if (error) { throw error; } return parsed; }
nodemon to exec "prestart" script at package.json
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" }