Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Grunt is a JavaScript task runner that automates repetitive tasks such as minification, compilation, unit testing, linting, and more. It is highly configurable and uses a Gruntfile to define tasks and load plugins.
Task Automation
This feature allows you to automate tasks such as minification. The provided code sample demonstrates how to configure Grunt to use the 'uglify' plugin to minify JavaScript files.
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['uglify']);
};
File Watching
Grunt can watch files and execute tasks when they change. The code sample shows how to configure Grunt to watch JavaScript files and run the 'jshint' task whenever a file changes.
module.exports = function(grunt) {
grunt.initConfig({
watch: {
scripts: {
files: ['**/*.js'],
tasks: ['jshint'],
options: {
spawn: false,
},
},
},
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['watch']);
};
Compilation
Grunt can compile preprocessor languages like Sass into CSS. The code sample demonstrates how to configure Grunt to compile a Sass file into a CSS file using the 'grunt-contrib-sass' plugin.
module.exports = function(grunt) {
grunt.initConfig({
sass: {
dist: {
files: {
'main.css': 'main.scss'
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.registerTask('default', ['sass']);
};
Gulp is another JavaScript task runner that uses a code-over-configuration approach, making it more flexible and easier to read than Grunt. It uses streams and code to define tasks, which can result in faster build times.
Webpack is a module bundler primarily for JavaScript, but it can transform front-end assets like HTML, CSS, and images if the corresponding loaders are included. It is more powerful and complex than Grunt, often used for modern JavaScript applications.
Broccoli is a JavaScript build tool that focuses on fast rebuilds and a simple, composable API. It is particularly well-suited for large projects where build performance is critical.
Grunt is a command line build tool for JavaScript projects.
As of now, grunt has the following predefined tasks:
(My TODO list includes a "project scaffolding" task as well as a "QUnit headless unit-testing" task)
And in addition to the predefined tasks, you can define your own.
Doing all this stuff manually is a total pain, and building all this stuff into a gigantic Makefile / Jakefile / Cakefile / Rakefile / ?akefile that's maintained across all my projects was also becoming a total pain. Since I always found myself performing the same tasks over and over again, for every project, it made sense to build a task-based build tool.
Being primarily a JavaScript developer, I decided to use Node.js and npm because the dependencies I most care about (JSHint and UglifyJS) were already npm modules. That being said, while Node.js was designed to support highly-concurrent asynchronous-IO-driven web servers, it was clearly NOT designed to make command-line build tools. But none of that matters, because grunt works. Just install it and see.
Grunt is currently in beta. While I'm already using it on multiple projects, it might have a minor issue or two. And things might change before its final release, based on your feedback. Please try it out in a project, and make suggestions or report bugs!
Grunt is available as an npm module. If you install grunt globally via npm install -g grunt
, it will be available for use in all of your projects.
Once grunt has been installed, you can type grunt --help
at the command line for more information. And if you want to see grunt "grunt" itself, cd into grunt's directory and type grunt
(in Windows, you might need to run it as grunt.cmd
).
When run, grunt looks in the current directory for a file named grunt.js
, and if not found, continues looking in parent directories until found. The gruntfile is typically placed in the root of your project repository, and is a valid JavaScript file comprised of two parts: project configuration, and tasks / helpers.
For example, this simple configuration would define a list of files to be linted when the task "lint:files" was run on the command line like this: grunt lint:files
.
config.init({
lint: {
files: ['lib/*.js', 'test/*.js', 'grunt.js']
}
});
Also note that because the "lint" task is a basic task, you can also run all lint sub-tasks with just grunt lint
.
You can store any arbitrary information inside of the configuration object, and as long as it doesn't conflict with a property one of your tasks is using, it will be ignored.
config.init({
// Generic project information used by some helpers and tasks.
meta: {},
// Lists of files to be concatenated, used by the "concat" task.
concat: {},
// Lists of files to be minififed with UglifyJS, used by the "min" task.
min: {},
// Lists of files to be unit tested with Nodeunit, used by the "test" task.
test: {},
// Lists of files to be linted with JSHint, used by the "lint" task.
lint: {},
// Global configuration options for JSHint.
jshint: {},
// Global configuration options for UglifyJS.
uglify: {}
});
Take a look at grunt's own grunt.js gruntfile or javascript-hooker's gruntfile or glob-whatev's gruntfile for a few examples.
Note: you don't need to specify configuration settings for tasks that you don't use.
## Tasks Tasks are the things you do most often, like [concat][concat], [lint][lint], [min][min] or [test][test] files. Every time grunt is run, one or more tasks must be specified, which tells grunt what you want it to do. Note that if you don't specify a task, but a task named "default" has been defined, that task will run (unsurprisingly) by default.You should probably create a "default" task in your gruntfile.
Tasks can be created in a few ways.
### Alias taskstask.registerTask(taskName, [description, ] taskList);
Note that the description is optional. If omitted, a useful description will be added for you automatically.
The following example defines a task named "theworks" that, when run, actually runs the "lint:files" "test:files" "concat" "min" tasks, in-order. so instead of typing grunt lint:files test:files concat min
at the command line, you can just type grunt theworks
. If this task were named "default" instead of "theworks" it would be run by default whenever grunt
was executed without specifying tasks.
task.registerTask('theworks', 'lint:files test:files concat min');
### Basic tasks
A basic task is a task that implicitly iterates over all of its configuration sub-properties if no sub-task is specified. For example, in the following, while `grunt lint:test` or `grunt lint:lib` will lint only those specific files, `grunt lint` will run the "test", "lib" and "grunt" sub-tasks for you, automatically. It's convenient.
config.init({
lint: {
test: ['test/*.js'],
lib: ['lib/*.js'],
grunt: ['grunt.js']
}
});
While it's probably more useful for you to check out the JavaScript source of the concat, lint, min or test tasks, this is how you'd define a Basic task:
task.registerBasicTask('log', 'This task logs something.', function(data, name) {
// data === the value of the config sub-prop
// name === the name of the config sub-prop
log.writeln(data);
if (failureOfSomeKind) { return false; }
log.writeln('Your success message.');
});
### Custom tasks
You can go crazy with tasks, though. They don't have to be basic.
task.registerTask('default', 'My "default" task description.', function() {
log.writeln('Currently running the "default" task.');
});
Inside a task, you can run other tasks.
task.registerTask('foo', 'My "foo" task.', function() {
// Enqueue "bar" and "baz" tasks, to run after 'foo' finishes, in-order.
task.run('bar baz');
// Or:
task.run(['bar', 'baz']);
});
Tasks can be asynchronous.
task.registerTask('async', 'My "foo" task.', function() {
// Force task into async mode and grab a handle to the "done" function.
var done = this.async();
// Run some sync stuff.
log.writeln('Processing task...');
// And some async stuff.
setTimeout(function() {
log.writeln('All done!');
done();
}, 1000);
});
Tasks can access their own name and arguments.
task.registerTask('foo', 'My "foo" task.', function(a, b) {
log.writeln(this.name, a, b);
});
// Usage:
// grunt foo foo:bar
// logs: "foo", undefined, undefined
// logs: "foo", "bar", undefined
// grunt foo:bar:baz
// logs: "foo", "bar", "baz"
Tasks can fail if any errors were logged.
task.registerTask('foo', 'My "foo" task.', function() {
if (someError) {
log.error('This is an error message.');
}
// Fail task if errors were logged.
if (task.hadErrors()) { return false; }
log.writeln('This is the success message');
});
When tasks fail, all subsequent tasks will be aborted unless --force
is specified.
task.registerTask('foo', 'My "foo" task.', function() {
// Fail synchronously.
return false;
});
task.registerTask('bar', 'My "bar" task.', function() {
var done = this.async();
setTimeout(function() {
// Fail asynchronously.
done(false);
}, 1000);
});
Tasks can be dependent on the successful execution of other tasks. Note that task.requires
won't actually RUN the other task. It'll just check to see that it has run and not failed.
task.registerTask('foo', 'My "foo" task.', function() {
return false;
});
task.registerTask('bar', 'My "bar" task.', function() {
// Fail task if "foo" task failed or never ran.
task.requires('foo');
// This code executes if the "foo" task ran successfully.
log.writeln('Hello, world.');
});
// Usage:
// grunt foo bar
// doesn't log, because foo failed.
// grunt bar
// doesn't log, because foo never ran.
Tasks can fail if required configuration properties don't exist.
task.registerTask('foo', 'My "foo" task.', function() {
// Fail task if "meta.name" config prop is missing.
config.requires('meta.name');
// Also fails if "meta.name" config prop is missing.
config.requires(['meta', 'name']);
// Log... conditionally.
log.writeln('This will only log if meta.name is defined in the config.');
});
Tasks can access configuration properties.
task.registerTask('foo', 'My "foo" task.', function() {
// Log the property value. Returns null if the property is undefined.
log.writeln('The meta.name property is: ' + config('meta.name'));
// Also logs the property value. Returns null if the property is undefined.
log.writeln('The meta.name property is: ' + config(['meta', 'name']));
});
Look at the built-in tasks for more examples.
Helpers are just utility functions, exposed through the task
global variable, so that they can be used by tasks in other files.
It's not much more complex than this:
task.registerHelper('foo', function(a, b) {
return a + b;
});
task.helper('foo', 2, 3) // 5
For example, in the min task, the majority of the actual minification work is done in an uglify helper, so that other tasks can utilize that code if they need to.
Directives are essentially string placeholders for helper functions, specified as values in the configuration object. It's not as crazy as it sounds.
A good example of directives would be the <json:package.json>
and <config:lint.files>
directives in grunt's own grunt.js gruntfile. Or the <banner>
and <file_strip_banner:lib/hooker.js>
directives in javascript-hooker's gruntfile.
In brief, when a directive like <foo>
is encountered, the foo
helper is executed, and its return value is used. If <foo:bar:baz>
is encountered, the foo
helper is executed, with arguments "bar"
and "baz"
passed in, and its return value is used.
Some of the built-in directives:
<config:prop.subprop>
- expand to the prop.subprop config property. Great for DRYing up file lists.<json:file.json>
- expand to the object parsed from file.json (a valid JSON file).<banner>
- the string in config property meta.banner
, parsed via handlebars.<banner:prop.subprop>
- same as above, but using a custom config property.<file_strip_banner:file.js>
- expand to the given file, with any leading /.../ banner stripped.Can you guess what these directives do? They're from grunt's own grunt.js gruntfile.
config.init({
pkg: '<json:package.json>',
lint: {
files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js']
},
watch: {
files: '<config:lint.files>',
tasks: 'default'
}
});
In an effort to make things easier, there are a lot of global variables.
underscore
- Underscore.jsutil
- miscellaneous utilitiestask
- the entire task interfacefile
- glob expansion, file reading, writing, directory traversingfail
- more serious than error logging, fail.warn
and fail.fatal
will halt everythingconfig
- reading values from the grunt configurationoption
- reading values from the command-line optionslog
- don't use console.log
, use log.writeln
instead! More info on log.verbose
- just like log
, but only logs if --verbose
was specified. More info on verbose.Unfortunately, I haven't documented everything yet. Fortunately, the source is open and browsable. Have fun!
## Logging I wanted grunt to look pretty. As such, there are a LOT of logging methods, and a few useful patterns:Note, all of the methods that actually log something are chainable.
log.write(msg)
- log msg, with no trailing newlinelog.writeln(msg)
- log msg, with trailing newlinelog.error(msg)
- if msg is omitted, logs ERROR in red, otherwise logs: >> msg, with trailing newlinelog.ok(msg)
- if msg is omitted, logs OK in green, otherwise logs: >> msg, with trailing newlinelog.subhead(msg)
- logs msg in bold, with trailing newlinelog.writeflags(obj, prefix)
- logs a list of obj properties (good for debugging flags)log.wordlist(arr)
- returns a comma-separated list of array itemslog.verbose
- contains all methods of log
but only logs if --verbose
was specified.log.notverbose
- contains all methods of log
but only logs if --verbose
wasn't specified.log.verbose.or
- reference to log.notverbose
log.notverbose.or
- reference to log.verbose
There are a few other methods, but you shouldn't use them in your tasks or helpers, so they've been omitted.
A common pattern is to only log when in --verbose
mode OR if an error occurs, like so:
task.registerHelper('something', function(arg) {
var result;
var msg = 'Doing something...';
verbose.write(msg);
try {
result = doSomethingThatThrowsAnExceptionOnError(arg);
// Success!
verbose.ok();
return result;
} catch(e) {
// Something went wrong.
verbose.or.write(msg).error().error(e.message);
fail.warn('Something went wrong.', 50);
}
});
An explanation of the above code:
verbose.write(msg);
logs the message (no newline), but only in --verbose
mode.verbose.ok();
logs OK in green, with a newline.verbose.or.write(msg).error().error(e.message);
does a few things:verbose.or.write(msg)
logs the message (no newline) if not in --verbose
mode, and returns the notverbose
object..error()
logs ERROR in red, with a newline, and returns the notverbose
object..error(e.message);
logs the actual error message (and returns the notverbose
object).fail.warn('Something went wrong.', 50);
logs a warning in bright yellow, existing grunt with exit code 50, unless --force
was specified.You can write crazy logging chains, omg!
## Exit Codes1
- Generic error.2
- Config file not found.3
- Generic task failed.10
- Uglify-JS error.11
- Banner generation error.20
- Init error.61-69
- Nodeunit errors.You should really do grunt lint:beforeconcat concat lint:afterconcat
.
config.init({
// When the "concat:dist/output.js" task is run, the specified "foo.js" and
// "bar.js" files will be concatenated in-order and saved to the "output.js"
// output file. Because the "concat" task is a Basic task, when it is run
// without an argument, all sub-tasks will automatically be run.
concat: {
'dist/output.js': ['src/foo.js', 'src/bar.js']
},
lint: {
// When the "lint:beforeconcat" task is run, the specified "foo.js" and
// "bar.js" files will be linted with JSHint. The same follows for the
// "lint:afterconcat" task. Because the "lint" task is a Basic task, when
// it is run without an argument, all sub-tasks will automatically be run.
beforeconcat: ['src/foo.js', 'src/bar.js'],
afterconcat: ['dist/output.js']
}
});
And to make your workflow easier, create an Alias Task:
task.registerTask('default', 'lint:beforeconcat concat lint:afterconcat');
(more examples coming... soon)
Fork, tweak, and make pull requests.. but you'd better successfully grunt
it first, or I'm not even looking.
(For now, this will only be updated for v0.x releases, not v0.x.x releases)
2012/01/11 - v0.1.0 - Initial release.
Copyright (c) 2012 "Cowboy" Ben Alman
Licensed under the MIT license.
http://benalman.com/about/license/
FAQs
The JavaScript Task Runner
The npm package grunt receives a total of 525,583 weekly downloads. As such, grunt popularity was classified as popular.
We found that grunt demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.