How to run Docker and node.js with remote configurations - javascript

I'd like to provide an easy and simple Docker container for an open source application that takes an URL of a configuration file as an argument and uses this file.
The Dockerfile is pretty straight forward:
FROM phusion/baseimage
# Use baseimage-docker's init system.
CMD ["/sbin/my_init"]
RUN curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
RUN apt-get update
RUN apt-get install -y nodejs git
ADD . /src
RUN cd /src; npm install; npm update
ENV NODE_ENV production
CMD ["/usr/bin/node", "/src/gitevents.js"]
I found no way of adding the file when the container runs (with ADD or ENTRYPOINT), so I'm trying to work it out in node.js:
docker run -e "CONFIG_URL=https://gist.githubusercontent.com/PatrickHeneise/c97ba221495df0cd9a3b/raw/fda1b8cd53874735349c6310a6643e6fc589a404/gitevents_config.js" gitevents
this sets CONFIG_URL as a environment variable that I can use in node. However, I need to download a file then, which is async, which kind of doesn't work in the current setup.
if (process.env.NODE_ENV === 'production') {
var exists = fs.accessSync(path.join(__dirname, 'common', 'production.js'), fs.R_OK);
if (exists) {
config = require('./production');
} else {
// https download, but then `config` is undefined when running the app the first time.
}
}
There's no synchronous download in node.js, any recommendations how I could solve this?
I'd love to have Docker do the job with ADD or CMD doing a curl download, but I'm not sure how that works?

Another thing would be to consider that your "config file" is not a file but just text and pass the content to the container at runtime.
CONFIG="$(curl -sL https://gist.githubusercontent.com/PatrickHeneise/c97ba221495df0cd9a3b/raw/fda1b8cd53874735349c6310a6643e6fc589a404/gitevents_config.js)"
docker run -e "CONFIG_URL=${CONFIG}" gitevents

How about a combination of ENTRYPOINT and environment variable? You'd have ENTRYPOINT in the Dockerfile set to a shell script that would download the configuration file specified in the environment variable and then start the application.
Since the entry point script would receive whatever is in CMD as it's arguments, the application start step could be accomplished by something like
# Execute CMD.
eval "$#"

I managed to re-write my config script to work asynchronous, still not the best solution in my eyes.
var config = {};
var https = require('https');
var fs = require('fs');
var path = require('path');
config.load = function(fn) {
if (process.env.NODE_ENV === 'production') {
fs.access(path.join(__dirname, 'production.js'), fs.R_OK, function(error, exists) {
if (exists) {
config = require('./production');
} else {
var file = fs.createWriteStream(path.join(__dirname, 'production.js'));
var url = process.env.CONFIG_URL;
if (!url) {
process.exit(-1);
} else {
https.get(url, function(response) {
response.pipe(file);
file.on('finish', function() {
file.close(function() {
return fn(require('./production'));
});
});
});
}
}
});
} else if (process.env.NODE_ENV === 'test') {
return fn(require('./test'));
} else {
return fn(require('./development'));
}
};
module.exports = exports = config;

Related

Uglify and Minify AngularJS Source Code without NodeJS

