diff --git a/Gruntfile.js b/Gruntfile.js index a7aea22..f7feb7e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2,7 +2,7 @@ * grunt-contrib-requirejs * http://gruntjs.com/ * - * Copyright (c) 2016 Tyler Kellen, contributors + * Copyright (c) 2012-2016 Tyler Kellen, contributors * Licensed under the MIT license. */ @@ -41,7 +41,8 @@ module.exports = function(grunt) { options: { baseUrl: 'test/fixtures', name: 'project', - out: 'tmp/requirejs-template.js' + out: 'tmp/requirejs-template.js', + verbose: true } }, onOptimize: { @@ -54,6 +55,44 @@ module.exports = function(grunt) { done(); } } + }, + 'custom-requirejs': { + options: { + requirejs: require('requirejs'), + baseUrl: 'test/fixtures', + name: 'project', + out: 'tmp/custom-requirejs.js' + } + }, + 'build-failure': { + options: { + baseUrl: 'test/failure', + name: 'project', + out: 'tmp/build-failure.js', + force: true + } + }, + 'done-failure': { + options: { + baseUrl: 'test/fixtures', + name: 'project', + out: 'tmp/done-failure.js', + done: function() { + throw new Error('in done'); + }, + force: true + } + }, + 'error-failure': { + options: { + baseUrl: 'test/failure', + name: 'project', + out: 'tmp/error-failure.js', + error: function() { + throw new Error('in error'); + }, + force: true + } } }, diff --git a/docs/requirejs-examples.md b/docs/requirejs-examples.md index f682984..f934efc 100644 --- a/docs/requirejs-examples.md +++ b/docs/requirejs-examples.md @@ -55,3 +55,19 @@ requirejs: { } } ``` + +#### Requirejs + +```js +requirejs: { + compile: { + options: { + requirejs: require('@my/requirejs'), + baseUrl: 'path/to/base', + mainConfigFile: 'path/to/config.js', + include: [ 'src/main.js' ], + out: 'path/to/optimized.js' + } + } +} +``` diff --git a/docs/requirejs-options.md b/docs/requirejs-options.md index 2650462..d4695ef 100644 --- a/docs/requirejs-options.md +++ b/docs/requirejs-options.md @@ -4,5 +4,24 @@ For a full list of possible options, [see the r.js example build file](https://g ## done(done, build) -The done option is an optional hook to receive the r.js build output. The first argument is the grunt async callback that you are required to call if you provide the done hook. This informs grunt that the task is complete. The second parameter is the build output from r.js. +The `done` option is an optional hook to receive the `r.js` build output. The first argument is the grunt async callback that you are required to call if you provide the `done` hook. This informs grunt that the task is complete. The second parameter is the build output from `r.js`. +## error(done, error) + +The `error` option is an optional hook to receive the `r.js` error. The first argument is the grunt async callback that you are required to call if you provide the `done` hook. This informs grunt that the task is complete. The second parameter is the error instance thrown from `r.js` in case of failure. + +## requirejs + +The `requirejs` option is an object exported from a module compatible with `r.js`. You can pass an alternative optimizer version to the task by this option. The module `requirejs` is used by `require('requirejs')` by default. + +## logLevel + +The `logLevel` option is a number to be passed to the `r.js` as the log level. The values can be 0 (tracing), 1 (information), 2 (warning) or 3 (error). The default if 2 (warning). + +## verbose + +The `verbose` option is a boolean to enable logging at the level 0 (tracing) if set to `true`. The default is `false`. + +## force + +The `force` option is a boolean, which if set to `true`, forces the grunt running and executing further tasks, although the current task failed. The default is `false`. diff --git a/tasks/requirejs.js b/tasks/requirejs.js index 4e7fe53..a6f04fa 100644 --- a/tasks/requirejs.js +++ b/tasks/requirejs.js @@ -2,46 +2,75 @@ * grunt-contrib-requirejs * http://gruntjs.com/ * - * Copyright (c) 2016 Tyler Kellen, contributors + * Copyright (c) 2012-2016 Tyler Kellen, contributors * Licensed under the MIT license. */ 'use strict'; module.exports = function(grunt) { - - var requirejs = require('requirejs'); var LOG_LEVEL_TRACE = 0, LOG_LEVEL_WARN = 2; + var force; - // TODO: extend this to send build log to grunt.log.ok / grunt.log.error - // by overriding the r.js logger (or submit issue to r.js to expand logging support) - requirejs.define('node/print', [], function() { - return function print(msg) { - if (msg.substring(0, 5) === 'Error') { - grunt.log.errorlns(msg); - grunt.fail.warn('RequireJS failed.'); - } else { - grunt.log.oklns(msg); - } - }; - }); + function warning() { + /* c8 ignore next */ + return force ? grunt.log.warn : grunt.fail.warn; + } - grunt.registerMultiTask('requirejs', 'Build a RequireJS project.', function() { + function fatal() { + /* c8 ignore next */ + return force ? grunt.log.fatal : grunt.fail.fatal; + } + + function initializeRequire(requirejs) { + if (!requirejs) { + requirejs = require('requirejs'); + } + + // TODO: extend this to send build log to grunt.log.ok / grunt.log.error + // by overriding the r.js logger (or submit issue to r.js to expand logging support) + requirejs.define('node/print', [], function() { + return function print(msg) { + if (msg.substring(0, 5) === 'Error') { + grunt.log.errorlns(msg); + warning()('RequireJS failed.'); + /* c8 ignore next 3 */ + } else { + grunt.log.oklns(msg); + } + }; + }); + return requirejs; + } + + grunt.registerMultiTask('requirejs', 'Build a RequireJS project.', function() { var done = this.async(); var options = this.options({ + requirejs: undefined, + /* c8 ignore next */ logLevel: grunt.option('verbose') ? LOG_LEVEL_TRACE : LOG_LEVEL_WARN, error: false, + force: false, + verbose: false, done: function(done) { done(); } }); + // If force is set, failures will be only logged and not abort the process. + force = options.force; + // If verbose is set, requirejs will print more about the progress on the console. + if (options.verbose) { + options.logLevel = LOG_LEVEL_TRACE; + } + // The following catches errors in the user-defined `done` function and outputs them. var tryCatchDone = function(fn, done, output) { try { fn(done, output); } catch (e) { - grunt.fail.warn('There was an error while processing your done function: "' + e + '"'); + warning()('There was an error while processing your done function: "' + e + '"'); + done(); } }; @@ -51,15 +80,22 @@ module.exports = function(grunt) { try { fn(done, err); } catch (e) { - grunt.fail.fatal('There was an error while processing your error function: "' + e + '"'); + fatal()('There was an error while processing your error function: "' + e + '"'); + done(); } }; + // Ensure that done() is always called. Not calling it would abort + // execution of still pending tasks after this one. + var doneNoArgs = function() { + done(); + }; + + var requirejs = initializeRequire(options.requirejs); requirejs.optimize( options, - tryCatchDone.bind(null, options.done, done), - options.error ? tryCatchError.bind(null, options.error, done):undefined + tryCatchDone.bind(null, options.done, doneNoArgs), + options.error ? tryCatchError.bind(null, options.error, doneNoArgs) : doneNoArgs ); - }); }; diff --git a/test/failure/invalid.js b/test/failure/invalid.js new file mode 100644 index 0000000..47f3aa9 --- /dev/null +++ b/test/failure/invalid.js @@ -0,0 +1 @@ +define(function(){return "never";} diff --git a/test/failure/project.js b/test/failure/project.js new file mode 100644 index 0000000..1df16da --- /dev/null +++ b/test/failure/project.js @@ -0,0 +1,3 @@ +require(['invalid'], function(invalid) { + console.log(invalid); +});