Display gulp --tasks from within task - node.js

I want to dump out a message within the default gulp task to tell the user to select a task, but then list out the tasks below this message the same way which gulp --tasks does.
Can't seem to find anything on Google which will do this without additional plugins, which I want to avoid, if there a way?
exports.default = (cb) => {
log(chalk.bgRed('Please run a task, a list has been provided below.'));
// dump out tasks here
cb();
};

If you just want to log the list of the gulp tasks, you can use child_process to execute gulp --task
const exec = require('child_process').execSync
exec('gulp --tasks', { stdio: 'inherit' })

Related

Unable to read a continuous data stream from a node child process in Node.js

First things first. The goal I want to achieve:
I have two processes:
the first one is webpack that just watches for file changes and pushes the bundled files into the dist/ directory
the second process (Shopify CLI) watches for any file changes in the dist/ directory and pushes them to a remote destination
My goal is to have only one command (like npm run start) which simultaneously runs both processes without printing anything to the terminal so I can print custom messages. And that's where the problem starts:
How can I continuously read child process terminal output?
Printing custom messages for webpack events at the right time is pretty easy, since webpack has a Node API for that. But the Shopify CLI only gives me the ability to capture their output and process it.
Normally, the Shopify CLI prints something like "Finished Upload" as soon as the changed file has been pushed. It works perfectly fine for the first time but after that, nothing is printed to the terminal anymore.
Here is a minimal representation of what my current setup looks like:
const spawn = require('spawn');
const childProcess = spawn('shopify', ['theme', 'serve'], {
stdio: 'pipe',
});
childProcess.stdout.on('data', (data) => {
console.log(data);
});
childProcess.stderr.on('data', (data) => {
// Just to make sure there are no errors
console.log(data);
});

Installing NPM Modules via the Frontend

I am working on an app wherein I would like to be able to install NPM modules via the frontend. I have no idea, though, how to do so. That is, I know how to do CRUD actions via the front end, but I don't know how to either interact with the command line or run command line functions via the front end.
Are there packages that can help with this or is this built into Node.js somehow?
In short, how can I connect my front-end to my backend in such a way that I can install an NPM package?
What you want is the child_process module. It's built-in so you don't need to install any additional module.
Mostly what you're looking for is either spawn() or exec().
For example, if you want to run npm install some_module you can do:
const { exec } = require('child_process');
let command = 'npm install some_module';
let options = { cwd: '/path/to/node/project' };
exec(command, options, (error, stdout, stderr) => {
// Do anything you want with program output here:
console.log('output:', stdout, stderr);
});
You may check the documentation for child_process in Node JS:
Child Process
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
The key difference between exec() and spawn() is how they return the data. As exec() stores all the output in a buffer, it is more memory intensive than spawn(), which streams the output as it comes.
Generally, if you are not expecting large amounts of data to be returned, you can use exec() for simplicity. Good examples of use-cases are creating a folder or getting the status of a file. However, if you are expecting a large amount of output from your command, then you should use spawn()
The easiest way to install an npm package via "the front end" is to have node spawn npm as a child process based off of the package name that the client provides.
var child = require('child_process').exec(`npm i ${package_name}`);
child.on('exit',function(){
//npm finished
});
This should install the module given that package_name is the name of the npm package, in the same directory that the script is running in. In terms of getting the package name from the front end to the back end, there are several different ways to do that.
You cannot run the your backend application without NPM modules installed, one thing that I think you can do is to make a plain nodejs file without any modules, which will receive the args when invoked, and you can use that args to the required modules, because that file will run with just core modules

How to create a "rebuild" task in gulp?

