How can I use grunt-regarde with grunt-contrib-coffee to only compile changed .coffee files? - coffeescript

My project has over 300 CoffeeScript files, so it takes several seconds to recompile everything. I'd like to only recompile the changed CoffeeScript files.
Here's the closest I've come so far, but the "frontend-src/coffeescript" folder structure is being copied from the src directory to the dest directory.
coffee: {
changed: {
expand: true,
cwd: './',
src: ['<%= grunt.regarde.changed %>'],
dest: 'public/js/',
ext: '.js'
}
},
regarde: {
coffee: {
files: 'frontend-src/coffeescript/**/*.coffee',
tasks: ['coffee:changed', 'livereload']
}
}
This is all with Grunt 0.4.0. Any help would be greatly appreciated!

I had this issue myself and I was able to come up with a solution for it inspired by the comments on this issue:
https://github.com/gruntjs/grunt-contrib-watch/issues/14
It is actually for the grunt-contrib-watch plugin, but it should also work for grunt-regarde, as it has similar events.
The idea is to bind a callback the watch event, in which you add a new task to the grunt configuration with the changed file's path, then run it.
From my Gruntfile.coffee:
coffee:
app:
expand: true
cwd: 'app/'
src: ['*.coffee',"**/*.coffee"]
dest: './public/temp'
ext: '.js'
watch:
coffee:
files: ['app/**/*.coffee']
tasks: ['livereload']
options:
nospawn: true
grunt.event.on 'watch', (action, filepath) ->
cwd = 'app/'
filepath = filepath.replace(cwd,'')
grunt.config.set('coffee',
changed:
expand: true
cwd: cwd
src: filepath
dest: './public/temp'
ext: '.js'
)
grunt.task.run('coffee:changed')
The nospawn is important for the watch task, so it runs the new task before the livereload task. I am pretty sure regarde does not spawn child processes by default.