I want to uglify then minify my AngularJS source codes.
I have been searching for samples then I found grunt but grunt needs NodeJS our website does not run with NodeJS.
I can't find any good alternatives.
Any ideas?
Uglify code is only needed when you want to publish your code. The server doesn't need it, because it doesn't take into account spaces in code.
To clear some things up I'm showing what "grunt" is on my development machine below:
shaun#laptop:~/.npm$ which grunt
/home/shaun/local/bin/grunt
shaun#laptop:~/.npm$ ls -al /home/shaun/local/bin/grunt
lrwxrwxrwx 1 shaun shaun 39 Apr 15 2015 /home/shaun/local/bin/grunt -> ../lib/node_modules/grunt-cli/bin/grunt
shaun#laptop:~/.npm$ cat /home/shaun/local/lib/node_modules/grunt-cli/bin/grunt
#!/usr/bin/env node
'use strict';
process.title = 'grunt';
// Especially badass external libs.
var findup = require('findup-sync');
var resolve = require('resolve').sync;
// Internal libs.
var options = require('../lib/cli').options;
var completion = require('../lib/completion');
var info = require('../lib/info');
var path = require('path');
var basedir = process.cwd();
var gruntpath;
// Do stuff based on CLI options.
if ('completion' in options) {
completion.print(options.completion);
} else if (options.version) {
info.version();
} else if (options.base && !options.gruntfile) {
basedir = path.resolve(options.base);
} else if (options.gruntfile) {
basedir = path.resolve(path.dirname(options.gruntfile));
}
try {
gruntpath = resolve('grunt', {basedir: basedir});
} catch (ex) {
gruntpath = findup('lib/grunt.js');
// No grunt install found!
if (!gruntpath) {
if (options.version) { process.exit(); }
if (options.help) { info.help(); }
info.fatal('Unable to find local grunt.', 99);
}
}
// Everything looks good. Require local grunt and run it.
require(gruntpath).cli();
As you can see Grunt is a node script so it does require node to run a grunt based plugin. That said you can just download and run any node script from github or wherever, they are just JS files.
https://github.com/mishoo/UglifyJS2
^^ if you were to clone the above repository and had node installed you could just run
git clone https://github.com/mishoo/UglifyJS2.git
cd UglifyJS2
bin/uglify -m -- /full/path/to/input.js
# note the above assumes you already have node installed on the
# development machine since the bin/uglify file is interpreted/run
# by node VM
This will output the mangled js which you can then put on the server (without node at all). To reiterate your build process/tools don't need to be installed on the server (probably shouldn't be ideally).

How to run a shell command from Grunt task function

I'm trying to move some icons in my app directory based on a function i have inside my Gruntfile.js. Would it be possible to do something like this? I've tried the following (going into dev or staging folder and copying all files to the previous directory), but coudn't get it to work. Thanks in advance.
grunt.registerTask('setAppIcon', 'Task that sets the app icon', function(environment) {
if (environment.toLowerCase() == "development") {
grunt.task.run(['exec:command:cd app/lib/extras/res/icon/ios/dev && cp -a . ../']);
} else if (environment.toLowerCase() == "staging") {
grunt.task.run(['exec:command:cd app/lib/extras/res/icon/ios/staging && cp -a . ../']);
}
});
Yes, it's possible to achieve your requirement, however, when you invoke the grunt.task.run command inside your function (i.e. custom task) you need to provide a reference to a task to run.
If you define a separate Target, (Let's call call them copy_dev and copy_staging - as shown in the example below), for each cd ... && cp ... command in the grunt-exec Task it should work successfully.
Gruntfile.js
The following Gruntfile.js gist shows how this can be achieved:
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-exec');
grunt.initConfig({
exec: {
copy_dev: {
cmd: 'cd app/lib/extras/res/icon/ios/dev && cp -a . ../'
},
copy_staging: {
cmd: 'cd app/lib/extras/res/icon/ios/staging && cp -a . ../'
}
}
});
grunt.registerTask('setAppIcon', 'Task that sets the app icon', function() {
var environment = process.env.NODE_ENV;
// Exit early if NODE_ENV variable has not been set.
if (!environment) {
grunt.log.writeln(
'\"setAppIcon\"" task failed - NODE_ENV has not been set.'['yellow']
)
return
}
if (environment.toLowerCase() == "development") {
grunt.task.run('exec:copy_dev');
grunt.log.writeln('>> Copying icons from \"dev\"...')
} else if (environment.toLowerCase() == "staging") {
grunt.task.run('exec:copy_staging');
grunt.log.writeln('>> Copying icons from \"staging\"...')
}
});
grunt.registerTask('default', [ 'setAppIcon' ]);
};
Additional notes
Inside the custom task/function named setAppIcon we obtain the current node environment using nodes builtin process.env
When running $ grunt via your CLI (using the gist shown above), and assuming your process.env.NODE_ENV variable has not been set, or it has possibly been unset by running $ unset NODE_ENV, you will see the following message:
"setAppIcon"" task failed - NODE_ENV has not been set.
However, if the process.env.NODE_ENV variable has been set to either development or staging the files will be copied as expected.
For example running either of the following via your CLI will work successfully:
$ export NODE_ENV=development && grunt
or
$ export NODE_ENV=staging && grunt
You will also see either of the following messages logged to the console:
>> Copying icons from "dev"...
or
>> Copying icons from "staging"...
After process.env.NODE_ENV has been set to either development or staging then just running $ grunt via your CLI, will copy files according to which environment is set.

