I have a node / angular project that uses npm for backend dependency management and bower for frontend dependency management. I'd like to use a grunt task to do both install commands. I haven't been able to figure out how to do it.
I made an attempt using exec, but it doesn't actually install anything.
module.exports = function(grunt) {
grunt.registerTask('install', 'install the backend and frontend dependencies', function() {
// adapted from http://www.dzone.com/snippets/execute-unix-command-nodejs
var exec = require('child_process').exec,
sys = require('sys');
function puts(error, stdout, stderr) { console.log(stdout); sys.puts(stdout) }
// assuming this command is run from the root of the repo
exec('bower install', {cwd: './frontend'}, puts);
});
};
When I cd into frontend, open up node, and run this code from the console, this works fine. What am I doing wrong in the grunt task?
(I also tried to use the bower and npm APIs, but couldn't make that work either.)
To install client side components during npm install at the same time than server side libs, you can add in your package.json
"dependencies": {
...
"bower" : ""
},
"scripts": {
...
"postinstall" : "bower install"
}
I prefer to make the difference between install and test/build
You need to tell grunt that you're using an async method (.exec) by calling the this.async() method, getting a callback, and calling that when exec is done.
This should work:
module.exports = function(grunt) {
grunt.registerTask('install', 'install the backend and frontend dependencies', function() {
var exec = require('child_process').exec;
var cb = this.async();
exec('bower install', {cwd: './frontend'}, function(err, stdout, stderr) {
console.log(stdout);
cb();
});
});
};
See Why doesn't my asynchronous task complete?
FYI, here is where i am for now.
You could also have taken the problem another way, i.e. let npm handle the execution of bower, and ultimately let grunt handle npm. See Use bower with heroku.
grunt.registerTask('install', 'install the backend and frontend dependencies', function() {
var async = require('async');
var exec = require('child_process').exec;
var done = this.async();
var runCmd = function(item, callback) {
process.stdout.write('running "' + item + '"...\n');
var cmd = exec(item);
cmd.stdout.on('data', function (data) {
grunt.log.writeln(data);
});
cmd.stderr.on('data', function (data) {
grunt.log.errorlns(data);
});
cmd.on('exit', function (code) {
if (code !== 0) throw new Error(item + ' failed');
grunt.log.writeln('done\n');
callback();
});
};
async.series({
npm: function(callback){
runCmd('npm install', callback);
},
bower: function(callback){
runCmd('bower install', callback);
}
},
function(err, results) {
if (err) done(false);
done();
});
});
Grunt task that does just this job (as per Sindre's solution above):
https://github.com/ahutchings/grunt-install-dependencies
Grunt task that does the bower install command :
https://github.com/yatskevich/grunt-bower-task
also , you can use
https://github.com/stephenplusplus/grunt-bower-install
to auto inject your dependencies into the index.html file
Related
I've created a webpack configuration file that builds the app, runs a dev server using webpack-dev-server and runs the test suite as below.
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 3000,
watchOptions: {
open: false
},
historyApiFallback: {
index: "/"
}
},
plugins: [
new WebpackShellPlugin({
onBuildEnd: [`./node_modules/.bin/cucumber-js ${testScope}`]
})
]
The command to start the whole thing is below
webpack-dev-server --config webpack.test.js
Everything is ok, but... what now? It never stops! I need some way to exit with a successful status when the test suite is ok. Otherwise, it would cause an infinite deploy (I use bitbucket pipelines to deploy the app).
Does someone know a way to exit the devServer execution after running a specific plugin?
Likely it is the difference between npm run start and npm start
When you use npm start, it runs node server.js which doesn't run inline with your console.
If you use npm run start, it executes the start script defined in your package.json using the shell of your operating system. Therefore, even if you are using Cygwin (or similar), it should still run it using cmd.exe.
Solution
Use npm run start instead of npm start and it should kill the process when you CTRL + C
I solved my own problem by creating a webpack plugin and added it to my config file. The plugin code is below. It runs the command I pass to it and exit the process, firing error if the command was successful or success if it was everything ok.
const spawn = require("child_process").spawn;
module.exports = class RunScriptAndExit {
constructor(command) {
this.command = command;
}
apply(compiler) {
compiler.plugin("after-emit", (compilation, callback) => {
const { command, args } = this.serializeScript(this.command);
const proc = spawn(command, args, { stdio: "inherit" });
proc.on("close", this.exit);
callback();
});
}
serializeScript(script) {
if (typeof script === "string") {
const [command, ...args] = script.split(" ");
return { command, args };
}
const { command, args } = script;
return { command, args };
}
exit(error, stdout, stderr) {
if (error) {
process.exit(1);
} else {
process.exit(0);
}
}
};
I added it to my plugin list as below:
plugins: [
new RunScriptAndExit(`./node_modules/.bin/cucumber-js ${testScope}`)
]
My Gulp was working fine until I installed browser-sync
npm install browser-sync gulp --save-dev
Then I started to get this error:
Error: Cannot find module 'lru-cache'
Which I solved using this: npm link lru-cache answer from https://github.com/npm/npm/issues/1154
However, now when I try to run gulp I get this new error:
~/Projects/starfeeder
❯ npm install browser-sync gulp --save-dev
npm WARN deprecated minimatch#2.0.10: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated node-uuid#1.4.8: Use uuid module instead
fsevents#1.1.2 install
/Users/leongaban/Projects/starfeeder/node_modules/fsevents
node install
My gulpfile if that helps:
"use strict";
const gulp = require('gulp'),
_ = require('lodash'), // https://www.npmjs.com/package/lodash
del = require('del'), // https://www.npmjs.com/package/del
fs = require('fs'), // Node file system
gutil = require('gulp-util'), // https://www.npmjs.com/package/gulp-util
htmlReplace = require('gulp-html-replace'), // https://www.npmjs.com/package/gulp-html-replace
notify = require("gulp-notify"), // https://www.npmjs.com/package/gulp-notify
runSequence = require('run-sequence'), // https://www.npmjs.com/package/run-sequence
sass = require('gulp-ruby-sass'), // https://www.npmjs.com/package/gulp-ruby-sass
sourcemaps = require('gulp-sourcemaps'); // https://www.npmjs.com/package/gulp-sourcemaps
const rootPath = process.cwd();
const paths = {
files: ['src/static/**']
};
const errorlog = err => {
gutil.log(gutil.colors.red.bold.inverse(' ERROR: '+err));
this.emit('end');
};
// Build tasks chain ///////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
gulp.task('build', function(cb) {
runSequence(
'build:app-css', // Minify and concat app styles
'build:move-files',
'build:index', // Replace scripts in index.html
'build:final', cb); // Remove app.min.js from build folder
});
gulp.task('build:move-files', () => gulp.src(paths.files).pipe(gulp.dest('starfeeder/')) );
// Preprocess SASS into CSS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
gulp.task('build:app-css', () => sass('src/sass/starfeeder.scss', { style: 'compressed' }).on('error', errorlog).pipe(gulp.dest('src/static/css/')) );
// Clear out all files and folders from build folder \\\\\\\\\\\\\\\\\\\\\\\\\\\
gulp.task('build:cleanfolder', cb => { del(['starfeeder/**'], cb); });
// Task to make the index file production ready \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
gulp.task('build:index', () => {
process.stdout.write(gutil.colors.white.inverse(' New asset paths in markup: \n'));
process.stdout.write(gutil.colors.yellow(' static/css/starfeeder.css\n'));
gulp.src('index.html')
.pipe(htmlReplace({
'app-css': 'css/starfeeder.css'
}))
.pipe(gulp.dest('starfeeder/'))
.pipe(notify('Starfeeder build created!'));
});
gulp.task('build:final', cb => {
process.stdout.write(gutil.colors.blue.bold ('###################################################### \n'));
process.stdout.write(gutil.colors.blue.inverse(' Starfeeder build created! \n'));
process.stdout.write(gutil.colors.blue.bold ('###################################################### \n'));
});
// Main Styles /////////////////////////////////////////////////////////////////
gulp.task('app-css', () => {
return sass('src/sass/starfeeder.scss', { style: 'compressed' })
.pipe(sourcemaps.init())
.on('error', errorlog)
.pipe(sourcemaps.write('./maps'))
.pipe(gulp.dest('src/static/css/'))
});
// Development watch /////////////////////////////////////////////////////////// 🤖☕️⏎→
gulp.task('watch', () => {
gulp.watch('src/sass/**/*.scss', ['app-css']).on('change', file => {
let filePath = file.path.split(rootPath);
logFileChanged(filePath[1]);
});
});
gulp.task('default', ['watch']);
I had this same issue with installing through2. The project used shrink wrap and had a package-lock.json which caused the a dependencies to break. Once I removed the lock and re-installed, all was good.
Ok so still not sure why I got those errors, but never installing browserSync again.
I had to npm link all my gulp plugins.
That work, but then it broke during the gulp build process.
Instead of doing npm link to everything, included other node modules I've never heard off. I removed browserSync and deleted my node_modules folder and did yarn(npm) install.
I tried to run a nodejs script with the built in child_process module and it works fine until i give it options. Specially when i add the env property to the options object.
let exec = require('child_process').exec;
exec('node random.js', { env: {} }, (err) => {
console.log(err);
})
Then i get this error: /bin/sh: 1: node: not found.
I have node installed with nvm, maybe that is the cause, but don't know why.
If you exec a new shell from your script this don't have the same environment of the parent shell (your script).
So you have to provide all the needed environment.
In your case I see 2 way you could do.
First: you create a node command with the full path:
let exec = require('child_process').exec;
let node_cmd = '/path/to/my/node/node';
exec(node_cmd + ' random.js', { env: {} }, (err) => {
console.log(err);
});
So you could use env variables to handle the path, or just change it when you need.
Second, pass the path variable to the command:
let exec = require('child_process').exec;
let env_variables = 'PATH='+process.env.PATH;
let cmd = env_variables + ' node random.js';
exec(cmd, { env: {} }, (err) => {
console.log(err);
});
Another way is using the dotenv package.
I need a complete guide or a good reference material to solve the running module commands within javascript file problem.
Say that I often run:
$ npm run webpack-dev-server --progress --colors -- files
How can I run this within a javascript file and execute with
$ node ./script.js
script.js
var webpackDevServer = require('webpack-dev-server');
// need help here
var result = webpackDevServer.execute({
progress: true,
colors: true,
}, files);
Answer
I do something like this for my Webpack bundles. You can simply use child_process.spawn to execute command-line programs and handle the process in a node script.
Here's an example:
var spawn = require('child_process').spawn
// ...
// Notice how your arguments are in an array of strings
var child = spawn('./node_modules/.bin/webpack-dev-server', [
'--progress',
'--colors',
'<YOUR ENTRY FILE>'
]);
child.stdout.on('data', function (data) {
process.stdout.write(data);
});
child.stderr.on('data', function (data) {
process.stdout.write(data);
});
child.on('exit', function (data) {
process.stdout.write('I\'m done!');
});
You can handle all of the events you like. This is a fairly powerful module that allows you to view the process' PID (child.pid) and even kill the process whenever you choose (child.kill()).
Addendum
A neat trick is to throw everything into a Promise. Here's a simplified example of what my version of script.js would look like:
module.exports = function () {
return new Promise(function (resolve, reject) {
var child = spawn('./node_modules/.bin/webpack', [
'-d'
]);
child.stdout.on('data', function (data) {
process.stdout.write(data);
});
child.on('error', function (data) {
reject('Webpack errored!');
});
child.on('exit', function () {
resolve('Webpack completed successfully');
});
});
}
Using this method, you can include your script.js in other files and make this code synchronous in your build system or whatever. The possibilities are endless!
Edit The child_process.exec also lets you execute command-line programs:
var exec = require('child_process').exec
// ...
var child = exec('webpack-dev-server --progress --colors <YOUR ENTRY FILES>',
function(err, stdout, stderr) {
if (err) throw err;
else console.log(stdout);
});
The accepted answer doesn't work on Windows and doesn't handle exit codes, so here's a fully featured and more concise version.
const spawn = require('child_process').spawn
const path = require('path')
function webpackDevServer() {
return new Promise((resolve, reject) => {
let child = spawn(
path.resolve('./node_modules/.bin/webpack-dev-server'),
[ '--progress', '--colors' ],
{ shell: true, stdio: 'inherit' }
)
child.on('error', reject)
child.on('exit', (code) => code === 0 ? resolve() : reject(code))
})
}
path.resolve() properly formats the path to the script, regardless of the host OS.
The last parameter to spawn() does two things. shell: true uses the shell, which appends .cmd on Windows, if necessary and stdio: 'inherit' passes through stdout and stderr, so you don't have to do it yourself.
Also, the exit code is important, especially when running linters and whatnot, so anything other than 0 gets rejected, just like in shell scripts.
Lastly, the error event occurs when the command fails to execute. When using the shell, the error is unfortunately always empty (undefined).
Do you need it to be webpack-dev-server? There is an equivalent webpack-dev-middleware for running within node/express:
'use strict';
let express = require('express');
let app = new express();
app.use(express.static(`${__dirname}/public`));
let webpackMiddleware = require('webpack-dev-middleware');
let webpack = require('webpack');
let webpackConfig = require('./webpack.config.js');
app.use(webpackMiddleware(webpack(webpackConfig), {}));
app.listen(3000, () => console.log(`Server running on port 3000...`));
https://github.com/webpack/webpack-dev-middleware
At the moment I'm using the "gulp-run" plugin to run a .bat file. That plugin has now been deprecated and I'm looking for the best way to execute the .bat now.
Current code:
var gulp = require('gulp');
var run = require('gulp-run');
module.exports = function() {
run('c:/xxx/xxx/runme.bat').exec();
};
Solution as per #cmrn suggestion:
var exec = require('child_process').exec;
var batchLocation = 'c:/xxx/xxx/runme.bat';
gulp.task('task', function (cb) {
exec(batchLocation, function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
cb(err);
});
})
If you need to run the script as part of a gulp stream (i.e. in pipe()) you can use gulp-exec. If not, you can just use child_process.exec as described in the gulp-exec README, copied below.
var exec = require('child_process').exec;
gulp.task('task', function (cb) {
exec('ping localhost', function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
cb(err);
});
})