Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge remote-tracking branch 'upstream/master' into abtris/heroku_nod…

…epack_upgrade
  • Loading branch information...
commit efde9f564ba0c7401fe263bdb8984c607cd590cf 2 parents d286139 + c962f1e
@abtris abtris authored
Showing with 3,299 additions and 2,321 deletions.
  1. +1 −0  .gitignore
  2. +0 −5 CHANGELOG.md
  3. +1 −1  LICENSE
  4. +72 −46 README.md
  5. +30 −0 bin/common.sh
  6. +102 −185 bin/compile
  7. +3 −5 bin/release
  8. +88 −38 bin/test
  9. +0 −79 support/aws/hmac
  10. +0 −230 support/aws/s3
  11. +0 −44 support/manifest
  12. +0 −48 support/package_nodejs
  13. +0 −53 support/package_npm
  14. +1 −0  test/caching/README.md
  15. +15 −0 test/caching/package.json
  16. +1 −0  test/dangerous-range-greater-than/README.md
  17. +12 −0 test/dangerous-range-greater-than/package.json
  18. +1 −0  test/dangerous-range-star/README.md
  19. +12 −0 test/dangerous-range-star/package.json
  20. +1 −0  test/invalid-dependency/README.md
  21. +15 −0 test/invalid-dependency/package.json
  22. +1 −0  test/invalid-node-version/README.md
  23. +12 −0 test/invalid-node-version/package.json
  24. +1 −0  test/modules-checked-in/README.md
  25. +191 −0 test/modules-checked-in/node_modules/hashish/README.markdown
  26. +9 −0 test/modules-checked-in/node_modules/hashish/examples/chain.js
  27. +7 −0 test/modules-checked-in/node_modules/hashish/examples/map.js
  28. +253 −0 test/modules-checked-in/node_modules/hashish/index.js
  29. +1 −0  test/modules-checked-in/node_modules/hashish/node_modules/traverse/.npmignore
  30. +3 −0  test/modules-checked-in/node_modules/hashish/node_modules/traverse/.travis.yml
  31. +24 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/LICENSE
  32. +16 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/json.js
  33. +15 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/leaves.js
  34. +8 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/negative.js
  35. +10 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/scrub.js
  36. +38 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/stringify.js
  37. +314 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/index.js
  38. +72 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/package.json
  39. +209 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/readme.markdown
  40. +117 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/circular.js
  41. +37 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/date.js
  42. +240 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/equal.js
  43. +11 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/error.js
  44. +15 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/has.js
  45. +17 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/instance.js
  46. +43 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/interface.js
  47. +49 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/json.js
  48. +31 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/keys.js
  49. +22 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/leaves.js
  50. +96 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/lib/deep_equal.js
  51. +300 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/mutability.js
  52. +21 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/negative.js
  53. +11 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/obj.js
  54. +37 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/siblings.js
  55. +44 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/stop.js
  56. +36 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/stringify.js
  57. +36 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/subexpr.js
  58. +56 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/test/super_deep.js
  59. +22 −0 test/modules-checked-in/node_modules/hashish/node_modules/traverse/testling/leaves.js
  60. +46 −0 test/modules-checked-in/node_modules/hashish/package.json
  61. +250 −0 test/modules-checked-in/node_modules/hashish/test/hash.js
  62. +69 −0 test/modules-checked-in/node_modules/hashish/test/property.js
  63. +15 −0 test/modules-checked-in/package.json
  64. +1 −0  test/no-script-hooks/README.md
  65. +12 −0 test/no-script-hooks/package.json
  66. +1 −0  test/no-version/README.md
  67. +9 −0 test/no-version/package.json
  68. +0 −9 test/package-json-invalidversion/package.json
  69. +0 −4 test/package-json-noversion/package.json
  70. +0 −9 test/package-json-version/package.json
  71. +1 −0  test/procfile-absent-npm-start-absent/README.md
  72. +15 −0 test/procfile-absent-npm-start-absent/package.json
  73. +1 −0  test/procfile-absent-npm-start-present/README.md
  74. +18 −0 test/procfile-absent-npm-start-present/package.json
  75. +1 −0  test/range-with-space/README.md
  76. +12 −0 test/range-with-space/package.json
  77. +1 −0  test/script-hooks/README.md
  78. +15 −0 test/script-hooks/package.json
  79. +1 −0  test/stable-node/README.md
  80. +15 −0 test/stable-node/package.json
  81. +1 −0  test/unstable-version/README.md
  82. +12 −0 test/unstable-version/package.json
  83. +7 −0 test/userconfig/.npmrc
  84. +1 −0  test/userconfig/README.md
  85. +15 −0 test/userconfig/package.json
  86. BIN  vendor/jq
  87. +0 −640 vendor/json/json
  88. +0 −23 vendor/node-semver/LICENSE
  89. +0 −119 vendor/node-semver/README.md
  90. +0 −71 vendor/node-semver/bin/semver
  91. +0 −11 vendor/node-semver/package.json
  92. +0 −304 vendor/node-semver/semver.js
  93. +0 −397 vendor/node-semver/test.js