I've had the same issue. I solved it using the regarde:file event.
First I listen for changed files by using the regarde:file event. This will feed the configuration for two tasks: clean:coffee if files in the source location has been deleted and coffee:refresh if files have been changed/added.
Then the regarde task will trigger its tasks, which will launch refresh:coffee (not to be mistaken from coffee:refresh). This task will check if there is configuration added for clean:coffee and/or for coffee:refresh and run these tasks if needed (via function grunt.task.run). If will also reset the flag, which will cause the next received regarde:file event to cleanup the configuration again.
In depth explanation:
First of all, regarde config:
// watch for changed coffeescript files
coffee: {
files: 'path/to/coffee/**/*.coffee',
tasks: ['refresh:coffee', 'livereload']
},
Then I listen for the regarde:file event, where I update the clean:coffee and coffee:refresh file lists in their config.
Feed the configuration based on the regarde:file event:
grunt.event.on('regarde:file', function (status, target, filepath) {
if (resetFlag) {
// clean file list from previous cycle, so clean clean:coffee and coffee:refresh
// file lists
...
resetFlag = false;
}
if (status === 'deleted') {
if (filepath) {
// calculate filepath's destination and
// add it to clean:coffee filelist
}
} else {
if (!grunt.file.isDir(filepath)) {
// add filepath to coffee:refresh filelist
}
}
}
It is easy to update configuration via grunt.config() function. Below the code snippets to feed coffee:refresh and clean:coffee.
Adding config to coffee:refresh:
var config = grunt.config('coffee') || {};
var value = config.refresh || {};
value.files = value.files || [];
...
var cwd = path.dirname(filepath),
src = path.basename(filepath),
dest = cwd.replace('path/to/source', 'path/to/dest');
value.files.push({
expand:true,
src:src,
dest:dest,
cwd:cwd,
ext:'.js'
});
grunt.config('coffee', config);
Adding config to clean:coffee:
var cwd = path.dirname(filepath),
src = path.basename(filepath),
dest = cwd.replace('path/to/source', 'path/to/dest');
value.src.push(path.join(dest, src.replace('coffee', 'js')));
// clean only what has been removed
config = grunt.config('clean') || {};
config.coffee = value;
grunt.config('clean', config);
Task refresh:coffee gets triggered:
grunt.registerMultiTask('refresh', 'refreshing the changed file(s)', function () {
this.requires('regarde');
var tasks = [];
var clean = grunt.config('clean');
// check if there is clean:refresh config available
if (clean && clean[this.target]) {
tasks.push('clean:' + this.target);
}
var config = grunt.config(this.target);
// check if there is coffee:refresh config available
if (config && config.refresh) {
tasks.push(this.target + ':refresh');
}
// run the tasks
grunt.task.run(tasks);
// set the resetFlag back to true
resetFlag = true;
});

grunt.regarde.changed is an array correct?
Should src: ['<%= grunt.regarde.changed %>']
be src: '<%= grunt.regarde.changed %>'
I looked through grunt-contrib-coffee's source for a second to see if it could be not correctly handling whatever you're giving it. Looked kind of likely that the stringified array you're giving it, doesn't get caught and dealt with.
I think what you're passing accidentally may be: src: [ '[path1, path2, path3, etc]' ]
If I'm way off base, leave a comment and I'll delete this answer.

Related

How to move/copy files during ember build

I wanted to moved some files between two folders in an ember app when build is run but I am having no success.
//ember-cli-build.js
module.exports = function (defaults) {
var app = new EmberApp(defaults, {
hinting: false,
minifyCSS: {
enabled: true
},
bless: {
enabled: true
}
});
var moveFile = new Funnel('./app/locales', {
srcDir: 'en',
files: ['test.js'],
destDir: 'en_US',
allowEmpty: true
});
return new MergeTrees([moveFile, app.toTree()]);
};
When I do the build, I get no errors but the file is also not getting moved.
UPDATE: I am trying to move the file before ember-cli puts compiles the files and puts it in the dist folder
You can use broccoli-static-compiler https://github.com/joliss/broccoli-static-compiler
In brocfile.js ( ember-cli-build.js )
// at top of file
var pickFiles = require('broccoli-static-compiler');
var mergeTrees = require('broccoli-merge-trees');
// inside exporting function
const bootstrapMap = pickFiles('bower_components/bootstrap/dist/css/',
{
srcDir: '/',
files: ['bootstrap.css.map'],
destDir: '/assets'
});
// and so on, as many times as you need
const zeroClipboard = pickFiles('bower_components/zeroclipboard/dist/',
{
srcDir: '/',
files: ['ZeroClipboard.swf'],
destDir: '/assets'
});
// at the end
return mergeTrees([
app.toTree(),
bootstrapMap,
zeroClipboard,
// ...
], { overwrite: true });
With 'broccoli build', your app is build into a destination folder, so broccoli is the wrong tool to move files in an existing folder structure. Here I'm assuming it's run with something like 'broccoli build dist' on the command line which will create a new folder 'dist' with the results of the build, and error out if the directory already exists.
So let's say your project directory looks like this:
.
|--brocfile.js
|--app/
|--locales/ <----- funnel root
|--en/ <----- srcDir
|--test.js <----- file
When you funnel from ./app/locales, your srcDir and files` are relative to that as a root. The output tree is then put into the 'destDir' under the build output directory. What that will do is this:
.
|--brocfile.js
|--app/ <----- not changed
|--dist/ <----- build output directory
|--en_US <----- destDir
|--test.js <----- file
I think you want your destDir to be locales/en_US or app/locales/en_US.

Ionic serve: how to rerun gulp task

I'm kinda new to ionic and gulp.
I was able to configure the ionic.project file in order to run the gulp tasks when I first run ionic serve.
But now when I change files I want that the gulp task will run again.. But this doesn't happen.. Is there a way to do that?
This is my ionic.project file:
{
"name": "test",
"app_id": "",
"gulpStartupTasks": [
"default"
],
"watchPatterns": [
"src/**/*",
"src/*",
"www/**/*",
"!www/lib/**/*"
]
}
I expected that when some file changes that match the wtachPatterns
it will invoke the gulp watch task, but this doesnt happen (I see that ionic see that the file has changed but nothing happen.)
this the the gulp watch task:
gulp.task('watch', function() {
gulp.watch(paths.sass, ['sass']);
gulp.watch(paths.script, ['script']);
});
Basically the task is minifying all the JS files and all the sass/scss files
and the index.html is looking on the minified files. so if the gulp task isn't invoked there are no changes in the minified file and I need to run ionic serve all over again.. Is there a proper way to do that?
UPDATE:
This is the complete gulpfile
var gulp = require('gulp');
var gutil = require('gulp-util');
var bower = require('bower');
require('require-dir')('./gulp/tasks');
var paths = {
sass: ['./scss/**/*.scss'],
style: ['./src/**/*.scss'],
script: ['./src/app.js'],
html:['./src/*.html']
};
gulp.task('default', ['sass', 'script','watch', 'html', 'style']);
gulp.task('watch', function() {
gulp.watch(paths.sass, ['sass']);
gulp.watch(paths.script, ['script']);
});
gulp.task('install', ['git-check'], function() {
return bower.commands.install()
.on('log', function(data) {
gutil.log('bower', gutil.colors.cyan(data.id), data.message);
});
});
gulp.task('git-check', function(done) {
if (!sh.which('git')) {
console.log(
' ' + gutil.colors.red('Git is not installed.'),
'\n Git, the version control system, is required to download Ionic.',
'\n Download git here:', gutil.colors.cyan('http://git- scm.com/downloads') + '.',
'\n Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.'
);
process.exit(1);
}
done();
});
And this is an example of one of the files who have the actual task:
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var stringify = require('stringify');
var paths = ['./src/app.js'];
gulp.task('script', function() {
return browserify(paths, {debug: true})
.transform(stringify(['.html']))
.bundle()
.pipe(source('app.js'))
.pipe(gulp.dest('./www/js'));
});
Well So the problem was with paths I watched.
I removed the ./ and now its working
First thing first, you misunderstand the watchPatterns is for livereload, which means, the web will refresh if there is any file changed on watch. It's definitely not having any relation to gulp.
Read more at: http://ionicframework.com/docs/cli/test.html
To watch for file changes with watch, update your watch task, which is
gulp.task('watch', function() {
gulp.watch(paths.sass, ['sass']); <-- any file in paths.sass changed will trigger `gulp sass`
gulp.watch(paths.script, ['script']); <-- any file in paths.script changed will trigger `gulp script`
});
So if you want to watch more files to be processed by Gulp, just add tasks and watch them in gulp watch.
Oh hey, you are watching only files in ./scss/**/*.scss and ./src/app.js. Add more if you wish.

Ember-CLI: How to exclude a folder within /public from build

I have Ember-CLI-application with a few thousand static assets (~1GB) and my build time is now about 30sec.
I have tried in my Brocfile.js without success:
var app = new EmberApp({
fingerprint: {
enabled: false,
exclude: ['large_folder']
}
});
Build time with assets: TreeMerger | 29738ms
/ without: TreeMerger | 9182ms.
Any ideas how to speed up the build?
(Ember-CLI 0.1.7)
You have enabled:false, you can set it to true.
Also, on exclude, would be better to say the path for the folder, such as:
If you have a large folder inside images, then you can do it like this:
fingerprint: {
exclude: ['assets/images/large_folder/', 'assets/uploads/other_large_folder/]
}
My own solution is currently to use the postBuild-hook and a symbolic link to the assets folder.
lib/link-after-build/index.js:
var fs = require('fs');
var path = require('path');
module.exports = {
name: 'link-after-build',
// link additional assets after build
postBuild: function(result) {
if (process.env.EMBER_ENV === 'development') {
var buildDirPath = result.directory;
var srcpath = path.resolve("/opt/local/apache2/htdocs/large_folder");
var dstpath = path.resolve(buildDirPath + "/large_folder");
fs.symlinkSync(srcpath,dstpath);
}
}
};

grunt-contrib-coffee one-to-one compile

I have several files named:
jquery.a.b.coffee
jquery.a.c.coffee
jquery.a.d.coffee
and they are all compiled into one jquery.js file in my output directory.
Although I guess this behavior might be nice in some cases, I would like to have them to compile into different files like jquery.a.b.js, jquery.a.c.js and so on. How can I tell grunt-contrib-coffeescript to do so?
My Gruntfile.js looks like this:
module.exports = function (grunt) {
grunt.initConfig({
coffee: {
dist: {
files: [{
expand: true,
flatten: true,
cwd: 'app/webroot/coffee',
src: ['{,*/}*.coffee'],
dest: 'app/webroot/js',
ext: '.js'
}]
}
}
});
grunt.loadNpmTasks('grunt-contrib-coffee');
};
Thanks for your help!
The problem lies on the filenames having multiple dots.
If it was jquery-a-b.coffee, jquery-a-c.coffee etc, you would have seen the expected output.
It is a known issue (extension is after last period only) and grunt developers made this on purpose.
Here is a quote from one of them:
There's two ways ext could work; it could consider everything after
the first dot the extension, or everything after the last dot the
extension. We chose the former because the use-case is more common (we
encounter .min.js files all the time). That being said, you can use
the rename option to specify a function that will use whatever custom
naming logic you need.
So, the only workaround for now is to remove ext and use rename like this:
coffee: {
dist: {
files: [{
expand: true,
cwd: 'app/webroot/coffee',
src: ['{,*/}*.coffee'],
dest: 'app/webroot/js',
rename: function(dest, src) {
return dest + '/' + src.replace(/\.coffee$/, '.js');
}
}]
}
}
Update as of Grunt 0.4.3:
You can now use the extDot option along with ext
ext: '.js',
extDot: 'last'
This works so you don't have to add the files by hand in your gruntFile:
coffee: {
glob_to_multiple: {
expand: true,
flatten: true,
cwd: 'app/webroot/coffee/',
src: ['*.coffee'],
dest: 'app/webroot/',
ext: '.js'
}
},
cwd: the folder where your files are
src: the matching pattern for your files, using glob
dest: The folder where your files are going.
See https://github.com/gruntjs/grunt-contrib-coffee#usage-examples for some sample usages

Gruntjs: How to make copy task to copy only changed files on watch

So on grunt-contrib-watch plugin info page, there is an example on how to make jshint run only for changed file.
grunt.initConfig({
watch: {
scripts: {
files: ['lib/*.js'],
tasks: ['jshint'],
options: {
nospawn: true,
},
},
},
jshint: {
all: ['lib/*.js'],
},
});
grunt.event.on('watch', function(action, filepath) {
grunt.config(['jshint', 'all'], filepath);
});
I have not tested example it self. But took this and applied to my copy task, unsuccessfully.
grunt-contrib-copy task set up to copy images and templates for my angular project. And I would be happy to know if I can make this work for copy task and if I can, what am I doing wrong.
Thank you so much.
Here is my stripped out Gruntfile.js.
// Build configurations.
module.exports = function(grunt){
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Copies directories and files from one location to another.
copy: {
// DEVELOPMENT
devTmpl: {
files: [{
cwd : 'src/tpl/',
src : ['**/*'],
dest : 'app/tpl/',
flatten : false,
expand : true
}]
},
devImg: {
files: [{
cwd : 'src/img/',
src : ['**/*'],
dest : 'app/img/',
flatten : false,
expand : true
}]
}
},
// Watch files for changes and run tasks
watch: {
// Templates, copy
templates: {
files : 'src/tpl/**/*',
tasks : ['copy:devTmpl'],
options: {
nospawn: true,
}
},
// Images, copy
images: {
files : 'src/img/**/*',
tasks : ['copy:devImg'],
options: {
nospawn: true,
}
}
}
});
// Watch events
grunt.event.on('watch', function(action, filepath) {
// configure copy:devTmpl to only run on changed file
grunt.config(['copy','devTmpl'], filepath);
// configure copy:devImg to only run on changed file
grunt.config(['copy','devImg'], filepath);
});
// PLUGINS:
grunt.loadNpmTasks('grunt-contrib-copy');
// TASKS:
/* DEV: Compiles the app with non-optimized build settings, places the build artifacts in the dist directory, and watches for file changes.
run: grunt dev */
grunt.registerTask('dev', 'Running "DEVELOPMENT", watching files and compiling...', [
'default',
'watch'
]);
/* DEFAULT: Compiles the app with non-optimized build settings and places the build artifacts in the dist directory.
run: grunt */
grunt.registerTask('default', 'Running "DEFAULT", compiling everything.', [
'copy:devTmpl',
'copy:devImg'
]);
}
Use grunt-sync (https://npmjs.org/package/grunt-sync) instead of grunt-contrib-copy, and watch the directories you want to be synced.
Update - here's an example:
grunt.initConfig({
sync: {
copy_resources_to_www: {
files: [
{ cwd: 'src', src: 'img/**', dest: 'www' },
{ cwd: 'src', src: 'res/**', dest: 'www' }
]
}
}
});
cwd means current working directory. copy_resources_to_www is just a label.
You need to point grunt.config to the correct property in your config:
grunt.event.on('watch', function(action, filepath) {
var cfgkey = ['copy', 'devTmpl', 'files'];
grunt.config.set(cfgkey, grunt.config.get(cfgkey).map(function(file) {
file.src = filepath;
return file;
}));
});
I have written a detailed example configuration file for synchronizing changed files in my projects. It runs automatically in any related project, and it can be updated to suit your specific needs.
Grunfile.js
module.exports = function (grunt) {
"use strict";
grunt.initConfig({
pkg: grunt.file.readJSON("package.json"),
sync: {
main: {
files: [
{
cwd: ".",
src: ["src/**", "LICENSE", "README.md"],
dest: "dist/<%= pkg.name%>/",
},
],
verbose: true,
pretend: false,
failOnError: true,
ignoreInDest: "**/.git/**",
updateAndDelete: true,
compareUsing: "md5",
},
}
});
grunt.util.linefeed = "\n";
};