[Pkg-javascript-commits] [grunt] 01/08: New upstream version 1.0.1
Sruthi Chandran
srud-guest at moszumanska.debian.org
Fri Nov 11 08:50:00 UTC 2016
This is an automated email from the git hooks/post-receive script.
srud-guest pushed a commit to branch master
in repository grunt.
commit 200235835f9277b4db6755ad17006ad6877ba236
Author: Sruthi <srud at disroot.org>
Date: Fri Nov 11 13:35:55 2016 +0530
New upstream version 1.0.1
---
.gitattributes | 2 +
.jscsrc | 5 +
.jshintrc | 14 +
.travis.yml | 15 +-
AUTHORS | 1 +
CHANGELOG | 100 +++++-
Gruntfile.js | 75 ++---
LICENSE | 35 ++
LICENSE-MIT | 22 --
README.md | 11 +-
appveyor.yml | 29 ++
bin/grunt | 3 +
internal-tasks/bump.js | 143 +++++++++
internal-tasks/subgrunt.js | 25 ++
lib/grunt.js | 28 +-
lib/grunt/cli.js | 79 +----
lib/grunt/config.js | 15 +-
lib/grunt/event.js | 9 -
lib/grunt/fail.js | 19 +-
lib/grunt/file.js | 82 +++--
lib/grunt/help.js | 10 -
lib/grunt/log.js | 352 ---------------------
lib/grunt/option.js | 9 -
lib/grunt/task.js | 85 ++---
lib/grunt/template.js | 13 +-
lib/grunt/util.js | 189 -----------
lib/util/exit.js | 26 --
lib/util/namespace.js | 63 ----
lib/util/task.js | 46 ++-
package.json | 79 ++---
test/fixtures/Gruntfile-cli.js | 16 +
test/fixtures/Gruntfile-print-text.js | 8 -
test/fixtures/exec.cmd | 1 -
test/fixtures/exec.sh | 2 -
test/fixtures/expand/css/baz.css | 1 +
test/fixtures/expand/css/qux.css | 1 +
test/fixtures/expand/deep/deep.txt | 1 +
test/fixtures/expand/deep/deeper/deeper.txt | 1 +
.../expand/deep/deeper/deepest/deepest.txt | 1 +
test/fixtures/expand/js/bar.js | 1 +
test/fixtures/expand/js/foo.js | 1 +
.../node_modules/grunt-foo-plugin/package.json | 6 +
.../node_modules/grunt-foo-plugin/tasks/foo.js | 7 +
test/fixtures/load-npm-tasks/package.json | 7 +
test/fixtures/spawn.js | 5 +-
test/grunt/cli_test.js | 53 ++++
test/grunt/config_test.js | 27 +-
test/grunt/event_test.js | 2 +-
test/grunt/file_test.js | 169 ++++++++--
test/grunt/log_test.js | 224 -------------
test/grunt/option_test.js | 8 +-
test/grunt/template_test.js | 2 +-
test/grunt/util_test.js | 301 ------------------
test/gruntfile/load-npm-tasks.js | 42 +++
test/gruntfile/multi-task-files.js | 118 ++++++-
test/util/namespace_test.js | 51 ---
test/util/task_test.js | 20 +-
57 files changed, 1042 insertions(+), 1618 deletions(-)
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..222ba62
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+* text=auto
+/test/fixtures/*.txt text eol=lf
diff --git a/.jscsrc b/.jscsrc
new file mode 100644
index 0000000..4a6720e
--- /dev/null
+++ b/.jscsrc
@@ -0,0 +1,5 @@
+{
+ "preset": "grunt",
+ // Nullified until the files in test/ can be cleaned
+ "maximumLineLength": null
+}
\ No newline at end of file
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..e3f057e
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,14 @@
+{
+ "boss": true,
+ "curly": true,
+ "eqeqeq": true,
+ "eqnull": true,
+ "immed": true,
+ "latedef": "nofunc",
+ "newcap": true,
+ "noarg": true,
+ "node": true,
+ "sub": true,
+ "undef": true,
+ "unused": true
+}
diff --git a/.travis.yml b/.travis.yml
index b30fcb7..c7b8f8c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,16 @@
+sudo: false
language: node_js
node_js:
- - 0.8
+ - "0.10"
+ - "0.12"
+ - "4.0"
+ - "4.1"
+ - "4.2"
+ - "5"
+ - "iojs"
+before_install:
+ - npm install -g npm
before_script:
- - npm install -g grunt-cli
+ - npm uninstall grunt # https://github.com/npm/npm/issues/3958
+matrix:
+ fast_finish: true
diff --git a/AUTHORS b/AUTHORS
index d9cace8..0a17765 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,3 +2,4 @@
Kyle Robinson Young (http://dontkry.com/)
Tyler Kellen (http://goingslowly.com)
Sindre Sorhus (http://sindresorhus.com)
+Vlad Filippov (http://vladfilippov.com/)
diff --git a/CHANGELOG b/CHANGELOG
index 81e937b..935c708 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,14 +1,110 @@
+v1.0.1
+ date: 2016-04-05
+ changes:
+ - minor fix for npm issues when installing grunt and grunt-cli at the same time. Pull #1500.
+v1.0.0
+ date: 2016-04-04
+ changes:
+ - full list of changes is on http://gruntjs.com, please also see changes from 1.0.0-rc1.
+ - if you have a Grunt plugin that includes `grunt` in the `peerDependencies`,
+ we recommend tagging with `"grunt": "">= 0.4.0"` and publishing a new version on npm.
+ - Prevent async callback from being called multiple times. Pull #1464.
+ - Update copyright to jQuery Foundation and remove redundant headers. Fixes #1478.
+ - Update glob to 7.0.x. Fixes #1467.
+ - Removing duplicate BOM strip code. Pull #1482.
+ - Update legacy log and util to 1.0.0.
+ - Update to latest cli ~1.2.0.
+ - Use grunt-known-options for shared options between Grunt and grunt-cli.
+v1.0.0-rc1
+ date: 2016-02-11
+ changes:
+ - full list of changes is on http://gruntjs.com
+ - if you have a Grunt plugin that includes `grunt` in the `peerDependencies`,
+ we recommend tagging with `"grunt": "">= 0.4.0"`
+ - `coffee-script` is upgraded to `~1.10.0` which could incur breaking changes
+ when using the language with plugins and Gruntfiles.
+ - `nopt` is upgraded to `~3.0.6` which has fixed many issues, including passing
+ multiple arguments and dealing with numbers as options. Be aware previously
+ `--foo bar` used to pass the value `'bar'` to the option `foo`. It will now
+ set the option `foo` to `true` and run the task `bar`.
+ -`glob` is upgraded to `~6.0.4` and `minimatch` is upgraded to `~3.0.0`. Results
+ are now sorted by default with `grunt.file.expandMapping()`. Pass the
+ `nosort: true` option if you don't want the results to be sorted.
+ - `lodash` was upgraded to `~4.3.0`. Many changes have occurred. Some of which
+ that directly effect Grunt are `grunt.util._.template()` returns a compile
+ function and `grunt.util._.flatten` no longer flattens deeply.
+ `grunt.util._` is deprecated and we highly encourage you to
+ `npm install lodash` and `var _ = require('lodash')` to use `lodash`.
+ Please see the lodash changelog for a full list of changes: https://github.com/lodash/lodash/wiki/Changelog
+ - `iconv-lite` is upgraded to `~0.4.13` and strips the BOM by default.
+ - `js-yaml` is upgraded to `~3.5.2` and may affect `grunt.file.readYAML`.
+ We encourage you to please `npm install js-yaml` and use
+ `var YAML = require('js-yaml')` directly in case of future deprecations.
+ - A file `mode` option can be passed into
+ [grunt.file.write()](http://gruntjs.com/api/grunt.file#grunt.file.write).
+ - `Done, without errors.` was changed to `Done.` to avoid failing by mistake
+ on the word `errors`.
+v0.4.5:
+ date: 2014-05-12
+ changes:
+ - Updated rimraf to 2.2.8. Closes gh-1134.
+ - Moved grunt.log into separate grunt-legacy-log module.
+ - Updated grunt-legacy-util to 0.2.0. Closes gh-971, gh-1129, gh-1118.
+ - Added grunt.task.exists method to check if a task exists. Closes gh-1131.
+ - Added grunt.config.merge method to deep merge config data. See gh-1039.
+ - Fixed symlink issues with 'file.isPathCwd' and 'file.doesPathContain'. Closes gh-1112.
+ - Config and util.recurse no longer mangle Buffer instances. See gh-971.
+ - Config and util.recurse now enumerate inherited object properties. See gh-1129.
+ - Config and util.recurse now throw useful circular reference error. See gh-1118.
+ - Warn instead of error when no new tasks found via '.loadTasks' method. Closes gh-1059.
+ - Added Windows CI testing. Closes gh-1110.
+ - Removed "CONTRIBUTING.md" from .npmignore. Closes gh-1093.
+v0.4.4:
+ date: 2014-03-12
+ changes:
+ - Only signal completion of tasks async if grunt.task.start is invoked with `{asyncDone:true}`.
+v0.4.3:
+ date: 2014-03-07
+ changes:
+ - When devving Grunt, do "npm install && npm uninstall grunt" (isaacs/npm#3958)
+ - Grunt is now tested on Node.js 0.11
+ - Extracted internal "util" lib to "grunt-legacy-util" lib
+ - task.normalizeMultiTaskFiles now flattens nested "files" arrays. Closes gh-1034.
+ - Better error in renameTask if task doesn't exist. Closes gh-1058.
+ - Update rimraf to latest version. Closes gh-1043.
+ - Empty string "ext" should strip extension. Closes gh-1087.
+ - Add expandMapping .extDot option. Can be 'first' or 'last' but defaults to 'first'. Closes gh-979.
+ - Add default array for util.spawn optional args. Closes gh-1064.
+ - util.spawn "grunt" option now uses proper Node, passes Node exec options. Closes gh-980, gh-981, gh-877.
+ - Make all tasks asynchronous to reduce call stack. Closes gh-1026.
+ - Fix <%= grunt.task.current.target %> in Multitask files. Closes gh-994.
+ - Generalize cli tests, see gh-983, gh-991.
+ - --debug option can optionally be Boolean. Closes Gh-983, gh-991.
+v0.4.2:
+ date: 2013-11-21
+ changes:
+ - Extract internal "namespace" lib to external "getobject" lib.
+ - '"Grunt collections" are now deprecated, use peerDependencies. See "grunt-contrib" 0.8.0 for details.'
+ - Fix stdout / stderr issues on Windows. Closes gh-940, gh-921, gh-744, gh-792, gh-644, gh-708.
+ - Fix pipe-redirecting on Windows. Closes gh-510.
+ - Fixed this.options() in renamed basic tasks. Closes gh-855.
+ - Update underscore.string dependency to follow semver. Closes gh-886.
+ - Output task options in verbose mode. Closes gh-749.
+ - Add file.preserveBOM property. Closes gh-806, gh-937.
+ - Test that file methods warn. Closes gh-909.
+ - Fixed a few spelling errors in code comments. Closes gh-849.
+ - Updated watch, jshint and nodeunit deps. Closes gh-914.
v0.4.1:
date: 2013-03-13
changes:
- Fix path.join thrown errors with expandMapping rename. Closes gh-725.
- Update copyright date to 2013. Closes gh-660.
- Remove some side effects from manually requiring Grunt. Closes gh-605.
- - grunt.log: add formatting support and implicitly cast msg to a string. Closes gh-703.
+ - "grunt.log: add formatting support and implicitly cast msg to a string. Closes gh-703."
- Update js-yaml to version 2. Closes gh-683.
- The grunt.util.spawn method now falls back to stdout when the `grunt` option is set. Closes gh-691.
- Making --verbose "Files:" warnings less scary. Closes gh-657.
- - Fixing typo: the grunt.fatal method now defaults to FATAL_ERROR. Closes gh-656, gh-707.
+ - "Fixing typo: the grunt.fatal method now defaults to FATAL_ERROR. Closes gh-656, gh-707."
- Removed a duplicate line. Closes gh-702.
- Gruntfile name should no longer be case sensitive. Closes gh-685.
- The grunt.file.delete method warns and returns false if file doesn't exist. Closes gh-635, gh-714.
diff --git a/Gruntfile.js b/Gruntfile.js
index 5704ab6..0631dfd 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
module.exports = function(grunt) {
@@ -14,32 +5,35 @@ module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
nodeunit: {
- all: ['test/{grunt,tasks,util}/**/*.js']
+ all: ['test/{grunt,tasks,util}/**/*.js'],
+ tap: {
+ src: '<%= nodeunit.all %>',
+ options: {
+ reporter: 'tap',
+ reporterOutput: 'tests.tap'
+ }
+ }
},
jshint: {
- gruntfile: ['Gruntfile.js'],
+ gruntfile_tasks: ['Gruntfile.js', 'internal-tasks/*.js'],
libs_n_tests: ['lib/**/*.js', '<%= nodeunit.all %>'],
subgrunt: ['<%= subgrunt.all %>'],
options: {
- curly: true,
- eqeqeq: true,
- immed: true,
- latedef: true,
- newcap: true,
- noarg: true,
- sub: true,
- undef: true,
- unused: true,
- boss: true,
- eqnull: true,
- node: true,
- es5: true
+ jshintrc: '.jshintrc'
}
},
+ jscs: {
+ src: [
+ 'lib/**/*.js',
+ 'internal-tasks/**/*.js',
+ 'test/**/*.js',
+ '!test/fixtures/**/*.js'
+ ]
+ },
watch: {
- gruntfile: {
- files: ['<%= jshint.gruntfile %>'],
- tasks: ['jshint:gruntfile']
+ gruntfile_tasks: {
+ files: ['<%= jshint.gruntfile_tasks %>'],
+ tasks: ['jshint:gruntfile_tasks']
},
libs_n_tests: {
files: ['<%= jshint.libs_n_tests %>'],
@@ -57,32 +51,19 @@ module.exports = function(grunt) {
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-jscs');
grunt.loadNpmTasks('grunt-contrib-nodeunit');
grunt.loadNpmTasks('grunt-contrib-watch');
+ // Some internal tasks. Maybe someday these will be released.
+ grunt.loadTasks('internal-tasks');
+
// "npm test" runs these tasks
- grunt.registerTask('test', ['jshint', 'nodeunit', 'subgrunt']);
+ grunt.registerTask('test', '', function(reporter) {
+ grunt.task.run(['jshint', 'jscs', 'nodeunit:' + (reporter || 'all'), 'subgrunt']);
+ });
// Default task.
grunt.registerTask('default', ['test']);
- // Run sub-grunt files, because right now, testing tasks is a pain.
- grunt.registerMultiTask('subgrunt', 'Run a sub-gruntfile.', function() {
- var path = require('path');
- grunt.util.async.forEachSeries(this.filesSrc, function(gruntfile, next) {
- grunt.util.spawn({
- grunt: true,
- args: ['--gruntfile', path.resolve(gruntfile)],
- }, function(error, result) {
- if (error) {
- grunt.log.error(result.stdout).writeln();
- next(new Error('Error running sub-gruntfile "' + gruntfile + '".'));
- } else {
- grunt.verbose.ok(result.stdout);
- next();
- }
- });
- }, this.async());
- });
-
};
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..dcf8a0c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,35 @@
+Copyright jQuery Foundation and other contributors, https://jquery.org/
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/gruntjs/grunt .
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+All files located in the node_modules directory are externally maintained
+libraries used by this software which have their own licenses; we recommend
+you read them, as their terms may differ from the terms above.
\ No newline at end of file
diff --git a/LICENSE-MIT b/LICENSE-MIT
deleted file mode 100644
index bb2aad6..0000000
--- a/LICENSE-MIT
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) 2013 "Cowboy" Ben Alman
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index e0d0f4a..dcd6f65 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,11 @@
-# Grunt: The JavaScript Task Runner [![Build Status](https://secure.travis-ci.org/gruntjs/grunt.png?branch=master)](http://travis-ci.org/gruntjs/grunt)
+# Grunt: The JavaScript Task Runner
+
+[![Build Status: Linux](https://travis-ci.org/gruntjs/grunt.svg?branch=master)](https://travis-ci.org/gruntjs/grunt)
+[![Build Status: Windows](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva/branch/master?svg=true)](https://ci.appveyor.com/project/gruntjs/grunt/branch/master)
+[![Built with Grunt](https://cdn.gruntjs.com/builtwith.svg)](http://gruntjs.com/)
+
+<img align="right" height="260" src="http://gruntjs.com/img/grunt-logo-no-wordmark.svg">
+
### Documentation
@@ -7,7 +14,7 @@ Visit the [gruntjs.com](http://gruntjs.com/) website for all the things.
### Support / Contributing
Before you make an issue, please read our [Contributing](http://gruntjs.com/contributing) guide.
-You can find the grunt team in [#grunt on irc.freenode.net](irc://irc.freenode.net/#grunt).
+You can find the grunt team in [#grunt on irc.freenode.net](http://webchat.freenode.net/?channels=grunt).
### Release History
See the [CHANGELOG](CHANGELOG).
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..9661d6e
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,29 @@
+# Fix line endings on Windows
+init:
+ - git config --global core.autocrlf true
+# What combinations to test
+environment:
+ matrix:
+ - nodejs_version: "0.10"
+ - nodejs_version: "0.12"
+ - nodejs_version: "4"
+ - nodejs_version: "5"
+platform:
+ - x86
+ - x64
+install:
+ - ps: Install-Product node $env:nodejs_version
+ - npm install -g npm
+ - npm install
+test_script:
+ # Output useful info for debugging.
+ - node --version && npm --version
+ # We test multiple Windows shells because of prior stdout buffering issues
+ # filed against Grunt. https://github.com/joyent/node/issues/3584
+ - ps: "npm test # PowerShell" # Pass comment to PS for easier debugging
+ - cmd: npm test
+build: off
+matrix:
+ fast_finish: true
+cache:
+ - node_modules -> package.json # local npm modules
diff --git a/bin/grunt b/bin/grunt
new file mode 100755
index 0000000..9ffa444
--- /dev/null
+++ b/bin/grunt
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+require('grunt-cli/bin/grunt');
diff --git a/internal-tasks/bump.js b/internal-tasks/bump.js
new file mode 100644
index 0000000..75a414a
--- /dev/null
+++ b/internal-tasks/bump.js
@@ -0,0 +1,143 @@
+'use strict';
+
+var semver = require('semver');
+var shell = require('shelljs');
+
+module.exports = function(grunt) {
+
+ grunt.registerTask('bump', 'Bump the version property of a JSON file.', function() {
+ // Validate specified semver increment modes.
+ var valids = ['major', 'minor', 'patch', 'prerelease'];
+ var modes = [];
+ this.args.forEach(function(mode) {
+ var matches = [];
+ valids.forEach(function(valid) {
+ if (valid.indexOf(mode) === 0) { matches.push(valid); }
+ });
+ if (matches.length === 0) {
+ grunt.log.error('Error: mode "' + mode + '" does not match any known modes.');
+ } else if (matches.length > 1) {
+ grunt.log.error('Error: mode "' + mode + '" is ambiguous (possibly: ' + matches.join(', ') + ').');
+ } else {
+ modes.push(matches[0]);
+ }
+ });
+ if (this.errorCount === 0 && modes.length === 0) {
+ grunt.log.error('Error: no modes specified.');
+ }
+ if (this.errorCount > 0) {
+ grunt.log.error('Valid modes are: ' + valids.join(', ') + '.');
+ throw new Error('Use valid modes (or unambiguous mode abbreviations).');
+ }
+ // Options.
+ var options = this.options({
+ filepaths: ['package.json'],
+ syncVersions: false,
+ commit: true,
+ commitMessage: 'Bumping version to {%= version %}.',
+ tag: true,
+ tagName: 'v{%= version %}',
+ tagMessage: 'Version {%= version %}',
+ tagPrerelease: false,
+ });
+ // Normalize filepaths to array.
+ var filepaths = Array.isArray(options.filepaths) ? options.filepaths : [options.filepaths];
+ // Process JSON files, in-order.
+ var versions = {};
+ filepaths.forEach(function(filepath) {
+ var o = grunt.file.readJSON(filepath);
+ var origVersion = o.version;
+ // If syncVersions is enabled, only grab version from the first file,
+ // guaranteeing new versions will always be in sync.
+ var firstVersion = Object.keys(versions)[0];
+ if (options.syncVersions && firstVersion) {
+ o.version = firstVersion;
+ }
+ modes.forEach(function(mode) {
+ var orig = o.version;
+ var s = semver.parse(o.version);
+ s.inc(mode);
+ o.version = String(s);
+ // Workaround for https://github.com/isaacs/node-semver/issues/50
+ if (/-/.test(orig) && mode === 'patch') {
+ o.version = o.version.replace(/\d+$/, function(n) { return n - 1; });
+ }
+ // If prerelease on an un-prerelease version, bump patch version first
+ if (!/-/.test(orig) && mode === 'prerelease') {
+ s.inc('patch');
+ s.inc('prerelease');
+ o.version = String(s);
+ }
+ });
+ if (versions[origVersion]) {
+ versions[origVersion].filepaths.push(filepath);
+ } else {
+ versions[origVersion] = {version: o.version, filepaths: [filepath]};
+ }
+ // Actually *do* something.
+ grunt.log.write('Bumping version in ' + filepath + ' from ' + origVersion + ' to ' + o.version + '...');
+ grunt.file.write(filepath, JSON.stringify(o, null, 2));
+ grunt.log.ok();
+ });
+ // Commit changed files?
+ if (options.commit) {
+ Object.keys(versions).forEach(function(origVersion) {
+ var o = versions[origVersion];
+ commit(o.filepaths, processTemplate(options.commitMessage, {
+ version: o.version,
+ origVersion: origVersion
+ }));
+ });
+ }
+ // We're only going to create one tag. And it's going to be the new
+ // version of the first bumped file. Because, sanity.
+ var newVersion = versions[Object.keys(versions)[0]].version;
+ if (options.tag) {
+ if (options.tagPrerelease || modes.indexOf('prerelease') === -1) {
+ tag(
+ processTemplate(options.tagName, {version: newVersion}),
+ processTemplate(options.tagMessage, {version: newVersion})
+ );
+ } else {
+ grunt.log.writeln('Not tagging (prerelease version).');
+ }
+ }
+ if (this.errorCount > 0) {
+ grunt.warn('There were errors.');
+ }
+ });
+
+ // Using custom delimiters keeps templates from being auto-processed.
+ grunt.template.addDelimiters('bump', '{%', '%}');
+
+ function processTemplate(message, data) {
+ return grunt.template.process(message, {
+ delimiters: 'bump',
+ data: data,
+ });
+ }
+
+ // Kinda borrowed from https://github.com/geddski/grunt-release
+ function commit(filepaths, message) {
+ grunt.log.writeln('Committing ' + filepaths.join(', ') + ' with message: ' + message);
+ run("git commit -m '" + message + "' '" + filepaths.join("' '") + "'");
+ }
+
+ function tag(name, message) {
+ grunt.log.writeln('Tagging ' + name + ' with message: ' + message);
+ run("git tag '" + name + "' -m '" + message + "'");
+ }
+
+ function run(cmd) {
+ if (grunt.option('no-write')) {
+ grunt.verbose.writeln('Not actually running: ' + cmd);
+ } else {
+ grunt.verbose.writeln('Running: ' + cmd);
+ var result = shell.exec(cmd, {silent: true});
+ if (result.code !== 0) {
+ grunt.log.error('Error (' + result.code + ') ' + result.output);
+ }
+ }
+ }
+
+};
diff --git a/internal-tasks/subgrunt.js b/internal-tasks/subgrunt.js
new file mode 100644
index 0000000..dba8d83
--- /dev/null
+++ b/internal-tasks/subgrunt.js
@@ -0,0 +1,25 @@
+'use strict';
+
+module.exports = function(grunt) {
+
+ // Run sub-grunt files, because right now, testing tasks is a pain.
+ grunt.registerMultiTask('subgrunt', 'Run a sub-gruntfile.', function() {
+ var path = require('path');
+ grunt.util.async.forEachSeries(this.filesSrc, function(gruntfile, next) {
+ grunt.log.write('Loading ' + gruntfile + '...');
+ grunt.util.spawn({
+ grunt: true,
+ args: ['--gruntfile', path.resolve(gruntfile)],
+ }, function(error, result) {
+ if (error) {
+ grunt.log.error().error(result.stdout).writeln();
+ next(new Error('Error running sub-gruntfile "' + gruntfile + '".'));
+ } else {
+ grunt.log.ok().verbose.ok(result.stdout);
+ next();
+ }
+ });
+ }, this.async());
+ });
+
+};
diff --git a/lib/grunt.js b/lib/grunt.js
index 7afaa17..072ca24 100644
--- a/lib/grunt.js
+++ b/lib/grunt.js
@@ -1,19 +1,10 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
// Nodejs libs.
var path = require('path');
// This allows grunt to require() .coffee files.
-require('coffee-script');
+require('coffee-script/register');
// The module to be exported.
var grunt = module.exports = {};
@@ -22,7 +13,15 @@ var grunt = module.exports = {};
function gRequire(name) {
return grunt[name] = require('./grunt/' + name);
}
-var util = gRequire('util');
+
+var util = require('grunt-legacy-util');
+grunt.util = util;
+grunt.util.task = require('./util/task');
+
+var Log = require('grunt-legacy-log').Log;
+var log = new Log({grunt: grunt});
+grunt.log = log;
+
gRequire('template');
gRequire('event');
var fail = gRequire('fail');
@@ -30,7 +29,6 @@ gRequire('file');
var option = gRequire('option');
var config = gRequire('config');
var task = gRequire('task');
-var log = gRequire('log');
var help = gRequire('help');
gRequire('cli');
var verbose = grunt.verbose = log.verbose;
@@ -110,7 +108,7 @@ grunt.tasks = function(tasks, options, done) {
tasks = task.parseArgs([tasksSpecified ? tasks : 'default']);
// Initialize tasks.
- task.init(tasks);
+ task.init(tasks, options);
verbose.writeln();
if (!tasksSpecified) {
@@ -152,5 +150,7 @@ grunt.tasks = function(tasks, options, done) {
// Execute all tasks, in order. Passing each task individually in a forEach
// allows the error callback to execute multiple times.
tasks.forEach(function(name) { task.run(name); });
- task.start();
+ // Run tasks async internally to reduce call-stack, per:
+ // https://github.com/gruntjs/grunt/pull/1026
+ task.start({asyncDone: true});
};
diff --git a/lib/grunt/cli.js b/lib/grunt/cli.js
index a86da8f..1252f54 100644
--- a/lib/grunt/cli.js
+++ b/lib/grunt/cli.js
@@ -1,27 +1,16 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
var grunt = require('../grunt');
-// Nodejs libs.
-var path = require('path');
-
// External libs.
var nopt = require('nopt');
+var gruntOptions = require('grunt-known-options');
// This is only executed when run via command line.
var cli = module.exports = function(options, done) {
// CLI-parsed options override any passed-in "default" options.
if (options) {
- // For each defult option...
+ // For each default option...
Object.keys(options).forEach(function(key) {
if (!(key in cli.options)) {
// If this option doesn't exist in the parsed cli.options, add it in.
@@ -39,69 +28,7 @@ var cli = module.exports = function(options, done) {
};
// Default options.
-var optlist = cli.optlist = {
- help: {
- short: 'h',
- info: 'Display this help text.',
- type: Boolean
- },
- base: {
- info: 'Specify an alternate base path. By default, all file paths are relative to the Gruntfile. (grunt.file.setBase) *',
- type: path
- },
- color: {
- info: 'Disable colored output.',
- type: Boolean,
- negate: true
- },
- gruntfile: {
- info: 'Specify an alternate Gruntfile. By default, grunt looks in the current or parent directories for the nearest Gruntfile.js or Gruntfile.coffee file.',
- type: path
- },
- debug: {
- short: 'd',
- info: 'Enable debugging mode for tasks that support it.',
- type: Number
- },
- stack: {
- info: 'Print a stack trace when exiting with a warning or fatal error.',
- type: Boolean
- },
- force: {
- short: 'f',
- info: 'A way to force your way past warnings. Want a suggestion? Don\'t use this option, fix your code.',
- type: Boolean
- },
- tasks: {
- info: 'Additional directory paths to scan for task and "extra" files. (grunt.loadTasks) *',
- type: Array
- },
- npm: {
- info: 'Npm-installed grunt plugins to scan for task and "extra" files. (grunt.loadNpmTasks) *',
- type: Array
- },
- write: {
- info: 'Disable writing files (dry run).',
- type: Boolean,
- negate: true
- },
- verbose: {
- short: 'v',
- info: 'Verbose mode. A lot more information output.',
- type: Boolean
- },
- version: {
- short: 'V',
- info: 'Print the grunt version. Combine with --verbose for more info.',
- type: Boolean
- },
- // Even though shell auto-completion is now handled by grunt-cli, leave this
- // option here for display in the --help screen.
- completion: {
- info: 'Output shell auto-completion rules. See the grunt-cli documentation for more information.',
- type: String
- },
-};
+var optlist = cli.optlist = gruntOptions;
// Parse `optlist` into a form that nopt can handle.
var aliases = {};
diff --git a/lib/grunt/config.js b/lib/grunt/config.js
index 75619a1..ef2bf80 100644
--- a/lib/grunt/config.js
+++ b/lib/grunt/config.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
var grunt = require('../grunt');
@@ -81,6 +72,12 @@ config.set = function(prop, value) {
return grunt.util.namespace.set(config.data, config.getPropString(prop), value);
};
+// Deep merge config data.
+config.merge = function(obj) {
+ grunt.util._.merge(config.data, obj);
+ return config.data;
+};
+
// Initialize config data.
config.init = function(obj) {
grunt.verbose.write('Initializing config...').ok();
diff --git a/lib/grunt/event.js b/lib/grunt/event.js
index 9e5ba0b..7ec1027 100644
--- a/lib/grunt/event.js
+++ b/lib/grunt/event.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
// External lib.
diff --git a/lib/grunt/fail.js b/lib/grunt/fail.js
index 7b8048d..631e249 100644
--- a/lib/grunt/fail.js
+++ b/lib/grunt/fail.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
var grunt = require('../grunt');
@@ -50,18 +41,18 @@ function dumpStack(e) {
}
}
-// A fatal error occured. Abort immediately.
+// A fatal error occurred. Abort immediately.
fail.fatal = function(e, errcode) {
writeln(e, 'fatal');
dumpStack(e);
- process.exit(typeof errcode === 'number' ? errcode : fail.code.FATAL_ERROR);
+ grunt.util.exit(typeof errcode === 'number' ? errcode : fail.code.FATAL_ERROR);
};
// Keep track of error and warning counts.
fail.errorcount = 0;
fail.warncount = 0;
-// A warning ocurred. Abort immediately unless -f or --force was used.
+// A warning occurred. Abort immediately unless -f or --force was used.
fail.warn = function(e, errcode) {
var message = typeof e === 'string' ? e : e.message;
fail.warncount++;
@@ -70,7 +61,7 @@ fail.warn = function(e, errcode) {
if (!grunt.option('force')) {
dumpStack(e);
grunt.log.writeln().fail('Aborted due to warnings.');
- process.exit(typeof errcode === 'number' ? errcode : fail.code.WARNING);
+ grunt.util.exit(typeof errcode === 'number' ? errcode : fail.code.WARNING);
}
};
@@ -79,6 +70,6 @@ fail.report = function() {
if (fail.warncount > 0) {
grunt.log.writeln().fail('Done, but with warnings.');
} else {
- grunt.log.writeln().success('Done, without errors.');
+ grunt.log.writeln().success('Done.');
}
};
diff --git a/lib/grunt/file.js b/lib/grunt/file.js
index a84d703..303e0ab 100644
--- a/lib/grunt/file.js
+++ b/lib/grunt/file.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
var grunt = require('../grunt');
@@ -25,6 +16,7 @@ file.findup = require('findup-sync');
var YAML = require('js-yaml');
var rimraf = require('rimraf');
var iconv = require('iconv-lite');
+var pathIsAbsolute = require('path-is-absolute');
// Windows?
var win32 = process.platform === 'win32';
@@ -50,7 +42,7 @@ var processPatterns = function(patterns, fn) {
// Filepaths to return.
var result = [];
// Iterate over flattened patterns array.
- grunt.util._.flatten(patterns).forEach(function(pattern) {
+ grunt.util._.flattenDeep(patterns).forEach(function(pattern) {
// If the first character is ! it should be omitted
var exclusion = pattern.indexOf('!') === 0;
// If the pattern is an exclusion, remove the !
@@ -122,7 +114,7 @@ file.expand = function() {
// If the file is of the right type and exists, this should work.
return fs.statSync(filepath)[options.filter]();
}
- } catch(e) {
+ } catch (e) {
// Otherwise, it's probably not the right type.
return false;
}
@@ -133,9 +125,17 @@ file.expand = function() {
var pathSeparatorRe = /[\/\\]/g;
+// The "ext" option refers to either everything after the first dot (default)
+// or everything after the last dot.
+var extDotRe = {
+ first: /(\.[^\/]*)?$/,
+ last: /(\.[^\/\.]*)?$/,
+};
+
// Build a multi task "files" object dynamically.
file.expandMapping = function(patterns, destBase, options) {
options = grunt.util._.defaults({}, options, {
+ extDot: 'first',
rename: function(destBase, destPath) {
return path.join(destBase || '', destPath);
}
@@ -150,8 +150,8 @@ file.expandMapping = function(patterns, destBase, options) {
destPath = path.basename(destPath);
}
// Change the extension?
- if (options.ext) {
- destPath = destPath.replace(/(\.[^\/]*)?$/, options.ext);
+ if ('ext' in options) {
+ destPath = destPath.replace(extDotRe[options.extDot], options.ext);
}
// Generate destination filename.
var dest = options.rename(destBase, destPath, options);
@@ -190,7 +190,7 @@ file.mkdir = function(dirpath, mode) {
if (!file.exists(subpath)) {
try {
fs.mkdirSync(subpath, mode);
- } catch(e) {
+ } catch (e) {
throw grunt.util.error('Unable to create directory "' + subpath + '" (Error code: ' + e.code + ').', e);
}
}
@@ -213,6 +213,8 @@ file.recurse = function recurse(rootdir, callback, subdir) {
// The default file encoding to use.
file.defaultEncoding = 'utf8';
+// Whether to preserve the BOM on file.read rather than strip it.
+file.preserveBOM = false;
// Read a file, return its contents.
file.read = function(filepath, options) {
@@ -224,15 +226,11 @@ file.read = function(filepath, options) {
// If encoding is not explicitly null, convert from encoded buffer to a
// string. If no encoding was specified, use the default.
if (options.encoding !== null) {
- contents = iconv.decode(contents, options.encoding || file.defaultEncoding);
- // Strip any BOM that might exist.
- if (contents.charCodeAt(0) === 0xFEFF) {
- contents = contents.substring(1);
- }
+ contents = iconv.decode(contents, options.encoding || file.defaultEncoding, {stripBOM: !file.preserveBOM});
}
grunt.verbose.ok();
return contents;
- } catch(e) {
+ } catch (e) {
grunt.verbose.error();
throw grunt.util.error('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e);
}
@@ -247,7 +245,7 @@ file.readJSON = function(filepath, options) {
result = JSON.parse(src);
grunt.verbose.ok();
return result;
- } catch(e) {
+ } catch (e) {
grunt.verbose.error();
throw grunt.util.error('Unable to parse "' + filepath + '" file (' + e.message + ').', e);
}
@@ -262,7 +260,7 @@ file.readYAML = function(filepath, options) {
result = YAML.load(src);
grunt.verbose.ok();
return result;
- } catch(e) {
+ } catch (e) {
grunt.verbose.error();
throw grunt.util.error('Unable to parse "' + filepath + '" file (' + e.problem + ').', e);
}
@@ -283,18 +281,36 @@ file.write = function(filepath, contents, options) {
}
// Actually write file.
if (!nowrite) {
- fs.writeFileSync(filepath, contents);
+ fs.writeFileSync(filepath, contents, 'mode' in options ? {mode: options.mode} : {});
}
grunt.verbose.ok();
return true;
- } catch(e) {
+ } catch (e) {
grunt.verbose.error();
throw grunt.util.error('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e);
}
};
// Read a file, optionally processing its content, then write the output.
-file.copy = function(srcpath, destpath, options) {
+// Or read a directory, recursively creating directories, reading files,
+// processing content, writing output.
+file.copy = function copy(srcpath, destpath, options) {
+ if (file.isDir(srcpath)) {
+ // Copy a directory, recursively.
+ // Explicitly create new dest directory.
+ file.mkdir(destpath);
+ // Iterate over all sub-files/dirs, recursing.
+ fs.readdirSync(srcpath).forEach(function(filepath) {
+ copy(path.join(srcpath, filepath), path.join(destpath, filepath), options);
+ });
+ } else {
+ // Copy a single file.
+ file._copy(srcpath, destpath, options);
+ }
+};
+
+// Read a file, optionally processing its content, then write the output.
+file._copy = function(srcpath, destpath, options) {
if (!options) { options = {}; }
// If a process function was specified, and noProcess isn't true or doesn't
// match the srcpath, process the file's source.
@@ -308,9 +324,9 @@ file.copy = function(srcpath, destpath, options) {
if (process) {
grunt.verbose.write('Processing source...');
try {
- contents = options.process(contents, srcpath);
+ contents = options.process(contents, srcpath, destpath);
grunt.verbose.ok();
- } catch(e) {
+ } catch (e) {
grunt.verbose.error();
throw grunt.util.error('Error while processing "' + srcpath + '" file.', e);
}
@@ -360,7 +376,7 @@ file.delete = function(filepath, options) {
}
grunt.verbose.ok();
return true;
- } catch(e) {
+ } catch (e) {
grunt.verbose.error();
throw grunt.util.error('Unable to delete "' + filepath + '" file (' + e.message + ').', e);
}
@@ -393,7 +409,7 @@ file.isFile = function() {
// Is a given file path absolute?
file.isPathAbsolute = function() {
var filepath = path.join.apply(path, arguments);
- return path.resolve(filepath) === filepath.replace(/[\/\\]+$/, '');
+ return pathIsAbsolute(filepath);
};
// Do all the specified paths refer to the same path?
@@ -421,8 +437,8 @@ file.doesPathContain = function(ancestor) {
file.isPathCwd = function() {
var filepath = path.join.apply(path, arguments);
try {
- return file.arePathsEquivalent(process.cwd(), fs.realpathSync(filepath));
- } catch(e) {
+ return file.arePathsEquivalent(fs.realpathSync(process.cwd()), fs.realpathSync(filepath));
+ } catch (e) {
return false;
}
};
@@ -431,8 +447,8 @@ file.isPathCwd = function() {
file.isPathInCwd = function() {
var filepath = path.join.apply(path, arguments);
try {
- return file.doesPathContain(process.cwd(), fs.realpathSync(filepath));
- } catch(e) {
+ return file.doesPathContain(fs.realpathSync(process.cwd()), fs.realpathSync(filepath));
+ } catch (e) {
return false;
}
};
diff --git a/lib/grunt/help.js b/lib/grunt/help.js
index 3e610fb..c01f727 100644
--- a/lib/grunt/help.js
+++ b/lib/grunt/help.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
var grunt = require('../grunt');
@@ -49,7 +40,6 @@ exports.display = function() {
exports.queue.forEach(function(name) { exports[name](); });
};
-
// Header.
exports.header = function() {
grunt.log.writeln('Grunt: The JavaScript Task Runner (v' + grunt.version + ')');
diff --git a/lib/grunt/log.js b/lib/grunt/log.js
deleted file mode 100644
index 41dd534..0000000
--- a/lib/grunt/log.js
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
-'use strict';
-
-var grunt = require('../grunt');
-
-// Nodejs libs.
-var util = require('util');
-
-// The module to be exported.
-var log = module.exports = {};
-
-// External lib. Requiring this here modifies the String prototype!
-var colors = require('colors');
-
-// Disable colors if --no-colors was passed.
-log.initColors = function() {
- var util = grunt.util;
- if (grunt.option('no-color')) {
- // String color getters should just return the string.
- colors.mode = 'none';
- // Strip colors from strings passed to console.log.
- util.hooker.hook(console, 'log', function() {
- var args = util.toArray(arguments);
- return util.hooker.filter(this, args.map(function(arg) {
- return util.kindOf(arg) === 'string' ? colors.stripColors(arg) : arg;
- }));
- });
- }
-};
-
-// Temporarily suppress output.
-var suppressOutput;
-
-// Allow external muting of output.
-log.muted = false;
-
-// True once anything has actually been logged.
-var hasLogged;
-
-// Parse certain markup in strings to be logged.
-function markup(str) {
- str = str || '';
- // Make _foo_ underline.
- str = str.replace(/(\s|^)_(\S|\S[\s\S]+?\S)_(?=[\s,.!?]|$)/g, '$1' + '$2'.underline);
- // Make *foo* bold.
- str = str.replace(/(\s|^)\*(\S|\S[\s\S]+?\S)\*(?=[\s,.!?]|$)/g, '$1' + '$2'.bold);
- return str;
-}
-
-// Similar to util.format in the standard library, however it'll always
-// cast the first argument to a string and treat it as the format string.
-function format(args) {
- // Args is a argument array so copy it in order to avoid wonky behavior.
- args = [].slice.call(args, 0);
- if (args.length > 0) {
- args[0] = String(args[0]);
- }
- return util.format.apply(util, args);
-}
-
-function write(msg) {
- msg = msg || '';
- // Actually write output.
- if (!log.muted && !suppressOutput) {
- hasLogged = true;
- // Users should probably use the colors-provided methods, but if they
- // don't, this should strip extraneous color codes.
- if (grunt.option('no-color')) { msg = colors.stripColors(msg); }
- // Actually write to stdout.
- process.stdout.write(markup(msg));
- }
-}
-
-function writeln(msg) {
- // Write blank line if no msg is passed in.
- msg = msg || '';
- write(msg + '\n');
-}
-
-// Write output.
-log.write = function() {
- write(format(arguments));
- return log;
-};
-
-// Write a line of output.
-log.writeln = function() {
- writeln(format(arguments));
- return log;
-};
-
-log.warn = function() {
- var msg = format(arguments);
- if (arguments.length > 0) {
- writeln('>> '.red + grunt.util._.trim(msg).replace(/\n/g, '\n>> '.red));
- } else {
- writeln('ERROR'.red);
- }
- return log;
-};
-log.error = function() {
- grunt.fail.errorcount++;
- log.warn.apply(log, arguments);
- return log;
-};
-log.ok = function() {
- var msg = format(arguments);
- if (arguments.length > 0) {
- writeln('>> '.green + grunt.util._.trim(msg).replace(/\n/g, '\n>> '.green));
- } else {
- writeln('OK'.green);
- }
- return log;
-};
-log.errorlns = function() {
- var msg = format(arguments);
- log.error(log.wraptext(77, msg));
- return log;
-};
-log.oklns = function() {
- var msg = format(arguments);
- log.ok(log.wraptext(77, msg));
- return log;
-};
-log.success = function() {
- var msg = format(arguments);
- writeln(msg.green);
- return log;
-};
-log.fail = function() {
- var msg = format(arguments);
- writeln(msg.red);
- return log;
-};
-log.header = function() {
- var msg = format(arguments);
- // Skip line before header, but not if header is the very first line output.
- if (hasLogged) { writeln(); }
- writeln(msg.underline);
- return log;
-};
-log.subhead = function() {
- var msg = format(arguments);
- // Skip line before subhead, but not if subhead is the very first line output.
- if (hasLogged) { writeln(); }
- writeln(msg.bold);
- return log;
-};
-// For debugging.
-log.debug = function() {
- var msg = format(arguments);
- if (grunt.option('debug')) {
- writeln('[D] ' + msg.magenta);
- }
- return log;
-};
-
-// Write a line of a table.
-log.writetableln = function(widths, texts) {
- writeln(log.table(widths, texts));
- return log;
-};
-
-// Wrap a long line of text to 80 columns.
-log.writelns = function() {
- var msg = format(arguments);
- writeln(log.wraptext(80, msg));
- return log;
-};
-
-// Display flags in verbose mode.
-log.writeflags = function(obj, prefix) {
- var wordlist;
- if (Array.isArray(obj)) {
- wordlist = log.wordlist(obj);
- } else if (typeof obj === 'object' && obj) {
- wordlist = log.wordlist(Object.keys(obj).map(function(key) {
- var val = obj[key];
- return key + (val === true ? '' : '=' + JSON.stringify(val));
- }));
- }
- writeln((prefix || 'Flags') + ': ' + (wordlist || '(none)'.cyan));
- return log;
-};
-
-// Create explicit "verbose" and "notverbose" functions, one for each already-
-// defined log function, that do the same thing but ONLY if -v or --verbose is
-// specified (or not specified).
-log.verbose = {};
-log.notverbose = {};
-
-// Iterate over all exported functions.
-Object.keys(log).filter(function(key) {
- return typeof log[key] === 'function';
-}).forEach(function(key) {
- // Like any other log function, but suppresses output if the "verbose" option
- // IS NOT set.
- log.verbose[key] = function() {
- suppressOutput = !grunt.option('verbose');
- log[key].apply(log, arguments);
- suppressOutput = false;
- return log.verbose;
- };
- // Like any other log function, but suppresses output if the "verbose" option
- // IS set.
- log.notverbose[key] = function() {
- suppressOutput = grunt.option('verbose');
- log[key].apply(log, arguments);
- suppressOutput = false;
- return log.notverbose;
- };
-});
-
-// A way to switch between verbose and notverbose modes. For example, this will
-// write 'foo' if verbose logging is enabled, otherwise write 'bar':
-// verbose.write('foo').or.write('bar');
-log.verbose.or = log.notverbose;
-log.notverbose.or = log.verbose;
-
-// Static methods.
-
-// Pretty-format a word list.
-log.wordlist = function(arr, options) {
- options = grunt.util._.defaults(options || {}, {
- separator: ', ',
- color: 'cyan'
- });
- return arr.map(function(item) {
- return options.color ? String(item)[options.color] : item;
- }).join(options.separator);
-};
-
-// Return a string, uncolored (suitable for testing .length, etc).
-log.uncolor = function(str) {
- return str.replace(/\x1B\[\d+m/g, '');
-};
-
-// Word-wrap text to a given width, permitting ANSI color codes.
-log.wraptext = function(width, text) {
- // notes to self:
- // grab 1st character or ansi code from string
- // if ansi code, add to array and save for later, strip from front of string
- // if character, add to array and increment counter, strip from front of string
- // if width + 1 is reached and current character isn't space:
- // slice off everything after last space in array and prepend it to string
- // etc
-
- // This result array will be joined on \n.
- var result = [];
- var matches, color, tmp;
- var captured = [];
- var charlen = 0;
-
- while (matches = text.match(/(?:(\x1B\[\d+m)|\n|(.))([\s\S]*)/)) {
- // Updated text to be everything not matched.
- text = matches[3];
-
- // Matched a color code?
- if (matches[1]) {
- // Save last captured color code for later use.
- color = matches[1];
- // Capture color code.
- captured.push(matches[1]);
- continue;
-
- // Matched a non-newline character?
- } else if (matches[2]) {
- // If this is the first character and a previous color code was set, push
- // that onto the captured array first.
- if (charlen === 0 && color) { captured.push(color); }
- // Push the matched character.
- captured.push(matches[2]);
- // Increment the current charlen.
- charlen++;
- // If not yet at the width limit or a space was matched, continue.
- if (charlen <= width || matches[2] === ' ') { continue; }
- // The current charlen exceeds the width and a space wasn't matched.
- // "Roll everything back" until the last space character.
- tmp = captured.lastIndexOf(' ');
- text = captured.slice(tmp === -1 ? tmp : tmp + 1).join('') + text;
- captured = captured.slice(0, tmp);
- }
-
- // The limit has been reached. Push captured string onto result array.
- result.push(captured.join(''));
-
- // Reset captured array and charlen.
- captured = [];
- charlen = 0;
- }
-
- result.push(captured.join(''));
- return result.join('\n');
-};
-
-// todo: write unit tests
-//
-// function logs(text) {
-// [4, 6, 10, 15, 20, 25, 30, 40].forEach(function(n) {
-// log(n, text);
-// });
-// }
-//
-// function log(n, text) {
-// console.log(Array(n + 1).join('-'));
-// console.log(wrap(n, text));
-// }
-//
-// var text = 'this is '.red + 'a simple'.yellow.inverse + ' test of'.green + ' ' + 'some wrapped'.blue + ' text over '.inverse.magenta + 'many lines'.red;
-// logs(text);
-//
-// var text = 'foolish '.red.inverse + 'monkeys'.yellow + ' eating'.green + ' ' + 'delicious'.inverse.blue + ' bananas '.magenta + 'forever'.red;
-// logs(text);
-//
-// var text = 'foolish monkeys eating delicious bananas forever'.rainbow;
-// logs(text);
-
-// Format output into columns, wrapping words as-necessary.
-log.table = function(widths, texts) {
- var rows = [];
- widths.forEach(function(width, i) {
- var lines = log.wraptext(width, texts[i]).split('\n');
- lines.forEach(function(line, j) {
- var row = rows[j];
- if (!row) { row = rows[j] = []; }
- row[i] = line;
- });
- });
-
- var lines = [];
- rows.forEach(function(row) {
- var txt = '';
- var column;
- for (var i = 0; i < row.length; i++) {
- column = row[i] || '';
- txt += column;
- var diff = widths[i] - log.uncolor(column).length;
- if (diff > 0) { txt += grunt.util.repeat(diff, ' '); }
- }
- lines.push(txt);
- });
-
- return lines.join('\n');
-};
diff --git a/lib/grunt/option.js b/lib/grunt/option.js
index 84b6763..88f8766 100644
--- a/lib/grunt/option.js
+++ b/lib/grunt/option.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
// The actual option data.
diff --git a/lib/grunt/task.js b/lib/grunt/task.js
index dcfe9e3..f31ff2e 100644
--- a/lib/grunt/task.js
+++ b/lib/grunt/task.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
var grunt = require('../grunt');
@@ -45,6 +36,8 @@ task.registerTask = function(name) {
// Override task function.
var _fn = thisTask.fn;
thisTask.fn = function(arg) {
+ // Guaranteed to always be the actual task name.
+ var name = thisTask.name;
// Initialize the errorcount for this task.
errorcount = grunt.fail.errorcount;
// Return the number of errors logged during this task.
@@ -58,13 +51,15 @@ task.registerTask = function(name) {
this.requires = task.requires.bind(task);
// Expose config.requires on `this`.
this.requiresConfig = grunt.config.requires;
- // Return an options object with the specified defaults overriden by task-
+ // Return an options object with the specified defaults overwritten by task-
// specific overrides, via the "options" property.
this.options = function() {
var args = [{}].concat(grunt.util.toArray(arguments)).concat([
grunt.config([name, 'options'])
]);
- return grunt.util._.extend.apply(null, args);
+ var options = grunt.util._.extend.apply(null, args);
+ grunt.verbose.writeflags(options, 'Options');
+ return options;
};
// If this task was an alias or a multi task called without a target,
// only log if in verbose mode.
@@ -103,7 +98,7 @@ task.normalizeMultiTaskFiles = function(data, target) {
files.push({src: data.files[prop], dest: grunt.config.process(prop)});
}
} else if (Array.isArray(data.files)) {
- data.files.forEach(function(obj) {
+ grunt.util._.flattenDeep(data.files).forEach(function(obj) {
var prop;
if ('src' in obj || 'dest' in obj) {
files.push(obj);
@@ -228,7 +223,7 @@ task.registerMultiTask = function(name, info, fn) {
}
// Fail if any required config properties have been omitted.
this.requiresConfig([name, target]);
- // Return an options object with the specified defaults overriden by task-
+ // Return an options object with the specified defaults overwritten by task-
// and/or target-specific overrides, via the "options" property.
this.options = function() {
var targetObj = grunt.config([name, target]);
@@ -236,8 +231,15 @@ task.registerMultiTask = function(name, info, fn) {
grunt.config([name, 'options']),
grunt.util.kindOf(targetObj) === 'object' ? targetObj.options : {}
]);
- return grunt.util._.extend.apply(null, args);
+ var options = grunt.util._.extend.apply(null, args);
+ grunt.verbose.writeflags(options, 'Options');
+ return options;
};
+ // Expose the current target.
+ this.target = target;
+ // Recreate flags object so that the target isn't set as a flag.
+ this.flags = {};
+ this.args.forEach(function(arg) { this.flags[arg] = true; }, this);
// Expose data on `this` (as well as task.current).
this.data = grunt.config([name, target]);
// Expose normalized files object.
@@ -246,14 +248,9 @@ task.registerMultiTask = function(name, info, fn) {
Object.defineProperty(this, 'filesSrc', {
enumerable: true,
get: function() {
- return grunt.util._(this.files).chain().pluck('src').flatten().uniq().value();
+ return grunt.util._(this.files).chain().map('src').flatten().uniq().value();
}.bind(this)
});
- // Expose the current target.
- this.target = target;
- // Recreate flags object so that the target isn't set as a flag.
- this.flags = {};
- this.args.forEach(function(arg) { this.flags[arg] = true; }, this);
// Call original task function, passing in the target and any other args.
return fn.apply(this, this.args);
});
@@ -271,24 +268,33 @@ task.registerInitTask = function(name, info, fn) {
// Override built-in renameTask to use the registry.
task.renameTask = function(oldname, newname) {
- // Add and remove task.
- registry.untasks.push(oldname);
- registry.tasks.push(newname);
- // Actually rename task.
- return parent.renameTask.apply(task, arguments);
+ var result;
+ try {
+ // Actually rename task.
+ result = parent.renameTask.apply(task, arguments);
+ // Add and remove task.
+ registry.untasks.push(oldname);
+ registry.tasks.push(newname);
+ // Return result.
+ return result;
+ } catch (e) {
+ grunt.log.error(e.message);
+ }
};
// If a property wasn't passed, run all task targets in turn.
task.runAllTargets = function(taskname, args) {
// Get an array of sub-property keys under the given config object.
var targets = Object.keys(grunt.config.getRaw(taskname) || {});
+ // Remove invalid target properties.
+ targets = targets.filter(isValidMultiTaskTarget);
// Fail if there are no actual properties to iterate over.
if (targets.length === 0) {
grunt.log.error('No "' + taskname + '" targets found.');
return false;
}
- // Iterate over all valid target properties, running a task for each.
- targets.filter(isValidMultiTaskTarget).forEach(function(target) {
+ // Iterate over all targets, running a task for each.
+ targets.forEach(function(target) {
// Be sure to pass in any additionally specified args.
task.run([taskname, target].concat(args || []).join(':'));
});
@@ -321,9 +327,9 @@ function loadTask(filepath) {
}
});
if (regCount === 0) {
- grunt.verbose.error('No tasks were registered or unregistered.');
+ grunt.verbose.warn('No tasks were registered or unregistered.');
}
- } catch(e) {
+ } catch (e) {
// Something went wrong.
grunt.log.write(msg).error().verbose.error(e.stack).or.error(e);
}
@@ -347,7 +353,7 @@ function loadTasks(tasksdir) {
files.forEach(function(filename) {
loadTask(path.join(tasksdir, filename));
});
- } catch(e) {
+ } catch (e) {
grunt.log.verbose.error(e.stack).or.error(e);
}
}
@@ -410,11 +416,18 @@ task.init = function(tasks, options) {
// Get any local Gruntfile or tasks that might exist. Use --gruntfile override
// if specified, otherwise search the current directory or any parent.
- var gruntfile = allInit ? null : grunt.option('gruntfile') ||
- grunt.file.findup('Gruntfile.{js,coffee}', {nocase: true});
+ var gruntfile, msg;
+ if (allInit || options.gruntfile === false) {
+ gruntfile = null;
+ } else {
+ gruntfile = grunt.option('gruntfile') ||
+ grunt.file.findup('Gruntfile.{js,coffee}', {nocase: true});
+ msg = 'Reading "' + (gruntfile ? path.basename(gruntfile) : '???') + '" Gruntfile...';
+ }
- var msg = 'Reading "' + (gruntfile ? path.basename(gruntfile) : '???') + '" Gruntfile...';
- if (gruntfile && grunt.file.exists(gruntfile)) {
+ if (options.gruntfile === false) {
+ // Grunt was run as a lib with {gruntfile: false}.
+ } else if (gruntfile && grunt.file.exists(gruntfile)) {
grunt.verbose.writeln().write(msg).ok();
// Change working directory so that all paths are relative to the
// Gruntfile's location (or the --base option, if specified).
@@ -439,7 +452,7 @@ task.init = function(tasks, options) {
}
// Load all user-specified --npm tasks.
- (grunt.option('npm') || []).forEach(task.loadNpmTasks);
+ (grunt.option('npm') || []).map(String).forEach(task.loadNpmTasks);
// Load all user-specified --tasks.
- (grunt.option('tasks') || []).forEach(task.loadTasks);
+ (grunt.option('tasks') || []).map(String).forEach(task.loadTasks);
};
diff --git a/lib/grunt/template.js b/lib/grunt/template.js
index 0db3bdb..d1967df 100644
--- a/lib/grunt/template.js
+++ b/lib/grunt/template.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
'use strict';
var grunt = require('../grunt');
@@ -51,7 +42,7 @@ template.setDelimiters = function(name) {
// Get the appropriate delimiters.
var delimiters = allDelimiters[name in allDelimiters ? name : 'config'];
// Tell Lo-Dash which delimiters to use.
- grunt.util._.templateSettings = delimiters.lodash;
+ grunt.util._.extend(grunt.util._.templateSettings, delimiters.lodash);
// Return the delimiters.
return delimiters;
};
@@ -72,7 +63,7 @@ template.process = function(tmpl, options) {
// As long as tmpl contains template tags, render it and get the result,
// otherwise just use the template string.
while (tmpl.indexOf(delimiters.opener) >= 0) {
- tmpl = grunt.util._.template(tmpl, data);
+ tmpl = grunt.util._.template(tmpl, options)(data);
// Abort if template didn't change - nothing left to process!
if (tmpl === last) { break; }
last = tmpl;
diff --git a/lib/grunt/util.js b/lib/grunt/util.js
deleted file mode 100644
index c14461c..0000000
--- a/lib/grunt/util.js
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
-'use strict';
-
-// Nodejs libs.
-var spawn = require('child_process').spawn;
-var nodeUtil = require('util');
-var path = require('path');
-
-// The module to be exported.
-var util = module.exports = {};
-
-// A few internal utilites, exposed.
-util.task = require('../util/task');
-util.namespace = require('../util/namespace');
-// Use instead of process.exit to ensure stdout/stderr are flushed
-// before exiting in Windows (Tested in Node.js v0.8.7)
-util.exit = require('../util/exit').exit;
-
-// External libs.
-util.hooker = require('hooker');
-util.async = require('async');
-var _ = util._ = require('lodash');
-var which = require('which').sync;
-
-// Mixin Underscore.string methods.
-_.str = require('underscore.string');
-_.mixin(_.str.exports());
-
-// Return a function that normalizes the given function either returning a
-// value or accepting a "done" callback that accepts a single value.
-util.callbackify = function(fn) {
- return function callbackable() {
- // Invoke original function, getting its result.
- var result = fn.apply(this, arguments);
- // If the same number or less arguments were specified than fn accepts,
- // assume the "done" callback was already handled.
- var length = arguments.length;
- if (length === fn.length) { return; }
- // Otherwise, if the last argument is a function, assume it is a "done"
- // callback and call it.
- var done = arguments[length - 1];
- if (typeof done === 'function') { done(result); }
- };
-};
-
-// Create a new Error object, with an origError property that will be dumped
-// if grunt was run with the --debug=9 option.
-util.error = function(err, origError) {
- if (!nodeUtil.isError(err)) { err = new Error(err); }
- if (origError) { err.origError = origError; }
- return err;
-};
-
-// The line feed char for the current system.
-util.linefeed = process.platform === 'win32' ? '\r\n' : '\n';
-
-// Normalize linefeeds in a string.
-util.normalizelf = function(str) {
- return str.replace(/\r\n|\n/g, util.linefeed);
-};
-
-// What "kind" is a value?
-// I really need to rework https://github.com/cowboy/javascript-getclass
-var kindsOf = {};
-'Number String Boolean Function RegExp Array Date Error'.split(' ').forEach(function(k) {
- kindsOf['[object ' + k + ']'] = k.toLowerCase();
-});
-util.kindOf = function(value) {
- // Null or undefined.
- if (value == null) { return String(value); }
- // Everything else.
- return kindsOf[kindsOf.toString.call(value)] || 'object';
-};
-
-// Coerce something to an Array.
-util.toArray = Function.call.bind(Array.prototype.slice);
-
-// Return the string `str` repeated `n` times.
-util.repeat = function(n, str) {
- return new Array(n + 1).join(str || ' ');
-};
-
-// Given str of "a/b", If n is 1, return "a" otherwise "b".
-util.pluralize = function(n, str, separator) {
- var parts = str.split(separator || '/');
- return n === 1 ? (parts[0] || '') : (parts[1] || '');
-};
-
-// Recurse through objects and arrays, executing fn for each non-object.
-util.recurse = function recurse(value, fn, fnContinue) {
- var obj;
- if (fnContinue && fnContinue(value) === false) {
- // Skip value if necessary.
- return value;
- } else if (util.kindOf(value) === 'array') {
- // If value is an array, recurse.
- return value.map(function(value) {
- return recurse(value, fn, fnContinue);
- });
- } else if (util.kindOf(value) === 'object') {
- // If value is an object, recurse.
- obj = {};
- Object.keys(value).forEach(function(key) {
- obj[key] = recurse(value[key], fn, fnContinue);
- });
- return obj;
- } else {
- // Otherwise pass value into fn and return.
- return fn(value);
- }
-};
-
-// Spawn a child process, capturing its stdout and stderr.
-util.spawn = function(opts, done) {
- // Build a result object and pass it (among other things) into the
- // done function.
- var callDone = function(code, stdout, stderr) {
- // Remove trailing whitespace (newline)
- stdout = _.rtrim(stdout);
- stderr = _.rtrim(stderr);
- // Create the result object.
- var result = {
- stdout: stdout,
- stderr: stderr,
- code: code,
- toString: function() {
- if (code === 0) {
- return stdout;
- } else if ('fallback' in opts) {
- return opts.fallback;
- } else if (opts.grunt) {
- // grunt.log.error uses standard out, to be fixed in 0.5.
- return stderr || stdout;
- }
- return stderr;
- }
- };
- // On error (and no fallback) pass an error object, otherwise pass null.
- done(code === 0 || 'fallback' in opts ? null : new Error(stderr), result, code);
- };
-
- var cmd, args;
- var pathSeparatorRe = /[\\\/]/g;
- if (opts.grunt) {
- cmd = process.argv[0];
- args = [process.argv[1]].concat(opts.args);
- } else {
- // On Windows, child_process.spawn will only file .exe files in the PATH,
- // not other executable types (grunt issue #155).
- try {
- if (!pathSeparatorRe.test(opts.cmd)) {
- // Only use which if cmd has no path component.
- cmd = which(opts.cmd);
- } else {
- cmd = opts.cmd.replace(pathSeparatorRe, path.sep);
- }
- } catch (err) {
- callDone(127, '', String(err));
- return;
- }
- args = opts.args;
- }
-
- var child = spawn(cmd, args, opts.opts);
- var stdout = new Buffer('');
- var stderr = new Buffer('');
- if (child.stdout) {
- child.stdout.on('data', function(buf) {
- stdout = Buffer.concat([stdout, new Buffer(buf)]);
- });
- }
- if (child.stderr) {
- child.stderr.on('data', function(buf) {
- stderr = Buffer.concat([stderr, new Buffer(buf)]);
- });
- }
- child.on('close', function(code) {
- callDone(code, stdout.toString(), stderr.toString());
- });
- return child;
-};
diff --git a/lib/util/exit.js b/lib/util/exit.js
deleted file mode 100644
index 867849c..0000000
--- a/lib/util/exit.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
-'use strict';
-
-// This seems to be required in Windows (as of Node.js 0.8.7) to ensure that
-// stdout/stderr are flushed before the process exits.
-
-// https://gist.github.com/3427148
-// https://gist.github.com/3427357
-
-exports.exit = function exit(exitCode) {
- if (process.stdout._pendingWriteReqs || process.stderr._pendingWriteReqs) {
- process.nextTick(function() {
- exit(exitCode);
- });
- } else {
- process.exit(exitCode);
- }
-};
diff --git a/lib/util/namespace.js b/lib/util/namespace.js
deleted file mode 100644
index 48bbae1..0000000
--- a/lib/util/namespace.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
-(function(exports) {
-
- 'use strict';
-
- // Split strings on dot, but only if dot isn't preceded by a backslash. Since
- // JavaScript doesn't support lookbehinds, use a placeholder for "\.", split
- // on dot, then replace the placeholder character with a dot.
- function getParts(str) {
- return str.replace(/\\\./g, '\uffff').split('.').map(function(s) {
- return s.replace(/\uffff/g, '.');
- });
- }
-
- // Get the value of a deeply-nested property exist in an object.
- exports.get = function(obj, parts, create) {
- if (typeof parts === 'string') {
- parts = getParts(parts);
- }
-
- var part;
- while (typeof obj === 'object' && obj && parts.length) {
- part = parts.shift();
- if (!(part in obj) && create) {
- obj[part] = {};
- }
- obj = obj[part];
- }
-
- return obj;
- };
-
- // Set a deeply-nested property in an object, creating intermediary objects
- // as we go.
- exports.set = function(obj, parts, value) {
- parts = getParts(parts);
-
- var prop = parts.pop();
- obj = exports.get(obj, parts, true);
- if (obj && typeof obj === 'object') {
- return (obj[prop] = value);
- }
- };
-
- // Does a deeply-nested property exist in an object?
- exports.exists = function(obj, parts) {
- parts = getParts(parts);
-
- var prop = parts.pop();
- obj = exports.get(obj, parts);
-
- return typeof obj === 'object' && obj && prop in obj;
- };
-
-}(typeof exports === 'object' && exports || this));
diff --git a/lib/util/task.js b/lib/util/task.js
index bb6feb0..d39ce1b 100644
--- a/lib/util/task.js
+++ b/lib/util/task.js
@@ -1,16 +1,9 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
(function(exports) {
'use strict';
+ var grunt = require('../grunt');
+
// Construct-o-rama.
function Task() {
// Information about the currently-running task.
@@ -21,7 +14,7 @@
this._queue = [];
// Queue placeholder (for dealing with nested tasks).
this._placeholder = {placeholder: true};
- // Queue marker (for clearing the queue programatically).
+ // Queue marker (for clearing the queue programmatically).
this._marker = {marker: true};
// Options.
this._options = {};
@@ -85,10 +78,18 @@
return !!this._tasks[name].fn.alias;
};
+ // Has the specified task been registered?
+ Task.prototype.exists = function(name) {
+ return name in this._tasks;
+ };
+
// Rename a task. This might be useful if you want to override the default
// behavior of a task, while retaining the old name. This is a billion times
// easier to implement than some kind of in-task "super" functionality.
Task.prototype.renameTask = function(oldname, newname) {
+ if (!this._tasks[oldname]) {
+ throw new Error('Cannot rename missing "' + oldname + '" task.');
+ }
// Rename task.
this._tasks[newname] = this._tasks[oldname];
// Update name property of task.
@@ -177,7 +178,7 @@
return this;
};
- // Add a marker to the queue to facilitate clearing it programatically.
+ // Add a marker to the queue to facilitate clearing it programmatically.
Task.prototype.mark = function() {
this._push(this._marker);
// Make chainable!
@@ -185,7 +186,7 @@
};
// Run a task function, handling this.async / return value.
- Task.prototype.runTaskFn = function(context, fn, done) {
+ Task.prototype.runTaskFn = function(context, fn, done, asyncDone) {
// Async flag.
var async = false;
@@ -212,7 +213,15 @@
if (!success && this._options.error) {
this._options.error.call({name: context.name, nameArgs: context.nameArgs}, err);
}
- done(err, success);
+ // only call done async if explicitly requested to
+ // see: https://github.com/gruntjs/grunt/pull/1026
+ if (asyncDone) {
+ process.nextTick(function() {
+ done(err, success);
+ });
+ } else {
+ done(err, success);
+ }
}.bind(this);
// When called, sets the async flag and returns a function that can
@@ -221,9 +230,9 @@
async = true;
// The returned function should execute asynchronously in case
// someone tries to do this.async()(); inside a task (WTF).
- return function(success) {
+ return grunt.util._.once(function(success) {
setTimeout(function() { complete(success); }, 1);
- };
+ });
};
// Expose some information about the currently-running task.
@@ -243,7 +252,10 @@
};
// Begin task queue processing. Ie. run all tasks.
- Task.prototype.start = function() {
+ Task.prototype.start = function(opts) {
+ if (!opts) {
+ opts = {};
+ }
// Abort if already running.
if (this._running) { return false; }
// Actually process the next task.
@@ -280,7 +292,7 @@
// Actually run the task function (handling this.async, etc)
this.runTaskFn(context, function() {
return thing.task.fn.apply(this, this.args);
- }, nextTask);
+ }, nextTask, !!opts.asyncDone);
}.bind(this);
diff --git a/package.json b/package.json
index 92f948a..85ce579 100644
--- a/package.json
+++ b/package.json
@@ -1,28 +1,21 @@
{
"name": "grunt",
"description": "The JavaScript Task Runner",
- "version": "0.4.1",
- "author": "\"Cowboy\" Ben Alman (http://benalman.com/)",
+ "version": "1.0.1",
+ "author": "Grunt Development Team (http://gruntjs.com/development-team)",
"homepage": "http://gruntjs.com/",
- "repository": {
- "type": "git",
- "url": "git://github.com/gruntjs/grunt.git"
- },
- "bugs": {
- "url": "http://github.com/gruntjs/grunt/issues"
+ "repository": "gruntjs/grunt",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
},
- "licenses": [
- {
- "type": "MIT",
- "url": "http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT"
- }
- ],
- "main": "lib/grunt",
"scripts": {
- "test": "grunt test"
+ "test": "grunt test",
+ "test-tap": "grunt test:tap"
},
- "engines": {
- "node": ">= 0.8.0"
+ "main": "lib/grunt",
+ "bin": {
+ "grunt": "bin/grunt"
},
"keywords": [
"task",
@@ -44,28 +37,36 @@
"tool"
],
"dependencies": {
- "async": "~0.1.22",
- "coffee-script": "~1.3.3",
- "colors": "~0.6.0-1",
- "dateformat": "1.0.2-1.2.3",
- "eventemitter2": "~0.4.9",
- "findup-sync": "~0.1.0",
- "glob": "~3.1.21",
- "hooker": "~0.2.3",
- "iconv-lite": "~0.2.5",
- "minimatch": "~0.2.6",
- "nopt": "~1.0.10",
- "rimraf": "~2.0.2",
- "lodash": "~0.9.0",
- "underscore.string": "~2.2.0rc",
- "which": "~1.0.5",
- "js-yaml": "~2.0.2"
+ "coffee-script": "~1.10.0",
+ "dateformat": "~1.0.12",
+ "eventemitter2": "~0.4.13",
+ "exit": "~0.1.1",
+ "findup-sync": "~0.3.0",
+ "glob": "~7.0.0",
+ "grunt-cli": "~1.2.0",
+ "grunt-known-options": "~1.1.0",
+ "grunt-legacy-log": "~1.0.0",
+ "grunt-legacy-util": "~1.0.0",
+ "iconv-lite": "~0.4.13",
+ "js-yaml": "~3.5.2",
+ "minimatch": "~3.0.0",
+ "nopt": "~3.0.6",
+ "path-is-absolute": "~1.0.0",
+ "rimraf": "~2.2.8"
},
"devDependencies": {
+ "difflet": "~0.2.3",
+ "grunt-contrib-jshint": "~1.0.0",
+ "grunt-contrib-nodeunit": "~0.4.1",
+ "grunt-contrib-watch": "~1.0.0",
+ "grunt-jscs": "~2.8.0",
+ "semver": "2.1.0",
+ "shelljs": "~0.5.3",
"temporary": "~0.0.4",
- "grunt-contrib-jshint": "~0.1.1",
- "grunt-contrib-nodeunit": "~0.1.2",
- "grunt-contrib-watch": "~0.2.0",
- "difflet": "~0.2.3"
- }
+ "through2": "~2.0.0"
+ },
+ "files": [
+ "lib",
+ "bin"
+ ]
}
diff --git a/test/fixtures/Gruntfile-cli.js b/test/fixtures/Gruntfile-cli.js
new file mode 100644
index 0000000..44a2ea0
--- /dev/null
+++ b/test/fixtures/Gruntfile-cli.js
@@ -0,0 +1,16 @@
+module.exports = function(grunt) {
+
+ var obj = {};
+ grunt.registerTask('finalize', 'Print all option values.', function() {
+ console.log('###' + JSON.stringify(obj) + '###');
+ });
+
+ // Create a per-CLI-option task that stores the value of that option
+ // to be output via the "finalize" task.
+ Object.keys(grunt.cli.optlist).forEach(function(name) {
+ grunt.registerTask(name, 'Store the current "' + name + '" option value.', function() {
+ obj[this.name] = grunt.option(this.name);
+ });
+ });
+
+};
diff --git a/test/fixtures/Gruntfile-print-text.js b/test/fixtures/Gruntfile-print-text.js
deleted file mode 100644
index 910197d..0000000
--- a/test/fixtures/Gruntfile-print-text.js
+++ /dev/null
@@ -1,8 +0,0 @@
-module.exports = function(grunt) {
-
- grunt.registerTask('print', 'Print the specified text.', function(text) {
- console.log('OUTPUT: ' + text);
- // console.log(process.cwd());
- });
-
-};
diff --git a/test/fixtures/exec.cmd b/test/fixtures/exec.cmd
deleted file mode 100755
index 6e4a52b..0000000
--- a/test/fixtures/exec.cmd
+++ /dev/null
@@ -1 +0,0 @@
- at echo done
diff --git a/test/fixtures/exec.sh b/test/fixtures/exec.sh
deleted file mode 100755
index 8890799..0000000
--- a/test/fixtures/exec.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-echo "done"
diff --git a/test/fixtures/expand/css/baz.css b/test/fixtures/expand/css/baz.css
index e69de29..3f95386 100644
--- a/test/fixtures/expand/css/baz.css
+++ b/test/fixtures/expand/css/baz.css
@@ -0,0 +1 @@
+baz
\ No newline at end of file
diff --git a/test/fixtures/expand/css/qux.css b/test/fixtures/expand/css/qux.css
index e69de29..78df5b0 100644
--- a/test/fixtures/expand/css/qux.css
+++ b/test/fixtures/expand/css/qux.css
@@ -0,0 +1 @@
+qux
\ No newline at end of file
diff --git a/test/fixtures/expand/deep/deep.txt b/test/fixtures/expand/deep/deep.txt
index e69de29..d1f857b 100644
--- a/test/fixtures/expand/deep/deep.txt
+++ b/test/fixtures/expand/deep/deep.txt
@@ -0,0 +1 @@
+deep
\ No newline at end of file
diff --git a/test/fixtures/expand/deep/deeper/deeper.txt b/test/fixtures/expand/deep/deeper/deeper.txt
index e69de29..2e4e0c1 100644
--- a/test/fixtures/expand/deep/deeper/deeper.txt
+++ b/test/fixtures/expand/deep/deeper/deeper.txt
@@ -0,0 +1 @@
+deeper
\ No newline at end of file
diff --git a/test/fixtures/expand/deep/deeper/deepest/deepest.txt b/test/fixtures/expand/deep/deeper/deepest/deepest.txt
index e69de29..d087677 100644
--- a/test/fixtures/expand/deep/deeper/deepest/deepest.txt
+++ b/test/fixtures/expand/deep/deeper/deepest/deepest.txt
@@ -0,0 +1 @@
+deepest
\ No newline at end of file
diff --git a/test/fixtures/expand/js/bar.js b/test/fixtures/expand/js/bar.js
index e69de29..ba0e162 100644
--- a/test/fixtures/expand/js/bar.js
+++ b/test/fixtures/expand/js/bar.js
@@ -0,0 +1 @@
+bar
\ No newline at end of file
diff --git a/test/fixtures/expand/js/foo.js b/test/fixtures/expand/js/foo.js
index e69de29..1910281 100644
--- a/test/fixtures/expand/js/foo.js
+++ b/test/fixtures/expand/js/foo.js
@@ -0,0 +1 @@
+foo
\ No newline at end of file
diff --git a/test/fixtures/load-npm-tasks/node_modules/grunt-foo-plugin/package.json b/test/fixtures/load-npm-tasks/node_modules/grunt-foo-plugin/package.json
new file mode 100644
index 0000000..decedf6
--- /dev/null
+++ b/test/fixtures/load-npm-tasks/node_modules/grunt-foo-plugin/package.json
@@ -0,0 +1,6 @@
+{
+ "private": true,
+ "name": "grunt-foo-plugin",
+ "description": "",
+ "version": "1.0.0"
+}
diff --git a/test/fixtures/load-npm-tasks/node_modules/grunt-foo-plugin/tasks/foo.js b/test/fixtures/load-npm-tasks/node_modules/grunt-foo-plugin/tasks/foo.js
new file mode 100644
index 0000000..8dd9048
--- /dev/null
+++ b/test/fixtures/load-npm-tasks/node_modules/grunt-foo-plugin/tasks/foo.js
@@ -0,0 +1,7 @@
+'use strict';
+
+module.exports = function(grunt) {
+ grunt.registerTask('foo', function() {
+ grunt.log.writeln(this.name + ' has ran.');
+ });
+};
diff --git a/test/fixtures/load-npm-tasks/package.json b/test/fixtures/load-npm-tasks/package.json
new file mode 100644
index 0000000..de5fb74
--- /dev/null
+++ b/test/fixtures/load-npm-tasks/package.json
@@ -0,0 +1,7 @@
+{
+ "private": true,
+ "name": "load-npm-tasks",
+ "devDependencies": {
+ "grunt-foo-plugin": "1.0.0"
+ }
+}
diff --git a/test/fixtures/spawn.js b/test/fixtures/spawn.js
index eb65657..14c7029 100644
--- a/test/fixtures/spawn.js
+++ b/test/fixtures/spawn.js
@@ -4,6 +4,5 @@ var code = Number(process.argv[2]);
process.stdout.write('stdout\n');
process.stderr.write('stderr\n');
-// Use instead of process.exit to ensure stdout/stderr are flushed
-// before exiting in Windows (Tested in Node.js v0.8.7)
-require('../../lib/util/exit').exit(code);
+// Instead of process.exit. See https://github.com/cowboy/node-exit
+require('exit')(code);
diff --git a/test/grunt/cli_test.js b/test/grunt/cli_test.js
new file mode 100644
index 0000000..beb5036
--- /dev/null
+++ b/test/grunt/cli_test.js
@@ -0,0 +1,53 @@
+'use strict';
+
+var grunt = require('../../lib/grunt');
+
+// Parse options printed by fixtures/Gruntfile-cli into an object.
+var optionValueRe = /###(.*?)###/;
+function getOptionValues(str) {
+ var matches = str.match(optionValueRe);
+ return matches ? JSON.parse(matches[1]) : {};
+}
+
+exports.cli = {
+ '--debug taskname': function(test) {
+ test.expect(1);
+ grunt.util.spawn({
+ grunt: true,
+ args: ['--gruntfile', 'test/fixtures/Gruntfile-cli.js', '--debug', 'debug', 'finalize'],
+ }, function(err, result) {
+ test.deepEqual(getOptionValues(result.stdout), {debug: 1}, 'Options should parse correctly.');
+ test.done();
+ });
+ },
+ 'taskname --debug': function(test) {
+ test.expect(1);
+ grunt.util.spawn({
+ grunt: true,
+ args: ['--gruntfile', 'test/fixtures/Gruntfile-cli.js', 'debug', '--debug', 'finalize'],
+ }, function(err, result) {
+ test.deepEqual(getOptionValues(result.stdout), {debug: 1}, 'Options should parse correctly.');
+ test.done();
+ });
+ },
+ '--debug --verbose': function(test) {
+ test.expect(1);
+ grunt.util.spawn({
+ grunt: true,
+ args: ['--gruntfile', 'test/fixtures/Gruntfile-cli.js', '--debug', '--verbose', 'debug', 'verbose', 'finalize'],
+ }, function(err, result) {
+ test.deepEqual(getOptionValues(result.stdout), {debug: 1, verbose: true}, 'Options should parse correctly.');
+ test.done();
+ });
+ },
+ '--verbose --debug': function(test) {
+ test.expect(1);
+ grunt.util.spawn({
+ grunt: true,
+ args: ['--gruntfile', 'test/fixtures/Gruntfile-cli.js', '--verbose', '--debug', 'debug', 'verbose', 'finalize'],
+ }, function(err, result) {
+ test.deepEqual(getOptionValues(result.stdout), {debug: 1, verbose: true}, 'Options should parse correctly.');
+ test.done();
+ });
+ },
+};
diff --git a/test/grunt/config_test.js b/test/grunt/config_test.js
index 8b731d7..6da5422 100644
--- a/test/grunt/config_test.js
+++ b/test/grunt/config_test.js
@@ -2,7 +2,7 @@
var grunt = require('../../lib/grunt');
-exports['config'] = {
+exports.config = {
setUp: function(done) {
this.origData = grunt.config.data;
grunt.config.init({
@@ -18,6 +18,7 @@ exports['config'] = {
bar: 'bar',
arr: ['foo', '<%= obj.foo2 %>'],
arr2: ['<%= arr %>', '<%= obj.Arr %>'],
+ buffer: new Buffer('test'),
});
done();
},
@@ -48,16 +49,19 @@ exports['config'] = {
test.done();
},
'config.process': function(test) {
- test.expect(5);
+ test.expect(7);
test.equal(grunt.config.process('<%= meta.foo %>'), 'bar', 'Should process templates.');
test.equal(grunt.config.process('<%= foo %>'), 'bar', 'Should process templates recursively.');
test.equal(grunt.config.process('<%= obj.foo %>'), 'bar', 'Should process deeply nested templates recursively.');
test.deepEqual(grunt.config.process(['foo', '<%= obj.foo2 %>']), ['foo', 'bar'], 'Should process templates in arrays.');
test.deepEqual(grunt.config.process(['<%= arr %>', '<%= obj.Arr %>']), [['foo', 'bar'], ['foo', 'bar']], 'Should expand <%= arr %> and <%= obj.Arr %> values as objects if possible.');
+ var buf = grunt.config.process('<%= buffer %>');
+ test.ok(Buffer.isBuffer(buf), 'Should retrieve Buffer instances as Buffer.');
+ test.deepEqual(buf, new Buffer('test'), 'Should return buffers as-is.');
test.done();
},
'config.get': function(test) {
- test.expect(8);
+ test.expect(10);
test.equal(grunt.config.get('foo'), 'bar', 'Should process templates.');
test.equal(grunt.config.get('foo2'), 'bar', 'Should process templates recursively.');
test.equal(grunt.config.get('obj.foo2'), 'bar', 'Should process deeply nested templates recursively.');
@@ -66,6 +70,9 @@ exports['config'] = {
test.deepEqual(grunt.config.get('obj.Arr'), ['foo', 'bar'], 'Should process templates in arrays.');
test.deepEqual(grunt.config.get('arr2'), [['foo', 'bar'], ['foo', 'bar']], 'Should expand <%= arr %> and <%= obj.Arr %> values as objects if possible.');
test.deepEqual(grunt.config.get(['obj', 'arr2']), [['foo', 'bar'], ['foo', 'bar']], 'Should expand <%= arr %> and <%= obj.Arr %> values as objects if possible.');
+ var buf = grunt.config.get('buffer');
+ test.ok(Buffer.isBuffer(buf), 'Should retrieve Buffer instances as Buffer.');
+ test.deepEqual(buf, new Buffer('test'), 'Should return buffers as-is.');
test.done();
},
'config.set': function(test) {
@@ -78,6 +85,20 @@ exports['config'] = {
test.equal(grunt.config.data.a.b.c, '<%= foo2 %>', 'Should have set the value.');
test.done();
},
+ 'config.merge': function(test) {
+ test.expect(4);
+ test.deepEqual(grunt.config.merge({}), grunt.config.getRaw(), 'Should return internal data object.');
+ grunt.config.set('obj', {a: 12});
+ grunt.config.merge({
+ foo: 'test',
+ baz: '123',
+ obj: {a: 34, b: 56},
+ });
+ test.deepEqual(grunt.config.getRaw('foo'), 'test', 'Should overwrite existing properties.');
+ test.deepEqual(grunt.config.getRaw('baz'), '123', 'Should add new properties.');
+ test.deepEqual(grunt.config.getRaw('obj'), {a: 34, b: 56}, 'Should deep merge.');
+ test.done();
+ },
'config': function(test) {
test.expect(10);
test.equal(grunt.config('foo'), 'bar', 'Should retrieve processed data.');
diff --git a/test/grunt/event_test.js b/test/grunt/event_test.js
index 0d72353..c8359dc 100644
--- a/test/grunt/event_test.js
+++ b/test/grunt/event_test.js
@@ -2,7 +2,7 @@
var grunt = require('../../lib/grunt');
-exports['event'] = function(test) {
+exports.event = function(test) {
test.expect(3);
grunt.event.on('test.foo', function(a, b, c) {
// This should get executed once (emit test.foo).
diff --git a/test/grunt/file_test.js b/test/grunt/file_test.js
index e996cf9..91466f2 100644
--- a/test/grunt/file_test.js
+++ b/test/grunt/file_test.js
@@ -8,9 +8,18 @@ var path = require('path');
var Tempfile = require('temporary/lib/file');
var Tempdir = require('temporary/lib/dir');
+var win32 = process.platform === 'win32';
+
var tmpdir = new Tempdir();
-fs.symlinkSync(path.resolve('test/fixtures/octocat.png'), path.join(tmpdir.path, 'octocat.png'), 'file');
-fs.symlinkSync(path.resolve('test/fixtures/expand'), path.join(tmpdir.path, 'expand'), 'dir');
+try {
+ fs.symlinkSync(path.resolve('test/fixtures/octocat.png'), path.join(tmpdir.path, 'octocat.png'), 'file');
+ fs.symlinkSync(path.resolve('test/fixtures/expand'), path.join(tmpdir.path, 'expand'), 'dir');
+} catch (err) {
+ console.error('** ERROR: Cannot create symbolic links; link-related tests will fail.');
+ if (win32) {
+ console.error('** Tests must be run with Administrator privileges on Windows.');
+ }
+}
exports['file.match'] = {
'empty set': function(test) {
@@ -198,10 +207,10 @@ exports['file.expand*'] = {
'exclusion': function(test) {
test.expect(8);
test.deepEqual(grunt.file.expand(['!js/*.js']), [], 'solitary exclusion should match nothing');
- test.deepEqual(grunt.file.expand(['js/bar.js','!js/bar.js']), [], 'exclusion should cancel match');
+ test.deepEqual(grunt.file.expand(['js/bar.js', '!js/bar.js']), [], 'exclusion should cancel match');
test.deepEqual(grunt.file.expand(['**/*.js', '!js/foo.js']), ['js/bar.js'], 'should omit single file from matched set');
test.deepEqual(grunt.file.expand(['!js/foo.js', '**/*.js']), ['js/bar.js', 'js/foo.js'], 'inclusion / exclusion order matters');
- test.deepEqual(grunt.file.expand(['**/*.js', '**/*.css', '!js/bar.js', '!css/baz.css']), ['js/foo.js','css/qux.css'], 'multiple exclusions should be removed from the set');
+ test.deepEqual(grunt.file.expand(['**/*.js', '**/*.css', '!js/bar.js', '!css/baz.css']), ['js/foo.js', 'css/qux.css'], 'multiple exclusions should be removed from the set');
test.deepEqual(grunt.file.expand(['**/*.js', '**/*.css', '!**/*.css']), ['js/bar.js', 'js/foo.js'], 'excluded wildcards should be removed from the matched set');
test.deepEqual(grunt.file.expand(['js/bar.js', 'js/foo.js', 'css/baz.css', 'css/qux.css', '!**/b*.*']), ['js/foo.js', 'css/qux.css'], 'different pattern for exclusion should still work');
test.deepEqual(grunt.file.expand(['js/bar.js', '!**/b*.*', 'js/foo.js', 'css/baz.css', 'css/qux.css']), ['js/foo.js', 'css/baz.css', 'css/qux.css'], 'inclusion / exclusion order matters');
@@ -272,7 +281,7 @@ exports['file.expandMapping'] = {
test.done();
},
'ext': function(test) {
- test.expect(2);
+ test.expect(3);
var actual, expected;
actual = grunt.file.expandMapping(['expand/**/*.txt'], 'dest', {ext: '.foo'});
expected = [
@@ -288,6 +297,35 @@ exports['file.expandMapping'] = {
{dest: 'dest/expand-mapping-ext/file.foo', src: ['expand-mapping-ext/file.ext.ension']},
];
test.deepEqual(actual, expected, 'specified extension should be added');
+ actual = grunt.file.expandMapping(['expand/**/*.txt'], 'dest', {ext: ''});
+ expected = [
+ {dest: 'dest/expand/deep/deep', src: ['expand/deep/deep.txt']},
+ {dest: 'dest/expand/deep/deeper/deeper', src: ['expand/deep/deeper/deeper.txt']},
+ {dest: 'dest/expand/deep/deeper/deepest/deepest', src: ['expand/deep/deeper/deepest/deepest.txt']},
+ ];
+ test.deepEqual(actual, expected, 'empty string extension should be added');
+ test.done();
+ },
+ 'extDot': function(test) {
+ test.expect(2);
+ var actual, expected;
+
+ actual = grunt.file.expandMapping(['expand-mapping-ext/**/file*'], 'dest', {ext: '.foo', extDot: 'first'});
+ expected = [
+ {dest: 'dest/expand-mapping-ext/dir.ectory/file-no-extension.foo', src: ['expand-mapping-ext/dir.ectory/file-no-extension']},
+ {dest: 'dest/expand-mapping-ext/dir.ectory/sub.dir.ectory/file.foo', src: ['expand-mapping-ext/dir.ectory/sub.dir.ectory/file.ext.ension']},
+ {dest: 'dest/expand-mapping-ext/file.foo', src: ['expand-mapping-ext/file.ext.ension']},
+ ];
+ test.deepEqual(actual, expected, 'extDot of "first" should replace everything after the first dot in the filename.');
+
+ actual = grunt.file.expandMapping(['expand-mapping-ext/**/file*'], 'dest', {ext: '.foo', extDot: 'last'});
+ expected = [
+ {dest: 'dest/expand-mapping-ext/dir.ectory/file-no-extension.foo', src: ['expand-mapping-ext/dir.ectory/file-no-extension']},
+ {dest: 'dest/expand-mapping-ext/dir.ectory/sub.dir.ectory/file.ext.foo', src: ['expand-mapping-ext/dir.ectory/sub.dir.ectory/file.ext.ension']},
+ {dest: 'dest/expand-mapping-ext/file.ext.foo', src: ['expand-mapping-ext/file.ext.ension']},
+ ];
+ test.deepEqual(actual, expected, 'extDot of "last" should replace everything after the last dot in the filename.');
+
test.done();
},
'cwd': function(test) {
@@ -324,6 +362,7 @@ exports['file.expandMapping'] = {
filter: 'isFile',
cwd: 'expand',
flatten: true,
+ nosort: true,
rename: function(destBase, destPath) {
return path.join(destBase, 'all' + path.extname(destPath));
}
@@ -339,7 +378,6 @@ exports['file.expandMapping'] = {
},
};
-
// Compare two buffers. Returns true if they are equivalent.
var compareBuffers = function(buf1, buf2) {
if (!Buffer.isBuffer(buf1) || !Buffer.isBuffer(buf2)) { return false; }
@@ -355,26 +393,45 @@ var compareFiles = function(filepath1, filepath2) {
return compareBuffers(fs.readFileSync(filepath1), fs.readFileSync(filepath2));
};
-exports['file'] = {
+exports.file = {
setUp: function(done) {
this.defaultEncoding = grunt.file.defaultEncoding;
grunt.file.defaultEncoding = 'utf8';
this.string = 'Ação é isso aí\n';
this.object = {foo: 'Ação é isso aí', bar: ['ømg', 'pønies']};
this.writeOption = grunt.option('write');
+
+ // Testing that warnings were displayed.
+ this.oldFailWarnFn = grunt.fail.warn;
+ this.oldLogWarnFn = grunt.log.warn;
+ this.resetWarnCount = function() {
+ this.warnCount = 0;
+ }.bind(this);
+ grunt.fail.warn = grunt.log.warn = function() {
+ this.warnCount += 1;
+ }.bind(this);
+
done();
},
tearDown: function(done) {
grunt.file.defaultEncoding = this.defaultEncoding;
grunt.option('write', this.writeOption);
+
+ grunt.fail.warn = this.oldFailWarnFn;
+ grunt.log.warn = this.oldLogWarnFn;
+
done();
},
'read': function(test) {
- test.expect(5);
+ test.expect(6);
test.strictEqual(grunt.file.read('test/fixtures/utf8.txt'), this.string, 'file should be read as utf8 by default.');
test.strictEqual(grunt.file.read('test/fixtures/iso-8859-1.txt', {encoding: 'iso-8859-1'}), this.string, 'file should be read using the specified encoding.');
test.ok(compareBuffers(grunt.file.read('test/fixtures/octocat.png', {encoding: null}), fs.readFileSync('test/fixtures/octocat.png')), 'file should be read as a buffer if encoding is specified as null.');
+
test.strictEqual(grunt.file.read('test/fixtures/BOM.txt'), 'foo', 'file should have BOM stripped.');
+ grunt.file.preserveBOM = true;
+ test.strictEqual(grunt.file.read('test/fixtures/BOM.txt'), '\ufeff' + 'foo', 'file should have BOM preserved.');
+ grunt.file.preserveBOM = false;
grunt.file.defaultEncoding = 'iso-8859-1';
test.strictEqual(grunt.file.read('test/fixtures/iso-8859-1.txt'), this.string, 'changing the default encoding should work.');
@@ -409,7 +466,7 @@ exports['file'] = {
test.done();
},
'write': function(test) {
- test.expect(5);
+ test.expect(6);
var tmpfile;
tmpfile = new Tempfile();
grunt.file.write(tmpfile.path, this.string);
@@ -421,6 +478,13 @@ exports['file'] = {
test.strictEqual(grunt.file.read(tmpfile.path, {encoding: 'iso-8859-1'}), this.string, 'file should be written using the specified encoding.');
tmpfile.unlinkSync();
+ tmpfile = new Tempfile();
+ tmpfile.unlinkSync();
+ grunt.file.write(tmpfile.path, this.string, {mode: parseInt('0444', 8)});
+ test.strictEqual(fs.statSync(tmpfile.path).mode & parseInt('0222', 8), 0, 'file should be read only.');
+ fs.chmodSync(tmpfile.path, parseInt('0666', 8));
+ tmpfile.unlinkSync();
+
grunt.file.defaultEncoding = 'iso-8859-1';
tmpfile = new Tempfile();
grunt.file.write(tmpfile.path, this.string);
@@ -465,12 +529,13 @@ exports['file'] = {
test.done();
},
'copy and process': function(test) {
- test.expect(13);
+ test.expect(14);
var tmpfile;
tmpfile = new Tempfile();
grunt.file.copy('test/fixtures/utf8.txt', tmpfile.path, {
- process: function(src, filepath) {
- test.equal(filepath, 'test/fixtures/utf8.txt', 'filepath should be passed in, as-specified.');
+ process: function(src, srcpath, destpath) {
+ test.equal(srcpath, 'test/fixtures/utf8.txt', 'srcpath should be passed in, as-specified.');
+ test.equal(destpath, tmpfile.path, 'destpath should be passed in, as-specified.');
test.equal(Buffer.isBuffer(src), false, 'when no encoding is specified, use default encoding and process src as a string');
test.equal(typeof src, 'string', 'when no encoding is specified, use default encoding and process src as a string');
return 'føø' + src + 'bår';
@@ -556,6 +621,32 @@ exports['file'] = {
test.done();
},
+ 'copy directory recursively': function(test) {
+ test.expect(34);
+ var copyroot1 = path.join(tmpdir.path, 'copy-dir-1');
+ var copyroot2 = path.join(tmpdir.path, 'copy-dir-2');
+ grunt.file.copy('test/fixtures/expand/', copyroot1);
+ grunt.file.recurse('test/fixtures/expand/', function(srcpath, rootdir, subdir, filename) {
+ var destpath = path.join(copyroot1, subdir || '', filename);
+ test.ok(grunt.file.isFile(srcpath), 'file should have been copied.');
+ test.equal(grunt.file.read(srcpath), grunt.file.read(destpath), 'file contents should be the same.');
+ });
+ grunt.file.mkdir(path.join(copyroot1, 'empty'));
+ grunt.file.mkdir(path.join(copyroot1, 'deep/deeper/empty'));
+ grunt.file.copy(copyroot1, copyroot2, {
+ process: function(contents) {
+ return '<' + contents + '>';
+ },
+ });
+ test.ok(grunt.file.isDir(path.join(copyroot2, 'empty')), 'empty directory should have been created.');
+ test.ok(grunt.file.isDir(path.join(copyroot2, 'deep/deeper/empty')), 'empty directory should have been created.');
+ grunt.file.recurse('test/fixtures/expand/', function(srcpath, rootdir, subdir, filename) {
+ var destpath = path.join(copyroot2, subdir || '', filename);
+ test.ok(grunt.file.isFile(srcpath), 'file should have been copied.');
+ test.equal('<' + grunt.file.read(srcpath) + '>', grunt.file.read(destpath), 'file contents should be processed correctly.');
+ });
+ test.done();
+ },
'delete': function(test) {
test.expect(2);
var oldBase = process.cwd();
@@ -570,16 +661,15 @@ exports['file'] = {
test.done();
},
'delete nonexistent file': function(test) {
- test.expect(1);
+ test.expect(2);
+ this.resetWarnCount();
test.ok(!grunt.file.delete('nonexistent'), 'should return false if file does not exist.');
+ test.ok(this.warnCount, 'should issue a warning when deleting non-existent file');
test.done();
},
'delete outside working directory': function(test) {
- test.expect(3);
+ test.expect(4);
var oldBase = process.cwd();
- var oldWarn = grunt.fail.warn;
- grunt.fail.warn = function() {};
-
var cwd = path.resolve(tmpdir.path, 'delete', 'folder');
var outsidecwd = path.resolve(tmpdir.path, 'delete', 'outsidecwd');
grunt.file.mkdir(cwd);
@@ -587,30 +677,31 @@ exports['file'] = {
grunt.file.setBase(cwd);
grunt.file.write(path.join(outsidecwd, 'test.js'), 'var test;');
+
+ this.resetWarnCount();
test.equal(grunt.file.delete(path.join(outsidecwd, 'test.js')), false, 'should not delete anything outside the cwd.');
+ test.ok(this.warnCount, 'should issue a warning when deleting outside working directory');
- test.ok(grunt.file.delete(path.join(outsidecwd), {force:true}), 'should delete outside cwd when using the --force.');
+ test.ok(grunt.file.delete(path.join(outsidecwd), {force: true}), 'should delete outside cwd when using the --force.');
test.equal(grunt.file.exists(outsidecwd), false, 'file outside cwd should have been deleted when using the --force.');
grunt.file.setBase(oldBase);
- grunt.fail.warn = oldWarn;
test.done();
},
'dont delete current working directory': function(test) {
- test.expect(2);
+ test.expect(3);
var oldBase = process.cwd();
- var oldWarn = grunt.fail.warn;
- grunt.fail.warn = function() {};
-
var cwd = path.resolve(tmpdir.path, 'dontdelete', 'folder');
grunt.file.mkdir(cwd);
grunt.file.setBase(cwd);
+ this.resetWarnCount();
test.equal(grunt.file.delete(cwd), false, 'should not delete the cwd.');
+ test.ok(this.warnCount, 'should issue a warning when trying to delete cwd');
+
test.ok(grunt.file.exists(cwd), 'the cwd should exist.');
grunt.file.setBase(oldBase);
- grunt.fail.warn = oldWarn;
test.done();
},
'dont actually delete with no-write option on': function(test) {
@@ -710,12 +801,17 @@ exports['file'] = {
test.done();
},
'isPathAbsolute': function(test) {
- test.expect(5);
+ test.expect(6);
test.ok(grunt.file.isPathAbsolute(path.resolve('/foo')), 'should return true');
test.ok(grunt.file.isPathAbsolute(path.resolve('/foo') + path.sep), 'should return true');
test.equal(grunt.file.isPathAbsolute('foo'), false, 'should return false');
test.ok(grunt.file.isPathAbsolute(path.resolve('test/fixtures/a.js')), 'should return true');
test.equal(grunt.file.isPathAbsolute('test/fixtures/a.js'), false, 'should return false');
+ if (win32) {
+ test.equal(grunt.file.isPathAbsolute('C:/Users/'), true, 'should return true');
+ } else {
+ test.equal(grunt.file.isPathAbsolute('/'), true, 'should return true');
+ }
test.done();
},
'arePathsEquivalent': function(test) {
@@ -761,4 +857,27 @@ exports['file'] = {
test.equal(grunt.file.isPathInCwd('nonexistent'), false, 'nonexistent path is not in cwd');
test.done();
},
+ 'cwdUnderSymlink': {
+ setUp: function(done) {
+ this.cwd = process.cwd();
+ process.chdir(path.join(tmpdir.path, 'expand'));
+ done();
+ },
+ tearDown: function(done) {
+ process.chdir(this.cwd);
+ done();
+ },
+ 'isPathCwd': function(test) {
+ test.expect(2);
+ test.ok(grunt.file.isPathCwd(process.cwd()), 'cwd is cwd');
+ test.ok(grunt.file.isPathCwd('.'), 'cwd is cwd');
+ test.done();
+ },
+ 'isPathInCwd': function(test) {
+ test.expect(2);
+ test.ok(grunt.file.isPathInCwd('deep'), 'subdirectory is in cwd');
+ test.ok(grunt.file.isPathInCwd(path.resolve('deep')), 'subdirectory is in cwd');
+ test.done();
+ },
+ }
};
diff --git a/test/grunt/log_test.js b/test/grunt/log_test.js
deleted file mode 100644
index 27b982c..0000000
--- a/test/grunt/log_test.js
+++ /dev/null
@@ -1,224 +0,0 @@
-'use strict';
-
-var grunt = require('../../lib/grunt');
-var log = grunt.log;
-
-var fooBuffer = new Buffer('foo');
-
-// Helper for testing stdout
-var hooker = grunt.util.hooker;
-var stdoutEqual = function(test, callback, expected) {
- var actual = '';
- // Hook process.stdout.write
- hooker.hook(process.stdout, 'write', {
- // This gets executed before the original process.stdout.write.
- pre: function(result) {
- // Concatenate uncolored result onto actual.
- actual += log.uncolor(result);
- // Prevent the original process.stdout.write from executing.
- return hooker.preempt();
- }
- });
- // Execute the logging code to be tested.
- callback();
- // Restore process.stdout.write to its original value.
- hooker.unhook(process.stdout, 'write');
- // Actually test the actually-logged stdout string to the expected value.
- test.equal(actual, expected);
-};
-
-exports['log'] = {
- 'setUp': function(done) {
- log.muted = false;
- grunt.fail.errorcount = 0;
- done();
- },
- 'write': function(test) {
- test.expect(4);
-
- stdoutEqual(test, function() { log.write(''); }, '');
- stdoutEqual(test, function() { log.write('foo'); }, 'foo');
- stdoutEqual(test, function() { log.write('%s', 'foo'); }, 'foo');
- stdoutEqual(test, function() { log.write(fooBuffer); }, 'foo');
-
- test.done();
- },
- 'writeln': function(test) {
- test.expect(4);
-
- stdoutEqual(test, function() { log.writeln(); }, '\n');
- stdoutEqual(test, function() { log.writeln('foo'); }, 'foo\n');
- stdoutEqual(test, function() { log.writeln('%s', 'foo'); }, 'foo\n');
- stdoutEqual(test, function() { log.writeln(fooBuffer); }, 'foo\n');
-
- test.done();
- },
- 'warn': function(test) {
- test.expect(5);
-
- stdoutEqual(test, function() { log.warn(); }, 'ERROR\n');
- stdoutEqual(test, function() { log.warn('foo'); }, '>> foo\n');
- stdoutEqual(test, function() { log.warn('%s', 'foo'); }, '>> foo\n');
- stdoutEqual(test, function() { log.warn(fooBuffer); }, '>> foo\n');
- test.equal(grunt.fail.errorcount, 0);
-
- test.done();
- },
- 'error': function(test) {
- test.expect(5);
-
- stdoutEqual(test, function() { log.error(); }, 'ERROR\n');
- stdoutEqual(test, function() { log.error('foo'); }, '>> foo\n');
- stdoutEqual(test, function() { log.error('%s', 'foo'); }, '>> foo\n');
- stdoutEqual(test, function() { log.error(fooBuffer); }, '>> foo\n');
- test.equal(grunt.fail.errorcount, 4);
-
- test.done();
- },
- 'ok': function(test) {
- test.expect(4);
-
- stdoutEqual(test, function() { log.ok(); }, 'OK\n');
- stdoutEqual(test, function() { log.ok('foo'); }, '>> foo\n');
- stdoutEqual(test, function() { log.ok('%s', 'foo'); }, '>> foo\n');
- stdoutEqual(test, function() { log.ok(fooBuffer); }, '>> foo\n');
-
- test.done();
- },
- 'errorlns': function(test) {
- test.expect(2);
-
- stdoutEqual(test, function() {
- log.errorlns(grunt.util._.repeat('foo', 30, ' '));
- }, '>> ' + grunt.util._.repeat('foo', 19, ' ') + '\n' +
- '>> ' + grunt.util._.repeat('foo', 11, ' ') + '\n');
- test.equal(grunt.fail.errorcount, 1);
-
- test.done();
- },
- 'oklns': function(test) {
- test.expect(1);
-
- stdoutEqual(test, function() {
- log.oklns(grunt.util._.repeat('foo', 30, ' '));
- }, '>> ' + grunt.util._.repeat('foo', 19, ' ') + '\n' +
- '>> ' + grunt.util._.repeat('foo', 11, ' ') + '\n');
-
- test.done();
- },
- 'success': function(test) {
- test.expect(4);
-
- stdoutEqual(test, function() { log.success(); }, '\n');
- stdoutEqual(test, function() { log.success('foo'); }, 'foo\n');
- stdoutEqual(test, function() { log.success('%s', 'foo'); }, 'foo\n');
- stdoutEqual(test, function() { log.success(fooBuffer); }, 'foo\n');
-
- test.done();
- },
- 'fail': function(test) {
- test.expect(4);
-
- stdoutEqual(test, function() { log.fail(); }, '\n');
- stdoutEqual(test, function() { log.fail('foo'); }, 'foo\n');
- stdoutEqual(test, function() { log.fail('%s', 'foo'); }, 'foo\n');
- stdoutEqual(test, function() { log.fail(fooBuffer); }, 'foo\n');
-
- test.done();
- },
- 'header': function(test) {
- test.expect(4);
-
- stdoutEqual(test, function() { log.header(); }, '\n\n');
- stdoutEqual(test, function() { log.header('foo'); }, '\nfoo\n');
- stdoutEqual(test, function() { log.header('%s', 'foo'); }, '\nfoo\n');
- stdoutEqual(test, function() { log.header(fooBuffer); }, '\nfoo\n');
-
- test.done();
- },
- 'subhead': function(test) {
- test.expect(4);
-
- stdoutEqual(test, function() { log.subhead(); }, '\n\n');
- stdoutEqual(test, function() { log.subhead('foo'); }, '\nfoo\n');
- stdoutEqual(test, function() { log.subhead('%s', 'foo'); }, '\nfoo\n');
- stdoutEqual(test, function() { log.subhead(fooBuffer); }, '\nfoo\n');
-
- test.done();
- },
- 'debug': function(test) {
- test.expect(5);
- var debug = grunt.option('debug');
-
- grunt.option('debug', true);
- stdoutEqual(test, function() { log.debug(); }, '[D] \n');
- stdoutEqual(test, function() { log.debug('foo'); }, '[D] foo\n');
- stdoutEqual(test, function() { log.debug('%s', 'foo'); }, '[D] foo\n');
- stdoutEqual(test, function() { log.debug(fooBuffer); }, '[D] foo\n');
-
- grunt.option('debug', false);
- stdoutEqual(test, function() { log.debug('foo'); }, '');
-
- grunt.option('debug', debug);
- test.done();
- },
- 'writetableln': function(test) {
- test.expect(1);
-
- stdoutEqual(test, function() {
- log.writetableln([10], [grunt.util._.repeat('foo', 10)]);
- }, 'foofoofoof\noofoofoofo\nofoofoofoo\n');
-
- test.done();
- },
- 'writelns': function(test) {
- test.expect(1);
-
- stdoutEqual(test, function() {
- log.writelns(grunt.util._.repeat('foo', 30, ' '));
- }, grunt.util._.repeat('foo', 20, ' ') + '\n' +
- grunt.util._.repeat('foo', 10, ' ') + '\n');
-
- test.done();
- },
- 'writeflags': function(test) {
- test.expect(1);
-
- stdoutEqual(test, function() {
- log.writeflags(['foo', 'bar'], 'test');
- }, 'test: foo, bar\n');
-
- test.done();
- },
- 'verbose': function(test) {
- test.expect(1);
- log.muted = true;
-
- // Test a chain to make sure it's always returning the verbose object.
- var obj;
- ['write','writeln','warn','error','ok'].forEach(function(key) {
- obj = obj ? obj[key]('foo') : log.verbose[key]('foo');
- });
-
- test.strictEqual(obj, log.verbose);
-
- test.done();
- },
- 'notverbose': function(test) {
- test.expect(1);
- hooker.hook(process.stdout, 'write', {
- pre: function() { return hooker.preempt(); }
- });
-
- // Test a chain to make sure it's always returning the notverbose object.
- var obj;
- ['write','writeln','warn','error','ok'].forEach(function(key) {
- obj = obj ? obj[key]('foo') : log.notverbose[key]('foo');
- });
-
- test.strictEqual(obj, log.notverbose);
-
- hooker.unhook(process.stdout, 'write');
- test.done();
- }
-};
diff --git a/test/grunt/option_test.js b/test/grunt/option_test.js
index 8917a57..de84799 100644
--- a/test/grunt/option_test.js
+++ b/test/grunt/option_test.js
@@ -2,7 +2,7 @@
var grunt = require('../../lib/grunt');
-exports['option'] = {
+exports.option = {
setUp: function(done) {
grunt.option.init();
done();
@@ -13,15 +13,15 @@ exports['option'] = {
},
'option.init': function(test) {
test.expect(1);
- var expected = {foo:'bar',bool:true,'bar':{foo:'bar'}};
+ var expected = {foo: 'bar', bool: true, 'bar': {foo: 'bar'}};
test.deepEqual(grunt.option.init(expected), expected);
test.done();
},
'option': function(test) {
test.expect(4);
test.equal(grunt.option('foo', 'bar'), grunt.option('foo'));
- grunt.option('foo', {foo:'bar'});
- test.deepEqual(grunt.option('foo'), {foo:'bar'});
+ grunt.option('foo', {foo: 'bar'});
+ test.deepEqual(grunt.option('foo'), {foo: 'bar'});
test.equal(grunt.option('no-there'), false);
grunt.option('there', false);
test.equal(grunt.option('no-there'), true);
diff --git a/test/grunt/template_test.js b/test/grunt/template_test.js
index b7903ac..e188b47 100644
--- a/test/grunt/template_test.js
+++ b/test/grunt/template_test.js
@@ -2,7 +2,7 @@
var grunt = require('../../lib/grunt');
-exports['template'] = {
+exports.template = {
'process': function(test) {
test.expect(4);
var obj = {
diff --git a/test/grunt/util_test.js b/test/grunt/util_test.js
deleted file mode 100644
index 01e8b1c..0000000
--- a/test/grunt/util_test.js
+++ /dev/null
@@ -1,301 +0,0 @@
-'use strict';
-
-var grunt = require('../../lib/grunt');
-
-var fs = require('fs');
-var path = require('path');
-
-var Tempfile = require('temporary/lib/file');
-
-exports['util.callbackify'] = {
- 'return': function(test) {
- test.expect(1);
- // This function returns a value.
- function add(a, b) {
- return a + b;
- }
- grunt.util.callbackify(add)(1, 2, function(result) {
- test.equal(result, 3, 'should be the correct result.');
- test.done();
- });
- },
- 'callback (sync)': function(test) {
- test.expect(1);
- // This function accepts a callback which it calls synchronously.
- function add(a, b, done) {
- done(a + b);
- }
- grunt.util.callbackify(add)(1, 2, function(result) {
- test.equal(result, 3, 'should be the correct result.');
- test.done();
- });
- },
- 'callback (async)': function(test) {
- test.expect(1);
- // This function accepts a callback which it calls asynchronously.
- function add(a, b, done) {
- setTimeout(done.bind(null, a + b), 0);
- }
- grunt.util.callbackify(add)(1, 2, function(result) {
- test.equal(result, 3, 'should be the correct result.');
- test.done();
- });
- }
-};
-
-exports['util'] = {
- 'error': function(test) {
- test.expect(9);
- var origError = new Error('Original error.');
-
- var err = grunt.util.error('Test message.');
- test.ok(err instanceof Error, 'Should be an Error.');
- test.equal(err.name, 'Error', 'Should be an Error.');
- test.equal(err.message, 'Test message.', 'Should have the correct message.');
-
- err = grunt.util.error('Test message.', origError);
- test.ok(err instanceof Error, 'Should be an Error.');
- test.equal(err.name, 'Error', 'Should be an Error.');
- test.equal(err.message, 'Test message.', 'Should have the correct message.');
- test.equal(err.origError, origError, 'Should reflect the original error.');
-
- var newError = new Error('Test message.');
- err = grunt.util.error(newError, origError);
- test.equal(err, newError, 'Should be the passed-in Error.');
- test.equal(err.origError, origError, 'Should reflect the original error.');
- test.done();
- },
- 'linefeed': function(test) {
- test.expect(1);
- if (process.platform === 'win32') {
- test.equal(grunt.util.linefeed, '\r\n', 'linefeed should be operating-system appropriate.');
- } else {
- test.equal(grunt.util.linefeed, '\n', 'linefeed should be operating-system appropriate.');
- }
- test.done();
- },
- 'normalizelf': function(test) {
- test.expect(1);
- if (process.platform === 'win32') {
- test.equal(grunt.util.normalizelf('foo\nbar\r\nbaz\r\n\r\nqux\n\nquux'), 'foo\r\nbar\r\nbaz\r\n\r\nqux\r\n\r\nquux', 'linefeeds should be normalized');
- } else {
- test.equal(grunt.util.normalizelf('foo\nbar\r\nbaz\r\n\r\nqux\n\nquux'), 'foo\nbar\nbaz\n\nqux\n\nquux', 'linefeeds should be normalized');
- }
- test.done();
- }
-};
-
-exports['util.spawn'] = {
- setUp: function(done) {
- this.script = path.resolve('test/fixtures/spawn.js');
- done();
- },
- 'exit code 0': function(test) {
- test.expect(6);
- grunt.util.spawn({
- cmd: process.execPath,
- args: [ this.script, 0 ],
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 0);
- test.equals(result.stdout, 'stdout');
- test.equals(result.stderr, 'stderr');
- test.equals(result.code, 0);
- test.equals(String(result), 'stdout');
- test.done();
- });
- },
- 'exit code 0, fallback': function(test) {
- test.expect(6);
- grunt.util.spawn({
- cmd: process.execPath,
- args: [ this.script, 0 ],
- fallback: 'ignored if exit code is 0'
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 0);
- test.equals(result.stdout, 'stdout');
- test.equals(result.stderr, 'stderr');
- test.equals(result.code, 0);
- test.equals(String(result), 'stdout');
- test.done();
- });
- },
- 'non-zero exit code': function(test) {
- test.expect(7);
- grunt.util.spawn({
- cmd: process.execPath,
- args: [ this.script, 123 ],
- }, function(err, result, code) {
- test.ok(err instanceof Error);
- test.equals(err.message, 'stderr');
- test.equals(code, 123);
- test.equals(result.stdout, 'stdout');
- test.equals(result.stderr, 'stderr');
- test.equals(result.code, 123);
- test.equals(String(result), 'stderr');
- test.done();
- });
- },
- 'non-zero exit code, fallback': function(test) {
- test.expect(6);
- grunt.util.spawn({
- cmd: process.execPath,
- args: [ this.script, 123 ],
- fallback: 'custom fallback'
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 123);
- test.equals(result.stdout, 'stdout');
- test.equals(result.stderr, 'stderr');
- test.equals(result.code, 123);
- test.equals(String(result), 'custom fallback');
- test.done();
- });
- },
- 'cmd not found': function(test) {
- test.expect(3);
- grunt.util.spawn({
- cmd: 'nodewtfmisspelled',
- }, function(err, result, code) {
- test.ok(err instanceof Error);
- test.equals(code, 127);
- test.equals(result.code, 127);
- test.done();
- });
- },
- 'cmd not found, fallback': function(test) {
- test.expect(4);
- grunt.util.spawn({
- cmd: 'nodewtfmisspelled',
- fallback: 'use a fallback or good luck'
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 127);
- test.equals(result.code, 127);
- test.equals(String(result), 'use a fallback or good luck');
- test.done();
- });
- },
- 'cmd not in path': function(test) {
- test.expect(6);
- var win32 = process.platform === 'win32';
- grunt.util.spawn({
- cmd: 'test\\fixtures\\exec' + (win32 ? '.cmd' : '.sh'),
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 0);
- test.equals(result.stdout, 'done');
- test.equals(result.stderr, '');
- test.equals(result.code, 0);
- test.equals(String(result), 'done');
- test.done();
- });
- },
- 'cmd not in path (with cwd)': function(test) {
- test.expect(6);
- var win32 = process.platform === 'win32';
- grunt.util.spawn({
- cmd: './exec' + (win32 ? '.cmd' : '.sh'),
- opts: {cwd: 'test/fixtures'},
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 0);
- test.equals(result.stdout, 'done');
- test.equals(result.stderr, '');
- test.equals(result.code, 0);
- test.equals(String(result), 'done');
- test.done();
- });
- },
- 'grunt': function(test) {
- test.expect(3);
- grunt.util.spawn({
- grunt: true,
- args: [ '--gruntfile', 'test/fixtures/Gruntfile-print-text.js', 'print:foo' ],
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 0);
- test.ok(/^OUTPUT: foo/m.test(result.stdout), 'stdout should contain output indicating the grunt task was run.');
- test.done();
- });
- },
- 'grunt (with cwd)': function(test) {
- test.expect(3);
- grunt.util.spawn({
- grunt: true,
- args: [ '--gruntfile', 'Gruntfile-print-text.js', 'print:foo' ],
- opts: {cwd: 'test/fixtures'},
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 0);
- test.ok(/^OUTPUT: foo/m.test(result.stdout), 'stdout should contain output indicating the grunt task was run.');
- test.done();
- });
- },
- 'grunt result.toString() with error': function(test) {
- // grunt.log.error uses standard out, to be fixed in 0.5.
- test.expect(4);
- grunt.util.spawn({
- grunt: true,
- args: [ 'nonexistentTask' ]
- }, function(err, result, code) {
- test.ok(err instanceof Error, 'Should be an Error.');
- test.equal(err.name, 'Error', 'Should be an Error.');
- test.equals(code, 3);
- test.ok(/Warning: Task "nonexistentTask" not found./m.test(result.toString()), 'stdout should contain output indicating the grunt task was (attempted to be) run.');
- test.done();
- });
- },
- 'custom stdio stream(s)': function(test) {
- test.expect(6);
- var stdoutFile = new Tempfile();
- var stderrFile = new Tempfile();
- var stdout = fs.openSync(stdoutFile.path, 'a');
- var stderr = fs.openSync(stderrFile.path, 'a');
- var child = grunt.util.spawn({
- cmd: process.execPath,
- args: [ this.script, 0 ],
- opts: {stdio: [null, stdout, stderr]},
- }, function(err, result, code) {
- test.equals(code, 0);
- test.equals(String(fs.readFileSync(stdoutFile.path)), 'stdout\n', 'Child process stdout should have been captured via custom stream.');
- test.equals(String(fs.readFileSync(stderrFile.path)), 'stderr\n', 'Child process stderr should have been captured via custom stream.');
- stdoutFile.unlinkSync();
- stderrFile.unlinkSync();
- test.equals(result.stdout, '', 'Nothing will be passed to the stdout string when spawn stdio is a custom stream.');
- test.done();
- });
- test.ok(!child.stdout, 'child should not have a stdout property.');
- test.ok(!child.stderr, 'child should not have a stderr property.');
- },
-};
-
-exports['util.spawn.multibyte'] = {
- setUp: function(done) {
- this.script = path.resolve('test/fixtures/spawn-multibyte.js');
- done();
- },
- 'partial stdout': function(test) {
- test.expect(4);
- grunt.util.spawn({
- cmd: process.execPath,
- args: [ this.script ],
- }, function(err, result, code) {
- test.equals(err, null);
- test.equals(code, 0);
- test.equals(result.stdout, 'こんにちは');
- test.equals(result.stderr, 'こんにちは');
- test.done();
- });
- }
-};
-
-exports['util.underscore.string'] = function(test) {
- test.expect(4);
- test.equals(grunt.util._.trim(' foo '), 'foo', 'Should have trimmed the string.');
- test.equals(grunt.util._.capitalize('foo'), 'Foo', 'Should have capitalized the first letter.');
- test.equals(grunt.util._.words('one two three').length, 3, 'Should have counted three words.');
- test.ok(grunt.util._.isBlank(' '), 'Should be blank.');
- test.done();
-};
diff --git a/test/gruntfile/load-npm-tasks.js b/test/gruntfile/load-npm-tasks.js
new file mode 100644
index 0000000..078b315
--- /dev/null
+++ b/test/gruntfile/load-npm-tasks.js
@@ -0,0 +1,42 @@
+'use strict';
+
+var Log = require('grunt-legacy-log').Log;
+var assert = require('assert');
+var through = require('through2');
+
+module.exports = function(grunt) {
+ grunt.file.setBase('../fixtures/load-npm-tasks');
+
+ // Create a custom log to assert output
+ var stdout = [];
+ var oldlog = grunt.log;
+ var stream = through(function(data, enc, next) {
+ stdout.push(data.toString());
+ next(null, data);
+ });
+ stream.pipe(process.stdout);
+ var log = new Log({
+ grunt: grunt,
+ outStream: stream,
+ });
+ grunt.log = log;
+
+ // Load a npm task
+ grunt.loadNpmTasks('grunt-foo-plugin');
+
+ // Run them
+ grunt.registerTask('default', ['foo', 'done']);
+
+ // Assert they loaded and ran correctly
+ grunt.registerTask('done', function() {
+ grunt.log = oldlog;
+ stdout = stdout.join('\n');
+ try {
+ assert.ok(stdout.indexOf('foo has ran.') !== -1, 'oh-four task should have ran.');
+ } catch (err) {
+ grunt.log.subhead(err.message);
+ grunt.log.error('Expected ' + err.expected + ' but actually: ' + err.actual);
+ throw err;
+ }
+ });
+};
diff --git a/test/gruntfile/multi-task-files.js b/test/gruntfile/multi-task-files.js
index 3fda2e4..d170d06 100644
--- a/test/gruntfile/multi-task-files.js
+++ b/test/gruntfile/multi-task-files.js
@@ -1,12 +1,3 @@
-/*
- * grunt
- * http://gruntjs.com/
- *
- * Copyright (c) 2013 "Cowboy" Ben Alman
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
- */
-
// For now, run this "test suite" with:
// grunt --gruntfile ./test/gruntfile/multi-task-files.js
@@ -34,7 +25,7 @@ module.exports = function(grunt) {
'dist/built1.js': ['src/*1.js', 'src/*2.js'],
// This is the "medium" format. The target name is arbitrary and can be
// used like "grunt run:built". Supports per-target options, templated
- // dest, and arbitrary "extra" paramters. Doesn't support >1 srcs-dest
+ // dest, and arbitrary "extra" parameters. Doesn't support >1 srcs-dest
// grouping.
built: {
options: {a: 2, c: 22},
@@ -59,7 +50,7 @@ module.exports = function(grunt) {
{'dist/built-<%= build %>-b.js': ['src/*1.js', 'src/*2.js']},
]
},
- // This "full" variant supports per srcs-dest arbitrary "extra" paramters.
+ // This "full" variant supports per srcs-dest arbitrary "extra" parameters.
long3: {
options: {a: 5, c: 55},
files: [
@@ -90,6 +81,19 @@ module.exports = function(grunt) {
}
]
},
+ long4_mapping: {
+ options: {a: 8, c: 88},
+ files: [
+ '<%= run.long3_mapping.files %>'
+ ]
+ },
+ long5_mapping: {
+ options: {a: 9, c: 99},
+ files: [
+ '<%= run.long3_mapping.files %>',
+ '<%= run.long4_mapping.files %>'
+ ]
+ },
// Need to ensure the task function is run if no files or options were
// specified!
no_files_or_options: {},
@@ -294,6 +298,94 @@ module.exports = function(grunt) {
},
],
},
+ 'run:long4_mapping': {
+ options: {a: 8, b: 11, c: 88, d: 9},
+ files: [
+ {
+ dest: 'foo/baz/file1.bar',
+ src: ['src/file1.js'],
+ extra: 123,
+ orig: {
+ expand: true,
+ cwd: grunt.config.get('mappings.cwd'),
+ src: ['*1.js', '*2.js'],
+ dest: grunt.config.get('mappings.dest'),
+ rename: grunt.config.get('mappings.rename'),
+ extra: 123,
+ },
+ },
+ {
+ dest: 'foo/baz/file2.bar',
+ src: ['src/file2.js'],
+ extra: 123,
+ orig: {
+ expand: true,
+ cwd: grunt.config.get('mappings.cwd'),
+ src: ['*1.js', '*2.js'],
+ dest: grunt.config.get('mappings.dest'),
+ rename: grunt.config.get('run.built_mapping.rename'),
+ extra: 123,
+ },
+ },
+ ],
+ },
+ 'run:long5_mapping': {
+ options: {a: 9, b: 11, c: 99, d: 9},
+ files: [
+ {
+ dest: 'foo/baz/file1.bar',
+ src: ['src/file1.js'],
+ extra: 123,
+ orig: {
+ expand: true,
+ cwd: grunt.config.get('mappings.cwd'),
+ src: ['*1.js', '*2.js'],
+ dest: grunt.config.get('mappings.dest'),
+ rename: grunt.config.get('mappings.rename'),
+ extra: 123,
+ },
+ },
+ {
+ dest: 'foo/baz/file2.bar',
+ src: ['src/file2.js'],
+ extra: 123,
+ orig: {
+ expand: true,
+ cwd: grunt.config.get('mappings.cwd'),
+ src: ['*1.js', '*2.js'],
+ dest: grunt.config.get('mappings.dest'),
+ rename: grunt.config.get('run.built_mapping.rename'),
+ extra: 123,
+ },
+ },
+ {
+ dest: 'foo/baz/file1.bar',
+ src: ['src/file1.js'],
+ extra: 123,
+ orig: {
+ expand: true,
+ cwd: grunt.config.get('mappings.cwd'),
+ src: ['*1.js', '*2.js'],
+ dest: grunt.config.get('mappings.dest'),
+ rename: grunt.config.get('mappings.rename'),
+ extra: 123,
+ },
+ },
+ {
+ dest: 'foo/baz/file2.bar',
+ src: ['src/file2.js'],
+ extra: 123,
+ orig: {
+ expand: true,
+ cwd: grunt.config.get('mappings.cwd'),
+ src: ['*1.js', '*2.js'],
+ dest: grunt.config.get('mappings.dest'),
+ rename: grunt.config.get('run.built_mapping.rename'),
+ extra: 123,
+ },
+ },
+ ],
+ },
};
var assert = require('assert');
@@ -353,6 +445,10 @@ module.exports = function(grunt) {
'test:built_mapping',
'run:long3_mapping',
'test:long3_mapping',
+ 'run:long4_mapping',
+ 'test:long4_mapping',
+ 'run:long5_mapping',
+ 'test:long5_mapping',
'run',
'test:all',
'test:counters',
diff --git a/test/util/namespace_test.js b/test/util/namespace_test.js
deleted file mode 100644
index 9a23616..0000000
--- a/test/util/namespace_test.js
+++ /dev/null
@@ -1,51 +0,0 @@
-'use strict';
-
-var namespace = require('../../lib/util/namespace.js');
-
-exports.get = {
- 'no create': function(test) {
- var obj = {a: {b: {c: 1, d: '', e: null, f: undefined, 'g.h.i': 2}}};
- test.strictEqual(namespace.get(obj, 'a'), obj.a, 'should get immediate properties.');
- test.strictEqual(namespace.get(obj, 'a.b'), obj.a.b, 'should get nested properties.');
- test.strictEqual(namespace.get(obj, 'a.x'), undefined, 'should return undefined for nonexistent properties.');
- test.strictEqual(namespace.get(obj, 'a.b.c'), 1, 'should return values.');
- test.strictEqual(namespace.get(obj, 'a.b.d'), '', 'should return values.');
- test.strictEqual(namespace.get(obj, 'a.b.e'), null, 'should return values.');
- test.strictEqual(namespace.get(obj, 'a.b.f'), undefined, 'should return values.');
- test.strictEqual(namespace.get(obj, 'a.b.g\\.h\\.i'), 2, 'literal backslash should escape period in property name.');
- test.done();
- },
- 'create': function(test) {
- var obj = {a: 1};
- test.strictEqual(namespace.get(obj, 'a', true), obj.a, 'should just return existing properties.');
- test.strictEqual(namespace.get(obj, 'b', true), obj.b, 'should create immediate properties.');
- test.strictEqual(namespace.get(obj, 'c.d.e', true), obj.c.d.e, 'should create nested properties.');
- test.done();
- }
-};
-
-exports.set = function(test) {
- var obj = {};
- test.strictEqual(namespace.set(obj, 'a', 1), 1, 'should return immediate property value.');
- test.strictEqual(obj.a, 1, 'should set property value.');
- test.strictEqual(namespace.set(obj, 'b.c.d', 1), 1, 'should return nested property value.');
- test.strictEqual(obj.b.c.d, 1, 'should set property value.');
- test.strictEqual(namespace.set(obj, 'e\\.f\\.g', 1), 1, 'literal backslash should escape period in property name.');
- test.strictEqual(obj['e.f.g'], 1, 'should set property value.');
- test.done();
-};
-
-exports.exists = function(test) {
- var obj = {a: {b: {c: 1, d: '', e: null, f: undefined, 'g.h.i': 2}}};
- test.ok(namespace.exists(obj, 'a'), 'immediate property should exist.');
- test.ok(namespace.exists(obj, 'a.b'), 'nested property should exist.');
- test.ok(namespace.exists(obj, 'a.b.c'), 'nested property should exist.');
- test.ok(namespace.exists(obj, 'a.b.d'), 'nested property should exist.');
- test.ok(namespace.exists(obj, 'a.b.e'), 'nested property should exist.');
- test.ok(namespace.exists(obj, 'a.b.f'), 'nested property should exist.');
- test.ok(namespace.exists(obj, 'a.b.g\\.h\\.i'), 'literal backslash should escape period in property name.');
- test.equal(namespace.exists(obj, 'x'), false, 'nonexistent property should not exist.');
- test.equal(namespace.exists(obj, 'a.x'), false, 'nonexistent property should not exist.');
- test.equal(namespace.exists(obj, 'a.b.x'), false, 'nonexistent property should not exist.');
- test.done();
-};
diff --git a/test/util/task_test.js b/test/util/task_test.js
index fc893ac..3791620 100644
--- a/test/util/task_test.js
+++ b/test/util/task_test.js
@@ -26,7 +26,7 @@ exports['new Task'] = {
}
};
-exports['Tasks'] = {
+exports.Tasks = {
setUp: function(done) {
result.reset();
this.task = requireTask().create();
@@ -169,6 +169,13 @@ exports['Tasks'] = {
});
task.run('syncs', 'asyncs').start();
},
+ 'Task#exists': function(test) {
+ test.expect(2);
+ var task = this.task;
+ test.equal(task.exists('nothing'), true, 'A task should not be exists (registered).');
+ test.equal(task.exists('notexistent'), false, 'A task should not be exists (registered).');
+ test.done();
+ },
'Task#run (nested, exception handling)': function(test) {
test.expect(2);
var task = this.task;
@@ -271,6 +278,17 @@ exports['Tasks'] = {
});
task.run('a', 'h').start();
},
+ 'Task#run (async task with multiple callbacks)': function(test) {
+ test.expect(1);
+ var task = this.task;
+ task.registerTask('a', 'Call async callback twice.', function() { var done = this.async(); done(); done(); });
+ task.registerTask('b', 'Never call async callback.', function() { this.async(); });
+ task.run('a', 'b').start();
+ delay(function() {
+ test.deepEqual(task.current.name, 'b', 'Should be stuck on task with no async callback');
+ test.done();
+ });
+ },
'Task#current': function(test) {
test.expect(8);
var task = this.task;
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/grunt.git
More information about the Pkg-javascript-commits
mailing list