While seemingly the tasks execute in proper order (bump first and than ngconstant creating a config file based on package.json-s version property) i think they actually execute parallely, and ngconstant reads up the package.json before bump has written it.
Running "bump" task
md
>> Version bumped to 2.0.6 (in package.json)
Running "ngconstant:production" (ngconstant) task
Creating module config at app/scripts/config.js...OK
The resultung package.json has 2.0.6 as version while config.js has 2.0.5.
My ngconstant config simply uses
grunt.file.readJSON('package.json')
to read up the json.
So, basically the question is, how can i make sure that bump's write is finished, before reading up the json with ngconstant, and what actually causes the above?
EDIT: the original Gruntfile: https://github.com/dekztah/sc2/blob/18acaff22ab027000026311ac8215a51846786b8/Gruntfile.js
EDIT: the updated Gruntfile that solves the problem: https://github.com/dekztah/sc2/blob/e7985db6b95846c025ba0b615bf239c4f9c11e8f/Gruntfile.js
Probably your package.json file is stored in memory and is not updated before your run the next task.
An workaround would be to create a script in your file package.json as:
"scripts": {
"bumb-and-ngconstant": "grunt:bump && grunt:build"
}
As per grunt-ng-constant documentation:
Or if you want to calculate the constants value at runtime you can create a lazy evaluated method which should be used if you generate your json file during the build process.
grunt.initConfig({
ngconstant: {
options: {
dest: 'dist/module.js',
name: 'someModule'
},
dist: {
constants: function () {
return {
lazyConfig: grunt.file.readJSON('build/lazy-config.json')
};
}
}
},
})
This forces the json to be read while the task runs, instead of when grunt inits the ngconstant task.
Related
The story:
We have a team of testers working on automating end-to-end tests using protractor for our internal AngularJS application. Here is the task they usually run for "local" testing:
grunt.registerTask('e2e:local', [
'build:prod',
'connect:test',
'protractor:local'
]);
It runs the "build" task, starts a webserver and runs the e2e tests against the local build.
The build:prod task itself is defined as:
grunt.registerTask(
'build:prod', [
'clean',
'copy:all',
'copy:assets',
'wiredep',
'ngtemplates',
'useminPrepare',
'concat',
'ngAnnotate',
'autoprefixer',
'uglify',
'cssmin',
'copy:cssfix',
'usemin',
'copy:html',
'bowercopy',
'template:setProdVersion'
]
);
Here we have a lot of subtasks (it definitely could be improved, but this is how it looks now).
The problem:
Currently, it takes about 25 seconds for the build to complete. And, every time a person is running end-to-end tests, the build task is executed.
The question:
How can I run the build:prod task only if there are changes in src directory?
Note that the requirement here is to make it transparent for the testers who run the tests. I don't want them to remember when they need to perform a build and when not.
In other words, the process should be automated. The goal is to automatically detect if build is needed or not.
Note that ideally I would like to leave the build task as is, so that if it is invoked directly via grunt build:prod it would rebuild regardless of the datestamp of the previous build.
Thoughts and tries:
there is the closely related grunt-newer package, but, since we have a rather complicated build, having a clean task at the beginning, I'm not sure how to apply it in my case
what I was also thinking about is to, inside the e2e:local task, manually check the timestamps of the files inside dist and src and, based on that, decide if build:prod is needed to be invoked. I think this is what grunt-newer is doing internally
we started to use jit-grunt that helped to improve the performance
Here's an idea if you use git:
How about using something like grunt-gitinfo and using the last commit in HEAD as a base?
The idea is:
You create a new grunt task that checks for latest commit hash
You'd save this commit hash in a file that's added to gitignore (and is NOT in the clean folder, typically can be in root of repo)
Before saving to file, it'd check the value already in it (standard node fs module can do the read/write easily)
If the hash doesn't match, run build:prod task then save new commit hash
The testers build would depend on your new task instead of build:prod directly
Another option (still using git):
You can use something like grunt-githooks and create a git hook that runs after pull and calls the git build:prod, then you can remove it from the dependencies of the grunt task that testers run.
You might have another code to check for githook and install it if required though, which can be a one-time extra step for testers, or maybe baked into the grunt task they call.
I'm surprised noone has mentioned grunt-contrib-watch yet (it's in the gruntjs.com example file and I thought it was pretty commonly known!). From github: "Run predefined tasks whenever watched file patterns are added, changed or deleted." - heres a sample grunt file that would run your tasks any time any .js files are modified in src/ or in test/, or if the Gruntfile is modified.
var filesToWatch = ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'];
grunt.initConfig({
watch: {
files: filesToWatch,
tasks: ['build:prod',
'connect:test',
'protractor:local']
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
You have your developers open a terminal and run grunt watch before they start modifying files, and every time those files are modified the tasks will automatically be run (no more going back to the terminal to run grunt build:prod every time).
It's an excellent package and I suggest you check it out. -- github -- npmjs.org
npm install grunt-contrib-watch --save-dev
Not the answer your are looking for with grunt, but this will be easy with gulp.
var fs = require('fs');
var gulpif = require('gulp-if');
var sourceChanged = fs.statSync('build/directory').mtime > fs.statSync('source/directory').mtime;
gulp.task('build:prod', function() {
if (!sourceChanged) {
return false;
}
return gulp.src('./src/*.js')
.pipe(.... build ....)
.pipe(gulp.dest('./dist/'));
});
Here's how we've done some Git HEAD sha work for our build. We use it to determine which version is currently deployed to our production environment - but I'm quite certain you could rework it to return a boolean and trigger the build if truthy.
Gruntfile.js
function getHeadSha() {
var curr, match, next = 'HEAD';
var repoDir = process.env.GIT_REPO_DIR || path.join(__dirname, '..');
try {
do {
curr = grunt.file.read(path.join(repoDir, '.git', next)).trim();
match = curr.match(/^ref: (.+)$/);
next = match && match[1];
} while (next);
} catch(ex) {
curr = 'not-found';
}
return curr;
}
grunt.initConfig({
replace: {
applicationVersion: {
src: '<%= config.dist %>/index.html',
overwrite: true,
replacements: [{
from: '{{APPLICATION_VERSION}}',
to: getHeadSha
}]
}
}
});
grunt.registerTask('build', {
'replace:applicationVersion',
/** other tasks **/
});
grunt.registerTask('e2e:local', {
'check_if_we_should_build',
/** other tasks **/
});
index.html
<html data-version="{{APPLICATION_VERSION}}">
<!-- -->
</html>
There's also the git-info package which would simplify this whole process, we're looking at switching over to that ourselves.
edit; I just noticed #meligy already pointed you in the direction of git-info. credit where credit is due.
I am not sure if its helpful or not but same things we have done it in our project using GULP framework. We have written a watcher in the gulp that continuously check for the source change and run a quick function to build the project. Its a Protractor Test case.
gulp.task('dome', function () {
gulp.src(["maintest.js"])
.pipe(notify("Change Found , Executing Scripts."))
.pipe(protractor({
configFile: "conf.js",
args: ['--baseUrl', 'http://127.0.0.1:8000']
})).on('error', function (e) {
throw e
});
})
gulp.task('default', function () {
gulp.watch('./webpages/*.js', ['dome']);
gulp.watch('maintest.js', ['dome']);
gulp.watch('conf.js', ['dome']);
});
Link to repo.
I don't have experience in protractor, but conceptually I think this could work.
What I could suggest is to set an alias in your ~/.cshrc to run the build commands only if a diff command returns true.
#./cshrc
alias build_on_diff 'diff -r branch_dir_1 branch_dir_2\
if ( $status == 1 ) then\
build:prod\
endif'
Just replace the diff command with whatever git uses, and it should work provided it returns a 1 status for differences detected. We apply a similar method at my workplace to avoid rebuilding files that haven't changed.
Context
I have a few grunt tasks that I've already written, and I'd like to use them with a new project I'm writing in Sails.js.
With Sails.js, you can add additional grunt tasks by adding a JS file to the /tasks/register folder. Before we get to the file I've added, let's talk about the problem.
The Problem
Sails won't lift. Debugger shows:
debug: --------------------------------------------------------
error: ** Grunt :: An error occurred. **
error:
------------------------------------------------------------------------
ERROR
>> Unable to process task.
Warning: Required config property "clean.dev" missing.
The issue in question is obviously with grunt, so then I try grunt build (which automatically runs with sails lift):
Running "clean:dev" (clean) task
Verifying property clean.dev exists in config...ERROR
>> Unable to process task.
Warning: Required config property "clean.dev" missing. Use --force to continue.
From this, I've garnered that this is a path issue. Let's take a look at the file I've added.
/tasks/register/customTask.js
The task here loads load-grunt-config, which is the source of my problems:
module.exports = function(grunt) {
// measures the time each task takes
require('time-grunt')(grunt);
// This require statement below causes my issue
require('load-grunt-config')(grunt, {
config: '../../package.json',
scope: 'devDependencies',
overridePath: require('path').join(process.cwd(), '/asset-library/grunt')
});
grunt.registerTask('customTask', [
'newer:jshint',
'newer:qunit',
'newer:concat',
'newer:cssmin',
'newer:uglify'
]);
};
I had assumed that using overridePath instead of configPath would solve my issue, but alas, it's not quite that simple. Is there some way to make it so that I can use my own custom tasks folder with load-grunt-config like I've done in other projects, or is there some magic conditional I can wrap the require statement around?
I only need it to run with grunt customTask, and not run with grunt * (anything else).
Okay, this was actually pretty easy. All I had to do was change the grunt.registerTask call in my customTask.js file from this:
grunt.registerTask('customTask', [
'newer:jshint',
'newer:qunit',
'newer:concat',
'newer:cssmin',
'newer:uglify'
]);
to this:
grunt.registerTask('customTask', 'My custom tasks', function() {
// The require statement is only run with "grunt customTask" now!
require('load-grunt-config')(grunt, {
config: '../../package.json',
scope: 'devDependencies',
overridePath: require('path').join(process.cwd(), '/asset-library/grunt')
});
grunt.task.run([
'newer:jshint',
'newer:qunit',
'newer:concat',
'newer:cssmin',
'newer:uglify'
]);
});
In case it's not clear, I did have to move the require('load-grunt-config') call, so if you're copy + pasting, make sure to remove the require statement that's outside the grunt.registerTask call.
You can find more information about custom Grunt tasks here.
I'm writing some frontend code with ECMAScript 6 (transpiled with BabelJS, and then browserified with Browserify) so that I can have a class in one file, export it and import it in another file.
The way I'm doing this is:
export class Game {
constructor(settings) {
...
}
}
And then on the file that imports the class I do:
import {Game} from "../../lib/pentagine_browserified.js";
var myGame = new Game(settings);
I then compile it with grunt, this is my Gruntfile:
module.exports = function(grunt) {
"use strict";
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-browserify');
grunt.initConfig({
"babel": {
options: {
sourceMap: false
},
dist: {
files: {
"lib/pentagine_babel.js": "lib/pentagine.js",
"demos/helicopter_game/PlayState_babel.js": "demos/helicopter_game/PlayState.js"
}
}
},
"browserify": {
dist: {
files: {
"lib/pentagine_browserified.js": "lib/pentagine_babel.js",
"demos/helicopter_game/PlayState_browserified.js": "demos/helicopter_game/PlayState_babel.js"
}
}
}
});
grunt.registerTask("default", ["babel", "browserify"]);
};
However, on the new Game( call, I get the following error:
Uncaught TypeError: undefined is not a function
As so, what I did was analyse the generated code by Babel and Browserify and I found this line on PlayState_browserified.js:
var Game = require("../../lib/pentagine_browserified.js").Game;
I decided to print the require output:
console.log(require("../../lib/pentagine_browserified.js"));
And it is nothing but an empty object. I decided to check out the pentagine_browserified.js file:
var Game = exports.Game = (function () {
It seems like it is correctly exporting the class, but for some other reason it is not being required on the other file.
Also, I'm sure the file is being required properly because changing the string "../../lib/pentagine_browserified.js" spits out a Not Found error, so it is going for the right file, that I'm sure about.
Browserify is meant to be fed a single "entry point" file, through which it recursively traverses all of your require statements, importing the code from other modules. So you should be require'ing the _babel.js versions of modules, not _browserified.js ones.
From the looks of it, you intend for your app's "entry point" to be demos/helicopter_game/PlayState_browserified.js, yeah? If that's the case:
In PlayState.js, change it to import {Game} from "../../lib/pentagine_babel.js";.
In Gruntfile.js, remove "lib/pentagine_browserified.js": "lib/pentagine_babel.js".
Works for me. Let me know if that suffices or I am misunderstanding your requirements here.
P.S. You can use babelify to avoid having separate Grunt tasks for Babel and Browserify. See my answer here for an example.
I had a slightly different file configuration, that gave me some difficulty to get the "require" syntax to work in Node, but this post gave me the hint on how to used the babel-ified version of the file name.
I am using WebStorm with the FileWatcher option set to Babel, and I have the FileWatcher configured to watch all files with suffix .jsx, and rename the compiled output file from {my_file}.jsx to {my_file}-compiled.js.
So in my test case, I have 2 files:
Person.jsx:
class Person { ... }
export { Person as default}
and another file that wants to import it:
Test.jsx:
var Person = require('./Person-compiled.js');
I couldn't get the "require" statement to find the module until I started the file path with './' and also add '-compiled.js' to properly specify the file name so that Node es5 could find the module.
I was also able to use the "import" syntax:
import Person from './Person-compiled.js';
Since I have set up my WebStorm project as a Node ES5 project, I have to run 'Test-compiled.js' (not 'Test.jsx').
So here is my hypothetical config object for a hypothetical fooTask that does something (not relevant to question) to a bunch of JS files
grunt.initConfig({
fooTask: {
app1: 'app1/*.js',
app2: 'app2/*.js',
app3: 'app3/*.js'
}
});
As you can see, with this approach, I have to run fooTask 3 times with each app specified as a target:
grunt fooTask:app1
grunt fooTask:app2
grunt fooTask:app3
Needless to say this does not scale as either the number of apps increase or the number of such foo tasks increase as one has to C&P the same code over and over for each app.
So ideally what I would like to define is just one target with the name of the app passed in as a config variable
grunt.initConfig({
fooTask: {
dist: '<%=appName%>/*.js'
}
});
I would then like to call fooTask 3 times, one for each app, with the right app set as appName
var apps = ['app1', 'app2', 'app3'];
apps.forEach(function(app) {
var currAppName = app;
// Run fooTask but how do I specify the new currAppName config?
grunt.task.run('fooTask');
});
As from code above, I know I can run my fooTask using grunt.task.run but how do I set the appName config for my task?
Note that this question is similar to this other one that also does not have the right answer yet - Pass Grunt config options from task.run
Thanks a lot.
EDIT 2:
So nevermind the garbage below the first edit, leaving as example of what doesn't work. In my case it was really important to be able to set the value within a task at run-time so I settled on the file system. Perhaps it suits your needs.
grunt.initConfig({
someTask: {
someKey: fs.readFileSync('file.txt', { encoding: 'utf8' })
}
});
of course you can do the readFile outside of the task if you need a bunch of different app names.
EDIT:
Hmmm. I swear I had this working when I wrote this...but now it is not. Grunt just sees the extra arguments as additional unfound tasks.
I was trying to figure this out myself, finally a "duh" just moment happened - why not parse process.argv before grunt.initConfig?
module.exports = function(grunt) {
var sourcefile = process.argv[2] || 'default.js'; // <- this
grunt.initConfig({
uglify: {
main: {
src: sourcefile, // <- voila :)
dest: sourcefile.substring(0, sourcefile.length-3) + '.min.js'
}
}
});
grunt.loadNpmTasks('uglify');
grunt.registerTask('default', ['uglify']);
};
and use from command line:
grunt mykillerscript.js
I didn't even try to use grunt.option for the same reason that all the examples only showed directing which task is run, but I wouldn't be surprised if there is a more "grunt" way to do this.
I've written a function which I'd like to use as a Grunt task. I can do this by adding this to the Gruntfile:
grunt.registerTask('foo', function () {
// code here
});
However, it makes more sense to keep the function code in a separate file. I plan to define a bunch of these custom tasks and I don't want to bloat the Gruntfile.
I'm not sure what the preferred way of registering such tasks is. I have found this to work:
grunt.registerTask('foo', function () {
require('./path/to/foo.js')(grunt);
});
So, I'm having the inline function like in the fist example, but this time, I'm loading an external file and invoking it immediately. In that external file, I of course have to write:
module.exports = function (grunt) {
// code here
}
This works, but it feels hackish. Is there a more proper way of doing this?
Short answer: the alternative to this
grunt.registerTask('foo', function () {
require('./path/to/foo.js')(grunt);
});
is http://gruntjs.com/api/grunt#grunt.loadtasks
Long answer:
Normally when you have tasks in external files there are served as other nodejs modules. So, if that is something that you will use in several projects you may want to register it in the registry. Later inside your Gruntfile.js you will have:
grunt.loadNpmTasks('yout-module-here');
The grunt's documentation says:
Load tasks from the specified Grunt plugin. This plugin must be installed locally via npm, and must be relative to the Gruntfile
However, if you don't want to upload anything to the registry you should use loadTasks
grunt.loadTasks('path/to/your/task/directory');
So, once the task is loaded you may use it in your configuration.
Here is a simple grunt task placed in external file:
'use strict';
module.exports = function(grunt) {
grunt.registerMultiTask('nameoftask', 'description', function() {
var self = this;
// this.data here contains your configuration
});
};
And later in Gruntfile.js
grunt.initConfig({
nameoftask: {
task: {
// parameters here
}
}
});
I had a similar problem.
I wanted to modularize my grunt config and custom tasks by functionnalities (big UX/UI blocks) rather than by technical features. AND I wanted to keep the config files next to task files... (better when working on a large legacy codebase with an varied team - 5 persons with varying JS knowledge)
So I externalized my tasks like Krasimir did.
In the gruntfile, I wrote :
//power of globbing for loading tasks
var tasksLocations = ['./grunt-config/default_tasks.js', './grunt-config/**/tasks.js'];
var taskFiles = grunt.file.expand({
filter: "isFile"
}, tasksLocations);
taskFiles.forEach(function(path) {
grunt.log.writeln("=> loading & registering : " + path);
require(path)(grunt);
});
You will find the whole boilerplate gruntfile here (external config and tasks loading) : https://gist.github.com/0gust1/7683132