Use Grunt newer with custom task - javascript

I'm trying to use grunt-newer to watch files from a folder and if any is changed, trigger a custom task.
I have something like this in my Gruntfile.js:
grunt.initConfig({
watch: {
widgets: {
files: "/somepath/*.js",
tasks: ['newer:mycustomtask']
}
}
});
grunt.registerTask("mycustomtask", ["description of my task"], function() {
console.log("me has been triggered");
});
Whenever I run "grunt watch", I have this output:
Running "watch" task
Waiting...
File "/somepath/WidgetA.js" changed.
Running "newer:mycustomtask" (newer) task
Fatal error: The "newer" prefix is not supported for aliases
I googled but didn't found anything about this. Anyone knows how could I implement this? I need to now in my "customtask" which files have been changed

If you reference a task (inside watch or concurrent e.g.) which is either not installed or not configured you get this error output.
This happens often when you copy-paste a watch config from a different project.

I came across a similar requirement and the solution I ended up with is roughly as follows. Let's assume that the project structure is:
Gruntfile.js
package.json
src/
config.js
data.js
tasks/
customtask.js
Here, the src directory contains data which will be monitored by watch, while the definition of the custom task is stored in tasks/customtask.js. For the purpose of this example, this task will only print the file names of the changed files:
var fs = require('fs');
var path = require('path');
module.exports = function(grunt) {
grunt.registerMultiTask('customtask', function() {
var done = this.async();
if(!this.files){ done(); return; }
this.files[0].src.forEach(file_name => {
console.log(file_name);
});
done();
});
};
Now, Gruntfile.js looks like:
module.exports = function(grunt) {
const files = ['src/config.js', 'src/data.js'];
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
customtask: {
release: {
src: files
}
},
watch: {
data: {
files: files,
tasks: ['customtask:release']
},
options: {
spawn: false
}
}
});
grunt.loadTasks('tasks');
grunt.loadNpmTasks('grunt-contrib-watch');
var changedFiles = Object.create(null);
var onChange = grunt.util._.debounce(function() {
grunt.config('customtask.release.src', Object.keys(changedFiles));
changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', function(action, filepath) {
changedFiles[filepath] = action;
onChange();
});
grunt.registerTask('build', ['watch:data']);
};
here, it specifies that:
the files of interest are ['src/config.js', 'src/data.js']
that our customtask operates in principle on these files (in case it would be invoked directly)
that watch is supposed to observe these files and launch customtask:release whenever something changes
grunt.loadTasks('tasks') loads all "tasks definitions" from the directory tasks, i.e., here only the customtask
grunt.registerTask('build', ['watch:data']) defines a "shortcut" for watch:data
Finally, in order to invoke customtask only for the changed files, this example uses the strategy employed in the documentation in the section "Compiling files as needed". In loose terms, it assembles all changed files in an object the keys of which are then used to modify the src property of the customtask on-the-fly.
Running grunt build then initiates the "watch". If one runs in another terminal window for example touch src/*.js, the output is:
Running "watch:data" (watch) task
Waiting...
>> File "src/config.js" changed.
>> File "src/data.js" changed.
Running "customtask:release" (customtask) task
src/config.js
src/data.js
where the last two lines come from customtask...

You just need to have a config entry (even an empty one) for your task:
grunt.initConfig({
mycustomtask: {
},
watch: {
widgets: {
files: "/somepath/*.js",
tasks: ['newer:mycustomtask']
}
}
});

Related

gulp tasks sharing a common setting without a global object

I have a number of gulp tasks each residing in its own file (using the require-dir module) rather than a monolithic file.
I am using modules for configuration settings instead of json files (which I prefer for comments and for derived values).
To keep things simple here is an example setup with a single key I need to share/set between the gulp tasks.
/config/index.js
var config = {}
config.buildType = ''; // set this to either 'dev' or 'dist'
module.exports = config;
here is default task for which I want to set config.buildType to 'dev'
default.js
var gulp = require('gulp');
var config = require('../config/');
gulp.task('default', ['build'], function(cb) {
});
here is a deploy task for which I want to set buildType to 'dist'
deploy.js
var gulp = require('gulp');
var config = require('../config/');
gulp.task('deploy-s3', ['build'], function() {
});
here is a build task that I want to change based on buildType
build.js
var gulp = require('gulp');
var runSequence = require('run-sequence');
var config = require('../config/');
gulp.task('build', function(cb) {
console.log('in build',config.buildType);
if (config.buildType == 'dev') runSequence('clean',['sass',config.htmlGenerator], 'watch', cb);
if (config.buildType == 'dist') runSequence('clean',['sass',config.htmlGenerator], cb);
});
So here is the issue If I set config.buildType in default.js or deploy.js outside gulp.task then since they are all lumped into essentially one file by require-dir the value is simply whichever file was loaded last. If I set it inside the gulp.task function I am confused about about the timing/scope of that setting.
Update: Found this related issue https://github.com/gulpjs/gulp/issues/193. It was pointed out in this issue the task function starts after all the queued tasks so that means I can't set something inside the task function and expect it to be executed before the listed tasks (in my case 'build')
one poster made a task to set a parameter like this
gulp.task('set-dist', function () {
config.buildType = 'dist';
});
gulp.task('deploy', ['set-dist', 'build']);
So some advice..... do I go the way of this "hack" or is there some better way to do this??
(fyi, I am just a couple months into learning node/javascript on my own so my experience is limited)
You can use process.env to hold config attributes for your project. Check this.
In your case you can do:
var gulp = require('gulp');
var runSequence = require('run-sequence');
gulp.task('build', function(cb) {
if (process.env.NODE_ENV === 'development') {
runSequence('clean',['sass',config.htmlGenerator], 'watch', cb);
} else {
runSequence('clean',['sass',config.htmlGenerator], cb);
}
});
gulp build NODE_ENV=production for production
or gulp build NODE_ENV=development.
This will play nicely with existing CI tools like Travis.
Decided to go with this dropping the 'build' task altogether. It will hopefully work in some similar form after gulp 4.0 is released. It allows me to modify any setting before calling other tasks. Can use it with gulpif in tasks like my 'sass' task which is a pipe only task. Still wondering if there is a "better" way.
var gulp = require('gulp');
var config = require('../config/');
var runSequence = require('run-sequence');
gulp.task('default', function(cb) {
config.buildType='dev'
config.url = 'http://localhost:' + config.localport;
runSequence('clean',['sass',config.htmlGenerator],'watch', cb);
});

Grunt watch for tasks instead for files?

I`m new to Grunt and I saw a few tutorials about watch plugin. The example:
watch:{
sass:{
files: 'sass/*.scss',
tasks: ['sass','cssmin']
}
}
My question is why do I need to watch about another plungin like "sass" instead of watching the actual file(s) 'sass/*.scss'?
I don`t understand, it is not logical to watch a plugin/task, rather than a file.
Why I can`t call like this:
watch:{
files: 'sass/*.scss',
tasks: ['sass','cssmin']
}
?
And what if I want to also watch about my js file? Do I need to put it also in the files of the sass tasks? It does not make sense to have js files into sass task...
You're by no means watching a plugin. Suppose you have another plugin less, which you want to work on a different set of files. How will you do it?
watch {
sass: ...
less: ...
uglify: ...
}
Even if you put js files with sass (ignoring that it is horrible practice), when grunt calls sass on that file, nothing will happen.
As an example
module.exports = function (grunt) {
grunt.initConfig({
jshint: {
// ...
},
uglify: {
// ...
},
sass: {
// ...
},
foo: {
// ...
},
watch: {
javascript: {
files: 'javascripts/*/**.js',
tasks: ['jshint', 'uglify']
},
sass: {
files: 'sass/*/**.scss',
tasks: ['sass', 'foo']
}
}
});
// ...
};
There are different watchers for js, like uglify.
It's not that you're "watching" that plugin, it's that you're watching a set of files and running tasks when the files changes. the tasks specification is where you specify what tasks you need. So in this case you would run the sass and cssmin tasks.
The watch plugin watches files matching the pattern specified by files section. Whenever any of the matching files are changed, the tasks mentioned in tasks is executed.
For example, let us consider the watch task in your question
watch:{
sass:{
files: 'sass/*.scss',
tasks: ['sass','cssmin']
}
}
Here, sass is just a name given to the operation. It can be anything; but since the operation compiles Saas files, the operation is named saas.
Whenever any scss file in sass directory is changed, the operation saas is triggered, executing the saas and cssmin tasks, which should be defined earlier.
References:
Getting Started - Grunt: The JavaScript Task Runner

How should project-level bundling be handled for non SPA use?

I am learning browserify and I am trying to do two basic things with it:
Transform (via shim) non-CommonJS modules for ease-of-use and dependency tracking
Bundle the libraries that are project-specific
I've found a working process for how to do all of this and automate it with Gulp. This works and produces the right output but, I am curious if it could be made simpler. It seems like I have to duplicate a lot of configuration on the project-based bundles. Here is the working example:
package.json
invalid comments added for clarification
{
//project info and dependencies omitted
//https://github.com/substack/node-browserify#browser-field
"browser": { //tell browserify about some of my libraries and where they reside
"jquery": "./bower_components/jquery/dist/jquery.js",
"bootstrap": "./bower_components/bootstrap/dist/js/bootstrap.js"
},
"browserify": {
//https://github.com/substack/node-browserify#browserifytransform
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
//shim the modules defined above as needed
"jquery": {
"exports": "$"
},
"bootstrap": {
"depends": "jquery:$"
}
}
}
config.js
contains all task-runner related configuration settings
module.exports = {
browserify: {
// Enable source maps and leave un-ulgified
debug: true,
extensions: [],
//represents a separate bundle per item
bundleConfigs: [
{
//I really want to refer to the bundles here made in the package.json but
//if I do, the shim is never applied and the dependencies aren't included
entries: ['/bundles/shared-bundle.js'],
dest: '/dist/js',
outputName: 'shared.js'
}
]
},
//...
};
shared-bundle.js
acts as a bundling file where node loads the dependencies and at this point, the shim has been applied
require('bootstrap');
browserify-task.js
contains the browserify bundling gulp task
//module requires omitted
gulp.task('browserify', function (callback) {
var bundleQueue = config.bundleConfigs.length;
var browserifyBundle = function (bundleConfig) {
var bundler = browserify({
entries: bundleConfig.entries,
extensions: config.extensions,
debug: config.debug,
});
var bundle = function () {
return bundler.bundle()
// Use vinyl-source-stream to make the stream gulp compatible
.pipe(source(bundleConfig.outputName))
// Specify the output destination
.pipe(gulp.dest(bundleConfig.dest))
.on('end', reportFinished);
};
var reportFinished = function () {
if (bundleQueue) {
bundleQueue--;
if (bundleQueue === 0) {
// If queue is empty, tell gulp the task is complete
callback();
}
}
};
return bundle();
};
config.bundleConfigs.forEach(browserifyBundle);
});
In config.js where the first bundleConfig item's entries is a source to a file that has the require() modules, I'd like replace those with module names of modules defined in the package.json browser key.
In the config.js, if I change the bundle configuration to:
bundleConfigs: [
{
entries: ['bootstrap'],
dest: '/dist/js',
outputName: 'shared.js'
}
]
and run the gulp task, it will include bootstrap.js but it doesn't run the shim transformation. jQuery is not being included at all.
This leaves me with a few questions:
Is there a better way to be bundling my js for use in a non-SPA application (ie am I going about this the wrong way)?
If not, is there a way to ensure the shim transformation is run prior to the bundling so that I can have my bundle configuration in one place?
Certainly, you just have to tell your gulp file that it should shim first. Looks like you can add your own shim object when calling browserify from your gulp file. Check out this example
If you want to ensure everything is shimmed before you bundle them, use the deps array: "An array of tasks to be executed and completed before your task will run."
It would look something like this:
gulp.task('shim', function() {
// ...
});
gulp.task('browserify', ['shim'], function(){
// ...
});