gulp.task('build', function () {
// Build, generate files...
});
gulp.task('clear', function () {
// Delete generated files.
});
gulp.task('rebuild', function () {
// First: clear
// Then: build
});
In most case, "build" task doesn't need previous generated files to be removed (which slows the build process).
But there is times when I want to run "clear" and "build" task in one command:
Of course "rebuild" task depends on "clear" and "build", but if I use dependency hint like
gulp.task('rebuild', ['clear', 'build']);
the two tasks run asynchronously, which results in unexpected problem!
The easy way to solve this problem is run:
gulp clear
gulp build
but a single command
gulp rebuild
is easier, right? :P
By the way, gulp.series() has been deprecated...
It's not perfect but should work. Extract build task into a function. Then excute in the build and rebuild tasks.
var build = function() {
return gulp.src().concat();
}
gulp.task('build', function () {
return build();
});
gulp.task('clear', function () {
// Delete generated files.
});
gulp.task('rebuild', ['clear'], function () {
return build();
});
My solution isn't via Gulp as you requested, but NPM may be a better choice since you probably have NPM right there handy. All you're trying to do is save a few keystrokes.
Add an NPM script "rg" (rebuild-gulp) which does what you need:
"scripts": {
"rg": "gulp clear && gulp build"
}
Invoke with npm run rg

Format output of spawned gulp process like the parent process

Usecase: On large projects it can be nice to separate small projects into folders of their own with their own build process.
The following setup basically works on both windows and mac - I get the output of the child gulp process logged in the console - only problem is that it's not colored like the output of the parent process.
var spawnCmd = require('spawn-cmd');
gulp.task('default', function () {
// Run all the parent projects tasks first
// ....
// ....
// ....
// When done, cd to child directory
process.chdir('./some-dir-that-has-a-gulpfile');
// Run `gulp` in the child directory
var child = spawnCmd.spawn('gulp', ['default']);
// And pipe the output to the current process
child.stdout.pipe(process.stdout);
});
My question is how to display the output of the child gulp process in exactly the same way as the normal gulp process.
Edit: Duplicate of Is it possible for child processes in Node.js to preserve colored output?
You should inherit the stdio of the parent process. This correctly pipes the output to the same output, with colors and all.
Because you are using gulp, you should also add the --color always flag, in order for gulp to properly detect that you want colors.
var spawnCmd = require('spawn-cmd');
gulp.task('default', function () {
// When done, cd to child directory
process.chdir('./some-dir-that-has-a-gulpfile');
// Run `gulp` in the child directory
var child = spawnCmd.spawn('gulp', ['default', '--color', 'always'], {stdio: 'inherit'});
});

Running grunt task with api, without command line

I want to create and run grunt task in node.js code for test use.
var foo = function() {
var grunt = require("grunt");
var options = {"blahblah": null} // ...creating dynamic grunt options, such as concat and jshint
grunt.initConfig(options);
grunt.registerTask('default', [/*grunt subtasks*/]);
}
But this doesn't work. Grunt doesn't seem to run any task. I'm almost sure that there is some API to run grunt task externally without command line, but don't know how to do it.
Is there any way to do it?
You can. I don't know why anyone would need to do this as currently Grunt is a command line tool. WARNING: I don't recommend running Grunt in this way. But here it is:
var grunt = require('grunt');
// hack to avoid loading a Gruntfile
// You can skip this and just use a Gruntfile instead
grunt.task.init = function() {};
// Init config
grunt.initConfig({
jshint: {
all: ['index.js']
}
});
// Register your own tasks
grunt.registerTask('mytask', function() {
grunt.log.write('Ran my task.');
});
// Load tasks from npm
grunt.loadNpmTasks('grunt-contrib-jshint');
// Finally run the tasks, with options and a callback when we're done
grunt.tasks(['mytask', 'jshint'], {}, function() {
grunt.log.ok('Done running tasks.');
});
You can get inspiration on how to run grunt from code by looking at grunt-cli which does this and which is a project maintained by the grunt folks.
Grunt is launched from code in grunt-cli/bin/grunt and you can read more about the options in grunt/lib/grunt/cli.js.
I use it in a private project like this:
var grunt = require("grunt");
grunt.cli({
gruntfile: __dirname + "/path/to/someGruntfile.js",
extra: {key: "value"}
});
The key "extra" will be available from inside the gruntfile as grunt.option("extra")
Here is a bloggpost that describes an alternative way to run a grunt task: http://andrewduthie.com/2014/01/14/running-grunt-tasks-without-grunt-cli/

Resources