pax_global_header00006660000000000000000000000064133006306510014507gustar00rootroot0000000000000052 comment=8f534d7761d2cadc6591eef285e59c9618b9c6f5 grunt-legacy-util-1.1.1/000077500000000000000000000000001330063065100150635ustar00rootroot00000000000000grunt-legacy-util-1.1.1/.gitignore000066400000000000000000000000151330063065100170470ustar00rootroot00000000000000node_modules grunt-legacy-util-1.1.1/.jshintrc000066400000000000000000000003251330063065100167100ustar00rootroot00000000000000{ "curly": true, "eqeqeq": true, "immed": true, "latedef": "nofunc", "newcap": true, "noarg": true, "sub": true, "undef": true, "unused": true, "boss": true, "eqnull": true, "node": true } grunt-legacy-util-1.1.1/.travis.yml000066400000000000000000000001231330063065100171700ustar00rootroot00000000000000sudo: false language: node_js node_js: - "6" - "8" matrix: fast_finish: true grunt-legacy-util-1.1.1/CHANGELOG000066400000000000000000000003461330063065100163000ustar00rootroot00000000000000v1.1.1: date: 2018-05-21 changes: - Fixes lodash versioning v1.1.0: date: 2018-05-19 changes: - Security fixes v1.0.0: date: 2016-04-03 changes: - test fixes for Windows - update to use the svg badge grunt-legacy-util-1.1.1/Gruntfile.js000066400000000000000000000011271330063065100173610ustar00rootroot00000000000000'use strict'; module.exports = function(grunt) { grunt.initConfig({ jshint: { options: { jshintrc: '.jshintrc', }, all: ['*.js', 'test/*.js'], }, nodeunit: { util: ['test/index.js'] }, watch: { all: { files: ['<%= jshint.all %>'], tasks: ['test'], }, }, }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-nodeunit'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('test', ['jshint', 'nodeunit']); grunt.registerTask('default', ['test', 'watch']); }; grunt-legacy-util-1.1.1/LICENSE-MIT000066400000000000000000000020461330063065100165210ustar00rootroot00000000000000Copyright (c) 2016 "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. grunt-legacy-util-1.1.1/README.md000066400000000000000000000032131330063065100163410ustar00rootroot00000000000000# grunt-legacy-util > deprecated utilities from grunt [![Build Status: Linux](https://travis-ci.org/gruntjs/grunt-legacy-util.svg?branch=master)](https://travis-ci.org/gruntjs/grunt-legacy-util) [![Build status: Windows](https://ci.appveyor.com/api/projects/status/63a5pjh5hy0wgtx0/branch/master?svg=true)](https://ci.appveyor.com/project/gruntjs/grunt-legacy-util/branch/master) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.svg)](http://gruntjs.com/) With the next major release of Grunt, we will no longer support these APIs. Where possible, please use the recommended modules in their place. If you would like to support or improve any of these APIs, please notify us when you have published a backwards compatible npm module—we will then recommend its usage here. `grunt.util.namespace` use [getobject] `grunt.util.hooker` use [hooker] `grunt.util.async` use [async] `grunt.util._` use [lodash] `grunt.util.exit` use [exit] `grunt.util.callbackify` `grunt.util.error` `grunt.util.linefeed` `grunt.util.normalizelf` `grunt.util.kindOf` use [lodash] `grunt.util.toArray` `grunt.util.repeat` `grunt.util.pluralize` `grunt.util.recurse` use [traverse] `grunt.util.spawn` use [require('child_process').spawn] [getobject]: https://www.npmjs.org/package/getobject [hooker]: https://www.npmjs.org/package/hooker [async]: https://www.npmjs.org/package/async [lodash]: https://www.npmjs.org/package/lodash [exit]: https://www.npmjs.org/package/exit [traverse]: https://www.npmjs.org/package/traverse [require('child_process').spawn]: https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options grunt-legacy-util-1.1.1/appveyor.yml000066400000000000000000000013501330063065100174520ustar00rootroot00000000000000# Fix line endings on Windows init: - git config --global core.autocrlf true # What combinations to test environment: matrix: - nodejs_version: "6" - nodejs_version: "8" platform: - x86 - x64 install: - ps: Install-Product node $env:nodejs_version - 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 grunt-legacy-util-1.1.1/index.js000066400000000000000000000143631330063065100165370ustar00rootroot00000000000000/* * grunt * http://gruntjs.com/ * * Copyright (c) 2016 "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 = {}; util.namespace = require('getobject'); // External libs. util.hooker = require('hooker'); util.async = require('async'); // Dont pollute other lodash: https://github.com/gruntjs/grunt-legacy-util/issues/17 var _ = util._ = require('lodash').runInContext(); var which = require('which').sync; // Instead of process.exit. See https://github.com/cowboy/node-exit util.exit = require('exit'); // 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 = _.toArray; // 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(value, fn, fnContinue) { function recurse(value, fn, fnContinue, state) { var error; if (state.objs.indexOf(value) !== -1) { error = new Error('Circular reference detected (' + state.path + ')'); error.path = state.path; throw error; } var obj, key; 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(item, index) { return recurse(item, fn, fnContinue, { objs: state.objs.concat([value]), path: state.path + '[' + index + ']', }); }); } else if (util.kindOf(value) === 'object' && !Buffer.isBuffer(value)) { // If value is an object, recurse. obj = {}; for (key in value) { obj[key] = recurse(value[key], fn, fnContinue, { objs: state.objs.concat([value]), path: state.path + (/\W/.test(key) ? '["' + key + '"]' : '.' + key), }); } return obj; } else { // Otherwise pass value into fn and return. return fn(value); } } return recurse(value, fn, fnContinue, {objs: [], path: ''}); }; // 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.execPath; args = process.execArgv.concat(process.argv[1], 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; }; grunt-legacy-util-1.1.1/package.json000066400000000000000000000017611330063065100173560ustar00rootroot00000000000000{ "name": "grunt-legacy-util", "description": "Some old grunt utils provided for backwards compatibility.", "version": "1.1.1", "author": "\"Cowboy\" Ben Alman (http://benalman.com/)", "homepage": "http://gruntjs.com/", "repository": { "type": "git", "url": "git://github.com/gruntjs/grunt-legacy-util.git" }, "bugs": { "url": "http://github.com/gruntjs/grunt-legacy-util/issues" }, "license": "MIT", "main": "index.js", "scripts": { "test": "grunt test" }, "engines": { "node": ">= 6" }, "keywords": [ "grunt", "legacy" ], "dependencies": { "async": "~1.5.2", "exit": "~0.1.1", "getobject": "~0.1.0", "hooker": "~0.2.3", "lodash": "~4.17.10", "underscore.string": "~3.3.4", "which": "~1.3.0" }, "devDependencies": { "grunt": "^1.0.2", "grunt-cli": "^1.2.0", "grunt-contrib-jshint": "^1.0.0", "grunt-contrib-nodeunit": "^2.0.0", "grunt-contrib-watch": "^1.0.0", "temporary": "0.0.8" } } grunt-legacy-util-1.1.1/test/000077500000000000000000000000001330063065100160425ustar00rootroot00000000000000grunt-legacy-util-1.1.1/test/fixtures/000077500000000000000000000000001330063065100177135ustar00rootroot00000000000000grunt-legacy-util-1.1.1/test/fixtures/Gruntfile-execArgv-child.js000066400000000000000000000002311330063065100250270ustar00rootroot00000000000000module.exports = function(grunt) { grunt.registerTask('default', function(text) { console.log('OUTPUT: ' + process.execArgv.join(' ')); }); }; grunt-legacy-util-1.1.1/test/fixtures/Gruntfile-execArgv.js000066400000000000000000000006411330063065100237530ustar00rootroot00000000000000module.exports = function(grunt) { var util = require('../../'); grunt.registerTask('default', function(text) { var done = this.async(); util.spawn({ grunt: true, args: ['--gruntfile', 'Gruntfile-execArgv-child.js'], }, function(err, result, code) { var matches = result.stdout.match(/^(OUTPUT: .*)/m); console.log(matches ? matches[1] : ''); done(); }); }); }; grunt-legacy-util-1.1.1/test/fixtures/Gruntfile-print-text.js000066400000000000000000000003011330063065100243160ustar00rootroot00000000000000module.exports = function(grunt) { grunt.registerTask('print', 'Print the specified text.', function(text) { console.log('OUTPUT: ' + text); // console.log(process.cwd()); }); }; grunt-legacy-util-1.1.1/test/fixtures/exec.cmd000066400000000000000000000000131330063065100213160ustar00rootroot00000000000000@echo done grunt-legacy-util-1.1.1/test/fixtures/exec.sh000077500000000000000000000000301330063065100211670ustar00rootroot00000000000000#!/bin/bash echo "done" grunt-legacy-util-1.1.1/test/fixtures/spawn-multibyte.js000066400000000000000000000012051330063065100234130ustar00rootroot00000000000000// This is a test fixture for a case where spawn receives incomplete // multibyte strings in separate data events. // A multibyte buffer containing all our output. We will slice it later. // In this case we are using a Japanese word for hello / good day, where each // character takes three bytes. var fullOutput = new Buffer('こんにちは'); // Output one full character and one third of a character process.stdout.write(fullOutput.slice(0, 4)); // Output the rest of the string process.stdout.write(fullOutput.slice(4)); // Do the same for stderr process.stderr.write(fullOutput.slice(0, 4)); process.stderr.write(fullOutput.slice(4)); grunt-legacy-util-1.1.1/test/fixtures/spawn.js000066400000000000000000000003061330063065100214000ustar00rootroot00000000000000 var code = Number(process.argv[2]); process.stdout.write('stdout\n'); process.stderr.write('stderr\n'); // Instead of process.exit. See https://github.com/cowboy/node-exit require('exit')(code); grunt-legacy-util-1.1.1/test/index.js000066400000000000000000000422011330063065100175060ustar00rootroot00000000000000'use strict'; var util = require('../'); 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; } 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); } 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); } 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 = 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 = 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 = 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(util.linefeed, '\r\n', 'linefeed should be operating-system appropriate.'); } else { test.equal(util.linefeed, '\n', 'linefeed should be operating-system appropriate.'); } test.done(); }, 'normalizelf': function(test) { test.expect(1); if (process.platform === 'win32') { test.equal(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(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); 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); 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); 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); 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); 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); 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'; 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'; 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); 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); 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 passes execArgv': function(test) { test.expect(3); util.spawn({ cmd: process.execPath, args: [ '--harmony', process.argv[1], '--gruntfile', 'test/fixtures/Gruntfile-execArgv.js'], }, function(err, result, code) { test.equals(err, null); test.equals(code, 0); test.ok(/^OUTPUT: --harmony/m.test(result.stdout), 'stdout should contain passed-through process.execArgv.'); test.done(); }); }, 'grunt result.toString() with error': function(test) { // grunt.log.error uses standard out, to be fixed in 0.5. test.expect(4); 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 = 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); 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(util._.trim(' foo '), 'foo', 'Should have trimmed the string.'); test.equals(util._.capitalize('foo'), 'Foo', 'Should have capitalized the first letter.'); test.equals(util._.words('one two three').length, 3, 'Should have counted three words.'); test.ok(util._.isBlank(' '), 'Should be blank.'); test.done(); }; function getType(val) { if (Buffer.isBuffer(val)) { return 'buffer'; } return Object.prototype.toString.call(val).slice(8, -1).toLowerCase(); } exports['util.recurse'] = { setUp: function(done) { this.typeValue = function(value) { return { value: value, type: getType(value), }; }; done(); }, 'primitives': function(test) { test.expect(1); var actual = util.recurse({ bool: true, num: 1, str: 'foo', nul: null, undef: undefined, }, this.typeValue); var expected = { bool: {type: 'boolean', value: true}, num: {type: 'number', value: 1}, str: {type: 'string', value: 'foo'}, nul: {type: 'null', value: null}, undef: {type: 'undefined', value: undefined}, }; test.deepEqual(actual, expected, 'Should process primitive values.'); test.done(); }, 'array': function(test) { test.expect(1); var actual = util.recurse({ arr: [ true, 1, 'foo', null, undefined, [ true, 1, 'foo', null, undefined, ], ], }, this.typeValue); var expected = { arr: [ {type: 'boolean', value: true}, {type: 'number', value: 1}, {type: 'string', value: 'foo'}, {type: 'null', value: null}, {type: 'undefined', value: undefined}, [ {type: 'boolean', value: true}, {type: 'number', value: 1}, {type: 'string', value: 'foo'}, {type: 'null', value: null}, {type: 'undefined', value: undefined}, ], ], }; test.deepEqual(actual, expected, 'Should recurse over arrays.'); test.done(); }, 'object': function(test) { test.expect(1); var actual = util.recurse({ obj: { bool: true, num: 1, str: 'foo', nul: null, undef: undefined, obj: { bool: true, num: 1, str: 'foo', nul: null, undef: undefined, }, }, }, this.typeValue); var expected = { obj: { bool: {type: 'boolean', value: true}, num: {type: 'number', value: 1}, str: {type: 'string', value: 'foo'}, nul: {type: 'null', value: null}, undef: {type: 'undefined', value: undefined}, obj: { bool: {type: 'boolean', value: true}, num: {type: 'number', value: 1}, str: {type: 'string', value: 'foo'}, nul: {type: 'null', value: null}, undef: {type: 'undefined', value: undefined}, }, }, }; test.deepEqual(actual, expected, 'Should recurse over objects.'); test.done(); }, 'array in object': function(test) { test.expect(1); var actual = util.recurse({ obj: { arr: [ true, 1, 'foo', null, undefined, ], }, }, this.typeValue); var expected = { obj: { arr: [ {type: 'boolean', value: true}, {type: 'number', value: 1}, {type: 'string', value: 'foo'}, {type: 'null', value: null}, {type: 'undefined', value: undefined}, ], }, }; test.deepEqual(actual, expected, 'Should recurse over arrays in objects.'); test.done(); }, 'object in array': function(test) { test.expect(1); var actual = util.recurse({ arr: [ true, { num: 1, str: 'foo', }, null, undefined, ], }, this.typeValue); var expected = { arr: [ {type: 'boolean', value: true}, { num: {type: 'number', value: 1}, str: {type: 'string', value: 'foo'}, }, {type: 'null', value: null}, {type: 'undefined', value: undefined}, ], }; test.deepEqual(actual, expected, 'Should recurse over objects in arrays.'); test.done(); }, 'buffer': function(test) { test.expect(1); var actual = util.recurse({ buf: new Buffer('buf'), }, this.typeValue); var expected = { buf: {type: 'buffer', value: new Buffer('buf')}, }; test.deepEqual(actual, expected, 'Should not mangle Buffer instances.'); test.done(); }, 'inherited properties': function(test) { test.expect(1); var actual = util.recurse({ obj: Object.create({num: 1}, { str: {value: 'foo', enumerable: true}, ignored: {value: 'ignored', enumerable: false}, }), }, this.typeValue); var expected = { obj: { num: {type: 'number', value: 1}, str: {type: 'string', value: 'foo'}, } }; test.deepEqual(actual, expected, 'Should enumerate inherited object properties.'); test.done(); }, 'circular references': function(test) { test.expect(6); function assertErrorWithPath(expectedPath) { return function(actual) { return actual.path === expectedPath && actual.message === 'Circular reference detected (' + expectedPath + ')'; }; } test.doesNotThrow(function() { var obj = { // wat a:[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]], // does b:[[[[],[[[],[[[[],[[[],[[[],[[[],[[[],[[[[],[[]]]]]]]]]]]]]]]]]]]]], // it c:{d:{e:{f:{g:{h:{i:{j:{k:{l:{m:{n:{o:{p:{q:{r:{s:{}}}}}}}}}}}}}}}}}, // mean t:[{u:[{v:[[[[],[[[],[[[{w:[{x:[[[],[[[{y:[[1]]}]]]]]}]}]]]]]]]]}]}], }; util.recurse(obj, function(v) { return v; }); }, 'Should not throw when no circular reference is detected.'); test.throws(function() { var obj = {a: 1, b: 2}; obj.obj = obj; util.recurse(obj, function(v) { return v; }); }, assertErrorWithPath('.obj'), 'Should throw when a circular reference is detected.'); test.throws(function() { var obj = {a:{'b b':{'c-c':{d_d:{e:{f:{g:{h:{i:{j:{k:{l:{}}}}}}}}}}}}}; obj.a['b b']['c-c'].d_d.e.f.g.h.i.j.k.l.obj = obj; util.recurse(obj, function(v) { return v; }); }, assertErrorWithPath('.a["b b"]["c-c"].d_d.e.f.g.h.i.j.k.l.obj'), 'Should throw when a circular reference is detected.'); test.throws(function() { var obj = {a: 1, b: 2}; obj.arr = [1, 2, obj, 3, 4]; util.recurse(obj, function(v) { return v; }); }, assertErrorWithPath('.arr[2]'), 'Should throw when a circular reference is detected.'); test.throws(function() { var obj = {a: 1, b: 2}; obj.arr = [{a:[1,{b:[2,{c:[3,obj,4]},5]},6]},7]; util.recurse(obj, function(v) { return v; }); }, assertErrorWithPath('.arr[0].a[1].b[1].c[1]'), 'Should throw when a circular reference is detected.'); test.throws(function() { var obj = {a: 1, b: 2}; obj.arr = []; obj.arr.push(0,{a:[1,{b:[2,{c:[3,obj.arr,4]},5]},6]},7); util.recurse(obj, function(v) { return v; }); }, assertErrorWithPath('.arr[1].a[1].b[1].c[1]'), 'Should throw when a circular reference is detected.'); test.done(); }, };