is it possible to hook the exit sign of an npm script?
"scripts": {
"serve": "docker-compose up && npm start"
}
I want to be able to call docker-compose down when exiting the script with ctrl+c
With a shell script, this is possible by 'trapping' the signed exit 0
#!/bin/bash
trap 'docker-compose down ; echo Stopped ; exit 0' SIGINT
docker-compose up &
npm start
done
I would rather not use a shell script so it can be run on other OS than Unix like systems.
I was able to use your initial script in an npm script (package.json) when running docker compose in the background (-d).
"scripts": {
...
"up": "trap 'docker-compose down ; echo Stopped ; exit 0' SIGINT; docker-compose up -d && npm start"
...
}
I would rather not use a shell script so it can be run on other OS than Unix like systems.
You can write your script in Node.js to make it compatible with any OS where npm start can be run.
#!/usr/bin/env node
'use strict';
const childProcess = require('child_process');
childProcess.spawnSync('docker-compose', ['up'], { stdio: 'inherit'});
process.on('SIGINT', () => {
console.log('SIGINT caught, running docker-compose down');
childProcess.spawnSync('docker-compose', ['down'], { stdio: 'inherit' });
process.exit(0);
});
console.log('go ahead, press control-c');
childProcess.spawnSync('npm', ['start'], { stdio: 'inherit' });
Related
I'm using vue 3 with jest for unit tests. My component is in the .vue file, with js and css in separate files and included in the .vue via src=:
<template>
<div>my library</div>
</template>
<script src="./library.js"></script>
<style src="./library.css"></style>
This works well for the most part, e.g. npm run serve reloads and refreshes everything as I save changes to those files.
However, when I run npm run test:unit the tests are running against a stale (cached?) version of my library.js. My package.json includes in scripts: "test:unit": "vue-cli-service test:unit",. I suspect it is cached because if I add a comment to the .vue file, it runs against the correct version of the .js, but if I remove the comment (so the file matches the previous version) then it runs against the stale .js again.
Possibly interesting is that running vue-cli-service test:unit --watch does re-run the tests when I change the .js file, but it runs against the stale version and not the new version that triggers the re-run.
The only workaround I seem to have is to append to a dummy comment in the .vue file, which is annoying. Or move to SFC which I find annoying because editor support for the different sections isn't as good as it is with separate files.
How can I get npm / vue-cli-service to bypass this apparent caching? Or is there a way to clear the cache?
The script below will reproduce the issue. Note in the output at the bottom that the tests are run three times:
On the first run the output should include "hello created 1".
Then the .js file is edited to change that string so that on the second run the output should be "hello created 2". However, when I run this script it provides "hello created 1" on both test runs.
Then the .vue file is edited to change a dummy comment. On the third run, the output is "hello created 2" as expected.
#!/bin/bash
if [[ -f package.json ]]
then
echo "'cd ..' and rerun this script"
echo "you need to be in the parent directory to the project directory"
exit 1
fi
if [[ ! -d utfail ]]
then
vue create -p __default_vue_3__ utfail
cd utfail
# specific versions are based on what I was using originally
npm install vue#3.0.3
npm install -D #vue/test-utils#2.0.0-beta.11
npm install -D #vue/compiler-sfc#3.0.3
npm install -D vue-jest#5.0.0-alpha.7
npm install -D #vue/cli-plugin-unit-jest#4.5.9
npm install -D typescript#3.9.7
else
cd utfail
fi
# hack: replace the default lint script with test:unit
sed -i -e 's/^.*"lint":.*$/ "test:unit": "vue-cli-service test:unit"/' package.json
cat <<EOF > jest.config.js
module.exports = {
moduleFileExtensions: ["js", "json", "vue"],
preset: '#vue/cli-plugin-unit-jest',
transform: {
'^.+\\.js$': "babel-jest",
'^.+\\.vue$': 'vue-jest'
},
"automock": false,
"setupFiles": [
"./setupJest.js"
]
}
EOF
cat <<EOF > src/components/HelloWorld.vue
<!-- dummy comment 1 -->
<template>
<div class="hello">blah</div>
</template>
<script src="./helloworld.js"></script>
<style></style>
EOF
cat <<EOF > src/components/helloworld.js
export default {
name: 'HelloWorld',
created() {
console.log("hello created 1")
}
}
EOF
cat <<EOF > setupJest.js
// require('jest-fetch-mock').enableMocks()
EOF
mkdir -p __tests__
cat <<EOF > __tests__/app.spec.js
import { mount } from '#vue/test-utils'
import App from './../src/App.vue'
import HelloWorld from './../src/components/HelloWorld.vue'
describe('HelloWorld', () => {
beforeEach(() => {
})
it('exists', () => {
const wrapper = mount(HelloWorld)
expect(wrapper.exists()).toBe(true)
})
})
EOF
printf '\n*\n*\n*\n*** about to run tests (round 1)\n*\n*\n*\n'
grep 'hello created' src/components/helloworld.js
npm run test:unit
sed -i -e '/hello created/s/1/2/' src/components/helloworld.js
printf '\n*\n*\n*\n*** about to run tests (round 2)\n*\n*\n*\n'
grep 'hello created' src/components/helloworld.js
npm run test:unit
sed -i -e '/dummy comment/s/1/2/' src/components/HelloWorld.vue
printf '\n*\n*\n*\n*** about to run tests (round 3)\n*\n*\n*\n'
grep 'hello created' src/components/helloworld.js
npm run test:unit
Configure Jest to disable the test cache:
// jest.config.js
module.exports = {
cache: false,
}
Or add the --no-cache flag to the test:unit npm script:
// package.json
{
"scripts": {
"test:unit": "vue-cli-service test:unit --no-cache"
}
}
Or clear the cache with this command from the project's root directory:
npx jest --clearCache
I'm working on an API that runs in the background, we have some helper methods that need to be launch as such nodemon --exec babel-node commands/router.js to display all routes for instance.
Note that node commands/router.js cannot work as we need babel
At the moment, the method runs without problem but I need to stop nodemon from running after execution. I understand that nodemon is supposed to keep running after execution but our project is designed as such and I need to use nodemon for execution and then kill it.
How can I kill/stop nodemon after run?
Code
package.json
{
...
scripts: {
"start": "nodemon --exec babel-node index.js",
"router": "nodemon --exec babel-node commands/router.js"
},
...
}
router.js
const script = () => {
// Fetch and display routes
}
script()
EDIT:
From Nodemon documentation, the correct way to handle this use case is to manually kill the process with the gracefulShutdown method, like so:
process.once('SIGUSR2', function () {
gracefulShutdown(function () {
process.kill(process.pid, 'SIGUSR2');
});
});
You can read more here.
We found a solution by removing nodemon and manually ending scripts with process.exit()
Final code
package.json
{
...
scripts: {
"start": "nodemon --exec babel-node index.js", // <= still called with nodemon
"router": "babel-node commands/router.js"
},
...
}
router.js
const script = () => {
// Fetch and display routes
process.exit()
}
script()
I'm creating an example project for an open source framework. For my demo to run, some of it's dependencies must be running local servers on other ports.
I'd rather just provide them a single command to run instead of telling them to open multiple terminals and run multiple commands in each.
What is the best/most proper/most elegant way to go about this?
This is how I accomplish this for two web servers. You should be able to play with more &'s and fg's to get more servers.
package.json:
{
"scripts": {
"start": "node node_modules/something/server.js & node server.js && fg
}
}
So the user only has to run npm install and then npm start to run two servers in one terminal and ctrl-c kills both.
Breakdown:
node node_modules/something/server.js & run this server in the background
node server.js && run my server in the foreground
fg move the most recently backgrounded shell into the foreground
If you use the npm package call 'concurrently' set up your package.json file as below
you can use the following 3 commands
run only server
npm run server
run only client
npm run client
run both
npm run dev
"scripts": {
"server": "nodemon server.js --ignore client",
"client": "npm start --prefix client",
"dev": "concurrently \"npm run server\" \"npm run client\""
},
For those who want this case:
If you want to run a single script that will open multiple terminals and run different nodejs servers on each you can do something like (this is for windows.. for other os you can change command):
You can write a single nodejs file that will start all your other servers in different terminal windows
startAllServers.js:
const child_process = require('child_process');
// commands list
const commands = [
{
name: 'Ap1-1',
command: 'cd ./api1 && start nodemon api1.js'
},
{
name: 'Ap1-2',
command: 'cd ./api2 && start nodemon api2.js'
}
];
// run command
function runCommand(command, name, callback) {
child_process.exec(command, function (error, stdout, stderr) {
if (stderr) {
callback(stderr, null);
} else {
callback(null, `Successfully executed ${name} ...`);
}
});
}
// main calling function
function main() {
commands.forEach(element => {
runCommand(element.command, element.name, (err, res) => {
if (err) {
console.error(err);
} else {
console.log(res);
}
});
});
}
// call main
main();
Use concurrently npm package.
concurrently "node server.js" "node client.js"
This allows you to write multiple commands with clean output in one go. And they don't just have to be node servers. You can combine any bash commands.
I have a gulp.js file that includes:
gulp.task('default', ['watch']);
Which starts up the watch task
gulp.task('watch', function(){
gulp.watch(productionScripts, ['autoConcat']);
});
Then on any saved changes to files in productionScripts, the watch task will concat the files.
What I would like to do, is in my package.json, I would like to spool up this watch when I type npm start (this already starts my node server).
package.json
"start": "node server.js",
UPDATE--------
Ben(b3nj4m.com), I tried what you stated. The watch and server start up. However, everything runs twice (probably due to the editor, not related), but I do lose my server log when I start it up with gulp.
[15:31:18] Starting 'autoConcat'...
[15:31:18] Finished 'autoConcat' after 147 ms
[15:31:19] Starting 'autoConcat'...
[15:31:19] Finished 'autoConcat' after 138 ms
[15:31:20] Starting 'autoConcat'...
[15:31:20] Finished 'autoConcat' after 127 ms
[15:31:23] Starting 'autoConcat'...
It's like there is a loop between the server restarting on a change, and the concatenated file changing.
You could run your server from your gulpfile:
var child = require('child_process');
var fs = require('fs');
gulp.task('default', ['server', 'watch']);
gulp.task('server', function() {
var server = child.spawn('node', ['server.js']);
var log = fs.createWriteStream('server.log', {flags: 'a'});
server.stdout.pipe(log);
server.stderr.pipe(log);
});
gulp.task('watch', function(){
gulp.watch(productionScripts, ['autoConcat']);
});
Then change your npm start definition to look like:
"scripts": {
"start": "gulp"
}
You could concatenate multiple tasks in your start in package.json using the package concurrently as such:
{
"start": "concurrent \"node server.js\" \"gulp\" "
}
And run npm start from your terminal. This would execute all statements within start.
For references: https://www.npmjs.com/package/concurrently
EDIT:
As pointed out by #Josh in the comments, the CLI name now matches the package name. Hence, you could write the script as:
{
"start": "concurrently \"node server.js\" \"gulp\" "
}
I have something like this in one of my projects. Note that it will background both processes - you can use ps to get the ID and stop it with kill <pid>.
"scripts": {
"start": "{ gulp watch & node server.js & }"
}
To disable logging, too:
"scripts": {
"start": "{ gulp watch --silent & node server.js & }"
}
One best practice to consider is to use nodemon and gulp-nodemon and then like the accepted answer, trigger the gulp script from npm with npm start. It's blazing fast and you get the node server restarted on file changes. For example:
gulpfile.js
var gulp = require('gulp');
var nodemon = require('gulp-nodemon');
...
var nodemonOptions = {
script: 'bin/www.js',
ext: 'js',
env: { 'NODE_ENV': 'development' },
verbose: false,
ignore: [],
watch: ['bin/*', 'routes/*', 'app.js']
};
gulp.task('start', function () {
nodemon(nodemonOptions)
.on('restart', function () {
console.log('restarted!')
});
});
package.json
{
...
"scripts": {
"start": "gulp start"
},
"devDependencies": {
"gulp": "^3.9.0",
"gulp-nodemon": "^2.0.4"
}
}
I have the following script with my nodeJS.
"scripts": {
"start": "grunt",
"test": "node --debug --harmony node_modules/grunt-cli/bin/grunt test"
}
I am running node v0.11.13 so I need to set --harmony flag. On grunt the tests are configured right if I start them with npm test, but I would prefer to have it all in a gruntfile. Is there a way to configure grunt to start the server and also run the test ?
You can create an alias task that spawns grunt with those node flags, like such:
grunt.registerTask('debug', function() {
var done = this.async();
// Specify tasks to run spawned
var tasks = Array.prototype.slice.call(arguments, 0);
grunt.util.spawn({
// Use the existing node path
cmd: process.execPath,
// Add the flags and use process.argv[1] to get path to grunt bin
args: ['--debug', '--harmony', process.argv[1]].concat(tasks),
// Print everything this process is doing to the parent stdio
opts: { stdio: 'inherit' }
}, done);
});
Then you can start the server and run tests with: grunt default debug:test
Or really any combination:
grunt server test (runs both without node flags)
grunt debug:server:test (runs both with node flags).