Disable Jasmine's fdescribe() and fit() based on environment

fdescribe() and fit() are great for reducing noise when you're working on a subset of tests. I sometimes forget to change them back to describe()/it() before merging my branch into master. (It's okay to have them in separate branch while working on code - i.e. a pre-commit check wouldn't work for me.)
My CI environment is Codeship. Is there a solution to this problem that would fail the tests in Codeship if it came across any focused methods?
Using something like no-focused-tests would be okay. Any idea how to enable this rule as an error in Codeship and disable it locally?
Edit 14.11.19:
To make things easier I created an installable package you can find at https://www.npmjs.com/package/tslint-jasmine
Original post:
If you're using TSLint and (like me) found that all the defocus and tslint-jasmine-noSkipOrFocus checkers are not working for you, I created a Gist for that: https://gist.github.com/djungowski/7d9126bb79970446b4ffeb5656c6bf1f
How to use:
Save Gist in a a folder called TSLint/Rules as noJasmineFocusRule.js
Add the Rules folder to your TSLint config: rulesDirectory: 'TSLint/Rules'
Enable option with "no-jasmine-focus": true
Using something like no-focused-tests would be okay. Any idea how to enable this rule as an error in Codeship and disable it locally?
You could use a combination of environment variables and redefining the fdescribe/fit global functions:
npm i --save cross-env
package.json:
"scripts": {
"test": "jasmine",
"test-safe": "cross-env FOCUSED_TESTS=off jasmine"
},
disableFocusedTestsIfNecessary.js (included after jasmine defines its globals):
if (process.env.FOCUSED_TESTS === "off") {
console.log("Focused tests must be off");
global.fdescribe = global.fit = function() {
throw new Error("fdescribe and fit are disabled in this environment");
};
}
else {
console.log("Focused tests enabled");
}
Tell codeship to run npm run test-safe instead of npm run test
For those interested, if you are using jasmine and eslint, you can use this plugin to ensure no focused tests: https://github.com/tlvince/eslint-plugin-jasmine.
First install eslint globally npm install -g eslint.
Then install the eslint-plugin-jasmine library npm install --save-dev eslint-plugin-jasmine.
Create a .eslintrc file which would look something like this:
{
"rules": {
"semi": 2
},
"plugins": ["jasmine"],
"env": {
"jasmine": true
},
"extends": "plugin:jasmine/recommended",
}
Then you are ready to run the linter eslint -c ./.eslintrc app.js
I'm late to the party.
I had a similar issue with my builds. We don't use ts / eslint so I just wrote a quick script to throw an error that would fail my dockerfile / build.
Here it is.
#!/bin/sh
files=$(find "./.." -type f -name '*.spec*')
errored=false
echo "Checking for focused tests"
for file in $files
do
if grep -E "fdescribe|fit" $file; [ $? -eq 0 ]; then
echo "-Focusing a test in the file $file"
errored=true
fi
done
if $errored; then
echo "Some tests were focused"
exit 1
else
echo "No tests were focused"
fi
This isn't the best solution. But it works for my needs.
To setup:
npm i lodash
npm i minimist
I call this from my gulp tasks:
node .\\build\\throwIfFocusedTest.js e2e/
node .\\build\\throwIfFocusedTest.js src/
throwIfFocusedTest.js:
const walkSync = require('./walkSync').default;
const _ = require('lodash');
const argv = require('minimist')(process.argv);
const fs = require('fs');
if (argv._.length !== 3) {
throw 'expecting 1 command line argument';
}
const directory = argv._[2];
const files = walkSync(directory);
const scriptFiles = _.filter(files, f => f.endsWith('.js') || f.endsWith('.ts'));
const invalidStrings = [
'fdescribe',
'fit',
];
_.each(scriptFiles, fileName => {
const contents = fs.readFileSync(fileName, 'utf8');
invalidStrings.forEach(is => {
if (contents.includes(is)) {
console.error(`throwIfFocusedTest: ${directory}: File contains ${is}: ${fileName}`);
process.exit(1);
}
});
});
console.log(`throwIfFocusedTest: ${directory}: No files contain: ${invalidStrings.join(', ')}`);
walkSync.js:
/**
* From: https://gist.github.com/kethinov/6658166
*/
exports.default = function walkSync(dir, filelist) {
var fs = fs || require('fs'),
files = fs.readdirSync(dir);
filelist = filelist || [];
files.forEach(function (file) {
var path = dir + file;
if (fs.statSync(dir + file).isDirectory()) {
filelist = walkSync(dir + file + '/', filelist);
}
else {
filelist.push(path);
}
});
return filelist;
};
If you're willing to fail on when tests are marked for focus or skip (fit + xit), there's a relatively new Karma feature that solves the problem with no plugins. Karma now supports a failOnSkippedTests config file / CLI option, which, per the docs, causes "failure on tests deliberately disabled, eg fit() or xit()".