Grunt concat files on a different domain or on different server

Edit working version and explanation
I want to concat files from different server into my destination folder using grunt, and grunt-concat and with something like that:
concat: {
options: {
separator: ';'
},
dist: {
src: ['dev.staticcontent.com/media/clientcontent/library/*.js', 'js/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
each time I tried, I received no error from grunt, but my dist/marketing-home.js file is empty... Like it didn't find anything.
Console:
C:\Project\My>grunt
Running "jshint:files" (jshint) task
>> 1 file lint free.
Running "concat:dist" (concat) task
File dist/marketing-home.js created.
Running "uglify:dist" (uglify) task
Done, without errors.
New Version
After the help of Kris, I was able to do it without passing through the web using grunt-exec and doing using COPY or XCOPY shell command.
ex.
exec: {
copy : {
cmd: function () {
var path = "\\\\dev-server123\\WebSites\\Static_Contents\\Media\\clientcontent";
return "copy dist\\*.min.js " + path + " /y";
}
}
}
Doesn't look like concat task handles absolute paths or files from remote locations. However I was able to get it to work using curl task combined with the concat task.
EXAMPLE:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
curl: {
'download/jquery.js': 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js',
},
concat: {
js: {
src: ['download/jquery.js'],
dest: 'output/test.js'
},
}
});
grunt.loadNpmTasks('grunt-curl');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['curl', 'concat']);
};
DEMO DIRECTORY STRUCTURE:
I used this Node Module Package for CURL. https://github.com/twolfson/grunt-curl, there may be better ones out there. But this one seemed to work fine.