View
1  .gitignore
@@ -1 +1,2 @@
.anvil
+.DS_Store
View
5 CHANGELOG.md
@@ -1,5 +0,0 @@
-## v7 (2012-03-27)
-
-* remove build asset caching
-* prevent installation of devDependencies
-* add warning if no nodejs version specified
View
2  LICENSE
@@ -1,6 +1,6 @@
MIT License:
-Copyright (C) 2012 Heroku, Inc.
+Copyright (C) 2012-2013 Heroku, Inc.
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:
View
118 README.md
@@ -1,65 +1,91 @@
-Heroku buildpack: Node.js with grunt support
-============================================
+Heroku Buildpack for Node.js
+============================
-This is a fork of [Heroku's official Node.js buildpack](https://github.com/heroku/heroku-buildpack-nodejs) with added [grunt](https://github.com/cowboy/grunt) support.
-Using this buildpack you do not need to commit the results of your grunt tasks (e.g. minification and concatination of files), keeping your repository clean.
+This is the official [Heroku buildpack](http://devcenter.heroku.com/articles/buildpacks) for Node.js apps. If you fork this repository, please **update this README** to explain what your fork does and why it's special.
-After all the default Node.js and NPM build tasks have finished, the buildpack checks if a gruntfile (`grunt.js`) exists and executes the `heroku` task by running `grunt heroku`. For details about grunt and how to define tasks, check out the [offical documentation](https://github.com/cowboy/grunt). You must add grunt to the NPM dependencies in your `package.json` file.
-If no gruntfile exists, the buildpacks simply skips the grunt step and executes like the standard Node.js buildpack.
-Usage
------
+How it Works
+------------
-Create a new app with this buildpack:
+Here's an overview of what this buildpack does:
- heroku create myapp --buildpack https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt.git
+- Uses the [semver.io](https://semver.io) webservice to find the latest version of node that satisfies the [engines.node semver range](https://npmjs.org/doc/json.html#engines) in your package.json.
+- Allows any recent version of node to be used, including [pre-release versions](https://semver.io/node.json).
+- Uses an [S3 caching proxy](https://github.com/heroku/s3pository#readme) of nodejs.org for faster downloads of the node binary.
+- Discourages use of dangerous semver ranges like `*` and `>0.10`.
+- Uses the version of `npm` that comes bundled with `node`.
+- Puts `node` and `npm` on the `PATH` so they can be executed with [heroku run](https://devcenter.heroku.com/articles/one-off-dynos#an-example-one-off-dyno).
+- Caches the `node_modules` directory across builds for fast deploys.
+- Doesn't use the cache if `node_modules` is checked into version control.
+- Runs `npm rebuild` if `node_modules` is checked into version control.
+- Always runs `npm install` to ensure [npm script hooks](https://npmjs.org/doc/misc/npm-scripts.html) are executed.
+- Always runs `npm prune` after restoring cached modules to ensure cleanup of unused dependencies.
-Or add this buildpack to your current app:
+For more technical details, see the [heavily-commented compile script](https://github.com/heroku/heroku-buildpack-nodejs/blob/master/bin/compile).
- heroku config:add BUILDPACK_URL=https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt.git
-Create your Node.js app and add a gruntfile named `grunt.js` with a `heroku` task:
+Documentation
+-------------
- grunt.registerTask('heroku', 'clean less mincss');
+For more information about using Node.js and buildpacks on Heroku, see these Dev Center articles:
-Don't forget to add grunt to your dependencies in `package.json`. If your grunt tasks depend on other pre-defined tasks make sure to add these dependencies as well:
+- [Heroku Node.js Support](https://devcenter.heroku.com/articles/nodejs-support)
+- [Getting Started with Node.js on Heroku](https://devcenter.heroku.com/articles/nodejs)
+- [Buildpacks](https://devcenter.heroku.com/articles/buildpacks)
+- [Buildpack API](https://devcenter.heroku.com/articles/buildpack-api)
- "dependencies": {
- ...
- "grunt": "*",
- "grunt-contrib": "*",
- "less": "*"
- }
-Push to heroku
+Legacy Compatibility
+--------------------
- git push heroku master
- ...
- -----> Heroku receiving push
- -----> Fetching custom buildpack... done
- -----> Node.js app detected
- -----> Resolving engine versions
- Using Node.js version: 0.8.2
- Using npm version: 1.1.41
- -----> Fetching Node.js binaries
- -----> Vendoring node into slug
- -----> Installing dependencies with npm
- ...
- Dependencies installed
- -----> Building runtime environment
- -----> Found gruntfile, running grunt heroku task
- Running "heroku" task
- ...
- -----> Discovering process types
+For most Node.js apps this buildpack should work just fine. If, however, you're unable to deploy using this new version of the buildpack, you can get your app working again by using the legacy branch:
-Further Information
--------------------
+```
+heroku config:set BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-nodejs#legacy -a my-app
+git commit -am "empty" --allow-empty # force a git commit
+git push heroku master
+```
-[Heroku: Buildpacks](https://devcenter.heroku.com/articles/buildpacks)
+Then please open a support ticket at [help.heroku.com](https://help.heroku.com/) so we can diagnose and get your app running on the default buildpack.
-[Heroku: Getting Started with Node.js](https://devcenter.heroku.com/articles/nodejs)
+Hacking
+-------
-[Buildpacks: Heroku for Everything](http://blog.heroku.com/archives/2012/7/17/buildpacks/)
+To make changes to this buildpack, fork it on Github. Push up changes to your fork, then create a new Heroku app to test it, or configure an existing app to use your buildpack:
-[Grunt: a task-based command line build tool for JavaScript projects](http://gruntjs.com/)
+```
+# Create a new Heroku app that uses your buildpack
+heroku create --buildpack <your-github-url>
+# Configure an existing Heroku app to use your buildpack
+heroku config:set BUILDPACK_URL=<your-github-url>
+
+# You can also use a git branch!
+heroku config:set BUILDPACK_URL=<your-github-url>#your-branch
+```
+
+For more detailed information about testing buildpacks, see [CONTRIBUTING.md](CONTRIBUTING.md)
+
+
+Testing
+-------
+
+[Anvil](https://github.com/ddollar/anvil) is a generic build server for Heroku.
+
+```
+gem install anvil-cli
+```
+
+The [heroku-anvil CLI plugin](https://github.com/ddollar/heroku-anvil) is a wrapper for anvil.
+
+```
+heroku plugins:install https://github.com/ddollar/heroku-anvil
+```
+
+The [ddollar/test](https://github.com/ddollar/buildpack-test) buildpack runs `bin/test` on your app/buildpack.
+
+```
+heroku build -b ddollar/test # -b can also point to a local directory
+```
+
+For more info on testing, see [Best Practices for Testing Buildpacks](https://discussion.heroku.com/t/best-practices-for-testing-buildpacks/294) on the Heroku discussion forum.
View
30 bin/common.sh
@@ -0,0 +1,30 @@
+error() {
+ echo " ! $*" >&2
+ exit 1
+}
+
+status() {
+ echo "-----> $*"
+}
+
+protip() {
+ echo
+ echo "PRO TIP: $*" | indent
+ echo "See https://devcenter.heroku.com/articles/nodejs-support" | indent
+ echo
+}
+
+# sed -l basically makes sed replace and buffer through stdin to stdout
+# so you get updates while the command runs and dont wait for the end
+# e.g. npm install | indent
+indent() {
+ c='s/^/ /'
+ case $(uname) in
+ Darwin) sed -l "$c";; # mac/bsd sed: -l buffers on line boundaries
+ *) sed -u "$c";; # unix/gnu sed: -u unbuffered (arbitrary) chunks of data
+ esac
+}
+
+cat_npm_debug_log() {
+ test -f $build_dir/npm-debug.log && cat $build_dir/npm-debug.log
+}
View
287 bin/compile
@@ -1,197 +1,114 @@
#!/usr/bin/env bash
-# bin/compile <build-dir> <cache-dir>
-
-# fail fast
-set -e
-
-# debug
-# set -x
-
-# preserve git directory, which is in /app/repo.git
-# GIT_DIR is '.', otherwise it would be good to join'em
-export APPLICATION_GIT_DIR=`pwd`
-
-# clean up leaking environment
-unset GIT_DIR
-
-# config
-SCONS_VERSION="1.2.0"
-S3_BUCKET="heroku-buildpack-nodejs"
-
-# parse and derive params
-BUILD_DIR=$1
-CACHE_DIR=$2
-LP_DIR=`cd $(dirname $0); cd ..; pwd`
-
-function error() {
- echo " ! $*" >&2
- exit 1
-}
-
-function mktmpdir() {
- dir=$(mktemp -t node-$1-XXXX)
- rm -rf $dir
- mkdir -p $dir
- echo $dir
-}
-
-function indent() {
- c='s/^/ /'
- case $(uname) in
- Darwin) sed -l "$c";;
- *) sed -u "$c";;
- esac
-}
-
-function run_npm() {
- command="$1"
-
- cd $BUILD_DIR
- HOME="$BUILD_DIR" $VENDORED_NODE/bin/node $VENDORED_NPM/cli.js $command 2>&1 | indent
-
- if [ "${PIPESTATUS[*]}" != "0 0" ]; then
- echo " ! Failed to $command dependencies with npm"
- exit 1
- fi
-}
-function manifest_versions() {
- curl "http://${S3_BUCKET}.s3.amazonaws.com/manifest.${1}" -s -o - | tr -s '\n' ' '
-}
+set -e # fail fast
+set -o pipefail # don't ignore exit codes when piping output
+# set -x # enable debugging
-function resolve_version() {
- available_versions="$1"
- requested_version="$2"
- default_version="$3"
+# Configure directories
+build_dir=$1
+cache_dir=$2
+bp_dir=$(cd $(dirname $0); cd ..; pwd)
- if [ "$2" == "" ]; then
- echo $3
- else
- args=""
- for version in $available_versions; do args="${args} -v \"${version}\""; done
- for version in $requested_version; do args="${args} -r \"${version}\""; done
- evaluated_version=$(eval $bootstrap_node/bin/node $LP_DIR/vendor/node-semver/bin/semver ${args} || echo "")
- echo "$evaluated_version" | tail -n 1
- fi
-}
+# Load some convenience functions like status(), echo(), and indent()
+source $bp_dir/bin/common.sh
-function package_engine_version() {
- version=$(cat $BUILD_DIR/package.json | $bootstrap_node/bin/node $LP_DIR/vendor/json/json engines.$1 2>/dev/null)
- if [ $? == 0 ]; then
- echo $version | sed -e 's/\([<>=]\) /\1/g'
- fi
-}
+# Output npm debug info on error
+trap cat_npm_debug_log ERR
-function package_resolve_version() {
- engine="$1"
- resolved_version=$(resolve_version "${engine_versions[$engine]}" "${engine_requests[$engine]}" "${engine_defaults[$engine]}")
+# Look in package.json's engines.node field for a semver range
+semver_range=$(cat $build_dir/package.json | $bp_dir/vendor/jq -r .engines.node)
- if [ "${resolved_version}" == "" ]; then
- error "Requested engine $engine version ${engine_requests[$engine]} does not match available versions: ${engine_versions[$engine]}"
- else
- echo $resolved_version
- fi
-}
-
-function package_download() {
- engine="$1"
- version="$2"
- location="$3"
-
- mkdir -p $location
- package="http://${S3_BUCKET}.s3.amazonaws.com/$engine-$version.tgz"
- curl $package -s -o - | tar xzf - -C $location
-}
-
-bootstrap_node=$(mktmpdir bootstrap_node)
-package_download "nodejs" "0.4.7" $bootstrap_node
-
-# make some associative arrays
-declare -A engine_versions
-declare -A engine_defaults
-declare -A engine_requests
-
-engine_defaults["node"]="0.4.7"
-engine_defaults["npm"]="1.0.106"
-
-engine_versions["node"]=$(manifest_versions "nodejs")
-engine_requests["node"]=$(package_engine_version "node")
-
-engine_versions["npm"]=$(manifest_versions "npm")
-engine_requests["npm"]=$(package_engine_version "npm")
-
-echo "-----> Resolving engine versions"
-
-# add a warning if no version of node specified
-if [ "${engine_requests["node"]}" == "" ]; then
- echo
- echo "WARNING: No version of Node.js specified in package.json, see:" | indent
- echo "https://devcenter.heroku.com/articles/nodejs-versions" | indent
- echo
- echo "WARNING: The default version of Node.js and npm on Heroku will begin" | indent
- echo "tracking the latest stable release starting September 1, 2012." | indent
- echo
+# Resolve node version using semver.io
+node_version=$(curl --silent --get --data-urlencode "range=${semver_range}" https://semver.io/node/resolve)
+
+# Recommend using semver ranges in a safe manner
+if [ "$semver_range" == "null" ]; then
+ protip "Specify a node version in package.json"
+ semver_range=""
+elif [ "$semver_range" == "*" ]; then
+ protip "Avoid using semver ranges like '*' in engines.node"
+elif [ ${semver_range:0:1} == ">" ]; then
+ protip "Avoid using semver ranges starting with '>' in engines.node"
+fi
+
+# Output info about requested range and resolved node version
+if [ "$semver_range" == "" ]; then
+ status "Defaulting to latest stable node: $node_version"
+else
+ status "Requested node range: $semver_range"
+ status "Resolved node version: $node_version"
+fi
+
+# Download node from Heroku's S3 mirror of nodejs.org/dist
+status "Downloading and installing node"
+node_url="http://s3pository.heroku.com/node/v$node_version/node-v$node_version-linux-x64.tar.gz"
+curl $node_url -s -o - | tar xzf - -C $build_dir
+
+# Move node (and npm) into ./vendor and make them executable
+mkdir -p $build_dir/vendor
+mv $build_dir/node-v$node_version-linux-x64 $build_dir/vendor/node
+chmod +x $build_dir/vendor/node/bin/*
+PATH=$PATH:$build_dir/vendor/node/bin
+
+# Run subsequent node/npm commands from the build path
+cd $build_dir
+
+# If node_modules directory is checked into source control then
+# rebuild any native deps. Otherwise, restore from the build cache.
+if test -d $build_dir/node_modules; then
+ status "Found existing node_modules directory; skipping cache"
+ status "Rebuilding any native dependencies"
+ npm rebuild 2>&1 | indent
+elif test -d $cache_dir/node_modules; then
+ status "Restoring node_modules directory from cache"
+ cp -r $cache_dir/node_modules $build_dir/
+
+ status "Pruning cached dependencies not specified in package.json"
+ npm prune 2>&1 | indent
fi
-NODE_VERSION=$(package_resolve_version "node")
-echo "Using Node.js version: ${NODE_VERSION}" | indent
-
-NPM_VERSION=$(package_resolve_version "npm")
-echo "Using npm version: ${NPM_VERSION}" | indent
-
-# cache directories
-CACHE_STORE_DIR="$CACHE_DIR/node_modules/$NODE_VERSION/$NPM_VERSION"
-CACHE_TARGET_DIR="$BUILD_DIR/node_modules"
-
-# s3 packages
-NODE_PACKAGE="http://${S3_BUCKET}.s3.amazonaws.com/nodejs-${NODE_VERSION}.tgz"
-NPM_PACKAGE="http://${S3_BUCKET}.s3.amazonaws.com/npm-${NPM_VERSION}.tgz"
-SCONS_PACKAGE="http://${S3_BUCKET}.s3.amazonaws.com/scons-${SCONS_VERSION}.tgz"
-
-# vendor directories
-VENDORED_NODE="$(mktmpdir node)"
-VENDORED_NPM="$(mktmpdir npm)"
-VENDORED_SCONS="$(mktmpdir scons)"
-
-# download and unpack packages
-echo "-----> Fetching Node.js binaries"
-package_download "nodejs" "${NODE_VERSION}" "${VENDORED_NODE}"
-package_download "npm" "${NPM_VERSION}" "${VENDORED_NPM}"
-package_download "scons" "${SCONS_VERSION}" "${VENDORED_SCONS}"
-
-# vendor node into the slug
-PATH="$BUILD_DIR/bin:$PATH"
-echo "-----> Vendoring node into slug"
-mkdir -p "$BUILD_DIR/bin"
-cp "$VENDORED_NODE/bin/node" "$BUILD_DIR/bin/node"
-
-# setting up paths for building
-PATH="$VENDORED_SCONS:$VENDORED_NODE/bin:$PATH"
-INCLUDE_PATH="$VENDORED_NODE/include"
-export CPATH="$INCLUDE_PATH"
-export CPPPATH="$INCLUDE_PATH"
-
-# install dependencies with npm
-echo "-----> Installing dependencies with npm"
-run_npm "install --production"
-run_npm "rebuild"
-echo "Dependencies installed" | indent
-
-echo "-----> Building runtime environment"
-if [ -f /app/tmp/push_metadata.yml ]; then
- export APPLICATION_PUSH_METADATA_FILE="/app/tmp/push_metadata.yml"
-fi;
-export APPLICATION_TMP_REPODIR=$(mktmpdir actual_repository)
-mkdir -p $BUILD_DIR/.profile.d
-echo "export PATH=\"\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\"" > $BUILD_DIR/.profile.d/nodejs.sh
-
-if [ -f $BUILD_DIR/grunt.js ] || [ -f $BUILD_DIR/Gruntfile.js ] || [ -f $BUILD_DIR/Gruntfile.coffee ]; then
- echo "-----> Found Gruntfile, running grunt heroku task"
- if [ -f $BUILD_DIR/node_modules/grunt-cli/bin/grunt ]; then
- $BUILD_DIR/node_modules/grunt-cli/bin/grunt heroku
+# Make npm output to STDOUT instead of its default STDERR
+status "Installing dependencies"
+npm install --userconfig $build_dir/.npmrc --production 2>&1 | indent
+
+status "Caching node_modules directory for future builds"
+rm -rf $cache_dir/node_modules
+mkdir -p $cache_dir
+test -d $build_dir/node_modules && cp -r $build_dir/node_modules $cache_dir/
+
+status "Cleaning up node-gyp and npm artifacts"
+rm -rf "$build_dir/.node-gyp"
+rm -rf "$build_dir/.npm"
+
+# If Procfile is absent, try to create one using `npm start`
+if [ ! -e $build_dir/Procfile ]; then
+ npm_start=$(cat $build_dir/package.json | $bp_dir/vendor/jq -r .scripts.start)
+
+ # If `scripts.start` is set in package.json, or a server.js file
+ # is present in the app root, then create a default Procfile
+ if [ "$npm_start" != "null" ] || [ -f $build_dir/server.js ]; then
+ status "No Procfile found; Adding npm start to new Procfile"
+ echo "web: npm start" > $build_dir/Procfile
else
- $BUILD_DIR/node_modules/grunt/bin/grunt heroku
+ status "Procfile not found and npm start script is undefined"
+ protip "Create a Procfile or specify a start script in package.json"
fi
-else
- echo "-----> No gruntfile (grunt.js) found"
fi
+
+# Update the PATH
+status "Building runtime environment"
+mkdir -p $build_dir/.profile.d
+echo "export PATH=\"\$HOME/vendor/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\";" > $build_dir/.profile.d/nodejs.sh
+
+# Post package.json to nomnom service
+# Use a subshell so failures won't break the build.
+(
+ curl \
+ --data @$build_dir/package.json \
+ --fail \
+ --silent \
+ --request POST \
+ --header "content-type: application/json" \
+ https://nomnom.heroku.com/?request_id=$REQUEST_ID \
+ > /dev/null
+) &
View
8 bin/release
@@ -1,8 +1,6 @@
#!/usr/bin/env bash
# bin/release <build-dir>
-
-cat <<EOF
----
-config_vars:
- PATH: bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin
+cat << EOF
+addons: []
+default_process_types: {}
EOF
View
126 bin/test
@@ -1,15 +1,8 @@
#!/usr/bin/env bash
-
-#
-# Create a Heroku app with the following buildpack:
-# https://github.com/ddollar/buildpack-test
-#
-# Push this Node.js buildpack to that Heroku app to
-# run the tests
-#
+# See README.md for info on running these tests.
testDetectWithPackageJson() {
- detect "package-json-version"
+ detect "stable-node"
assertCaptured "Node.js"
assertCapturedSuccess
}
@@ -19,48 +12,105 @@ testDetectWithoutPackageJson() {
assertCapturedError 1 ""
}
-testPackageJsonWithVersion() {
- compile "package-json-version"
- assertNotCaptured "WARNING: No version of Node.js specified"
- assertCaptured "Using Node.js version: 0.6.11"
- assertCaptured "Using npm version: 1.1.9"
+testNoVersion() {
+ compile "no-version"
+ assertCaptured "PRO TIP: Specify a node version in package.json"
+ assertCaptured "Defaulting to latest stable node"
assertCapturedSuccess
}
-testPackageJsonWithoutVersion() {
- compile "package-json-noversion"
- assertCaptured "WARNING: No version of Node.js specified"
- assertCaptured "Using Node.js version: 0.4.7"
- assertCaptured "Using npm version: 1.0.106"
+testDangerousRangeStar() {
+ compile "dangerous-range-star"
+ assertCaptured "PRO TIP: Avoid using semver ranges like '*'"
+ assertCaptured "Requested node range: *"
+ assertCaptured "Resolved node version: 0.10"
assertCapturedSuccess
}
-testPackageJsonWithInvalidVersion() {
- compile "package-json-invalidversion"
- assertCapturedError 1 "Requested engine npm version 1.1.5 does not"
+testDangerousRangeGreaterThan() {
+ compile "dangerous-range-greater-than"
+ assertCaptured "PRO TIP: Avoid using semver ranges starting with '>'"
+ assertCaptured "Requested node range: >"
+ assertCaptured "Resolved node version: 0.10."
+ assertCapturedSuccess
}
-testNothingCached() {
- cache=$(mktmpdir)
- compile "package-json-version" $cache
+testRangeWithSpace() {
+ compile "range-with-space"
+ assertCaptured "Requested node range: >= 0.8.x"
+ assertCaptured "Resolved node version: 0.10."
+ assertCapturedSuccess
+}
+
+testStableVersion() {
+ compile "stable-node"
+ assertNotCaptured "PRO TIP: Avoid using semver"
+ assertNotCaptured "PRO TIP: Specify"
+ assertCaptured "Resolved node version"
+ assertCapturedSuccess
+}
+
+testUnstableVersion() {
+ compile "unstable-version"
+ assertCaptured "Requested node range: >0.11.0"
+ assertCaptured "Resolved node version: 0.11."
assertCapturedSuccess
- assertEquals "0" "$(ls -1 $cache | wc -l)"
}
testProfileCreated() {
- compile "package-json-version"
+ compile "stable-node"
assertCaptured "Building runtime environment"
- assertFile "export PATH=\"\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\"" ".profile.d/nodejs.sh"
+ assertFile "export PATH=\"\$HOME/vendor/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\";" ".profile.d/nodejs.sh"
+ assertCapturedSuccess
+}
+
+testInvalidDependency() {
+ compile "invalid-dependency"
+ assertCaptured "not in the npm registry"
+ assertCapturedError 1 ""
+}
+
+testNodeModulesCached() {
+ cache=$(mktmpdir)
+ compile "caching" $cache
+ assertCaptured "Caching node"
+ assertEquals "1" "$(ls -1 $cache/ | wc -l)"
+}
+
+testModulesCheckedIn() {
+ compile "modules-checked-in"
+ assertCaptured "Found existing node_modules directory; skipping cache"
+ assertCaptured "Rebuilding any native dependencies"
+ assertCapturedSuccess
+}
+
+testUserConfig() {
+ compile "userconfig"
+ assertCaptured "https://www.google.com/"
+ assertCaptured "registry error"
+ assertCapturedError 1 ""
+}
+
+testProcfileAbsentNpmStartPresent() {
+ compile "procfile-absent-npm-start-present"
+ assertCaptured "No Procfile found; Adding npm start to new Procfile"
+ assertFile "web: npm start" "Procfile"
+ assertCapturedSuccess
+}
+
+testProcfileAbsentNpmStartAbsent() {
+ compile "procfile-absent-npm-start-absent"
+ assertCaptured "Create a Procfile or specify a start script in package.json"
assertCapturedSuccess
}
-## utils ########################################
+# Utils
pushd $(dirname 0) >/dev/null
-BASE=$(pwd)
+bp_dir=$(pwd)
popd >/dev/null
-source ${BASE}/vendor/test-utils/test-utils
+source ${bp_dir}/vendor/test-utils/test-utils
mktmpdir() {
dir=$(mktemp -t testXXXXX)
@@ -70,19 +120,19 @@ mktmpdir() {
}
detect() {
- capture ${BASE}/bin/detect ${BASE}/test/$1
+ capture ${bp_dir}/bin/detect ${bp_dir}/test/$1
}
-COMPILE_DIR=""
+compile_dir=""
compile() {
- COMPILE_DIR=$(mktmpdir)
- cp -r ${BASE}/test/$1/* ${COMPILE_DIR}/
- capture ${BASE}/bin/compile ${COMPILE_DIR} $2
+ compile_dir=$(mktmpdir)
+ cp -r ${bp_dir}/test/$1/. ${compile_dir}
+ capture ${bp_dir}/bin/compile ${compile_dir} ${2:-$(mktmpdir)}
}
assertFile() {
- assertEquals "$1" "$(cat ${COMPILE_DIR}/$2)"
+ assertEquals "$1" "$(cat ${compile_dir}/$2)"
}
-source ${BASE}/vendor/shunit2/shunit2
+source ${bp_dir}/vendor/shunit2/shunit2
View
79 support/aws/hmac
@@ -1,79 +0,0 @@
-#!/bin/bash
-# Implement HMAC functionality on top of the OpenSSL digest functions.
-# licensed under the terms of the GNU GPL v2
-# Copyright 2007 Victor Lowther <victor.lowther@gmail.com>
-
-die() {
- echo $*
- exit 1
-}
-
-check_deps() {
- local res=0
- while [ $# -ne 0 ]; do
- which "${1}" >& /dev/null || { res=1; echo "${1} not found."; }
- shift
- done
- (( res == 0 )) || die "aborting."
-}
-
-# write a byte (passed as hex) to stdout
-write_byte() {
- # $1 = byte to write
- printf "\\x$(printf "%x" ${1})"
-}
-
-# make an hmac pad out of a key.
-# this is not the most secure way of doing it, but it is
-# the most expedient.
-make_hmac_pad() {
- # using key in file $1 and byte in $2, create the appropriate hmac pad
- # Pad keys out to $3 bytes
- # if key is longer than $3, use hash $4 to hash the key first.
- local x y a size remainder oifs
- (( remainder = ${3} ))
- # in case someone else was messing with IFS.
- for x in $(echo -n "${1}" | od -v -t u1 | cut -b 9-);
- do
- write_byte $((${x} ^ ${2}))
- (( remainder -= 1 ))
- done
- for ((y=0; remainder - y ;y++)); do
- write_byte $((0 ^ ${2}))
- done
-}
-
-# utility functions for making hmac pads
-hmac_ipad() {
- make_hmac_pad "${1}" 0x36 ${2} "${3}"
-}
-
-hmac_opad() {
- make_hmac_pad "${1}" 0x5c ${2} "${3}"
-}
-
-# hmac something
-do_hmac() {
- # $1 = algo to use. Must be one that openssl knows about
- # $2 = keyfile to use
- # $3 = file to hash. uses stdin if none is given.
- # accepts input on stdin, leaves it on stdout.
- # Output is binary, if you want something else pipe it accordingly.
- local blocklen keysize x
- case "${1}" in
- sha) blocklen=64 ;;
- sha1) blocklen=64 ;;
- md5) blocklen=64 ;;
- md4) blocklen=64 ;;
- sha256) blocklen=64 ;;
- sha512) blocklen=128 ;;
- *) die "Unknown hash ${1} passed to hmac!" ;;
- esac
- cat <(hmac_ipad ${2} ${blocklen} "${1}") "${3:--}" | openssl dgst "-${1}" -binary | \
- cat <(hmac_opad ${2} ${blocklen} "${1}") - | openssl dgst "-${1}" -binary
-}
-
-[[ ${1} ]] || die "Must pass the name of the hash function to use to ${0}".
-
-check_deps od openssl
-do_hmac "${@}"
View
230 support/aws/s3
@@ -1,230 +0,0 @@
-#!/bin/bash
-# basic amazon s3 operations
-# Licensed under the terms of the GNU GPL v2
-# Copyright 2007 Victor Lowther <victor.lowther@gmail.com>
-
-set -e
-
-basedir="$( cd -P "$( dirname "$0" )" && pwd )"
-PATH="$basedir:$PATH"
-
-# print a message and bail
-die() {
- echo $*
- exit 1
-}
-
-# check to see if the variable name passed exists and holds a value.
-# Die if it does not.
-check_or_die() {
- [[ ${!1} ]] || die "Environment variable ${1} is not set."
-}
-
-# check to see if we have all the needed S3 variables defined.
-# Bail if we do not.
-check_s3() {
- local sak x
- for x in AWS_ID AWS_SECRET; do
- check_or_die ${x};
- done
- sak="$(echo -n $AWS_SECRET | wc -c)"
- (( ${sak%%[!0-9 ]*} == 40 )) || \
- die "S3 Secret Access Key is not exactly 40 bytes long. Please fix it."
-}
-# check to see if our external dependencies exist
-check_dep() {
- local res=0
- while [[ $# -ne 0 ]]; do
- which "${1}" >& /dev/null || { res=1; echo "${1} not found."; }
- shift
- done
- (( res == 0 )) || die "aborting."
-}
-
-check_deps() {
- check_dep openssl date hmac cat grep curl
- check_s3
-}
-
-urlenc() {
- # $1 = string to url encode
- # output is on stdout
- # we don't urlencode everything, just enough stuff.
- echo -n "${1}" |
- sed 's/%/%25/g
- s/ /%20/g
- s/#/%23/g
- s/\$/%24/g
- s/\&/%26/g
- s/+/%2b/g
- s/,/%2c/g
- s/:/%3a/g
- s/;/%3b/g
- s/?/%3f/g
- s/@/%40/g
- s/ /%09/g'
-}
-
-xmldec() {
- # no parameters.
- # accept input on stdin, put it on stdout.
- # patches accepted to get more stuff
- sed 's/\&quot;/\"/g
- s/\&amp;/\&/g
- s/\&lt;/</g
- s/\&gt;/>/g'
-}
-
-## basic S3 functionality. x-amz-header functionality is not implemented.
-# make an S3 signature string, which will be output on stdout.
-s3_signature_string() {
- # $1 = HTTP verb
- # $2 = date string, must be in UTC
- # $3 = bucket name, if any
- # $4 = resource path, if any
- # $5 = content md5, if any
- # $6 = content MIME type, if any
- # $7 = canonicalized headers, if any
- # signature string will be output on stdout
- local verr="Must pass a verb to s3_signature_string!"
- local verb="${1:?verr}"
- local bucket="${3}"
- local resource="${4}"
- local derr="Must pass a date to s3_signature_string!"
- local date="${2:?derr}"
- local mime="${6}"
- local md5="${5}"
- local headers="${7}"
- printf "%s\n%s\n%s\n%s\n%s\n%s%s" \
- "${verb}" "${md5}" "${mime}" "${date}" \
- "${headers}" "${bucket}" "${resource}" | \
- hmac sha1 "${AWS_SECRET}" | openssl base64 -e -a
-}
-
-# cheesy, but it is the best way to have multiple headers.
-curl_headers() {
- # each arg passed will be output on its own line
- local parms=$#
- for ((;$#;)); do
- echo "header = \"${1}\""
- shift
- done
-}
-
-s3_curl() {
- # invoke curl to do all the heavy HTTP lifting
- # $1 = method (one of GET, PUT, or DELETE. HEAD is not handled yet.)
- # $2 = remote bucket.
- # $3 = remote name
- # $4 = local name.
- # $5 = mime type
- local bucket remote date sig md5 arg inout headers
- # header handling is kinda fugly, but it works.
- bucket="${2:+/${2}}/" # slashify the bucket
- remote="$(urlenc "${3}")" # if you don't, strange things may happen.
- stdopts="--connect-timeout 10 --fail --silent"
- mime="${5}"
- [[ $CURL_S3_DEBUG == true ]] && stdopts="${stdopts} --show-error --fail"
- case "${1}" in
- GET) arg="-o" inout="${4:--}" # stdout if no $4
- headers[${#headers[@]}]="x-amz-acl: public-read"
- ;;
- PUT) [[ ${2} ]] || die "PUT can has bucket?"
- if [[ ! ${3} ]]; then
- arg="-X PUT"
- headers[${#headers[@]}]="Content-Length: 0"
- elif [[ -f ${4} ]]; then
- md5="$(openssl dgst -md5 -binary "${4}"|openssl base64 -e -a)"
- arg="-T" inout="${4}"
- headers[${#headers[@]}]="x-amz-acl: public-read"
- headers[${#headers[@]}]="Expect: 100-continue"
- if [ "$mime" != "" ]; then
- headers[${#headers[@]}]="Content-Type: $mime"
- fi
- else
- die "Cannot write non-existing file ${4}"
- fi
- ;;
- DELETE) arg="-X DELETE"
- ;;
- HEAD) arg="-I" ;;
- *) die "Unknown verb ${1}. It probably would not have worked anyways." ;;
- esac
- date="$(TZ=UTC date '+%a, %e %b %Y %H:%M:%S %z')"
- sig=$(s3_signature_string ${1} "${date}" "${bucket}" "${remote}" "${md5}" "${mime}" "x-amz-acl:public-read")
-
- headers[${#headers[@]}]="Authorization: AWS ${AWS_ID}:${sig}"
- headers[${#headers[@]}]="Date: ${date}"
- [[ ${md5} ]] && headers[${#headers[@]}]="Content-MD5: ${md5}"
- curl ${arg} "${inout}" ${stdopts} -o - -K <(curl_headers "${headers[@]}") \
- "http://s3.amazonaws.com${bucket}${remote}"
- return $?
-}
-
-s3_put() {
- # $1 = remote bucket to put it into
- # $2 = remote name to put
- # $3 = file to put. This must be present if $2 is.
- # $4 = mime type
- s3_curl PUT "${1}" "${2}" "${3:-${2}}" "${4}"
- return $?
-}
-
-s3_get() {
- # $1 = bucket to get file from
- # $2 = remote file to get
- # $3 = local file to get into. Will be overwritten if it exists.
- # If this contains a path, that path must exist before calling this.
- s3_curl GET "${1}" "${2}" "${3:-${2}}"
- return $?
-}
-
-s3_test() {
- # same args as s3_get, but uses the HEAD verb instead of the GET verb.
- s3_curl HEAD "${1}" "${2}" >/dev/null
- return $?
-}
-
-# Hideously ugly, but it works well enough.
-s3_buckets() {
- s3_get |grep -o '<Name>[^>]*</Name>' |sed 's/<[^>]*>//g' |xmldec
- return $?
-}
-
-# this will only return the first thousand entries, alas
-# Mabye some kind soul can fix this without writing an XML parser in bash?
-# Also need to add xml entity handling.
-s3_list() {
- # $1 = bucket to list
- [ "x${1}" == "x" ] && return 1
- s3_get "${1}" |grep -o '<Key>[^>]*</Key>' |sed 's/<[^>]*>//g'| xmldec
- return $?
-}
-
-s3_delete() {
- # $1 = bucket to delete from
- # $2 = item to delete
- s3_curl DELETE "${1}" "${2}"
- return $?
-}
-
-# because this uses s3_list, it suffers from the same flaws.
-s3_rmrf() {
- # $1 = bucket to delete everything from
- s3_list "${1}" | while read f; do
- s3_delete "${1}" "${f}";
- done
-}
-
-check_deps
-case $1 in
- put) shift; s3_put "$@" ;;
- get) shift; s3_get "$@" ;;
- rm) shift; s3_delete "$@" ;;
- ls) shift; s3_list "$@" ;;
- test) shift; s3_test "$@" ;;
- buckets) s3_buckets ;;
- rmrf) shift; s3_rmrf "$@" ;;
- *) die "Unknown command ${1}."
- ;;
-esac
View
44 support/manifest
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-set -e
-
-manifest_type="$1"
-
-if [ "$manifest_type" == "" ]; then
- echo "usage: $0 <nodejs|npm>"
- exit 1
-fi
-
-if [ "$AWS_ID" == "" ]; then
- echo "must set AWS_ID"
- exit 1
-fi
-
-if [ "$AWS_SECRET" == "" ]; then
- echo "must set AWS_SECRET"
- exit 1
-fi
-
-if [ "$S3_BUCKET" == "" ]; then
- echo "must set S3_BUCKET"
- exit 1
-fi
-
-basedir="$( cd -P "$( dirname "$0" )" && pwd )"
-
-# make a temp directory
-tempdir="$( mktemp -t node_XXXX )"
-rm -rf $tempdir
-mkdir -p $tempdir
-pushd $tempdir
-
-# generate manifest
-$basedir/aws/s3 ls $S3_BUCKET \
- | grep "^${manifest_type}" \
- | sed -e "s/${manifest_type}-\([0-9.]*\)\\.tgz/\\1/" \
- | awk 'BEGIN {FS="."} {printf("%03d.%03d.%03d %s\n",$1,$2,$3,$0)}' | sort -r | cut -d" " -f2 \
- > manifest.${manifest_type}
-
-# upload manifest to s3
-$basedir/aws/s3 put $S3_BUCKET \
- manifest.${manifest_type} "" "text/plain"
View
48 support/package_nodejs
@@ -1,48 +0,0 @@
-#!/bin/sh
-
-set -e
-
-node_version="$1"
-
-if [ "$node_version" == "" ]; then
- echo "usage: $0 VERSION"
- exit 1
-fi
-
-if [ "$AWS_ID" == "" ]; then
- echo "must set AWS_ID"
- exit 1
-fi
-
-if [ "$AWS_SECRET" == "" ]; then
- echo "must set AWS_SECRET"
- exit 1
-fi
-
-if [ "$S3_BUCKET" == "" ]; then
- echo "must set S3_BUCKET"
- exit 1
-fi
-
-basedir="$( cd -P "$( dirname "$0" )" && pwd )"
-
-# make a temp directory
-tempdir="$( mktemp -t node_XXXX )"
-rm -rf $tempdir
-mkdir -p $tempdir
-cd $tempdir
-
-# build and package nodejs for heroku
-vulcan build -v \
- -n node \
- -c "cd node-v${node_version} && ./configure --prefix=/app/vendor/node && make install" \
- -p /app/vendor/node \
- -s http://nodejs.org/dist/v${node_version}/node-v${node_version}.tar.gz \
- -o $tempdir/node-${node_version}.tgz
-
-# upload nodejs to s3
-$basedir/aws/s3 put $S3_BUCKET \
- nodejs-${node_version}.tgz $tempdir/node-${node_version}.tgz
-
-# generate manifest
-$basedir/manifest nodejs
View
53 support/package_npm
@@ -1,53 +0,0 @@
-#!/bin/sh
-
-set -e
-
-npm_version="$1"
-
-if [ "$npm_version" == "" ]; then
- echo "usage: $0 VERSION"
- exit 1
-fi
-
-if [ "$AWS_ID" == "" ]; then
- echo "must set AWS_ID"
- exit 1
-fi
-
-if [ "$AWS_SECRET" == "" ]; then
- echo "must set AWS_SECRET"
- exit 1
-fi
-
-if [ "$S3_BUCKET" == "" ]; then
- echo "must set S3_BUCKET"
- exit 1
-fi
-
-basedir="$( cd -P "$( dirname "$0" )" && pwd )"
-
-# make a temp directory
-tempdir="$( mktemp -t node_XXXX )"
-rm -rf $tempdir
-mkdir -p $tempdir
-cd $tempdir
-
-# download npm
-git clone https://github.com/isaacs/npm.git
-
-# go into npm dir
-pushd npm
-
-# grab the right version
-git checkout v${npm_version}
-git submodule update --init --recursive
-
-# package it up
-tar czvf $tempdir/npm-${npm_version}.tgz *
-
-# upload npm to s3
-$basedir/aws/s3 put $S3_BUCKET \
- npm-${npm_version}.tgz $tempdir/npm-${npm_version}.tgz
-
-# generate manifest
-$basedir/manifest npm
View
1  test/caching/README.md
@@ -0,0 +1 @@
+A fake README, to keep npm from polluting stderr.
View
15 test/caching/package.json
@@ -0,0 +1,15 @@
+{
+ "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"
+ },
+ "engines": {
+ "node": "0.10.18"
+ },
+ "dependencies": {
+ "express": "latest"
+ }
+}
View
1  test/dangerous-range-greater-than/README.md
@@ -0,0 +1 @@
+A fake README, to keep npm from polluting stderr.
View
12 test/dangerous-range-greater-than/package.json
@@ -0,0 +1,12 @@
+{
+ "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"
+ },
+ "engines": {
+ "node": ">0.4"
+ }
+}
View
1  test/dangerous-range-star/README.md
@@ -0,0 +1 @@
+A fake README, to keep npm from polluting stderr.
View
12 test/dangerous-range-star/package.json
@@ -0,0 +1,12 @@
+{
+ "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"
+ },
+ "engines": {
+ "node": "*"
+ }
+}
View
1  test/invalid-dependency/README.md
@@ -0,0 +1 @@
+A fake README, to keep npm from polluting stderr.
View
15 test/invalid-dependency/package.json
@@ -0,0 +1,15 @@
+{
+ "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"
+ },
+ "engines": {
+ "node": "~0.10.20"
+ },
+ "dependencies": {
+ "some-crazy-dep-that-doesnt-exist": "*"
+ }
+}
View
1  test/invalid-node-version/README.md
@@ -0,0 +1 @@
+A fake README, to keep npm from polluting stderr.
View
12 test/invalid-node-version/package.json
@@ -0,0 +1,12 @@
+{
+ "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"
+ },
+ "engines": {
+ "node": ">2.0"
+ }
+}
View
1  test/modules-checked-in/README.md
@@ -0,0 +1 @@
+A fake README, to keep npm from polluting stderr.
View
191 test/modules-checked-in/node_modules/hashish/README.markdown
@@ -0,0 +1,191 @@
+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
View
9 test/modules-checked-in/node_modules/hashish/examples/chain.js
@@ -0,0 +1,9 @@
+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);
+ })
+;
View
7 test/modules-checked-in/node_modules/hashish/examples/map.js
@@ -0,0 +1,7 @@
+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);
View
253 test/modules-checked-in/node_modules/hashish/index.js
@@ -0,0 +1,253 @@
+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;
+};
View
1  test/modules-checked-in/node_modules/hashish/node_modules/traverse/.npmignore
@@ -0,0 +1 @@
+node_modules
View
3  test/modules-checked-in/node_modules/hashish/node_modules/traverse/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+ - 0.6
View
24 test/modules-checked-in/node_modules/hashish/node_modules/traverse/LICENSE
@@ -0,0 +1,24 @@
+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.
View
16 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/json.js
@@ -0,0 +1,16 @@
+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);
View
15 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/leaves.js
@@ -0,0 +1,15 @@
+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);
View
8 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/negative.js
@@ -0,0 +1,8 @@
+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);
View
10 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/scrub.js
@@ -0,0 +1,10 @@
+// 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);
View
38 test/modules-checked-in/node_modules/hashish/node_modules/traverse/examples/stringify.js
@@ -0,0 +1,38 @@
+#!/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);
View
314 test/modules-checked-in/node_modules/hashish/node_modules/traverse/index.js
@@ -0,0 +1,314 @@
+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) ===