How to exec in NodeJS using the user's environment?

I am trying to execute a command in Node:
cd "/www/foo/" && "/path/to/git/bin/git.exe" config --list --global
Basically, I want to execute a Git command that returns global configuration (for the current user).
This works fine for me when I execute the command directly on cli. It also works if I do it like this in Node:
var exec = require('child_process').exec;
var config = {
maxBuffer: 10000 * 1024
};
exec('cd "/www/foo/" && "/path/to/git/bin/git.exe" config --list --global', config, function() {
console.log(arguments);
});
However, as soon as I specify the environment to the config:
var exec = require('child_process').exec;
var config = {
maxBuffer: 10000 * 1024,
env: { // <--- this one
}
};
exec('cd "/www/foo/" && "/path/to/git/bin/git.exe" config --list --global', config, function() {
console.log(arguments);
});
Things break:
Command failed: fatal: unable to read config file
'(null)/(null)/.gitconfig': No such file or directory
I know the reason, it is because the executed Git program is no longer executed under the user environment (like it is in cli), and can't retrieve the user directory to read global configuration (in Git, global config = user config) and I believe /(null)/(null)/.gitconfig should be /Users/MyName/.gitconfig which does exist.
My question is, how can I specify the current user environment while still being able to specify my own additional environment properties?
I solved it.
I retrieved the current environment from process.env and extended it with my own properties:
var environment = process.env;
environment.customProp = 'foo';
var config = {
maxBuffer: 10000 * 1024,
env: environment
};
So the problem was I missed the entire environment when I overwrote it.

How to auto-reload files in Node.js?