Build multi client project with requirejs and grunt

I'm working on a project where the main code based should be used by a bunch of different client. So we have a requirejs project and my initial idea is to have simple bootstrap.js file that will require an app.js files that is different for every client.
bootstrap.js
requirejs(['app'],function(app){
//some initial code here
app.start();
}
So the project structure will be look like this:
|_bootstrap.js
|_commonModules
|_someModule.js
|_client1
|_app.js
|_modules
|_module.js
|_client2
|_app.js
|_modules
|_module.js
So my ideas is to compile the app for every client using requirejs' r compiler and set the path to app in every compilation to clientX/app.js by crearting a new build.js for every step like this:
({
paths: {
"app": "client1/app"
}
})
So at the moment I have a grunt build task that is using a bunch of other tasks like uglify, usemin, md5 and so on. Can I create a new task that use this task but changing the requireJs settings for every client? Or is there a better way to achieve my goals?
So after all it wasn't that hard. The cool thing is that you can change the configuration for the actual running task and that you can call previous defined task in a running task.
//this was the old task to build one distribution
grunt.registerTask('build', ['clean:build', 'copy:build', 'useminPrepare', 'usemin', 'requirejs', 'concat', 'uglify', 'mincss', 'md5', 'manifest', 'copy:toClientFolder']);
grunt.registerTask('buildAll', function() {
['client1', 'client2'].forEach(function(client) {
//before every build task run a task to change the config
grunt.task.run('updateConfig:' + client, 'build');
});
});
//we need to change the config in a separate task,
//otherwise, change the config just in the forEach, would result in the same
//config for both task, using the client2 settings
grunt.registerTask('updateConfig', function(client) {
var requireJsName = 'requirejs.compile.options.name';
var clientFolder = 'copy.toClientFolder.files';
grunt.config(requireJsName, 'clients/' + client + '/Bootstrap');
grunt.config(clientFolder, [
{expand: true, cwd: 'dist', src: '**', dest: 'dist_' + client}
]);
});
And a app.js file for a client looks like this:
requirejs.config({
paths: {
'commonModules/someModule': 'clients1/modules/module'
}
});
requirejs(['boootstrap',
'commonModules/someModule1'],
function(boootstrap, someModule1) {
$(function() {
boootstrap();
someModule1();
});
});

Categories

Resources