Unverified Commit bb9b5a91 authored by Jeremy Morrell's avatar Jeremy Morrell Committed by GitHub

Install devDependencies by default (#519)

Fixes #431 

Many users getting started with Heroku try to run a build step with webpack, gulp, etc only to find that it's in their devDependencies and not installed by default on Heroku. They then have to figure out how to tell Heroku to install these. Defaulting to installing everything makes for a smoother onboarding.

Changes the default behavior to install `devDependencies`, run the build step, and then prune the `devDependencies` once the build step is complete.
parent c9a02566
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
## Master ## Master
- Install and prune devDependencies by default (#519)
## v118 (2018-02-02) ## v118 (2018-02-02)
- Fix issue with `NODE_ENV` in CI (#513) - Fix issue with `NODE_ENV` in CI (#513)
......
...@@ -211,6 +211,17 @@ cache_build() { ...@@ -211,6 +211,17 @@ cache_build() {
header "Caching build" header "Caching build"
cache_build | output "$LOG_FILE" cache_build | output "$LOG_FILE"
prune_devdependencies() {
if $YARN; then
yarn_prune_devdependencies "$BUILD_DIR"
else
npm_prune_devdependencies "$BUILD_DIR"
fi
}
header "Pruning devDependencies"
prune_devdependencies | output "$LOG_FILE"
summarize_build() { summarize_build() {
if $NODE_VERBOSE; then if $NODE_VERBOSE; then
list_dependencies "$BUILD_DIR" list_dependencies "$BUILD_DIR"
......
...@@ -87,14 +87,34 @@ log_build_scripts() { ...@@ -87,14 +87,34 @@ log_build_scripts() {
yarn_node_modules() { yarn_node_modules() {
local build_dir=${1:-} local build_dir=${1:-}
local production=${YARN_PRODUCTION:-false}
echo "Installing node modules (yarn.lock)" echo "Installing node modules (yarn.lock)"
cd "$build_dir" cd "$build_dir"
yarn install --frozen-lockfile --ignore-engines 2>&1 yarn install --production=$production --frozen-lockfile --ignore-engines 2>&1
}
yarn_prune_devdependencies() {
local build_dir=${1:-}
if [ "$NODE_ENV" == "test" ]; then
echo "Skipping because NODE_ENV is 'test'"
return 0
elif [ "$NODE_ENV" != "production" ]; then
echo "Skipping because NODE_ENV is not 'production'"
return 0
elif [ -n "$YARN_PRODUCTION" ] && [ "$YARN_PRODUCTION" != "true" ]; then
echo "Skipping because YARN_PRODUCTION is not 'true'"
return 0
else
cd "$build_dir"
yarn install --frozen-lockfile --ignore-engines --ignore-scripts --prefer-offline 2>&1
fi
} }
npm_node_modules() { npm_node_modules() {
local build_dir=${1:-} local build_dir=${1:-}
local production=${NPM_CONFIG_PRODUCTION:-false}
if [ -e $build_dir/package.json ]; then if [ -e $build_dir/package.json ]; then
cd $build_dir cd $build_dir
...@@ -106,7 +126,7 @@ npm_node_modules() { ...@@ -106,7 +126,7 @@ npm_node_modules() {
else else
echo "Installing node modules (package.json)" echo "Installing node modules (package.json)"
fi fi
npm install --unsafe-perm --userconfig $build_dir/.npmrc 2>&1 npm install --production=$production --unsafe-perm --userconfig $build_dir/.npmrc 2>&1
else else
echo "Skipping (no package.json)" echo "Skipping (no package.json)"
fi fi
...@@ -114,6 +134,7 @@ npm_node_modules() { ...@@ -114,6 +134,7 @@ npm_node_modules() {
npm_rebuild() { npm_rebuild() {
local build_dir=${1:-} local build_dir=${1:-}
local production=${NPM_CONFIG_PRODUCTION:-false}
if [ -e $build_dir/package.json ]; then if [ -e $build_dir/package.json ]; then
cd $build_dir cd $build_dir
...@@ -124,8 +145,32 @@ npm_rebuild() { ...@@ -124,8 +145,32 @@ npm_rebuild() {
else else
echo "Installing any new modules (package.json)" echo "Installing any new modules (package.json)"
fi fi
npm install --unsafe-perm --userconfig $build_dir/.npmrc 2>&1 npm install --production=$production --unsafe-perm --userconfig $build_dir/.npmrc 2>&1
else else
echo "Skipping (no package.json)" echo "Skipping (no package.json)"
fi fi
} }
npm_prune_devdependencies() {
local build_dir=${1:-}
local npm_version=$(npm --version)
if [ $NODE_ENV == "test" ]; then
echo "Skipping because NODE_ENV is 'test'"
return 0
elif [ $NODE_ENV != "production" ]; then
echo "Skipping because NODE_ENV is not 'production'"
return 0
elif [ -n "$NPM_CONFIG_PRODUCTION" ] && [ "$NPM_CONFIG_PRODUCTION" != "true" ]; then
echo "Skipping because NPM_CONFIG_PRODUCTION is not 'true'"
return 0
elif [ $npm_version == "5.3.0" ]; then
mcount "skip-prune-issue-npm-5.3.0"
echo "Skipping because npm 5.3.0 fails when running 'npm prune' due to a known issue"
echo "https://github.com/npm/npm/issues/17781"
return 0
else
cd "$build_dir"
npm prune --userconfig $build_dir/.npmrc 2>&1
fi
}
...@@ -16,7 +16,6 @@ platform="$os-$cpu" ...@@ -16,7 +16,6 @@ platform="$os-$cpu"
export JQ="$BP_DIR/vendor/jq-$os" export JQ="$BP_DIR/vendor/jq-$os"
create_default_env() { create_default_env() {
export NPM_CONFIG_PRODUCTION=${NPM_CONFIG_PRODUCTION:-true}
export NPM_CONFIG_LOGLEVEL=${NPM_CONFIG_LOGLEVEL:-error} export NPM_CONFIG_LOGLEVEL=${NPM_CONFIG_LOGLEVEL:-error}
export NODE_MODULES_CACHE=${NODE_MODULES_CACHE:-true} export NODE_MODULES_CACHE=${NODE_MODULES_CACHE:-true}
export NODE_ENV=${NODE_ENV:-production} export NODE_ENV=${NODE_ENV:-production}
...@@ -68,7 +67,7 @@ write_ci_profile() { ...@@ -68,7 +67,7 @@ write_ci_profile() {
local bp_dir="$1" local bp_dir="$1"
local build_dir="$2" local build_dir="$2"
write_profile "$1" "$2" write_profile "$1" "$2"
cp $bp_dir/ci-profile/* $build_dir/.profile.d/ cp $bp_dir/ci-profile/* $build_dir/.profile.d/
} }
write_export() { write_export() {
......
...@@ -122,8 +122,7 @@ fail_yarn_outdated() { ...@@ -122,8 +122,7 @@ fail_yarn_outdated() {
local log_file="$1" local log_file="$1"
local yarn_engine=$(read_json "$BUILD_DIR/package.json" ".engines.yarn") local yarn_engine=$(read_json "$BUILD_DIR/package.json" ".engines.yarn")
if grep -qi 'error: unknown option .--frozen-lockfile' "$log_file"; then if grep -qi 'error .install. has been replaced with .add. to add new dependencies' "$log_file"; then
echo "ran"
mcount "failures.outdated-yarn" mcount "failures.outdated-yarn"
echo "" echo ""
warn "Outdated Yarn version: $yarn_engine warn "Outdated Yarn version: $yarn_engine
......
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository": {
"type": "git",
"url": "http://github.com/example/example.git"
},
"dependencies": {
"hashish": "*"
},
"license": "MIT",
"engines": {
"yarn": "1.4.0"
},
"devDependencies": {
"lodash": "^2.4.1"
},
"scripts": {
"test": "ls node_modules"
}
}
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
hashish@*:
version "0.0.4"
resolved "https://registry.yarnpkg.com/hashish/-/hashish-0.0.4.tgz#6d60bc6ffaf711b6afd60e426d077988014e6554"
dependencies:
traverse ">=0.2.4"
lodash@^2.4.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e"
traverse@>=0.2.4:
version "0.6.6"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
...@@ -10,9 +10,12 @@ ...@@ -10,9 +10,12 @@
"hashish": "*" "hashish": "*"
}, },
"engines": { "engines": {
"node": "~0.10.0" "node": "8.x"
}, },
"devDependencies": { "devDependencies": {
"lodash": "^2.4.1" "lodash": "^2.4.1"
},
"scripts": {
"test": "ls node_modules"
} }
} }
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
Hashish
=======
Hashish is a node.js library for manipulating hash data structures.
It is distilled from the finest that ruby, perl, and haskell have to offer by
way of hash/map interfaces.
Hashish provides a chaining interface, where you can do:
var Hash = require('hashish');
Hash({ a : 1, b : 2, c : 3, d : 4 })
.map(function (x) { return x * 10 })
.filter(function (x) { return x < 30 })
.forEach(function (x, key) {
console.log(key + ' => ' + x);
})
;
Output:
a => 10
b => 20
Some functions and attributes in the chaining interface are terminal, like
`.items` or `.detect()`. They return values of their own instead of the chain
context.
Each function in the chainable interface is also attached to `Hash` in chainless
form:
var Hash = require('hashish');
var obj = { a : 1, b : 2, c : 3, d : 4 };
var mapped = Hash.map(obj, function (x) {
return x * 10
});
console.dir(mapped);
Output:
{ a: 10, b: 20, c: 30, d: 40 }
In either case, the 'this' context of the function calls is the same object that
the chained functions return, so you can make nested chains.
Methods
=======
forEach(cb)
-----------
For each key/value in the hash, calls `cb(value, key)`.
map(cb)
-------
For each key/value in the hash, calls `cb(value, key)`.
The return value of `cb` is the new value at `key` in the resulting hash.
filter(cb)
----------
For each key/value in the hash, calls `cb(value, key)`.
The resulting hash omits key/value pairs where `cb` returned a falsy value.
detect(cb)
----------
Returns the first value in the hash for which `cb(value, key)` is non-falsy.
Order of hashes is not well-defined so watch out for that.
reduce(cb)
----------
Returns the accumulated value of a left-fold over the key/value pairs.
some(cb)
--------
Returns a boolean: whether or not `cb(value, key)` ever returned a non-falsy
value.
update(obj1, [obj2, obj3, ...])
-----------
Mutate the context hash, merging the key/value pairs from the passed objects
and overwriting keys from the context hash if the current `obj` has keys of
the same name. Falsy arguments are silently ignored.
updateAll([ obj1, obj2, ... ])
------------------------------
Like multi-argument `update()` but operate on an array directly.
merge(obj1, [obj2, obj3, ...])
----------
Merge the key/value pairs from the passed objects into the resultant hash
without modifying the context hash. Falsy arguments are silently ignored.
mergeAll([ obj1, obj2, ... ])
------------------------------
Like multi-argument `merge()` but operate on an array directly.
has(key)
--------
Return whether the hash has a key, `key`.
valuesAt(keys)
--------------
Return an Array with the values at the keys from `keys`.
tap(cb)
-------
Call `cb` with the present raw hash.
This function is chainable.
extract(keys)
-------------
Filter by including only those keys in `keys` in the resulting hash.
exclude(keys)
-------------
Filter by excluding those keys in `keys` in the resulting hash.
Attributes
==========
These are attributes in the chaining interface and functions in the `Hash.xxx`
interface.
keys
----
Return all the enumerable attribute keys in the hash.
values
------
Return all the enumerable attribute values in the hash.
compact
-------
Filter out values which are `=== undefined`.
clone
-----
Make a deep copy of the hash.
copy
----
Make a shallow copy of the hash.
length
------
Return the number of key/value pairs in the hash.
Note: use `Hash.size()` for non-chain mode.
size
----
Alias for `length` since `Hash.length` is masked by `Function.prototype`.
See Also
========
See also [creationix's pattern/hash](http://github.com/creationix/pattern),
which does a similar thing except with hash inputs and array outputs.
Installation
============
To install with [npm](http://github.com/isaacs/npm):
npm install hashish
To run the tests with [expresso](http://github.com/visionmedia/expresso):
expresso
var Hash = require('hashish');
Hash({ a : 1, b : 2, c : 3, d : 4 })
.map(function (x) { return x * 10 })
.filter(function (x) { return x < 30 })
.forEach(function (x, key) {
console.log(key + ' => ' + x);
})
;
var Hash = require('hashish');
var obj = { a : 1, b : 2, c : 3, d : 4 };
var mapped = Hash.map(obj, function (x) {
return x * 10
});
console.dir(mapped);
module.exports = Hash;
var Traverse = require('traverse');
function Hash (hash, xs) {
if (Array.isArray(hash) && Array.isArray(xs)) {
var to = Math.min(hash.length, xs.length);
var acc = {};
for (var i = 0; i < to; i++) {
acc[hash[i]] = xs[i];
}
return Hash(acc);
}
if (hash === undefined) return Hash({});
var self = {
map : function (f) {
var acc = { __proto__ : hash.__proto__ };
Object.keys(hash).forEach(function (key) {
acc[key] = f.call(self, hash[key], key);
});
return Hash(acc);
},
forEach : function (f) {
Object.keys(hash).forEach(function (key) {
f.call(self, hash[key], key);
});
return self;
},
filter : function (f) {
var acc = { __proto__ : hash.__proto__ };
Object.keys(hash).forEach(function (key) {
if (f.call(self, hash[key], key)) {
acc[key] = hash[key];
}
});
return Hash(acc);
},
detect : function (f) {
for (var key in hash) {
if (f.call(self, hash[key], key)) {
return hash[key];
}
}
return undefined;
},
reduce : function (f, acc) {
var keys = Object.keys(hash);
if (acc === undefined) acc = keys.shift();
keys.forEach(function (key) {
acc = f.call(self, acc, hash[key], key);
});
return acc;
},
some : function (f) {
for (var key in hash) {
if (f.call(self, hash[key], key)) return true;
}
return false;
},
update : function (obj) {
if (arguments.length > 1) {
self.updateAll([].slice.call(arguments));
}
else {
Object.keys(obj).forEach(function (key) {
hash[key] = obj[key];
});
}
return self;
},
updateAll : function (xs) {
xs.filter(Boolean).forEach(function (x) {
self.update(x);
});
return self;
},
merge : function (obj) {
if (arguments.length > 1) {
return self.copy.updateAll([].slice.call(arguments));
}
else {
return self.copy.update(obj);
}
},
mergeAll : function (xs) {
return self.copy.updateAll(xs);
},
has : function (key) { // only operates on enumerables
return Array.isArray(key)
? key.every(function (k) { return self.has(k) })
: self.keys.indexOf(key.toString()) >= 0;
},
valuesAt : function (keys) {
return Array.isArray(keys)
? keys.map(function (key) { return hash[key] })
: hash[keys]
;
},
tap : function (f) {
f.call(self, hash);
return self;
},
extract : function (keys) {
var acc = {};
keys.forEach(function (key) {
acc[key] = hash[key];
});
return Hash(acc);
},
exclude : function (keys) {
return self.filter(function (_, key) {
return keys.indexOf(key) < 0
});
},
end : hash,
items : hash
};
var props = {
keys : function () { return Object.keys(hash) },
values : function () {
return Object.keys(hash).map(function (key) { return hash[key] });
},
compact : function () {
return self.filter(function (x) { return x !== undefined });
},
clone : function () { return Hash(Hash.clone(hash)) },
copy : function () { return Hash(Hash.copy(hash)) },
length : function () { return Object.keys(hash).length },
size : function () { return self.length }
};
if (Object.defineProperty) {
// es5-shim has an Object.defineProperty but it throws for getters
try {
for (var key in props) {
Object.defineProperty(self, key, { get : props[key] });
}
}
catch (err) {
for (var key in props) {
if (key !== 'clone' && key !== 'copy' && key !== 'compact') {
// ^ those keys use Hash() so can't call them without
// a stack overflow
self[key] = props[key]();
}
}
}
}
else if (self.__defineGetter__) {
for (var key in props) {
self.__defineGetter__(key, props[key]);
}
}
else {
// non-lazy version for browsers that suck >_<
for (var key in props) {
self[key] = props[key]();
}
}
return self;
};
// deep copy
Hash.clone = function (ref) {
return Traverse.clone(ref);
};
// shallow copy
Hash.copy = function (ref) {
var hash = { __proto__ : ref.__proto__ };
Object.keys(ref).forEach(function (key) {
hash[key] = ref[key];
});
return hash;
};
Hash.map = function (ref, f) {
return Hash(ref).map(f).items;
};
Hash.forEach = function (ref, f) {
Hash(ref).forEach(f);
};
Hash.filter = function (ref, f) {
return Hash(ref).filter(f).items;
};
Hash.detect = function (ref, f) {
return Hash(ref).detect(f);
};
Hash.reduce = function (ref, f, acc) {
return Hash(ref).reduce(f, acc);
};
Hash.some = function (ref, f) {
return Hash(ref).some(f);
};
Hash.update = function (a /*, b, c, ... */) {
var args = Array.prototype.slice.call(arguments, 1);
var hash = Hash(a);
return hash.update.apply(hash, args).items;
};
Hash.merge = function (a /*, b, c, ... */) {
var args = Array.prototype.slice.call(arguments, 1);
var hash = Hash(a);
return hash.merge.apply(hash, args).items;
};
Hash.has = function (ref, key) {
return Hash(ref).has(key);
};
Hash.valuesAt = function (ref, keys) {
return Hash(ref).valuesAt(keys);
};
Hash.tap = function (ref, f) {
return Hash(ref).tap(f).items;
};
Hash.extract = function (ref, keys) {
return Hash(ref).extract(keys).items;
};
Hash.exclude = function (ref, keys) {
return Hash(ref).exclude(keys).items;
};
Hash.concat = function (xs) {
var hash = Hash({});
xs.forEach(function (x) { hash.update(x) });
return hash.items;
};
Hash.zip = function (xs, ys) {
return Hash(xs, ys).items;
};
// .length is already defined for function prototypes
Hash.size = function (ref) {
return Hash(ref).size;
};
Hash.compact = function (ref) {
return Hash(ref).compact.items;
};
{
"_from": "hashish@*",
"_id": "hashish@0.0.4",
"_inBundle": false,
"_integrity": "sha1-bWC8b/r3Ebav1g5CbQd5iAFOZVQ=",
"_location": "/hashish",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "hashish@*",
"name": "hashish",
"escapedName": "hashish",
"rawSpec": "*",
"saveSpec": null,
"fetchSpec": "*"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz",
"_shasum": "6d60bc6ffaf711b6afd60e426d077988014e6554",
"_spec": "hashish@*",
"_where": "/Users/jmorrell/workspace/heroku-buildpack-nodejs/test/fixtures/dependencies-modules-checked-in-with-devdependencies",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"bugs": {
"url": "https://github.com/substack/node-hashish/issues"
},
"bundleDependencies": false,
"dependencies": {
"traverse": ">=0.2.4"
},
"deprecated": false,
"description": "Hash data structure manipulation functions",
"devDependencies": {
"expresso": ">=0.6.0"
},
"engine": [
"node >=0.2.0"
],
"engines": {
"node": "*"
},
"homepage": "https://github.com/substack/node-hashish#readme",
"keywords": [
"hash",
"object",
"convenience",
"manipulation",
"data structure"
],
"license": "MIT/X11",
"main": "./index.js",
"name": "hashish",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/substack/node-hashish.git"
},
"scripts": {
"test": "expresso"
},
"version": "0.0.4"
}
var Hash = require('hashish');
var assert = require('assert');
exports.map = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).map(function (v) { return v + 1 }).items;
var hash = Hash.map(ref, function (v) { return v + 1 });
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 2, b : 3 });
assert.deepEqual(hash, { a : 2, b : 3 });
};
exports['cloned map'] = function () {
var ref = { foo : [1,2], bar : [4,5] };
var hash = Hash(ref).clone.map(
function (v) { v.unshift(v[0] - 1); return v }
).items;
assert.deepEqual(ref.foo, [1,2]);
assert.deepEqual(ref.bar, [4,5]);
assert.deepEqual(hash.foo, [0,1,2]);
assert.deepEqual(hash.bar, [3,4,5]);
};
exports.forEach = function () {
var ref = { a : 5, b : 2, c : 7, 1337 : 'leet' };
var xs = [];
Hash(ref).forEach(function (x, i) {
xs.push([ i, x ]);
});
assert.eql(
xs.map(function (x) { return x[0] }).sort(),
[ '1337', 'a', 'b', 'c' ]
);
assert.eql(
xs.map(function (x) { return x[1] }).sort(),
[ 2, 5, 7, 'leet' ]
);
var ys = [];
Hash.forEach(ref, function (x, i) {
ys.push([ i, x ]);
});
assert.eql(xs.sort(), ys.sort());
};
exports.filter_items = function () {
var ref = { a : 5, b : 2, c : 7, 1337 : 'leet' };
var items = Hash(ref).filter(function (v, k) {
return v > 5 || k > 5
}).items;
var hash = Hash.filter(ref, function (v, k) { return v > 5 || k > 5 });
assert.deepEqual(items, { 1337 : 'leet', c : 7 });
assert.deepEqual(hash, { 1337 : 'leet', c : 7 });
assert.deepEqual(ref, { a : 5, b : 2, c : 7, 1337 : 'leet' });
assert.equal(Hash(ref).length, 4);
};
exports.detect = function () {
var h = { a : 5, b : 6, c : 7, d : 8 };
var hh = Hash(h);
var gt6hh = hh.detect(function (x) { return x > 6 });
assert.ok(gt6hh == 7 || gt6hh == 8);
var gt6h = Hash.detect(h, function (x) { return x > 6 });
assert.ok(gt6h == 7 || gt6h == 8);
assert.equal(hh.detect(function (x) { return x > 100 }), undefined);
};
exports.reduce = function () {
var ref = { foo : [1,2], bar : [4,5] };
var sum1 = Hash(ref).reduce(function (acc, v) {
return acc + v.length
}, 0);
assert.equal(sum1, 4);
var sum2 = Hash.reduce(ref, function (acc, v) {
return acc + v.length
}, 0);
assert.equal(sum2, 4);
};
exports.some = function () {
var h = { a : 5, b : 6, c : 7, d : 8 };
var hh = Hash(h);
assert.ok(Hash.some(h, function (x) { return x > 7 }));
assert.ok(Hash.some(h, function (x) { return x < 6 }));
assert.ok(!Hash.some(h, function (x) { return x > 10 }));
assert.ok(!Hash.some(h, function (x) { return x < 0 }));
assert.ok(hh.some(function (x) { return x > 7 }));
assert.ok(hh.some(function (x) { return x < 6 }));
assert.ok(!hh.some(function (x) { return x > 10 }));
assert.ok(!hh.some(function (x) { return x < 0 }));
};
exports.update = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).clone.update({ c : 3, a : 0 }).items;
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 0, b : 2, c : 3 });
var hash = Hash.update(ref, { c : 3, a : 0 });
assert.deepEqual(ref, hash);
assert.deepEqual(hash, { a : 0, b : 2, c : 3 });
var ref2 = {a: 1};
var hash2 = Hash.update(ref2, { b: 2, c: 3 }, undefined, { d: 4 });
assert.deepEqual(ref2, { a: 1, b: 2, c: 3, d: 4 });
};
exports.merge = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).merge({ b : 3, c : 3.14 }).items;
var hash = Hash.merge(ref, { b : 3, c : 3.14 });
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 1, b : 3, c : 3.14 });
assert.deepEqual(hash, { a : 1, b : 3, c : 3.14 });
var ref2 = { a : 1 };
var hash2 = Hash.merge(ref, { b: 2, c: 3 }, undefined, { d: 4 });
assert.deepEqual(hash2, { a: 1, b: 2, c: 3, d: 4 });
};
exports.has = function () {
var h = { a : 4, b : 5 };
var hh = Hash(h);
assert.ok(hh.has('a'));
assert.equal(hh.has('c'), false);
assert.ok(hh.has(['a','b']));
assert.equal(hh.has(['a','b','c']), false);
assert.ok(Hash.has(h, 'a'));
assert.equal(Hash.has(h, 'c'), false);
assert.ok(Hash.has(h, ['a','b']));
assert.equal(Hash.has(h, ['a','b','c']), false);
};
exports.valuesAt = function () {
var h = { a : 4, b : 5, c : 6 };
assert.equal(Hash(h).valuesAt('a'), 4);
assert.equal(Hash(h).valuesAt(['a'])[0], 4);
assert.deepEqual(Hash(h).valuesAt(['a','b']), [4,5]);
assert.equal(Hash.valuesAt(h, 'a'), 4);
assert.deepEqual(Hash.valuesAt(h, ['a']), [4]);
assert.deepEqual(Hash.valuesAt(h, ['a','b']), [4,5]);
};
exports.tap = function () {
var h = { a : 4, b : 5, c : 6 };
var hh = Hash(h);
hh.tap(function (x) {
assert.ok(this === hh)
assert.eql(x, h);
});
Hash.tap(h, function (x) {
assert.eql(
Object.keys(this).sort(),
Object.keys(hh).sort()
);
assert.eql(x, h);
});
};
exports.extract = function () {
var hash = Hash({ a : 1, b : 2, c : 3 }).clone;
var extracted = hash.extract(['a','b']);
assert.equal(extracted.length, 2);
assert.deepEqual(extracted.items, { a : 1, b : 2 });
};
exports.exclude = function () {
var hash = Hash({ a : 1, b : 2, c : 3 }).clone;
var extracted = hash.exclude(['a','b']);
assert.equal(extracted.length, 1);
assert.deepEqual(extracted.items, { c : 3 });
};
exports.concat = function () {
var ref1 = { a : 1, b : 2 };
var ref2 = { foo : 100, bar : 200 };
var ref3 = { b : 3, c : 4, bar : 300 };
assert.deepEqual(
Hash.concat([ ref1, ref2 ]),
{ a : 1, b : 2, foo : 100, bar : 200 }
);
assert.deepEqual(
Hash.concat([ ref1, ref2, ref3 ]),
{ a : 1, b : 3, c : 4, foo : 100, bar : 300 }
);
};
exports.zip = function () {
var xs = ['a','b','c'];
var ys = [1,2,3,4];
var h = Hash(xs,ys);
assert.equal(h.length, 3);
assert.deepEqual(h.items, { a : 1, b : 2, c : 3 });
var zipped = Hash.zip(xs,ys);
assert.deepEqual(zipped, { a : 1, b : 2, c : 3 });
};
exports.length = function () {
assert.equal(Hash({ a : 1, b : [2,3], c : 4 }).length, 3);
assert.equal(Hash({ a : 1, b : [2,3], c : 4 }).size, 3);
assert.equal(Hash.size({ a : 1, b : [2,3], c : 4 }), 3);
};
exports.compact = function () {
var hash = {
a : 1,
b : undefined,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
};
var compacted = Hash(hash).compact;
assert.deepEqual(
{
a : 1,
b : undefined,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
},
hash, 'compact modified the hash'
);
assert.deepEqual(
compacted.items,
{
a : 1,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
}
);
var h = Hash.compact(hash);
assert.deepEqual(h, compacted.items);
};
var Hash = require('hashish');
var assert = require('assert');
var vm = require('vm');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/../index.js', 'utf8');
exports.defineGetter = function () {
var context = {
module : { exports : {} },
Object : {
keys : Object.keys,
defineProperty : undefined,
},
require : require,
};
context.exports = context.module.exports;
vm.runInNewContext('(function () {' + src + '})()', context);
var Hash_ = context.module.exports;
var times = 0;
Hash_.__proto__.__proto__.__defineGetter__ = function () {
times ++;
return Object.__defineGetter__.apply(this, arguments);
};
assert.equal(vm.runInNewContext('Object.defineProperty', context), null);
assert.deepEqual(
Hash_({ a : 1, b : 2, c : 3 }).values,
[ 1, 2, 3 ]
);
assert.ok(times > 5);
};
exports.defineProperty = function () {
var times = 0;
var context = {
module : { exports : {} },
Object : {
keys : Object.keys,
defineProperty : function (prop) {
times ++;
if (prop.get) throw new TypeError('engine does not support')
assert.fail('should have asserted by now');
},
},
require : require
};
context.exports = context.module.exports;
vm.runInNewContext('(function () {' + src + '})()', context);
var Hash_ = context.module.exports;
Hash_.__proto__.__proto__.__defineGetter__ = function () {
assert.fail('getter called when a perfectly good'
+ ' defineProperty was available'
);
};
assert.deepEqual(
Hash_({ a : 1, b : 2, c : 3 }).values,
[ 1, 2, 3 ]
);
assert.equal(times, 1);
};
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
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.
\ No newline at end of file
# Lo-Dash v2.4.2
A utility library delivering consistency, [customization](https://lodash.com/custom-builds), [performance](https://lodash.com/benchmarks), & [extras](https://lodash.com/#features).
## Download
Check out our [wiki]([https://github.com/lodash/lodash/wiki/build-differences]) for details over the differences between builds.
* Modern builds perfect for newer browsers/environments:<br>
[Development](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.js) &
[Production](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.min.js)
* Compatibility builds for older environment support too:<br>
[Development](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.compat.js) &
[Production](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.compat.min.js)
* Underscore builds to use as a drop-in replacement:<br>
[Development](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.underscore.js) &
[Production](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.underscore.min.js)
CDN copies are available on [cdnjs](http://cdnjs.com/libraries/lodash.js/) & [jsDelivr](http://www.jsdelivr.com/#!lodash). For smaller file sizes, create [custom builds](https://lodash.com/custom-builds) with only the features needed.
Love modules? We’ve got you covered with [lodash-amd](https://npmjs.org/package/lodash-amd), [lodash-es6](https://github.com/lodash/lodash-es6), [lodash-node](https://npmjs.org/package/lodash-node), & [npm packages](https://npmjs.org/browse/keyword/lodash-modularized) per method.
## Dive in
There’s plenty of **[documentation](https://lodash.com/docs)**, [unit tests](https://lodash.com/tests), & [benchmarks](https://lodash.com/benchmarks).<br>
Check out <a href="http://devdocs.io/lodash/">DevDocs</a> as a fast, organized, & searchable interface for our documentation.
The full changelog for this release is available on our [wiki](https://github.com/lodash/lodash/wiki/Changelog).<br>
A list of upcoming features is available on our [roadmap](https://github.com/lodash/lodash/wiki/Roadmap).
## Installation & usage
In browsers:
```html
<script src="lodash.js"></script>
```
Using [`npm`](http://npmjs.org/):
```bash
npm i --save lodash
{sudo} npm i -g lodash
npm ln lodash
```
In [Node.js](http://nodejs.org/) & [Ringo](http://ringojs.org/):
```js
var _ = require('lodash');
// or as Underscore
var _ = require('lodash/dist/lodash.underscore');
```
**Notes:**
* Don’t assign values to [special variable](http://nodejs.org/api/repl.html#repl_repl_features) `_` when in the REPL
* If Lo-Dash is installed globally, run [`npm ln lodash`](http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation/) in your project’s root directory *before* requiring it
In [Rhino](http://www.mozilla.org/rhino/):
```js
load('lodash.js');
```
In an AMD loader:
```js
require({
'packages': [
{ 'name': 'lodash', 'location': 'path/to/lodash', 'main': 'lodash' }
]
},
['lodash'], function(_) {
console.log(_.VERSION);
});
```
## Resources
* Podcasts
- [JavaScript Jabber](http://javascriptjabber.com/079-jsj-lo-dash-with-john-david-dalton/)
* Posts
- [Say “Hello” to Lo-Dash](http://kitcambridge.be/blog/say-hello-to-lo-dash/)
- [Custom builds in Lo-Dash 2.0](http://kitcambridge.be/blog/custom-builds-in-lo-dash-2-dot-0/)
* Videos
- [Introduction](https://vimeo.com/44154599)
- [Origins](https://vimeo.com/44154600)
- [Optimizations & builds](https://vimeo.com/44154601)
- [Native method use](https://vimeo.com/48576012)
- [Testing](https://vimeo.com/45865290)
- [CascadiaJS ’12](http://www.youtube.com/watch?v=dpPy4f_SeEk)
A list of other community created podcasts, posts, & videos is available on our [wiki](https://github.com/lodash/lodash/wiki/Resources).
## Features
* AMD loader support ([curl](https://github.com/cujojs/curl), [dojo](http://dojotoolkit.org/), [requirejs](http://requirejs.org/), etc.)
* [_(…)](https://lodash.com/docs#_) supports intuitive chaining
* [_.at](https://lodash.com/docs#at) for cherry-picking collection values
* [_.bindKey](https://lodash.com/docs#bindKey) for binding [*“lazy”*](http://michaux.ca/articles/lazy-function-definition-pattern) defined methods
* [_.clone](https://lodash.com/docs#clone) supports shallow cloning of `Date` & `RegExp` objects
* [_.cloneDeep](https://lodash.com/docs#cloneDeep) for deep cloning arrays & objects
* [_.constant](https://lodash.com/docs#constant) & [_.property](https://lodash.com/docs#property) function generators for composing functions
* [_.contains](https://lodash.com/docs#contains) accepts a `fromIndex`
* [_.create](https://lodash.com/docs#create) for easier object inheritance
* [_.createCallback](https://lodash.com/docs#createCallback) for extending callbacks in methods & mixins
* [_.curry](https://lodash.com/docs#curry) for creating [curried](http://hughfdjackson.com/javascript/2013/07/06/why-curry-helps/) functions
* [_.debounce](https://lodash.com/docs#debounce) & [_.throttle](https://lodash.com/docs#throttle) accept additional `options` for more control
* [_.findIndex](https://lodash.com/docs#findIndex) & [_.findKey](https://lodash.com/docs#findKey) for finding indexes & keys
* [_.forEach](https://lodash.com/docs#forEach) is chainable & supports exiting early
* [_.forIn](https://lodash.com/docs#forIn) for iterating own & inherited properties
* [_.forOwn](https://lodash.com/docs#forOwn) for iterating own properties
* [_.isPlainObject](https://lodash.com/docs#isPlainObject) for checking if values are created by `Object`
* [_.mapValues](https://lodash.com/docs#mapValues) for [mapping](https://lodash.com/docs#map) values to an object
* [_.memoize](https://lodash.com/docs#memoize) exposes the `cache` of memoized functions
* [_.merge](https://lodash.com/docs#merge) for a deep [_.extend](https://lodash.com/docs#extend)
* [_.noop](https://lodash.com/docs#noop) for function placeholders
* [_.now](https://lodash.com/docs#now) as a cross-browser `Date.now` alternative
* [_.parseInt](https://lodash.com/docs#parseInt) for consistent behavior
* [_.pull](https://lodash.com/docs#pull) & [_.remove](https://lodash.com/docs#remove) for mutating arrays
* [_.random](https://lodash.com/docs#random) supports returning floating-point numbers
* [_.runInContext](https://lodash.com/docs#runInContext) for easier mocking
* [_.sortBy](https://lodash.com/docs#sortBy) supports sorting by multiple properties
* [_.support](https://lodash.com/docs#support) for flagging environment features
* [_.template](https://lodash.com/docs#template) supports [*“imports”*](https://lodash.com/docs#templateSettings_imports) options & [ES6 template delimiters](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals)
* [_.transform](https://lodash.com/docs#transform) as a powerful alternative to [_.reduce](https://lodash.com/docs#reduce) for transforming objects
* [_.where](https://lodash.com/docs#where) supports deep object comparisons
* [_.xor](https://lodash.com/docs#xor) as a companion to [_.difference](https://lodash.com/docs#difference), [_.intersection](https://lodash.com/docs#intersection), & [_.union](https://lodash.com/docs#union)
* [_.zip](https://lodash.com/docs#zip) is capable of unzipping values
* [_.omit](https://lodash.com/docs#omit), [_.pick](https://lodash.com/docs#pick), &
[more](https://lodash.com/docs "_.assign, _.clone, _.cloneDeep, _.first, _.initial, _.isEqual, _.last, _.merge, _.rest") accept callbacks
* [_.contains](https://lodash.com/docs#contains), [_.toArray](https://lodash.com/docs#toArray), &
[more](https://lodash.com/docs "_.at, _.countBy, _.every, _.filter, _.find, _.forEach, _.forEachRight, _.groupBy, _.invoke, _.map, _.max, _.min, _.pluck, _.reduce, _.reduceRight, _.reject, _.shuffle, _.size, _.some, _.sortBy, _.where") accept strings
* [_.filter](https://lodash.com/docs#filter), [_.map](https://lodash.com/docs#map), &
[more](https://lodash.com/docs "_.countBy, _.every, _.find, _.findKey, _.findLast, _.findLastIndex, _.findLastKey, _.first, _.groupBy, _.initial, _.last, _.max, _.min, _.reject, _.rest, _.some, _.sortBy, _.sortedIndex, _.uniq") support *“_.pluck”* & *“_.where”* shorthands
* [_.findLast](https://lodash.com/docs#findLast), [_.findLastIndex](https://lodash.com/docs#findLastIndex), &
[more](https://lodash.com/docs "_.findLastKey, _.forEachRight, _.forInRight, _.forOwnRight, _.partialRight") right-associative methods
## Support
Tested in Chrome 5~31, Firefox 2~25, IE 6-11, Opera 9.25-17, Safari 3-7, Node.js 0.6.21-0.10.22, Narwhal 0.3.2, PhantomJS 1.9.2, RingoJS 0.9, & Rhino 1.7RC5.
{
"_from": "lodash@^2.4.1",
"_id": "lodash@2.4.2",
"_inBundle": false,
"_integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
"_location": "/lodash",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "lodash@^2.4.1",
"name": "lodash",
"escapedName": "lodash",
"rawSpec": "^2.4.1",
"saveSpec": null,
"fetchSpec": "^2.4.1"
},
"_requiredBy": [
"#DEV:/"
],
"_resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
"_shasum": "fadd834b9683073da179b3eae6d9c0d15053f73e",
"_spec": "lodash@^2.4.1",
"_where": "/Users/jmorrell/workspace/heroku-buildpack-nodejs/test/fixtures/dependencies-modules-checked-in-with-devdependencies",
"author": {
"name": "John-David Dalton",
"email": "john.david.dalton@gmail.com",
"url": "http://allyoucanleet.com/"
},
"bugs": {
"url": "https://github.com/lodash/lodash/issues"
},
"bundleDependencies": false,
"contributors": [
{
"name": "John-David Dalton",
"email": "john.david.dalton@gmail.com",
"url": "http://allyoucanleet.com/"
},
{
"name": "Blaine Bublitz",
"email": "blaine@iceddev.com",
"url": "http://www.iceddev.com/"
},
{
"name": "Kit Cambridge",
"email": "github@kitcambridge.be",
"url": "http://kitcambridge.be/"
},
{
"name": "Mathias Bynens",
"email": "mathias@qiwi.be",
"url": "http://mathiasbynens.be/"
}
],
"deprecated": false,
"description": "A utility library delivering consistency, customization, performance, & extras.",
"engines": [
"node",
"rhino"
],
"files": [
"LICENSE.txt",
"lodash.js",
"dist/lodash.js",
"dist/lodash.min.js",
"dist/lodash.compat.js",
"dist/lodash.compat.min.js",
"dist/lodash.underscore.js",
"dist/lodash.underscore.min.js"
],
"homepage": "http://lodash.com/",
"jam": {
"main": "dist/lodash.compat.js",
"include": [
"LICENSE.txt",
"dist/lodash.js",
"dist/lodash.min.js",
"dist/lodash.compat.js",
"dist/lodash.compat.min.js",
"dist/lodash.underscore.js",
"dist/lodash.underscore.min.js"
]
},
"keywords": [
"amd",
"browser",
"client",
"customize",
"functional",
"server",
"util"
],
"license": "MIT",
"main": "dist/lodash.js",
"name": "lodash",
"repository": {
"type": "git",
"url": "git+https://github.com/lodash/lodash.git"
},
"version": "2.4.2",
"volo": {
"type": "directory",
"ignore": [
".*",
"*.custom.*",
"*.min.*",
"*.template.*",
"*.map",
"*.md",
"lodash.js",
"index.js",
"bower.json",
"component.json",
"doc",
"modularize",
"node_modules",
"perf",
"test",
"vendor"
]
}
}
Copyright 2010 James Halliday (mail@substack.net)
This project is free software released under the MIT/X11 license:
http://www.opensource.org/licenses/mit-license.php
Copyright 2010 James Halliday (mail@substack.net)
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.
var traverse = require('traverse');
var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
var scrubbed = traverse(obj).map(function (x) {
if (typeof x === 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
});
console.dir(scrubbed);
console.dir(callbacks);
var traverse = require('traverse');
var obj = {
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 },
};
var leaves = traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
return acc;
}, []);
console.dir(leaves);
var traverse = require('traverse');
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
traverse(obj).forEach(function (x) {
if (x < 0) this.update(x + 128);
});
console.dir(obj);
// scrub out circular references
var traverse = require('traverse');
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
obj.c.push(obj);
var scrubbed = traverse(obj).map(function (x) {
if (this.circular) this.remove()
});
console.dir(scrubbed);
#!/usr/bin/env node
var traverse = require('traverse');
var obj = [ 'five', 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
var s = '';
traverse(obj).forEach(function to_s (node) {
if (Array.isArray(node)) {
this.before(function () { s += '[' });
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += ']' });
}
else if (typeof node == 'object') {
this.before(function () { s += '{' });
this.pre(function (x, key) {
to_s(key);
s += ':';
});
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += '}' });
}
else if (typeof node == 'string') {
s += '"' + node.toString().replace(/"/g, '\\"') + '"';
}
else if (typeof node == 'function') {
s += 'null';
}
else {
s += node.toString();
}
});
console.log('JSON.stringify: ' + JSON.stringify(obj));
console.log('this stringify: ' + s);
var traverse = module.exports = function (obj) {
return new Traverse(obj);
};
function Traverse (obj) {
this.value = obj;
}
Traverse.prototype.get = function (ps) {
var node = this.value;
for (var i = 0; i < ps.length; i ++) {
var key = ps[i];
if (!node || !hasOwnProperty.call(node, key)) {
node = undefined;
break;
}
node = node[key];
}
return node;
};
Traverse.prototype.has = function (ps) {
var node = this.value;
for (var i = 0; i < ps.length; i ++) {
var key = ps[i];
if (!node || !hasOwnProperty.call(node, key)) {
return false;
}
node = node[key];
}
return true;
};
Traverse.prototype.set = function (ps, value) {
var node = this.value;
for (var i = 0; i < ps.length - 1; i ++) {
var key = ps[i];
if (!hasOwnProperty.call(node, key)) node[key] = {};
node = node[key];
}
node[ps[i]] = value;
return value;
};
Traverse.prototype.map = function (cb) {
return walk(this.value, cb, true);
};
Traverse.prototype.forEach = function (cb) {
this.value = walk(this.value, cb, false);
return this.value;
};
Traverse.prototype.reduce = function (cb, init) {
var skip = arguments.length === 1;
var acc = skip ? this.value : init;
this.forEach(function (x) {
if (!this.isRoot || !skip) {
acc = cb.call(this, acc, x);
}
});
return acc;
};
Traverse.prototype.paths = function () {
var acc = [];
this.forEach(function (x) {
acc.push(this.path);
});
return acc;
};
Traverse.prototype.nodes = function () {
var acc = [];
this.forEach(function (x) {
acc.push(this.node);
});
return acc;
};
Traverse.prototype.clone = function () {
var parents = [], nodes = [];
return (function clone (src) {
for (var i = 0; i < parents.length; i++) {
if (parents[i] === src) {
return nodes[i];
}
}
if (typeof src === 'object' && src !== null) {
var dst = copy(src);
parents.push(src);
nodes.push(dst);
forEach(objectKeys(src), function (key) {
dst[key] = clone(src[key]);
});
parents.pop();
nodes.pop();
return dst;
}
else {
return src;
}
})(this.value);
};
function walk (root, cb, immutable) {
var path = [];
var parents = [];
var alive = true;
return (function walker (node_) {
var node = immutable ? copy(node_) : node_;
var modifiers = {};
var keepGoing = true;
var state = {
node : node,
node_ : node_,
path : [].concat(path),
parent : parents[parents.length - 1],
parents : parents,
key : path.slice(-1)[0],
isRoot : path.length === 0,
level : path.length,
circular : null,
update : function (x, stopHere) {
if (!state.isRoot) {
state.parent.node[state.key] = x;
}
state.node = x;
if (stopHere) keepGoing = false;
},
'delete' : function (stopHere) {
delete state.parent.node[state.key];
if (stopHere) keepGoing = false;
},
remove : function (stopHere) {
if (isArray(state.parent.node)) {
state.parent.node.splice(state.key, 1);
}
else {
delete state.parent.node[state.key];
}
if (stopHere) keepGoing = false;
},
keys : null,
before : function (f) { modifiers.before = f },
after : function (f) { modifiers.after = f },
pre : function (f) { modifiers.pre = f },
post : function (f) { modifiers.post = f },
stop : function () { alive = false },
block : function () { keepGoing = false }
};
if (!alive) return state;
function updateState() {
if (typeof state.node === 'object' && state.node !== null) {
if (!state.keys || state.node_ !== state.node) {
state.keys = objectKeys(state.node)
}
state.isLeaf = state.keys.length == 0;
for (var i = 0; i < parents.length; i++) {
if (parents[i].node_ === node_) {
state.circular = parents[i];
break;
}
}
}
else {
state.isLeaf = true;
state.keys = null;
}
state.notLeaf = !state.isLeaf;
state.notRoot = !state.isRoot;
}
updateState();
// use return values to update if defined
var ret = cb.call(state, state.node);
if (ret !== undefined && state.update) state.update(ret);
if (modifiers.before) modifiers.before.call(state, state.node);
if (!keepGoing) return state;
if (typeof state.node == 'object'
&& state.node !== null && !state.circular) {
parents.push(state);
updateState();
forEach(state.keys, function (key, i) {
path.push(key);
if (modifiers.pre) modifiers.pre.call(state, state.node[key], key);
var child = walker(state.node[key]);
if (immutable && hasOwnProperty.call(state.node, key)) {
state.node[key] = child.node;
}
child.isLast = i == state.keys.length - 1;
child.isFirst = i == 0;
if (modifiers.post) modifiers.post.call(state, child);
path.pop();
});
parents.pop();
}
if (modifiers.after) modifiers.after.call(state, state.node);
return state;
})(root).node;
}
function copy (src) {
if (typeof src === 'object' && src !== null) {
var dst;
if (isArray(src)) {
dst = [];
}
else if (isDate(src)) {
dst = new Date(src.getTime ? src.getTime() : src);
}
else if (isRegExp(src)) {
dst = new RegExp(src);
}
else if (isError(src)) {
dst = { message: src.message };
}
else if (isBoolean(src)) {
dst = new Boolean(src);
}
else if (isNumber(src)) {
dst = new Number(src);
}
else if (isString(src)) {
dst = new String(src);
}
else if (Object.create && Object.getPrototypeOf) {
dst = Object.create(Object.getPrototypeOf(src));
}
else if (src.constructor === Object) {
dst = {};
}
else {
var proto =
(src.constructor && src.constructor.prototype)
|| src.__proto__
|| {}
;
var T = function () {};
T.prototype = proto;
dst = new T;
}
forEach(objectKeys(src), function (key) {
dst[key] = src[key];
});
return dst;
}
else return src;
}
var objectKeys = Object.keys || function keys (obj) {
var res = [];
for (var key in obj) res.push(key)
return res;
};
function toS (obj) { return Object.prototype.toString.call(obj) }
function isDate (obj) { return toS(obj) === '[object Date]' }
function isRegExp (obj) { return toS(obj) === '[object RegExp]' }
function isError (obj) { return toS(obj) === '[object Error]' }
function isBoolean (obj) { return toS(obj) === '[object Boolean]' }
function isNumber (obj) { return toS(obj) === '[object Number]' }
function isString (obj) { return toS(obj) === '[object String]' }
var isArray = Array.isArray || function isArray (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
var forEach = function (xs, fn) {
if (xs.forEach) return xs.forEach(fn)
else for (var i = 0; i < xs.length; i++) {
fn(xs[i], i, xs);
}
};
forEach(objectKeys(Traverse.prototype), function (key) {
traverse[key] = function (obj) {
var args = [].slice.call(arguments, 1);
var t = new Traverse(obj);
return t[key].apply(t, args);
};
});
var hasOwnProperty = Object.hasOwnProperty || function (obj, key) {
return key in obj;
};
{
"_from": "traverse@>=0.2.4",
"_id": "traverse@0.6.6",
"_inBundle": false,
"_integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=",
"_location": "/traverse",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "traverse@>=0.2.4",
"name": "traverse",
"escapedName": "traverse",
"rawSpec": ">=0.2.4",
"saveSpec": null,
"fetchSpec": ">=0.2.4"
},
"_requiredBy": [
"/hashish"
],
"_resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
"_shasum": "cbdf560fd7b9af632502fed40f918c157ea97137",
"_spec": "traverse@>=0.2.4",
"_where": "/Users/jmorrell/workspace/heroku-buildpack-nodejs/test/fixtures/dependencies-modules-checked-in-with-devdependencies/node_modules/hashish",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"bugs": {
"url": "https://github.com/substack/js-traverse/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "traverse and transform objects by visiting every node on a recursive walk",
"devDependencies": {
"tape": "~1.0.4"
},
"directories": {
"example": "example",
"test": "test"
},
"homepage": "https://github.com/substack/js-traverse",
"keywords": [
"traverse",
"walk",
"recursive",
"map",
"forEach",
"deep",
"clone"
],
"license": "MIT",
"main": "index.js",
"name": "traverse",
"repository": {
"type": "git",
"url": "git://github.com/substack/js-traverse.git"
},
"scripts": {
"test": "tape test/*.js"
},
"testling": {
"files": "test/*.js",
"browsers": {
"iexplore": [
"6.0",
"7.0",
"8.0",
"9.0"
],
"chrome": [
"10.0",
"20.0"
],
"firefox": [
"10.0",
"15.0"
],
"safari": [
"5.1"
],
"opera": [
"12.0"
]
}
},
"version": "0.6.6"
}
# traverse
Traverse and transform objects by visiting every node on a recursive walk.
[![browser support](http://ci.testling.com/substack/js-traverse.png)](http://ci.testling.com/substack/js-traverse)
[![build status](https://secure.travis-ci.org/substack/js-traverse.png)](http://travis-ci.org/substack/js-traverse)
# examples
## transform negative numbers in-place
negative.js
````javascript
var traverse = require('traverse');
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
traverse(obj).forEach(function (x) {
if (x < 0) this.update(x + 128);
});
console.dir(obj);
````
Output:
[ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ]
## collect leaf nodes
leaves.js
````javascript
var traverse = require('traverse');
var obj = {
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 },
};
var leaves = traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
return acc;
}, []);
console.dir(leaves);
````
Output:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
## scrub circular references
scrub.js:
````javascript
var traverse = require('traverse');
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
obj.c.push(obj);
var scrubbed = traverse(obj).map(function (x) {
if (this.circular) this.remove()
});
console.dir(scrubbed);
````
output:
{ a: 1, b: 2, c: [ 3, 4 ] }
# methods
Each method that takes an `fn` uses the context documented below in the context
section.
## .map(fn)
Execute `fn` for each node in the object and return a new object with the
results of the walk. To update nodes in the result use `this.update(value)`.
## .forEach(fn)
Execute `fn` for each node in the object but unlike `.map()`, when
`this.update()` is called it updates the object in-place.
## .reduce(fn, acc)
For each node in the object, perform a
[left-fold](http://en.wikipedia.org/wiki/Fold_(higher-order_function))
with the return value of `fn(acc, node)`.
If `acc` isn't specified, `acc` is set to the root object for the first step
and the root element is skipped.
## .paths()
Return an `Array` of every possible non-cyclic path in the object.
Paths are `Array`s of string keys.
## .nodes()
Return an `Array` of every node in the object.
## .clone()
Create a deep clone of the object.
## .get(path)
Get the element at the array `path`.
## .set(path, value)
Set the element at the array `path` to `value`.
## .has(path)
Return whether the element at the array `path` exists.
# context
Each method that takes a callback has a context (its `this` object) with these
attributes:
## this.node
The present node on the recursive walk
## this.path
An array of string keys from the root to the present node
## this.parent
The context of the node's parent.
This is `undefined` for the root node.
## this.key
The name of the key of the present node in its parent.
This is `undefined` for the root node.
## this.isRoot, this.notRoot
Whether the present node is the root node
## this.isLeaf, this.notLeaf
Whether or not the present node is a leaf node (has no children)
## this.level
Depth of the node within the traversal
## this.circular
If the node equals one of its parents, the `circular` attribute is set to the
context of that parent and the traversal progresses no deeper.
## this.update(value, stopHere=false)
Set a new value for the present node.
All the elements in `value` will be recursively traversed unless `stopHere` is
true.
## this.remove(stopHere=false)
Remove the current element from the output. If the node is in an Array it will
be spliced off. Otherwise it will be deleted from its parent.
## this.delete(stopHere=false)
Delete the current element from its parent in the output. Calls `delete` even on
Arrays.
## this.before(fn)
Call this function before any of the children are traversed.
You can assign into `this.keys` here to traverse in a custom order.
## this.after(fn)
Call this function after any of the children are traversed.
## this.pre(fn)
Call this function before each of the children are traversed.
## this.post(fn)
Call this function after each of the children are traversed.
# install
Using [npm](http://npmjs.org) do:
$ npm install traverse
# license
MIT
var test = require('tape');
var traverse = require('../');
var deepEqual = require('./lib/deep_equal');
var util = require('util');
test('circular', function (t) {
t.plan(1);
var obj = { x : 3 };
obj.y = obj;
traverse(obj).forEach(function (x) {
if (this.path.join('') == 'y') {
t.equal(
util.inspect(this.circular.node),
util.inspect(obj)
);
}
});
});
test('deepCirc', function (t) {
t.plan(2);
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
var times = 0;
traverse(obj).forEach(function (x) {
if (this.circular) {
t.same(this.circular.path, []);
t.same(this.path, [ 'y', 2 ]);
}
});
});
test('doubleCirc', function (t) {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
obj.x.push(obj.y);
var circs = [];
traverse(obj).forEach(function (x) {
if (this.circular) {
circs.push({ circ : this.circular, self : this, node : x });
}
});
t.same(circs[0].self.path, [ 'x', 3, 2 ]);
t.same(circs[0].circ.path, []);
t.same(circs[1].self.path, [ 'y', 2 ]);
t.same(circs[1].circ.path, []);
t.same(circs.length, 2);
t.end();
});
test('circDubForEach', function (t) {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
obj.x.push(obj.y);
traverse(obj).forEach(function (x) {
if (this.circular) this.update('...');
});
t.same(obj, { x : [ 1, 2, 3, [ 4, 5, '...' ] ], y : [ 4, 5, '...' ] });
t.end();
});
test('circDubMap', function (t) {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
obj.x.push(obj.y);
var c = traverse(obj).map(function (x) {
if (this.circular) {
this.update('...');
}
});
t.same(c, { x : [ 1, 2, 3, [ 4, 5, '...' ] ], y : [ 4, 5, '...' ] });
t.end();
});
test('circClone', function (t) {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
obj.x.push(obj.y);
var clone = traverse.clone(obj);
t.ok(obj !== clone);
t.ok(clone.y[2] === clone);
t.ok(clone.y[2] !== obj);
t.ok(clone.x[3][2] === clone);
t.ok(clone.x[3][2] !== obj);
t.same(clone.x.slice(0,3), [1,2,3]);
t.same(clone.y.slice(0,2), [4,5]);
t.end();
});
test('circMapScrub', function (t) {
var obj = { a : 1, b : 2 };
obj.c = obj;
var scrubbed = traverse(obj).map(function (node) {
if (this.circular) this.remove();
});
t.same(
Object.keys(scrubbed).sort(),
[ 'a', 'b' ]
);
t.ok(deepEqual(scrubbed, { a : 1, b : 2 }));
t.equal(obj.c, obj);
t.end();
});
var test = require('tape');
var traverse = require('../');
test('dateEach', function (t) {
var obj = { x : new Date, y : 10, z : 5 };
var counts = {};
traverse(obj).forEach(function (node) {
var t = (node instanceof Date && 'Date') || typeof node;
counts[t] = (counts[t] || 0) + 1;
});
t.same(counts, {
object : 1,
Date : 1,
number : 2,
});
t.end();
});
test('dateMap', function (t) {
var obj = { x : new Date, y : 10, z : 5 };
var res = traverse(obj).map(function (node) {
if (typeof node === 'number') this.update(node + 100);
});
t.ok(obj.x !== res.x);
t.same(res, {
x : obj.x,
y : 110,
z : 105,
});
t.end();
});
var test = require('tape');
var traverse = require('../');
var deepEqual = require('./lib/deep_equal');
test('deepDates', function (t) {
t.plan(2);
t.ok(
deepEqual(
{ d : new Date, x : [ 1, 2, 3 ] },
{ d : new Date, x : [ 1, 2, 3 ] }
),
'dates should be equal'
);
var d0 = new Date;
setTimeout(function () {
t.ok(
!deepEqual(
{ d : d0, x : [ 1, 2, 3 ], },
{ d : new Date, x : [ 1, 2, 3 ] }
),
'microseconds should count in date equality'
);
}, 5);
});
test('deepCircular', function (t) {
var a = [1];
a.push(a); // a = [ 1, *a ]
var b = [1];
b.push(a); // b = [ 1, [ 1, *a ] ]
t.ok(
!deepEqual(a, b),
'circular ref mount points count towards equality'
);
var c = [1];
c.push(c); // c = [ 1, *c ]
t.ok(
deepEqual(a, c),
'circular refs are structurally the same here'
);
var d = [1];
d.push(a); // c = [ 1, [ 1, *d ] ]
t.ok(
deepEqual(b, d),
'non-root circular ref structural comparison'
);
t.end();
});
test('deepInstances', function (t) {
t.ok(
!deepEqual([ new Boolean(false) ], [ false ]),
'boolean instances are not real booleans'
);
t.ok(
!deepEqual([ new String('x') ], [ 'x' ]),
'string instances are not real strings'
);
t.ok(
!deepEqual([ new Number(4) ], [ 4 ]),
'number instances are not real numbers'
);
t.ok(
deepEqual([ new RegExp('x') ], [ /x/ ]),
'regexp instances are real regexps'
);
t.ok(
!deepEqual([ new RegExp(/./) ], [ /../ ]),
'these regexps aren\'t the same'
);
t.ok(
!deepEqual(
[ function (x) { return x * 2 } ],
[ function (x) { return x * 2 } ]
),
'functions with the same .toString() aren\'t necessarily the same'
);
var f = function (x) { return x * 2 };
t.ok(
deepEqual([ f ], [ f ]),
'these functions are actually equal'
);
t.end();
});
test('deepEqual', function (t) {
t.ok(
!deepEqual([ 1, 2, 3 ], { 0 : 1, 1 : 2, 2 : 3 }),
'arrays are not objects'
);
t.end();
});
test('falsy', function (t) {
t.ok(
!deepEqual([ undefined ], [ null ]),
'null is not undefined!'
);
t.ok(
!deepEqual([ null ], [ undefined ]),
'undefined is not null!'
);
t.ok(
!deepEqual(
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
{ a : 1, b : 2, c : [ 3, null, 5 ] }
),
'undefined is not null, however deeply!'
);
t.ok(
!deepEqual(
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
{ a : 1, b : 2, c : [ 3, null, 5 ] }
),
'null is not undefined, however deeply!'
);
t.ok(
!deepEqual(
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
{ a : 1, b : 2, c : [ 3, null, 5 ] }
),
'null is not undefined, however deeply!'
);
t.end();
});
test('deletedArrayEqual', function (t) {
var xs = [ 1, 2, 3, 4 ];
delete xs[2];
var ys = Object.create(Array.prototype);
ys[0] = 1;
ys[1] = 2;
ys[3] = 4;
t.ok(
deepEqual(xs, ys),
'arrays with deleted elements are only equal to'
+ ' arrays with similarly deleted elements'
);
t.ok(
!deepEqual(xs, [ 1, 2, undefined, 4 ]),
'deleted array elements cannot be undefined'
);
t.ok(
!deepEqual(xs, [ 1, 2, null, 4 ]),
'deleted array elements cannot be null'
);
t.end();
});
test('deletedObjectEqual', function (t) {
var obj = { a : 1, b : 2, c : 3 };
delete obj.c;
t.ok(
deepEqual(obj, { a : 1, b : 2 }),
'deleted object elements should not show up'
);
t.ok(
!deepEqual(obj, { a : 1, b : 2, c : undefined }),
'deleted object elements are not undefined'
);
t.ok(
!deepEqual(obj, { a : 1, b : 2, c : null }),
'deleted object elements are not null'
);
t.end();
});
test('emptyKeyEqual', function (t) {
t.ok(!deepEqual(
{ a : 1 }, { a : 1, '' : 55 }
));
t.end();
});
test('deepArguments', function (t) {
t.ok(
!deepEqual(
[ 4, 5, 6 ],
(function () { return arguments })(4, 5, 6)
),
'arguments are not arrays'
);
t.ok(
deepEqual(
(function () { return arguments })(4, 5, 6),
(function () { return arguments })(4, 5, 6)
),
'arguments should equal'
);
t.end();
});
test('deepUn', function (t) {
t.ok(!deepEqual({ a : 1, b : 2 }, undefined));
t.ok(!deepEqual({ a : 1, b : 2 }, {}));
t.ok(!deepEqual(undefined, { a : 1, b : 2 }));
t.ok(!deepEqual({}, { a : 1, b : 2 }));
t.ok(deepEqual(undefined, undefined));
t.ok(deepEqual(null, null));
t.ok(!deepEqual(undefined, null));
t.end();
});
test('deepLevels', function (t) {
var xs = [ 1, 2, [ 3, 4, [ 5, 6 ] ] ];
t.ok(!deepEqual(xs, []));
t.end();
});
var test = require('tape');
var traverse = require('../');
test('traverse an Error', function (t) {
var obj = new Error("test");
var results = traverse(obj).map(function (node) {});
t.same(results, { message: 'test' });
t.end();
});
var test = require('tape');
var traverse = require('../');
test('has', function (t) {
var obj = { a : 2, b : [ 4, 5, { c : 6 } ] };
t.equal(traverse(obj).has([ 'b', 2, 'c' ]), true)
t.equal(traverse(obj).has([ 'b', 2, 'c', 0 ]), false)
t.equal(traverse(obj).has([ 'b', 2, 'd' ]), false)
t.equal(traverse(obj).has([]), true)
t.equal(traverse(obj).has([ 'a' ]), true)
t.equal(traverse(obj).has([ 'a', 2 ]), false)
t.end();
});
var test = require('tape');
var traverse = require('../');
var EventEmitter = require('events').EventEmitter;
test('check instanceof on node elems', function (t) {
var counts = { emitter : 0 };
traverse([ new EventEmitter, 3, 4, { ev : new EventEmitter }])
.forEach(function (node) {
if (node instanceof EventEmitter) counts.emitter ++;
})
;
t.equal(counts.emitter, 2);
t.end();
});
var test = require('tape');
var traverse = require('../');
test('interface map', function (t) {
var obj = { a : [ 5,6,7 ], b : { c : [8] } };
t.same(
traverse.paths(obj)
.sort()
.map(function (path) { return path.join('/') })
.slice(1)
.join(' ')
,
'a a/0 a/1 a/2 b b/c b/c/0'
);
t.same(
traverse.nodes(obj),
[
{ a: [ 5, 6, 7 ], b: { c: [ 8 ] } },
[ 5, 6, 7 ], 5, 6, 7,
{ c: [ 8 ] }, [ 8 ], 8
]
);
t.same(
traverse.map(obj, function (node) {
if (typeof node == 'number') {
return node + 1000;
}
else if (Array.isArray(node)) {
return node.join(' ');
}
}),
{ a: '5 6 7', b: { c: '8' } }
);
var nodes = 0;
traverse.forEach(obj, function (node) { nodes ++ });
t.same(nodes, 8);
t.end();
});
var test = require('tape');
var traverse = require('../');
test('json test', function (t) {
var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
var scrubbed = traverse(obj).map(function (x) {
if (typeof x === 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
});
t.equal(
scrubbed.moo, '[Function]',
'obj.moo replaced with "[Function]"'
);
t.equal(
scrubbed.foo[3], '[Function]',
'obj.foo[3] replaced with "[Function]"'
);
t.same(scrubbed, {
moo : '[Function]',
foo : [ 2, 3, 4, "[Function]" ]
}, 'Full JSON string matches');
t.same(
typeof obj.moo, 'function',
'Original obj.moo still a function'
);
t.same(
typeof obj.foo[3], 'function',
'Original obj.foo[3] still a function'
);
t.same(callbacks, {
54: { id: 54, f : obj.moo, path: [ 'moo' ] },
55: { id: 55, f : obj.foo[3], path: [ 'foo', '3' ] },
}, 'Check the generated callbacks list');
t.end();
});
var test = require('tape');
var traverse = require('../');
test('sort test', function (t) {
var acc = [];
traverse({
a: 30,
b: 22,
id: 9
}).forEach(function (node) {
if ((! Array.isArray(node)) && typeof node === 'object') {
this.before(function(node) {
this.keys = Object.keys(node);
this.keys.sort(function(a, b) {
a = [a === "id" ? 0 : 1, a];
b = [b === "id" ? 0 : 1, b];
return a < b ? -1 : a > b ? 1 : 0;
});
});
}
if (this.isLeaf) acc.push(node);
});
t.equal(
acc.join(' '),
'9 30 22',
'Traversal in a custom order'
);
t.end();
});
var test = require('tape');
var traverse = require('../');
test('leaves test', function (t) {
var acc = [];
traverse({
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 }
}).forEach(function (x) {
if (this.isLeaf) acc.push(x);
});
t.equal(
acc.join(' '),
'1 2 3 4 5 6 7 8 9',
'Traversal in the right(?) order'
);
t.end();
});
var traverse = require('../../');
module.exports = function (a, b) {
if (arguments.length !== 2) {
throw new Error(
'deepEqual requires exactly two objects to compare against'
);
}
var equal = true;
var node = b;
traverse(a).forEach(function (y) {
var notEqual = (function () {
equal = false;
//this.stop();
return undefined;
}).bind(this);
//if (node === undefined || node === null) return notEqual();
if (!this.isRoot) {
/*
if (!Object.hasOwnProperty.call(node, this.key)) {
return notEqual();
}
*/
if (typeof node !== 'object') return notEqual();
node = node[this.key];
}
var x = node;
this.post(function () {
node = x;
});
var toS = function (o) {
return Object.prototype.toString.call(o);
};
if (this.circular) {
if (traverse(b).get(this.circular.path) !== x) notEqual();
}
else if (typeof x !== typeof y) {
notEqual();
}
else if (x === null || y === null || x === undefined || y === undefined) {
if (x !== y) notEqual();
}
else if (x.__proto__ !== y.__proto__) {
notEqual();
}
else if (x === y) {
// nop
}
else if (typeof x === 'function') {
if (x instanceof RegExp) {
// both regexps on account of the __proto__ check
if (x.toString() != y.toString()) notEqual();
}
else if (x !== y) notEqual();
}
else if (typeof x === 'object') {
if (toS(y) === '[object Arguments]'
|| toS(x) === '[object Arguments]') {
if (toS(x) !== toS(y)) {
notEqual();
}
}
else if (toS(y) === '[object RegExp]'
|| toS(x) === '[object RegExp]') {
if (!x || !y || x.toString() !== y.toString()) notEqual();
}
else if (x instanceof Date || y instanceof Date) {
if (!(x instanceof Date) || !(y instanceof Date)
|| x.getTime() !== y.getTime()) {
notEqual();
}
}
else {
var kx = Object.keys(x);
var ky = Object.keys(y);
if (kx.length !== ky.length) return notEqual();
for (var i = 0; i < kx.length; i++) {
var k = kx[i];
if (!Object.hasOwnProperty.call(y, k)) {
notEqual();
}
}
}
}
});
return equal;
};
var test = require('tape');
var traverse = require('../');
var deepEqual = require('./lib/deep_equal');
test('mutate', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse(obj).forEach(function (x) {
if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
t.same(obj, res);
t.same(obj, { a : 1, b : 20, c : [ 3, 40 ] });
t.end();
});
test('mutateT', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse.forEach(obj, function (x) {
if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
t.same(obj, res);
t.same(obj, { a : 1, b : 20, c : [ 3, 40 ] });
t.end();
});
test('map', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse(obj).map(function (x) {
if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
t.same(obj, { a : 1, b : 2, c : [ 3, 4 ] });
t.same(res, { a : 1, b : 20, c : [ 3, 40 ] });
t.end();
});
test('mapT', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse.map(obj, function (x) {
if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
t.same(obj, { a : 1, b : 2, c : [ 3, 4 ] });
t.same(res, { a : 1, b : 20, c : [ 3, 40 ] });
t.end();
});
test('clone', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse(obj).clone();
t.same(obj, res);
t.ok(obj !== res);
obj.a ++;
t.same(res.a, 1);
obj.c.push(5);
t.same(res.c, [ 3, 4 ]);
t.end();
});
test('cloneT', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse.clone(obj);
t.same(obj, res);
t.ok(obj !== res);
obj.a ++;
t.same(res.a, 1);
obj.c.push(5);
t.same(res.c, [ 3, 4 ]);
t.end();
});
test('reduce', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
return acc;
}, []);
t.same(obj, { a : 1, b : 2, c : [ 3, 4 ] });
t.same(res, [ 1, 2, 3, 4 ]);
t.end();
});
test('reduceInit', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse(obj).reduce(function (acc, x) {
if (this.isRoot) assert.fail('got root');
return acc;
});
t.same(obj, { a : 1, b : 2, c : [ 3, 4 ] });
t.same(res, obj);
t.end();
});
test('remove', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
traverse(obj).forEach(function (x) {
if (this.isLeaf && x % 2 == 0) this.remove();
});
t.same(obj, { a : 1, c : [ 3 ] });
t.end();
});
exports.removeNoStop = function() {
var obj = { a : 1, b : 2, c : { d: 3, e: 4 }, f: 5 };
var keys = [];
traverse(obj).forEach(function (x) {
keys.push(this.key)
if (this.key == 'c') this.remove();
});
t.same(keys, [undefined, 'a', 'b', 'c', 'd', 'e', 'f'])
t.end();
}
exports.removeStop = function() {
var obj = { a : 1, b : 2, c : { d: 3, e: 4 }, f: 5 };
var keys = [];
traverse(obj).forEach(function (x) {
keys.push(this.key)
if (this.key == 'c') this.remove(true);
});
t.same(keys, [undefined, 'a', 'b', 'c', 'f'])
t.end();
}
test('removeMap', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse(obj).map(function (x) {
if (this.isLeaf && x % 2 == 0) this.remove();
});
t.same(obj, { a : 1, b : 2, c : [ 3, 4 ] });
t.same(res, { a : 1, c : [ 3 ] });
t.end();
});
test('delete', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
traverse(obj).forEach(function (x) {
if (this.isLeaf && x % 2 == 0) this.delete();
});
t.ok(!deepEqual(
obj, { a : 1, c : [ 3, undefined ] }
));
t.ok(deepEqual(
obj, { a : 1, c : [ 3 ] }
));
t.ok(!deepEqual(
obj, { a : 1, c : [ 3, null ] }
));
t.end();
});
test('deleteNoStop', function (t) {
var obj = { a : 1, b : 2, c : { d: 3, e: 4 } };
var keys = [];
traverse(obj).forEach(function (x) {
keys.push(this.key)
if (this.key == 'c') this.delete();
});
t.same(keys, [undefined, 'a', 'b', 'c', 'd', 'e'])
t.end();
});
test('deleteStop', function (t) {
var obj = { a : 1, b : 2, c : { d: 3, e: 4 } };
var keys = [];
traverse(obj).forEach(function (x) {
keys.push(this.key)
if (this.key == 'c') this.delete(true);
});
t.same(keys, [undefined, 'a', 'b', 'c'])
t.end();
});
test('deleteRedux', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4, 5 ] };
traverse(obj).forEach(function (x) {
if (this.isLeaf && x % 2 == 0) this.delete();
});
t.ok(!deepEqual(
obj, { a : 1, c : [ 3, undefined, 5 ] }
));
t.ok(deepEqual(
obj, { a : 1, c : [ 3 ,, 5 ] }
));
t.ok(!deepEqual(
obj, { a : 1, c : [ 3, null, 5 ] }
));
t.ok(!deepEqual(
obj, { a : 1, c : [ 3, 5 ] }
));
t.end();
});
test('deleteMap', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse(obj).map(function (x) {
if (this.isLeaf && x % 2 == 0) this.delete();
});
t.ok(deepEqual(
obj,
{ a : 1, b : 2, c : [ 3, 4 ] }
));
var xs = [ 3, 4 ];
delete xs[1];
t.ok(deepEqual(
res, { a : 1, c : xs }
));
t.ok(deepEqual(
res, { a : 1, c : [ 3, ] }
));
t.ok(deepEqual(
res, { a : 1, c : [ 3 ] }
));
t.end();
});
test('deleteMapRedux', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4, 5 ] };
var res = traverse(obj).map(function (x) {
if (this.isLeaf && x % 2 == 0) this.delete();
});
t.ok(deepEqual(
obj,
{ a : 1, b : 2, c : [ 3, 4, 5 ] }
));
var xs = [ 3, 4, 5 ];
delete xs[1];
t.ok(deepEqual(
res, { a : 1, c : xs }
));
t.ok(!deepEqual(
res, { a : 1, c : [ 3, 5 ] }
));
t.ok(deepEqual(
res, { a : 1, c : [ 3 ,, 5 ] }
));
t.end();
});
test('objectToString', function (t) {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = traverse(obj).forEach(function (x) {
if (typeof x === 'object' && !this.isRoot) {
this.update(JSON.stringify(x));
}
});
t.same(obj, res);
t.same(obj, { a : 1, b : 2, c : "[3,4]" });
t.end();
});
test('stringToObject', function (t) {
var obj = { a : 1, b : 2, c : "[3,4]" };
var res = traverse(obj).forEach(function (x) {
if (typeof x === 'string') {
this.update(JSON.parse(x));
}
else if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
t.deepEqual(obj, res);
t.deepEqual(obj, { a : 1, b : 20, c : [ 3, 40 ] });
t.end();
});
var traverse = require('../');
var test = require('tape');
test('negative update test', function (t) {
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
var fixed = traverse.map(obj, function (x) {
if (x < 0) this.update(x + 128);
});
t.same(fixed,
[ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ],
'Negative values += 128'
);
t.same(obj,
[ 5, 6, -3, [ 7, 8, -2, 1 ], { f: 10, g: -13 } ],
'Original references not modified'
);
t.end();
});
var test = require('tape');
var traverse = require('../');
test('traverse an object with nested functions', function (t) {
t.plan(1);
function Cons (x) {
t.equal(x, 10)
};
traverse(new Cons(10));
});
var test = require('tape');
var traverse = require('../');
test('siblings', function (t) {
var obj = { a : 1, b : 2, c : [ 4, 5, 6 ] };
var res = traverse(obj).reduce(function (acc, x) {
var p = '/' + this.path.join('/');
if (this.parent) {
acc[p] = {
siblings : this.parent.keys,
key : this.key,
index : this.parent.keys.indexOf(this.key)
};
}
else {
acc[p] = {
siblings : [],
key : this.key,
index : -1
}
}
return acc;
}, {});
t.same(res, {
'/' : { siblings : [], key : undefined, index : -1 },
'/a' : { siblings : [ 'a', 'b', 'c' ], key : 'a', index : 0 },
'/b' : { siblings : [ 'a', 'b', 'c' ], key : 'b', index : 1 },
'/c' : { siblings : [ 'a', 'b', 'c' ], key : 'c', index : 2 },
'/c/0' : { siblings : [ '0', '1', '2' ], key : '0', index : 0 },
'/c/1' : { siblings : [ '0', '1', '2' ], key : '1', index : 1 },
'/c/2' : { siblings : [ '0', '1', '2' ], key : '2', index : 2 }
});
t.end();
});
var test = require('tape');
var traverse = require('../');
test('stop', function (t) {
var visits = 0;
traverse('abcdefghij'.split('')).forEach(function (node) {
if (typeof node === 'string') {
visits ++;
if (node === 'e') this.stop()
}
});
t.equal(visits, 5);
t.end();
});
test('stopMap', function (t) {
var s = traverse('abcdefghij'.split('')).map(function (node) {
if (typeof node === 'string') {
if (node === 'e') this.stop()
return node.toUpperCase();
}
}).join('');
t.equal(s, 'ABCDEfghij');
t.end();
});
test('stopReduce', function (t) {
var obj = {
a : [ 4, 5 ],
b : [ 6, [ 7, 8, 9 ] ]
};
var xs = traverse(obj).reduce(function (acc, node) {
if (this.isLeaf) {
if (node === 7) this.stop();
else acc.push(node)
}
return acc;
}, []);
t.same(xs, [ 4, 5, 6 ]);
t.end();
});
var test = require('tape');
var traverse = require('../');
test('stringify', function (t) {
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
var s = '';
traverse(obj).forEach(function (node) {
if (Array.isArray(node)) {
this.before(function () { s += '[' });
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += ']' });
}
else if (typeof node == 'object') {
this.before(function () { s += '{' });
this.pre(function (x, key) {
s += '"' + key + '"' + ':';
});
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += '}' });
}
else if (typeof node == 'function') {
s += 'null';
}
else {
s += node.toString();
}
});
t.equal(s, JSON.stringify(obj));
t.end();
});
var traverse = require('../');
var test = require('tape');
test('subexpr', function (t) {
var obj = [ 'a', 4, 'b', 5, 'c', 6 ];
var r = traverse(obj).map(function (x) {
if (typeof x === 'number') {
this.update([ x - 0.1, x, x + 0.1 ], true);
}
});
t.same(obj, [ 'a', 4, 'b', 5, 'c', 6 ]);
t.same(r, [
'a', [ 3.9, 4, 4.1 ],
'b', [ 4.9, 5, 5.1 ],
'c', [ 5.9, 6, 6.1 ],
]);
t.end();
});
test('block', function (t) {
var obj = [ [ 1 ], [ 2 ], [ 3 ] ];
var r = traverse(obj).map(function (x) {
if (Array.isArray(x) && !this.isRoot) {
if (x[0] === 5) this.block()
else this.update([ [ x[0] + 1 ] ])
}
});
t.same(r, [
[ [ [ [ [ 5 ] ] ] ] ],
[ [ [ [ 5 ] ] ] ],
[ [ [ 5 ] ] ],
]);
t.end();
});
var test = require('tape');
var traverse = require('../');
var deepEqual = require('./lib/deep_equal');
test('super_deep', function (t) {
var util = require('util');
var a0 = make();
var a1 = make();
t.ok(deepEqual(a0, a1));
a0.c.d.moo = true;
t.ok(!deepEqual(a0, a1));
a1.c.d.moo = true;
t.ok(deepEqual(a0, a1));
// TODO: this one
//a0.c.a = a1;
//t.ok(!deepEqual(a0, a1));
t.end();
});
function make () {
var a = { self : 'a' };
var b = { self : 'b' };
var c = { self : 'c' };
var d = { self : 'd' };
var e = { self : 'e' };
a.a = a;
a.b = b;
a.c = c;
b.a = a;
b.b = b;
b.c = c;
c.a = a;
c.b = b;
c.c = c;
c.d = d;
d.a = a;
d.b = b;
d.c = c;
d.d = d;
d.e = e;
e.a = a;
e.b = b;
e.c = c;
e.d = d;
e.e = e;
return a;
}
var traverse = require('./');
var test = require('testling');
test('leaves', function (t) {
var obj = {
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 }
};
var acc = [];
traverse(obj).forEach(function (x) {
if (this.isLeaf) acc.push(x);
});
t.deepEqual(
acc, [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
'traversal in the proper order'
);
t.end();
});
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"hashish": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz",
"integrity": "sha1-bWC8b/r3Ebav1g5CbQd5iAFOZVQ=",
"requires": {
"traverse": "0.6.6"
}
},
"lodash": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
"integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
"dev": true
},
"traverse": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
"integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc="
}
}
}
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository": {
"type": "git",
"url": "http://github.com/example/example.git"
},
"dependencies": {
"hashish": "*"
},
"engines": {
"node": "8.x"
},
"devDependencies": {
"lodash": "^2.4.1"
},
"scripts": {
"heroku-postbuild": "ls node_modules"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
Hashish
=======
Hashish is a node.js library for manipulating hash data structures.
It is distilled from the finest that ruby, perl, and haskell have to offer by
way of hash/map interfaces.
Hashish provides a chaining interface, where you can do:
var Hash = require('hashish');
Hash({ a : 1, b : 2, c : 3, d : 4 })
.map(function (x) { return x * 10 })
.filter(function (x) { return x < 30 })
.forEach(function (x, key) {
console.log(key + ' => ' + x);
})
;
Output:
a => 10
b => 20
Some functions and attributes in the chaining interface are terminal, like
`.items` or `.detect()`. They return values of their own instead of the chain
context.
Each function in the chainable interface is also attached to `Hash` in chainless
form:
var Hash = require('hashish');
var obj = { a : 1, b : 2, c : 3, d : 4 };
var mapped = Hash.map(obj, function (x) {
return x * 10
});
console.dir(mapped);
Output:
{ a: 10, b: 20, c: 30, d: 40 }
In either case, the 'this' context of the function calls is the same object that
the chained functions return, so you can make nested chains.
Methods
=======
forEach(cb)
-----------
For each key/value in the hash, calls `cb(value, key)`.
map(cb)
-------
For each key/value in the hash, calls `cb(value, key)`.
The return value of `cb` is the new value at `key` in the resulting hash.
filter(cb)
----------
For each key/value in the hash, calls `cb(value, key)`.
The resulting hash omits key/value pairs where `cb` returned a falsy value.
detect(cb)
----------
Returns the first value in the hash for which `cb(value, key)` is non-falsy.
Order of hashes is not well-defined so watch out for that.
reduce(cb)
----------
Returns the accumulated value of a left-fold over the key/value pairs.
some(cb)
--------
Returns a boolean: whether or not `cb(value, key)` ever returned a non-falsy
value.
update(obj1, [obj2, obj3, ...])
-----------
Mutate the context hash, merging the key/value pairs from the passed objects
and overwriting keys from the context hash if the current `obj` has keys of
the same name. Falsy arguments are silently ignored.
updateAll([ obj1, obj2, ... ])
------------------------------
Like multi-argument `update()` but operate on an array directly.
merge(obj1, [obj2, obj3, ...])
----------
Merge the key/value pairs from the passed objects into the resultant hash
without modifying the context hash. Falsy arguments are silently ignored.
mergeAll([ obj1, obj2, ... ])
------------------------------
Like multi-argument `merge()` but operate on an array directly.
has(key)
--------
Return whether the hash has a key, `key`.
valuesAt(keys)
--------------
Return an Array with the values at the keys from `keys`.
tap(cb)
-------
Call `cb` with the present raw hash.
This function is chainable.
extract(keys)
-------------
Filter by including only those keys in `keys` in the resulting hash.
exclude(keys)
-------------
Filter by excluding those keys in `keys` in the resulting hash.
Attributes
==========
These are attributes in the chaining interface and functions in the `Hash.xxx`
interface.
keys
----
Return all the enumerable attribute keys in the hash.
values
------
Return all the enumerable attribute values in the hash.
compact
-------
Filter out values which are `=== undefined`.
clone
-----
Make a deep copy of the hash.
copy
----
Make a shallow copy of the hash.
length
------
Return the number of key/value pairs in the hash.
Note: use `Hash.size()` for non-chain mode.
size
----
Alias for `length` since `Hash.length` is masked by `Function.prototype`.
See Also
========
See also [creationix's pattern/hash](http://github.com/creationix/pattern),
which does a similar thing except with hash inputs and array outputs.
Installation
============
To install with [npm](http://github.com/isaacs/npm):
npm install hashish
To run the tests with [expresso](http://github.com/visionmedia/expresso):
expresso
var Hash = require('hashish');
Hash({ a : 1, b : 2, c : 3, d : 4 })
.map(function (x) { return x * 10 })
.filter(function (x) { return x < 30 })
.forEach(function (x, key) {
console.log(key + ' => ' + x);
})
;
var Hash = require('hashish');
var obj = { a : 1, b : 2, c : 3, d : 4 };
var mapped = Hash.map(obj, function (x) {
return x * 10
});
console.dir(mapped);
module.exports = Hash;
var Traverse = require('traverse');
function Hash (hash, xs) {
if (Array.isArray(hash) && Array.isArray(xs)) {
var to = Math.min(hash.length, xs.length);
var acc = {};
for (var i = 0; i < to; i++) {
acc[hash[i]] = xs[i];
}
return Hash(acc);
}
if (hash === undefined) return Hash({});
var self = {
map : function (f) {
var acc = { __proto__ : hash.__proto__ };
Object.keys(hash).forEach(function (key) {
acc[key] = f.call(self, hash[key], key);
});
return Hash(acc);
},
forEach : function (f) {
Object.keys(hash).forEach(function (key) {
f.call(self, hash[key], key);
});
return self;
},
filter : function (f) {
var acc = { __proto__ : hash.__proto__ };
Object.keys(hash).forEach(function (key) {
if (f.call(self, hash[key], key)) {
acc[key] = hash[key];
}
});
return Hash(acc);
},
detect : function (f) {
for (var key in hash) {
if (f.call(self, hash[key], key)) {
return hash[key];
}
}
return undefined;
},
reduce : function (f, acc) {
var keys = Object.keys(hash);
if (acc === undefined) acc = keys.shift();
keys.forEach(function (key) {
acc = f.call(self, acc, hash[key], key);
});
return acc;
},
some : function (f) {
for (var key in hash) {
if (f.call(self, hash[key], key)) return true;
}
return false;
},
update : function (obj) {
if (arguments.length > 1) {
self.updateAll([].slice.call(arguments));
}
else {
Object.keys(obj).forEach(function (key) {
hash[key] = obj[key];
});
}
return self;
},
updateAll : function (xs) {
xs.filter(Boolean).forEach(function (x) {
self.update(x);
});
return self;
},
merge : function (obj) {
if (arguments.length > 1) {
return self.copy.updateAll([].slice.call(arguments));
}
else {
return self.copy.update(obj);
}
},
mergeAll : function (xs) {
return self.copy.updateAll(xs);
},
has : function (key) { // only operates on enumerables
return Array.isArray(key)
? key.every(function (k) { return self.has(k) })
: self.keys.indexOf(key.toString()) >= 0;
},
valuesAt : function (keys) {
return Array.isArray(keys)
? keys.map(function (key) { return hash[key] })
: hash[keys]
;
},
tap : function (f) {
f.call(self, hash);
return self;
},
extract : function (keys) {
var acc = {};
keys.forEach(function (key) {
acc[key] = hash[key];
});
return Hash(acc);
},
exclude : function (keys) {
return self.filter(function (_, key) {
return keys.indexOf(key) < 0
});
},
end : hash,
items : hash
};
var props = {
keys : function () { return Object.keys(hash) },
values : function () {
return Object.keys(hash).map(function (key) { return hash[key] });
},
compact : function () {
return self.filter(function (x) { return x !== undefined });
},
clone : function () { return Hash(Hash.clone(hash)) },
copy : function () { return Hash(Hash.copy(hash)) },
length : function () { return Object.keys(hash).length },
size : function () { return self.length }
};
if (Object.defineProperty) {
// es5-shim has an Object.defineProperty but it throws for getters
try {
for (var key in props) {
Object.defineProperty(self, key, { get : props[key] });
}
}
catch (err) {
for (var key in props) {
if (key !== 'clone' && key !== 'copy' && key !== 'compact') {
// ^ those keys use Hash() so can't call them without
// a stack overflow
self[key] = props[key]();
}
}
}
}
else if (self.__defineGetter__) {
for (var key in props) {
self.__defineGetter__(key, props[key]);
}
}
else {
// non-lazy version for browsers that suck >_<
for (var key in props) {
self[key] = props[key]();
}
}
return self;
};
// deep copy
Hash.clone = function (ref) {
return Traverse.clone(ref);
};
// shallow copy
Hash.copy = function (ref) {
var hash = { __proto__ : ref.__proto__ };
Object.keys(ref).forEach(function (key) {
hash[key] = ref[key];
});
return hash;
};
Hash.map = function (ref, f) {
return Hash(ref).map(f).items;
};
Hash.forEach = function (ref, f) {
Hash(ref).forEach(f);
};
Hash.filter = function (ref, f) {
return Hash(ref).filter(f).items;
};
Hash.detect = function (ref, f) {
return Hash(ref).detect(f);
};
Hash.reduce = function (ref, f, acc) {
return Hash(ref).reduce(f, acc);
};
Hash.some = function (ref, f) {
return Hash(ref).some(f);
};
Hash.update = function (a /*, b, c, ... */) {
var args = Array.prototype.slice.call(arguments, 1);
var hash = Hash(a);
return hash.update.apply(hash, args).items;
};
Hash.merge = function (a /*, b, c, ... */) {
var args = Array.prototype.slice.call(arguments, 1);
var hash = Hash(a);
return hash.merge.apply(hash, args).items;
};
Hash.has = function (ref, key) {
return Hash(ref).has(key);
};
Hash.valuesAt = function (ref, keys) {
return Hash(ref).valuesAt(keys);
};
Hash.tap = function (ref, f) {
return Hash(ref).tap(f).items;
};
Hash.extract = function (ref, keys) {
return Hash(ref).extract(keys).items;
};
Hash.exclude = function (ref, keys) {
return Hash(ref).exclude(keys).items;
};
Hash.concat = function (xs) {
var hash = Hash({});
xs.forEach(function (x) { hash.update(x) });
return hash.items;
};
Hash.zip = function (xs, ys) {
return Hash(xs, ys).items;
};
// .length is already defined for function prototypes
Hash.size = function (ref) {
return Hash(ref).size;
};
Hash.compact = function (ref) {
return Hash(ref).compact.items;
};
{
"_from": "hashish@*",
"_id": "hashish@0.0.4",
"_inBundle": false,
"_integrity": "sha1-bWC8b/r3Ebav1g5CbQd5iAFOZVQ=",
"_location": "/hashish",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "hashish@*",
"name": "hashish",
"escapedName": "hashish",
"rawSpec": "*",
"saveSpec": null,
"fetchSpec": "*"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz",
"_shasum": "6d60bc6ffaf711b6afd60e426d077988014e6554",
"_spec": "hashish@*",
"_where": "/Users/jmorrell/workspace/heroku-buildpack-nodejs/test/fixtures/dependencies-modules-checked-in-without-devdependencies",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"bugs": {
"url": "https://github.com/substack/node-hashish/issues"
},
"bundleDependencies": false,
"dependencies": {
"traverse": ">=0.2.4"
},
"deprecated": false,
"description": "Hash data structure manipulation functions",
"devDependencies": {
"expresso": ">=0.6.0"
},
"engine": [
"node >=0.2.0"
],
"engines": {
"node": "*"
},
"homepage": "https://github.com/substack/node-hashish#readme",
"keywords": [
"hash",
"object",
"convenience",
"manipulation",
"data structure"
],
"license": "MIT/X11",
"main": "./index.js",
"name": "hashish",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/substack/node-hashish.git"
},
"scripts": {
"test": "expresso"
},
"version": "0.0.4"
}
var Hash = require('hashish');
var assert = require('assert');
exports.map = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).map(function (v) { return v + 1 }).items;
var hash = Hash.map(ref, function (v) { return v + 1 });
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 2, b : 3 });
assert.deepEqual(hash, { a : 2, b : 3 });
};
exports['cloned map'] = function () {
var ref = { foo : [1,2], bar : [4,5] };
var hash = Hash(ref).clone.map(
function (v) { v.unshift(v[0] - 1); return v }
).items;
assert.deepEqual(ref.foo, [1,2]);
assert.deepEqual(ref.bar, [4,5]);
assert.deepEqual(hash.foo, [0,1,2]);
assert.deepEqual(hash.bar, [3,4,5]);
};
exports.forEach = function () {
var ref = { a : 5, b : 2, c : 7, 1337 : 'leet' };
var xs = [];
Hash(ref).forEach(function (x, i) {
xs.push([ i, x ]);
});
assert.eql(
xs.map(function (x) { return x[0] }).sort(),
[ '1337', 'a', 'b', 'c' ]
);
assert.eql(
xs.map(function (x) { return x[1] }).sort(),
[ 2, 5, 7, 'leet' ]
);
var ys = [];
Hash.forEach(ref, function (x, i) {
ys.push([ i, x ]);
});
assert.eql(xs.sort(), ys.sort());
};
exports.filter_items = function () {
var ref = { a : 5, b : 2, c : 7, 1337 : 'leet' };
var items = Hash(ref).filter(function (v, k) {
return v > 5 || k > 5
}).items;
var hash = Hash.filter(ref, function (v, k) { return v > 5 || k > 5 });
assert.deepEqual(items, { 1337 : 'leet', c : 7 });
assert.deepEqual(hash, { 1337 : 'leet', c : 7 });
assert.deepEqual(ref, { a : 5, b : 2, c : 7, 1337 : 'leet' });
assert.equal(Hash(ref).length, 4);
};
exports.detect = function () {
var h = { a : 5, b : 6, c : 7, d : 8 };
var hh = Hash(h);
var gt6hh = hh.detect(function (x) { return x > 6 });
assert.ok(gt6hh == 7 || gt6hh == 8);
var gt6h = Hash.detect(h, function (x) { return x > 6 });
assert.ok(gt6h == 7 || gt6h == 8);
assert.equal(hh.detect(function (x) { return x > 100 }), undefined);
};
exports.reduce = function () {
var ref = { foo : [1,2], bar : [4,5] };
var sum1 = Hash(ref).reduce(function (acc, v) {
return acc + v.length
}, 0);
assert.equal(sum1, 4);
var sum2 = Hash.reduce(ref, function (acc, v) {
return acc + v.length
}, 0);
assert.equal(sum2, 4);
};
exports.some = function () {
var h = { a : 5, b : 6, c : 7, d : 8 };
var hh = Hash(h);
assert.ok(Hash.some(h, function (x) { return x > 7 }));
assert.ok(Hash.some(h, function (x) { return x < 6 }));
assert.ok(!Hash.some(h, function (x) { return x > 10 }));
assert.ok(!Hash.some(h, function (x) { return x < 0 }));
assert.ok(hh.some(function (x) { return x > 7 }));
assert.ok(hh.some(function (x) { return x < 6 }));
assert.ok(!hh.some(function (x) { return x > 10 }));
assert.ok(!hh.some(function (x) { return x < 0 }));
};
exports.update = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).clone.update({ c : 3, a : 0 }).items;
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 0, b : 2, c : 3 });
var hash = Hash.update(ref, { c : 3, a : 0 });
assert.deepEqual(ref, hash);
assert.deepEqual(hash, { a : 0, b : 2, c : 3 });
var ref2 = {a: 1};
var hash2 = Hash.update(ref2, { b: 2, c: 3 }, undefined, { d: 4 });
assert.deepEqual(ref2, { a: 1, b: 2, c: 3, d: 4 });
};
exports.merge = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).merge({ b : 3, c : 3.14 }).items;
var hash = Hash.merge(ref, { b : 3, c : 3.14 });
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 1, b : 3, c : 3.14 });
assert.deepEqual(hash, { a : 1, b : 3, c : 3.14 });
var ref2 = { a : 1 };
var hash2 = Hash.merge(ref, { b: 2, c: 3 }, undefined, { d: 4 });
assert.deepEqual(hash2, { a: 1, b: 2, c: 3, d: 4 });
};
exports.has = function () {
var h = { a : 4, b : 5 };
var hh = Hash(h);
assert.ok(hh.has('a'));
assert.equal(hh.has('c'), false);
assert.ok(hh.has(['a','b']));
assert.equal(hh.has(['a','b','c']), false);
assert.ok(Hash.has(h, 'a'));
assert.equal(Hash.has(h, 'c'), false);
assert.ok(Hash.has(h, ['a','b']));
assert.equal(Hash.has(h, ['a','b','c']), false);
};
exports.valuesAt = function () {
var h = { a : 4, b : 5, c : 6 };
assert.equal(Hash(h).valuesAt('a'), 4);
assert.equal(Hash(h).valuesAt(['a'])[0], 4);
assert.deepEqual(Hash(h).valuesAt(['a','b']), [4,5]);
assert.equal(Hash.valuesAt(h, 'a'), 4);
assert.deepEqual(Hash.valuesAt(h, ['a']), [4]);
assert.deepEqual(Hash.valuesAt(h, ['a','b']), [4,5]);
};
exports.tap = function () {
var h = { a : 4, b : 5, c : 6 };
var hh = Hash(h);
hh.tap(function (x) {
assert.ok(this === hh)
assert.eql(x, h);
});
Hash.tap(h, function (x) {
assert.eql(
Object.keys(this).sort(),
Object.keys(hh).sort()
);
assert.eql(x, h);
});
};
exports.extract = function () {
var hash = Hash({ a : 1, b : 2, c : 3 }).clone;
var extracted = hash.extract(['a','b']);
assert.equal(extracted.length, 2);
assert.deepEqual(extracted.items, { a : 1, b : 2 });
};
exports.exclude = function () {
var hash = Hash({ a : 1, b : 2, c : 3 }).clone;
var extracted = hash.exclude(['a','b']);
assert.equal(extracted.length, 1);
assert.deepEqual(extracted.items, { c : 3 });
};
exports.concat = function () {
var ref1 = { a : 1, b : 2 };
var ref2 = { foo : 100, bar : 200 };
var ref3 = { b : 3, c : 4, bar : 300 };
assert.deepEqual(
Hash.concat([ ref1, ref2 ]),
{ a : 1, b : 2, foo : 100, bar : 200 }
);
assert.deepEqual(
Hash.concat([ ref1, ref2, ref3 ]),
{ a : 1, b : 3, c : 4, foo : 100, bar : 300 }
);
};
exports.zip = function () {
var xs = ['a','b','c'];
var ys = [1,2,3,4];
var h = Hash(xs,ys);
assert.equal(h.length, 3);
assert.deepEqual(h.items, { a : 1, b : 2, c : 3 });
var zipped = Hash.zip(xs,ys);
assert.deepEqual(zipped, { a : 1, b : 2, c : 3 });
};
exports.length = function () {
assert.equal(Hash({ a : 1, b : [2,3], c : 4 }).length, 3);
assert.equal(Hash({ a : 1, b : [2,3], c : 4 }).size, 3);
assert.equal(Hash.size({ a : 1, b : [2,3], c : 4 }), 3);
};
exports.compact = function () {
var hash = {
a : 1,
b : undefined,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
};
var compacted = Hash(hash).compact;
assert.deepEqual(
{
a : 1,
b : undefined,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
},
hash, 'compact modified the hash'
);
assert.deepEqual(
compacted.items,
{
a : 1,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
}
);
var h = Hash.compact(hash);
assert.deepEqual(h, compacted.items);
};
var Hash = require('hashish');
var assert = require('assert');
var vm = require('vm');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/../index.js', 'utf8');
exports.defineGetter = function () {
var context = {
module : { exports : {} },
Object : {
keys : Object.keys,
defineProperty : undefined,
},
require : require,
};
context.exports = context.module.exports;
vm.runInNewContext('(function () {' + src + '})()', context);
var Hash_ = context.module.exports;
var times = 0;
Hash_.__proto__.__proto__.__defineGetter__ = function () {
times ++;
return Object.__defineGetter__.apply(this, arguments);
};
assert.equal(vm.runInNewContext('Object.defineProperty', context), null);
assert.deepEqual(
Hash_({ a : 1, b : 2, c : 3 }).values,
[ 1, 2, 3 ]
);
assert.ok(times > 5);
};
exports.defineProperty = function () {
var times = 0;
var context = {
module : { exports : {} },
Object : {
keys : Object.keys,
defineProperty : function (prop) {
times ++;
if (prop.get) throw new TypeError('engine does not support')
assert.fail('should have asserted by now');
},
},
require : require
};
context.exports = context.module.exports;
vm.runInNewContext('(function () {' + src + '})()', context);
var Hash_ = context.module.exports;
Hash_.__proto__.__proto__.__defineGetter__ = function () {
assert.fail('getter called when a perfectly good'
+ ' defineProperty was available'
);
};
assert.deepEqual(
Hash_({ a : 1, b : 2, c : 3 }).values,
[ 1, 2, 3 ]
);
assert.equal(times, 1);
};
Copyright 2010 James Halliday (mail@substack.net)
This project is free software released under the MIT/X11 license:
http://www.opensource.org/licenses/mit-license.php
Copyright 2010 James Halliday (mail@substack.net)
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.
var traverse = require('traverse');
var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
var scrubbed = traverse(obj).map(function (x) {
if (typeof x === 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
});
console.dir(scrubbed);
console.dir(callbacks);
var traverse = require('traverse');
var obj = {
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 },
};
var leaves = traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
return acc;
}, []);
console.dir(leaves);
var traverse = require('traverse');
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
traverse(obj).forEach(function (x) {
if (x < 0) this.update(x + 128);
});
console.dir(obj);
// scrub out circular references
var traverse = require('traverse');
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
obj.c.push(obj);
var scrubbed = traverse(obj).map(function (x) {
if (this.circular) this.remove()
});
console.dir(scrubbed);
#!/usr/bin/env node
var traverse = require('traverse');
var obj = [ 'five', 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
var s = '';
traverse(obj).forEach(function to_s (node) {
if (Array.isArray(node)) {
this.before(function () { s += '[' });
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += ']' });
}
else if (typeof node == 'object') {
this.before(function () { s += '{' });
this.pre(function (x, key) {
to_s(key);
s += ':';
});
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += '}' });
}
else if (typeof node == 'string') {
s += '"' + node.toString().replace(/"/g, '\\"') + '"';
}
else if (typeof node == 'function') {
s += 'null';
}
else {
s += node.toString();
}
});
console.log('JSON.stringify: ' + JSON.stringify(obj));
console.log('this stringify: ' + s);
var traverse = module.exports = function (obj) {
return new Traverse(obj);
};
function Traverse (obj) {
this.value = obj;
}
Traverse.prototype.get = function (ps) {
var node = this.value;
for (var i = 0; i < ps.length; i ++) {
var key = ps[i];
if (!node || !hasOwnProperty.call(node, key)) {
node = undefined;
break;
}
node = node[key];
}
return node;
};
Traverse.prototype.has = function (ps) {
var node = this.value;
for (var i = 0; i < ps.length; i ++) {
var key = ps[i];
if (!node || !hasOwnProperty.call(node, key)) {
return false;
}
node = node[key];
}
return true;
};
Traverse.prototype.set = function (ps, value) {
var node = this.value;
for (var i = 0; i < ps.length - 1; i ++) {
var key = ps[i];
if (!hasOwnProperty.call(node, key)) node[key] = {};
node = node[key];
}
node[ps[i]] = value;
return value;
};
Traverse.prototype.map = function (cb) {
return walk(this.value, cb, true);
};
Traverse.prototype.forEach = function (cb) {
this.value = walk(this.value, cb, false);
return this.value;
};
Traverse.prototype.reduce = function (cb, init) {
var skip = arguments.length === 1;
var acc = skip ? this.value : init;
this.forEach(function (x) {
if (!this.isRoot || !skip) {
acc = cb.call(this, acc, x);
}
});
return acc;
};
Traverse.prototype.paths = function () {
var acc = [];
this.forEach(function (x) {
acc.push(this.path);
});
return acc;
};
Traverse.prototype.nodes = function () {
var acc = [];
this.forEach(function (x) {
acc.push(this.node);
});
return acc;
};
Traverse.prototype.clone = function () {
var parents = [], nodes = [];
return (function clone (src) {
for (var i = 0; i < parents.length; i++) {
if (parents[i] === src) {
return nodes[i];
}
}
if (typeof src === 'object' && src !== null) {
var dst = copy(src);
parents.push(src);
nodes.push(dst);
forEach(objectKeys(src), function (key) {
dst[key] = clone(src[key]);
});
parents.pop();
nodes.pop();
return dst;
}
else {
return src;
}
})(this.value);
};
function walk (root, cb, immutable) {
var path = [];
var parents = [];
var alive = true;
return (function walker (node_) {
var node = immutable ? copy(node_) : node_;
var modifiers = {};
var keepGoing = true;
var state = {
node : node,
node_ : node_,
path : [].concat(path),
parent : parents[parents.length - 1],
parents : parents,
key : path.slice(-1)[0],
isRoot : path.length === 0,
level : path.length,
circular : null,
update : function (x, stopHere) {
if (!state.isRoot) {
state.parent.node[state.key] = x;
}
state.node = x;
if (stopHere) keepGoing = false;
},
'delete' : function (stopHere) {
delete state.parent.node[state.key];
if (stopHere) keepGoing = false;
},
remove : function (stopHere) {
if (isArray(state.parent.node)) {
state.parent.node.splice(state.key, 1);
}
else {
delete state.parent.node[state.key];
}
if (stopHere) keepGoing = false;
},
keys : null,
before : function (f) { modifiers.before = f },
after : function (f) { modifiers.after = f },
pre : function (f) { modifiers.pre = f },
post : function (f) { modifiers.post = f },
stop : function () { alive = false },
block : function () { keepGoing = false }
};
if (!alive) return state;
function updateState() {
if (typeof state.node === 'object' && state.node !== null) {
if (!state.keys || state.node_ !== state.node) {
state.keys = objectKeys(state.node)
}
state.isLeaf = state.keys.length == 0;
for (var i = 0; i < parents.length; i++) {
if (parents[i].node_ === node_) {
state.circular = parents[i];
break;
}
}
}
else {
state.isLeaf = true;
state.keys = null;
}
state.notLeaf = !state.isLeaf;
state.notRoot = !state.isRoot;
}
updateState();
// use return values to update if defined
var ret = cb.call(state, state.node);
if (ret !== undefined && state.update) state.update(ret);
if (modifiers.before) modifiers.before.call(state, state.node);
if (!keepGoing) return state;
if (typeof state.node == 'object'
&& state.node !== null && !state.circular) {
parents.push(state);
updateState();
forEach(state.keys, function (key, i) {
path.push(key);
if (modifiers.pre) modifiers.pre.call(state, state.node[key], key);
var child = walker(state.node[key]);
if (immutable && hasOwnProperty.call(state.node, key)) {
state.node[key] = child.node;
}
child.isLast = i == state.keys.length - 1;
child.isFirst = i == 0;
if (modifiers.post) modifiers.post.call(state, child);
path.pop();
});
parents.pop();
}
if (modifiers.after) modifiers.after.call(state, state.node);
return state;
})(root).node;
}
function copy (src) {
if (typeof src === 'object' && src !== null) {
var dst;
if (isArray(src)) {
dst = [];
}
else if (isDate(src)) {
dst = new Date(src.getTime ? src.getTime() : src);
}
else if (isRegExp(src)) {
dst = new RegExp(src);
}
else if (isError(src)) {
dst = { message: src.message };
}
else if (isBoolean(src)) {
dst = new Boolean(src);
}
else if (isNumber(src)) {
dst = new Number(src);
}
else if (isString(src)) {
dst = new String(src);
}
else if (Object.create && Object.getPrototypeOf) {
dst = Object.create(Object.getPrototypeOf(src));
}
else if (src.constructor === Object) {
dst = {};
}
else {
var proto =
(src.constructor && src.constructor.prototype)
|| src.__proto__
|| {}
;
var T = function () {};
T.prototype = proto;
dst = new T;
}
forEach(objectKeys(src), function (key) {
dst[key] = src[key];
});
return dst;
}
else return src;
}
var objectKeys = Object.keys || function keys (obj) {
var res = [];
for (var key in obj) res.push(key)
return res;
};
function toS (obj) { return Object.prototype.toString.call(obj) }
function isDate (obj) { return toS(obj) === '[object Date]' }
function isRegExp (obj) { return toS(obj) === '[object RegExp]' }
function isError (obj) { return toS(obj) === '[object Error]' }
function isBoolean (obj) { return toS(obj) === '[object Boolean]' }
function isNumber (obj) { return toS(obj) === '[object Number]' }
function isString (obj) { return toS(obj) === '[object String]' }
var isArray = Array.isArray || function isArray (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
var forEach = function (xs, fn) {
if (xs.forEach) return xs.forEach(fn)
else for (var i = 0; i < xs.length; i++) {
fn(xs[i], i, xs);
}
};
forEach(objectKeys(Traverse.prototype), function (key) {
traverse[key] = function (obj) {
var args = [].slice.call(arguments, 1);
var t = new Traverse(obj);
return t[key].apply(t, args);
};
});
var hasOwnProperty = Object.hasOwnProperty || function (obj, key) {
return key in obj;
};
{
"_from": "traverse@>=0.2.4",
"_id": "traverse@0.6.6",
"_inBundle": false,
"_integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=",
"_location": "/traverse",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "traverse@>=0.2.4",
"name": "traverse",
"escapedName": "traverse",
"rawSpec": ">=0.2.4",
"saveSpec": null,
"fetchSpec": ">=0.2.4"
},
"_requiredBy": [
"/hashish"
],
"_resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
"_shasum": "cbdf560fd7b9af632502fed40f918c157ea97137",
"_spec": "traverse@>=0.2.4",
"_where": "/Users/jmorrell/workspace/heroku-buildpack-nodejs/test/fixtures/dependencies-modules-checked-in-without-devdependencies/node_modules/hashish",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"bugs": {
"url": "https://github.com/substack/js-traverse/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "traverse and transform objects by visiting every node on a recursive walk",
"devDependencies": {
"tape": "~1.0.4"
},
"directories": {
"example": "example",
"test": "test"
},
"homepage": "https://github.com/substack/js-traverse",
"keywords": [
"traverse",
"walk",
"recursive",
"map",
"forEach",
"deep",
"clone"
],
"license": "MIT",
"main": "index.js",
"name": "traverse",
"repository": {
"type": "git",
"url": "git://github.com/substack/js-traverse.git"
},
"scripts": {
"test": "tape test/*.js"
},
"testling": {
"files": "test/*.js",
"browsers": {
"iexplore": [
"6.0",
"7.0",
"8.0",
"9.0"
],
"chrome": [
"10.0",
"20.0"
],
"firefox": [
"10.0",
"15.0"
],
"safari": [
"5.1"
],
"opera": [
"12.0"
]
}
},
"version": "0.6.6"
}
var test = require('tape');
var traverse = require('../');
test('dateEach', function (t) {
var obj = { x : new Date, y : 10, z : 5 };
var counts = {};
traverse(obj).forEach(function (node) {
var t = (node instanceof Date && 'Date') || typeof node;
counts[t] = (counts[t] || 0) + 1;
});
t.same(counts, {
object : 1,
Date : 1,
number : 2,
});
t.end();
});
test('dateMap', function (t) {
var obj = { x : new Date, y : 10, z : 5 };
var res = traverse(obj).map(function (node) {
if (typeof node === 'number') this.update(node + 100);
});
t.ok(obj.x !== res.x);
t.same(res, {
x : obj.x,
y : 110,
z : 105,
});
t.end();
});
var test = require('tape');
var traverse = require('../');
test('traverse an Error', function (t) {
var obj = new Error("test");
var results = traverse(obj).map(function (node) {});
t.same(results, { message: 'test' });
t.end();
});
var test = require('tape');
var traverse = require('../');
test('has', function (t) {
var obj = { a : 2, b : [ 4, 5, { c : 6 } ] };
t.equal(traverse(obj).has([ 'b', 2, 'c' ]), true)
t.equal(traverse(obj).has([ 'b', 2, 'c', 0 ]), false)
t.equal(traverse(obj).has([ 'b', 2, 'd' ]), false)
t.equal(traverse(obj).has([]), true)
t.equal(traverse(obj).has([ 'a' ]), true)
t.equal(traverse(obj).has([ 'a', 2 ]), false)
t.end();
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment