I'm trying to create a Yeoman generator.
I need to know how is possibile to call a function after bowerInstall finished to download my package.
Here is my piece of code:
Generator.prototype.myFiles = function myFiles() {
console.info('download');
this.bowerInstall('my-package-name', { save: true });
};
Generator.prototype.moveFiles = function moveFiles() {
console.info('move');
};
After download I need to move some files so I have to wait until all package is downloaded.
But function moveFiles is called immediately when the download start not when the download is finished.
Is there anyway to call moveFiles after download my package?
Thanks
First of all, you should read the documentation to understand how Yeoman generators works. For this question, you're asking about the task queue and the priorities: http://yeoman.io/authoring/running-context.html
Installation actions happens in the install. So to do anything after the installation complete, you need to add your tasks inside the end priority.
In your case, it'd look like:
Generator.prototype.install = function myFiles() {
console.info('download');
this.bowerInstall('my-package-name', { save: true });
};
Generator.prototype.end = function moveFiles() {
console.info('move');
};
Related
I have a set of Gulp (v4) tasks that do things like compile Webpack and Sass, compress images, etc. These tasks are automated with a "watch" task while I'm working on a project.
When my watch task is running, if I save a file, the "default" set of tasks gets ran. If I save again before the "default" task finishes, another "default" task begins, resulting in multiple "default" tasks running concurrently.
I've fixed this by checking that the "default" task isn't running before triggering a new one, but this has caused some slow down issues when I save a file, then rapidly make another minor tweak, and save again. Doing this means that only the first change gets compiled, and I have to wait for the entire process to finish, then save again for the new change to get compiled.
My idea to circumvent this is to kill all the old "default" tasks whenever a new one gets triggered. This way, multiples of the same task won't run concurrently, but I can rely on the most recent code being compiled.
I did a bit of research, but I couldn't locate anything that seemed to match my situation.
How can I kill all the "old" gulp tasks, without killing the "watch" task?
EDIT 1: Current working theory is to store the "default" task set as a variable and somehow use that to kill the process, but that doesn't seem to work how I expected it to. I've placed my watch task below for reference.
// watch task, runs through all primary tasks, triggers when a file is saved
GULP.task("watch", () => {
// set up a browser_sync server, if --sync is passed
if (PLUGINS.argv.sync) {
CONFIG_MODULE.config(GULP, PLUGINS, "browsersync").then(() => {
SYNC_MODULE.sync(GULP, PLUGINS, CUSTOM_NOTIFIER);
});
}
// watch for any changes
const WATCHER = GULP.watch("src/**/*");
// run default task on any change
WATCHER.on("all", () => {
if (!currently_running) {
currently_running = true;
GULP.task("default")();
}
});
// end the task
return;
});
https://github.com/JacobDB/new-site/blob/4bcd5e82165905fdc05d38441605087a86c7b834/gulpfile.js#L202-L224
EDIT 2: Thinking about this more, maybe this is more a Node.js question than a Gulp question – how can I stop a function from processing from outside that function? Basically I want to store the executing function as a variable somehow, and kill it when I need to restart it.
There are two ways to set up a Gulp watch. They look very similar, but have the important difference that one supports queueing (and some other features) and the other does not.
The way you're using, which boils down to
const watcher = watch(<path glob>)
watcher.on(<event>, function(path, stats) {
<event handler>
});
uses the chokidar instance that underlies Gulp's watch().
When using the chokidar instance, you do not have access to the Gulp watch() queue.
The other way to run a watch boils down to
function watch() {
gulp.watch(<path>, function(callback) {
<handler>
callback();
});
}
or more idiomatically
function myTask = {…}
const watch = () => gulp.watch(<path>, myTask)
Set up like this watch events should queue the way you're expecting, without your having to do anything extra.
In your case, that's replacing your const WATCHER = GULP.watch("src/**/*"); with
GULP.watch("src/**/*", default);
and deleting your entire WATCHER.on(…);
Bonus 1
That said, be careful with recursion there. I'm extrapolating from your use of a task named "default"… You don't want to find yourself in
const watch = () => gulp.watch("src/**/*", default);
const default = gulp.series(clean, build, serve, watch);
Bonus 2
Using the chokidar instance can be useful for logging:
function handler() {…}
const watcher = gulp.watch(glob, handler);
watcher.on('all', (path, stats) => {
console.log(path + ': ' + stats + 'detected') // e.g. "src/test.txt: change detected" is logged immediately
}
Bonus 3
Typically Browsersync would be set up outside of the watch function, and the watch would end in reloading the server. Something like
…
import browserSync from 'browser-sync';
const server = browserSync.create();
function serve(done) {
server.init(…);
done();
}
function reload(done) {
server.reload();
done();
}
function changeHandler() {…}
const watch = () => gulp.watch(path, gulp.series(changeHandler, reload);
const run = gulp.series(serve, watch);
try installing gulp restart
npm install gulp-restart
As #henry stated, if you switch to the non-chokidar version you get queuing for free (because it is the default). See no queue with chokidar.
But that doesn't speed up your task completion time. There was an issue requesting that the ability to stop a running task be added to gulp - how to stop a running task - it was summarily dealt with.
If one of your concerns is to speed up execution time, you can try the lastRun() function option. gulp lastRun documentation
Retrieves the last time a task was successfully completed during the
current running process. Most useful on subsequent task runs while a
watcher is running.
When combined with src(), enables incremental builds to speed up
execution times by skipping files that haven't changed since the last
successful task completion.
const { src, dest, lastRun, watch } = require('gulp');
const imagemin = require('gulp-imagemin');
function images() {
return src('src/images/**/*.jpg', { since: lastRun(images) })
.pipe(imagemin())
.pipe(dest('build/img/'));
}
exports.default = function() {
watch('src/images/**/*.jpg', images);
};
Example from the same documentation. In this case, if an image was successfully compressed during the current running task, it will not be re-compressed. Depending on your other tasks, this may cut down on your wait time for the queued tasks to finish.
I currently use nodemon or supervisor for automatic server restarting and automatic test cases execution. But currently my requirement is to run specific test cases when certain files are changed. For example if app\models\user.js is modified, I want test\model\user-test.js to be executed.
I order to achieve that I need to identify which are the files that are modified. How can I achieve that using nodemon or supervisor?
I dont know if you can do that with nodemon or supervisor, but you always could write your own:
var watch = require('watch');
function methodToDoTestOnFile(file) {
//IMPLEMENT
}
watch.createMonitor(filesToWatch, function (monitor) {
monitor.on('changed', function (f) {
//do some test on f
methodToDoTestOnFile(f)
});
});
I'm getting frustrated with part of a Yeoman Generator I'm building. As it's my first, I have no doubt I'm missing something obvious, but here it goes.
Simply put, I'm trying to log a message, Do Things™ and then log another message only when those things have been done.
Here's the method:
repos: function () {
var self = this;
this.log(highlightColour('Pulling down the repositories'));
// Skeleton
this.remote('user', 'skeleton', 'master', function(err, remote) {
if (!err) {
remote.bulkDirectory('.', self.destinationRoot());
} else {
self.log('\n');
self.log(alertColour('Failed to pull down Skeleton'));
repoErr = true;
}
});
//
// Three more near identical remote() tasks
//
if (!repoErr) {
self.log(successColour('Success!'));
self.log('\n');
} else {
self.log(alertColour('One or more repositories failed to download!'));
}
},
Each of the individual remote() tasks are working fine, but I get both the first and last self.log() messages before the file copying happens. It seems trivial, but I simply want the success message to come after everything has been completed.
For example, in the terminal I see:
Pulling down the repositories
Success!
file copying results
It should be:
Pulling down the repositories
file copying results
Success!
I thought it could be something to do with using this.async() with done() at the end of each remote() task, and I tried that, but whenever I do, none of the code fires at all.
I've even tried breaking everything (including the messages) into separate methods, but still no luck.
Such a simple goal, but I'm out of ideas! I'd be grateful for your help!
EDIT: In case you're wondering, I know the messages are coming first because any alerts regarding file conflicts are coming after the messages :)
This is not an issue related to Yeoman. You have asynchronous code, but you're handling it as if it was synchronous.
In the example you posted here, just do the logging as part of this.remote callback:
repos: function () {
var self = this;
this.log(highlightColour('Pulling down the repositories'));
// Skeleton
this.remote('user', 'skeleton', 'master', function(err, remote) {
if (!err) {
remote.bulkDirectory('.', self.destinationRoot());
self.log(successColour('Success!'));
self.log('\n');
} else {
self.log('\n');
self.log(alertColour('Failed to pull down Skeleton'));
self.log(alertColour('One or more repositories failed to download!'));
}
});
},
Maybe your actual use case is more complex; in this case you can use a module like async (or any other alternative) to handle more complex async flow. Either way, Yeoman doesn't provide helpers to handle asynchronous code as this is the bread and butter of Node.js.
I need to provide with error logging in my Windows 8 Metro application developed in Html/WinJS
so that user can get to know what went wrong from a log file located in the app's local folder.
I have checked WinJS.log(message, tags, type); which will write to the console but not able to find anything via which i can get it on a local file.
What is the best way to do the same and if there are any 3rd party libraries/js available for error logging in metro applications developed in WinJS ?
Thanks in advance.
WinJS.log is just a placeholder. Without proper initialization it does nothing (in fact, it's not set at all). If you just call WinJS.Utilities.startLog() at your application startup, it defaults to wiring up a logger for the console.
If you want something more complete, you'll need to build it. I've built a small sample below.
function startFileLog() {
// choose where the file will be stored:
var fileDestination = Windows.Storage.ApplicationData.current.localFolder;
var logger = new WinJS.Promise(function (complete) {
var logfilename = new Date().toISOString().replace(/[:-]/g, "");
logfilename = "log-" + logfilename + ".log";
fileDestination.createFileAsync(logfilename,
Windows.Storage.CreationCollisionOption.generateUniqueName)
.done(function (file) {
complete(file);
});
});
var actionFn = function (message, tag, type) {
logger.then(function (file) {
var m = WinJS.Utilities.formatLog(message, tag, type);
Windows.Storage.FileIO.appendTextAsync(file, m).done();
});
};
WinJS.Utilities.startLog({ action: actionFn });
}
By calling the startFileLog function above, it creates a new log file (by using the current Date/time as part of the file name) within a promise. Then, a function called actionFn is passed to the startLog function. By passing an optional property of the options named action, the default "write to console" behavior is overwritten (if you didn't want it overwritten, you could call startLog without the action, then copy the function reference from WinJS.log and replace it with your own function, and call it as well). When the log function is called, it now calls actionFn which uses the promise created earlier to verify that the log file is in fact available for writing before continuing. If it's not ready yet, it will be queued. So, this means that even though the file may not be ready immediately, the log will, in the end, contain the results you'd expect. There would be a short period of time where, due to async nature of WinJS, if the application crashed before the file completely opened, that logged items will be missed. You could delay the application startup if you wanted until the file was opened by returning the logger promise:
function startFileLog() {
/// ... etc..
return logger;
}
startFileLog().then(function() {
// the application can now be assured that the log file is ready to accept
// writes ... (but again, it's all async, so a write may be missed in
// extreme cases)
});
You'd likely want to create a function at the end of your application to clean/close the log file.
I'm building a custom Yeoman generator that installs a lot of pre-processed language compilers like CoffeeScript, LESS and Jade. In the Gruntfile that my generator creates I have a build task which compiles everything. However, until that build task is run at least once, the compiled HTML, CSS and Javascript files don't exist, which can be confusing if I try to run the grunt watch/connect server after freshly scaffolding.
What is the best way to have my generator run that Grunt build step at the end of the installation? The end event that's already being used to call this.installDependencies seems like the right place to do that, but how should I communicate with Grunt?
If you follow the stack, this.installDependencies eventually works its way down to https://github.com/yeoman/generator/blob/45258c0a48edfb917ecf915e842b091a26d17f3e/lib/actions/install.js#L36:
this.spawnCommand(installer, args, cb)
.on('error', cb)
.on('exit', this.emit.bind(this, installer + 'Install:end', paths))
.on('exit', function (err) {
if (err === 127) {
this.log.error('Could not find ' + installer + '. Please install with ' +
'`npm install -g ' + installer + '`.');
}
cb(err);
}.bind(this));
Chasing this down further, this.spawnCommand comes from https://github.com/yeoman/generator/blob/master/lib/actions/spawn_command.js:
var spawn = require('child_process').spawn;
var win32 = process.platform === 'win32';
/**
* Normalize a command across OS and spawn it.
*
* #param {String} command
* #param {Array} args
*/
module.exports = function spawnCommand(command, args) {
var winCommand = win32 ? 'cmd' : command;
var winArgs = win32 ? ['/c'].concat(command, args) : args;
return spawn(winCommand, winArgs, { stdio: 'inherit' });
};
In other words, in your Generator's code, you can call this.spawnCommand anytime, and pass it the arguments you wish the terminal to run. As in, this.spawnCommand('grunt', ['build']).
So then the next question is where do you put that? Thinking linearly, you can only trust that grunt build will work after all of your dependencies have been installed.
From https://github.com/yeoman/generator/blob/45258c0a48edfb917ecf915e842b091a26d17f3e/lib/actions/install.js#L67-69,
this.installDependencies accepts a callback, so your code might look like this:
this.on('end', function () {
this.installDependencies({
skipInstall: this.options['skip-install'],
callback: function () {
this.spawnCommand('grunt', ['build']);
}.bind(this) // bind the callback to the parent scope
});
});
Give it a shot! If all goes well, you should add some error handling on top of that new this.spawnCommand call to be safe.
I've used Stephen's great answer, implemented in the following way with a custom event to keep things tidy.
MyGenerator = module.exports = function MyGenerator(args, options, config) {
this.on('end', function () {
this.installDependencies({
skipInstall: options['skip-install'],
callback: function() {
// Emit a new event - dependencies installed
this.emit('dependenciesInstalled');
}.bind(this)
});
});
// Now you can bind to the dependencies installed event
this.on('dependenciesInstalled', function() {
this.spawnCommand('grunt', ['build']);
});
};
This question is a bit old already, but i still want to make this addition if somebody missed it. Post install processes are now way easier to implement. Have a look at the run loop and use the end method where you can run all the post install things.