Any ideas on how I could implement an auto-reload of files in Node.js? I'm tired of restarting the server every time I change a file.
Apparently Node.js' require() function does not reload files if they already have been required, so I need to do something like this:
var sys = require('sys'),
http = require('http'),
posix = require('posix'),
json = require('./json');
var script_name = '/some/path/to/app.js';
this.app = require('./app').app;
process.watchFile(script_name, function(curr, prev){
posix.cat(script_name).addCallback(function(content){
process.compile( content, script_name );
});
});
http.createServer(this.app).listen( 8080 );
And in the app.js file I have:
var file = require('./file');
this.app = function(req, res) {
file.serveFile( req, res, 'file.js');
}
But this also isn't working - I get an error in the process.compile() statement saying that 'require' is not defined. process.compile is evaling the app.js, but has no clue about the node.js globals.
A good, up to date alternative to supervisor is nodemon:
Monitor for any changes in your node.js application and automatically restart the server - perfect for development
To use nodemon with version of Node without npx (v8.1 and below, not advised):
$ npm install nodemon -g
$ nodemon app.js
Or to use nodemon with versions of Node with npx bundled in (v8.2+):
$ npm install nodemon
$ npx nodemon app.js
Or as devDependency in with an npm script in package.json:
"scripts": {
"start": "nodemon app.js"
},
"devDependencies": {
"nodemon": "..."
}
node-supervisor is awesome
usage to restart on save for old Node versions (not advised):
npm install supervisor -g
supervisor app.js
usage to restart on save for Node versions that come with npx:
npm install supervisor
npx supervisor app.js
or directly call supervisor in an npm script:
"scripts": {
"start": "supervisor app.js"
}
i found a simple way:
delete require.cache['/home/shimin/test2.js']
If somebody still comes to this question and wants to solve it using only the standard modules I made a simple example:
var process = require('process');
var cp = require('child_process');
var fs = require('fs');
var server = cp.fork('server.js');
console.log('Server started');
fs.watchFile('server.js', function (event, filename) {
server.kill();
console.log('Server stopped');
server = cp.fork('server.js');
console.log('Server started');
});
process.on('SIGINT', function () {
server.kill();
fs.unwatchFile('server.js');
process.exit();
});
This example is only for one file (server.js), but can be adapted to multiple files using an array of files, a for loop to get all file names, or by watching a directory:
fs.watch('./', function (event, filename) { // sub directory changes are not seen
console.log(`restart server`);
server.kill();
server = cp.fork('server.js');
})
This code was made for Node.js 0.8 API, it is not adapted for some specific needs but will work in some simple apps.
UPDATE:
This functional is implemented in my module simpleR, GitHub repo
nodemon came up first in a google search, and it seems to do the trick:
npm install nodemon -g
cd whatever_dir_holds_my_app
nodemon app.js
nodemon is a great one. I just add more parameters for debugging and watching options.
package.json
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon --watch server --inspect ./server/server.js"
}
The command: nodemon --watch server --inspect ./server/server.js
Whereas:
--watch server Restart the app when changing .js, .mjs, .coffee, .litcoffee, and .json files in the server folder (included subfolders).
--inspect Enable remote debug.
./server/server.js The entry point.
Then add the following config to launch.json (VS Code) and start debugging anytime.
{
"type": "node",
"request": "attach",
"name": "Attach",
"protocol": "inspector",
"port": 9229
}
Note that it's better to install nodemon as dev dependency of project. So your team members don't need to install it or remember the command arguments, they just npm run dev and start hacking.
See more on nodemon docs: https://github.com/remy/nodemon#monitoring-multiple-directories
Nodemon has been the go to for restarting server for file changes for long time. Now with Node.js 19 they have introduced a --watch flag, which does the same [experimental]. Docs
node --watch index.js
node-dev works great. npm install node-dev
It even gives a desktop notification when the server is reloaded and will give success or errors on the message.
start your app on command line with:
node-dev app.js
There is Node-Supervisor that you can install by
npm install supervisor
see http://github.com/isaacs/node-supervisor
You can use nodemon from NPM.
And if you are using Express generator then you can using this command inside your project folder:
nodemon npm start
or using Debug mode
DEBUG=yourapp:* nodemon npm start
you can also run directly
nodemon your-app-file.js
Hope this help.
There was a recent (2009) thread about this subject on the node.js mailing list. The short answer is no, it's currently not possible auto-reload required files, but several people have developed patches that add this feature.
With Node.js 19 you can monitor file changes with the --watch option. After a file is changed, the process is restarted automatically, reflecting new changes.
node --watch server.js
yet another solution for this problem is using forever
Another useful capability of Forever is that it can optionally restart
your application when any source files have changed. This frees you
from having to manually restart each time you add a feature or fix a
bug. To start Forever in this mode, use the -w flag:
forever -w start server.js
Here is a blog post about Hot Reloading for Node. It provides a github Node branch that you can use to replace your installation of Node to enable Hot Reloading.
From the blog:
var requestHandler = require('./myRequestHandler');
process.watchFile('./myRequestHandler', function () {
module.unCacheModule('./myRequestHandler');
requestHandler = require('./myRequestHandler');
}
var reqHandlerClosure = function (req, res) {
requestHandler.handle(req, res);
}
http.createServer(reqHandlerClosure).listen(8000);
Now, any time you modify myRequestHandler.js, the above code will no­tice and re­place the local re­questHandler with the new code. Any ex­ist­ing re­quests will con­tin­ue to use the old code, while any new in­com­ing re­quests will use the new code. All with­out shut­ting down the serv­er, bounc­ing any re­quests, pre­ma­ture­ly killing any re­quests, or even re­ly­ing on an in­tel­li­gent load bal­ancer.
I am working on making a rather tiny node "thing" that is able to load/unload modules at-will (so, i.e. you could be able to restart part of your application without bringing the whole app down).
I am incorporating a (very stupid) dependency management, so that if you want to stop a module, all the modules that depends on that will be stopped too.
So far so good, but then I stumbled into the issue of how to reload a module. Apparently, one could just remove the module from the "require" cache and have the job done. Since I'm not keen to change directly the node source code, I came up with a very hacky-hack that is: search in the stack trace the last call to the "require" function, grab a reference to it's "cache" field and..well, delete the reference to the node:
var args = arguments
while(!args['1'] || !args['1'].cache) {
args = args.callee.caller.arguments
}
var cache = args['1'].cache
util.log('remove cache ' + moduleFullpathAndExt)
delete( cache[ moduleFullpathAndExt ] )
Even easier, actually:
var deleteCache = function(moduleFullpathAndExt) {
delete( require.cache[ moduleFullpathAndExt ] )
}
Apparently, this works just fine. I have absolutely no idea of what that arguments["1"] means, but it's doing its job. I believe that the node guys will implement a reload facility someday, so I guess that for now this solution is acceptable too.
(btw. my "thing" will be here: https://github.com/cheng81/wirez , go there in a couple of weeks and you should see what I'm talking about)
solution at:
http://github.com/shimondoodkin/node-hot-reload
notice that you have to take care by yourself of the references used.
that means if you did : var x=require('foo'); y=x;z=x.bar; and hot reloaded
it.
it means you have to replace the references stored in x, y and z. in the hot reaload callback function.
some people confuse hot reload with auto restart
my nodejs-autorestart module also has upstart integration to enable auto start on boot.
if you have a small app auto restart is fine, but when you have a large app hot reload is more suitable. simply because hot reload is faster.
Also I like my node-inflow module.
Here's a low tech method for use in Windows. Put this in a batch file called serve.bat:
#echo off
:serve
start /wait node.exe %*
goto :serve
Now instead of running node app.js from your cmd shell, run serve app.js.
This will open a new shell window running the server. The batch file will block (because of the /wait) until you close the shell window, at which point the original cmd shell will ask "Terminate batch job (Y/N)?" If you answer "N" then the server will be relaunched.
Each time you want to restart the server, close the server window and answer "N" in the cmd shell.
my app structure:
NodeAPP (folder)
|-- app (folder)
|-- all other file is here
|-- node_modules (folder)
|-- package.json
|-- server.js (my server file)
first install reload with this command:
npm install [-g] [--save-dev] reload
then change package.json:
"scripts": {
"start": "nodemon -e css,ejs,js,json --watch app"
}
now you must use reload in your server file:
var express = require('express');
var reload = require('reload');
var app = express();
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() {
console.log( 'server is running on port ' + app.get('port'));
});
reload(server, app);
and for last change, end of your response send this script:
<script src="/reload/reload.js"></script>
now start your app with this code:
npm start
You can do it with browser-refresh. Your node app restarts automatically, your result page in browser also refreshes automatically. Downside is that you have to put js snippet on generated page. Here's the repo for the working example.
const http = require('http');
const hostname = 'localhost';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
res.write('Simple refresh!');
res.write(`<script src=${process.env.BROWSER_REFRESH_URL}></script>`);
res.end();
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
if (process.send) {
process.send({ event: 'online', url: `http://${hostname}:${port}/` })
}
});
Not necessary to use nodemon or other tools like that. Just use capabilities of your IDE.
Probably best one is IntelliJ WebStorm with hot reload feature (automatic server and browser reload) for node.js.
I have tried pm2 : installation is easy and easy to use too; the result is satisfying. However, we have to take care of which edition of pm2 that we want. pm 2 runtime is the free edition, whereas pm2 plus and pm2 enterprise are not free.
As for Strongloop, my installation failed or was not complete, so I couldn't use it.
If your talking about server side NodeJS hot-reloading, lets say you wish to have an Javascript file on the server which has an express route described and you want this Javascript file to hot reload rather than the server re-starting on file change then razzle can do that.
An example of this is basic-server
https://github.com/jaredpalmer/razzle/tree/master/examples/basic-server
The file https://github.com/jaredpalmer/razzle/blob/master/examples/basic-server/src/server.js will hot-reload if it is changed and saved, the server does not re-start.
This means you can program a REST server which can hot-reload using this razzle.
it's quite simple to just do this yourself without any dependency... the built in file watcher have matured enough that it dose not sucks as much as before
you don't need any complicated child process to spawn/kill & pipe std to in/out... you just need a simple web worker, that's all! A web Worker is also what i would have used in browsers too... so stick to web techniques! worker will also log to the console
import { watch } from 'node:fs/promises'
import { Worker } from 'node:worker_threads'
let worker = new Worker('./app.js')
async function reloadOnChange (dir) {
const watcher = watch(dir, { recursive: true })
for await (const change of watcher) {
if (change.filename.endsWith('.js')) {
worker.terminate()
worker = new Worker('./app.js')
}
}
}
// All the folder to watch for
['./src', './lib', './test'].map(reloadOnChange)
this might not be the best solution where you use anything else other than javascript and do not depend on some build process.
Use this:
function reload_config(file) {
if (!(this instanceof reload_config))
return new reload_config(file);
var self = this;
self.path = path.resolve(file);
fs.watchFile(file, function(curr, prev) {
delete require.cache[self.path];
_.extend(self, require(file));
});
_.extend(self, require(file));
}
All you have to do now is:
var config = reload_config("./config");
And config will automatically get reloaded :)
loaddir is my solution for quick loading of a directory, recursively.
can return
{ 'path/to/file': 'fileContents...' }
or
{ path: { to: { file: 'fileContents'} } }
It has callback which will be called when the file is changed.
It handles situations where files are large enough that watch gets called before they're done writing.
I've been using it in projects for a year or so, and just recently added promises to it.
Help me battle test it!
https://github.com/danschumann/loaddir
You can use auto-reload to reload the module without shutdown the server.
install
npm install auto-reload
example
data.json
{ "name" : "Alan" }
test.js
var fs = require('fs');
var reload = require('auto-reload');
var data = reload('./data', 3000); // reload every 3 secs
// print data every sec
setInterval(function() {
console.log(data);
}, 1000);
// update data.json every 3 secs
setInterval(function() {
var data = '{ "name":"' + Math.random() + '" }';
fs.writeFile('./data.json', data);
}, 3000);
Result:
{ name: 'Alan' }
{ name: 'Alan' }
{ name: 'Alan' }
{ name: 'Alan' }
{ name: 'Alan' }
{ name: '0.8272748321760446' }
{ name: '0.8272748321760446' }
{ name: '0.8272748321760446' }
{ name: '0.07935990858823061' }
{ name: '0.07935990858823061' }
{ name: '0.07935990858823061' }
{ name: '0.20851597073487937' }
{ name: '0.20851597073487937' }
{ name: '0.20851597073487937' }
another simple solution is to use fs.readFile instead of using require
you can save a text file contaning a json object, and create a interval on the server to reload this object.
pros:
no need to use external libs
relevant for production (reloading config file on change)
easy to implement
cons:
you can't reload a module - just a json containing key-value data
For people using Vagrant and PHPStorm, file watcher is a faster approach
disable immediate sync of the files so you run the command only on save then create a scope for the *.js files and working directories and add this command
vagrant ssh -c "/var/www/gadelkareem.com/forever.sh restart"
where forever.sh is like
#!/bin/bash
cd /var/www/gadelkareem.com/ && forever $1 -l /var/www/gadelkareem.com/.tmp/log/forever.log -a app.js
I recently came to this question because the usual suspects were not working with linked packages. If you're like me and are taking advantage of npm link during development to effectively work on a project that is made up of many packages, it's important that changes that occur in dependencies trigger a reload as well.
After having tried node-mon and pm2, even following their instructions for additionally watching the node_modules folder, they still did not pick up changes. Although there are some custom solutions in the answers here, for something like this, a separate package is cleaner. I came across node-dev today and it works perfectly without any options or configuration.
From the Readme:
In contrast to tools like supervisor or nodemon it doesn't scan the filesystem for files to be watched. Instead it hooks into Node's require() function to watch only the files that have been actually required.
const cleanCache = (moduleId) => {
const module = require.cache[moduleId];
if (!module) {
return;
}
// 1. clean parent
if (module.parent) {
module.parent.children.splice(module.parent.children.indexOf(module), 1);
}
// 2. clean self
require.cache[moduleId] = null;
};

Categories