*.swp
-test/bin
-test/output.log
-test/packages/*/node_modules
-test/packages/npm-test-depends-on-spark/which-spark.log
-test/packages/test-package/random-data.txt
-test/root
-node_modules/ronn
-node_modules/.bin
npm-debug.log
-./npmrc
-.gitignore
-release/
+/test/bin
+/test/output.log
+/test/packages/*/node_modules
+/test/packages/npm-test-depends-on-spark/which-spark.log
+/test/packages/test-package/random-data.txt
+/test/root
+/node_modules/ronn
+/node_modules/tap
+/node_modules/.bin
+/npmrc
+/release/
# don't need these in the npm package.
html/*.png
Dalmais Maxence <github@maxired.fr>
Marcus Ekwall <marcus.ekwall@gmail.com>
Aaron Stacy <aaron.r.stacy@gmail.com>
+Phillip Howell <phowell@cothm.org>
+Domenic Denicola <domenic@domenicdenicola.com>
+James Halliday <mail@substack.net>
+Jeremy Cantrell <jmcantrell@gmail.com>
+Ribettes <patlogan29@gmail.com>
"npm Logo" created by Mathias Pettersson and Brian Hammond,
used with permission.
+"Gubblebum Blocky" font
+Copyright (c) 2007 by Tjarda Koster, http://jelloween.deviantart.com
+included for use in the npm website and documentation,
+used with permission.
+
This program uses "node-uuid", Copyright (c) 2010 Robert Kieffer,
according to the terms of the MIT license.
git clone https://github.com/isaacs/npm.git
cd npm
- git submodule update --init --recursive
sudo make install # (or: `node cli.js install -gf`)
If you're sitting in the code folder reading this document in your
terminal, then you've already got the code. Just do:
- git submodule update --init --recursive
sudo make install
and npm will install itself.
-Note that github tarballs **do not contain submodules**, so
-those won't work. You'll have to also fetch the appropriate submodules
-listed in the .gitmodules file.
-
## Permissions when Using npm to Install Other Stuff
**tl;dr**
#!/bin/sh
-if [ -x "`dirname "$0"`/node.exe" ]; then
- "`dirname "$0"`/node.exe" "`dirname "$0"`/node_modules/npm/bin/npm-cli.js" "$@"
+
+basedir=`dirname "$0"`
+
+case `uname` in
+ *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node.exe" ]; then
+ "$basedir/node.exe" "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
else
- node "`dirname "$0"`/node_modules/npm/bin/npm-cli.js" "$@"
+ node "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
fi
process.title = "npm"
-var log = require("../lib/utils/log.js")
-log.waitForConfig()
-log.info("ok", "it worked if it ends with")
+var log = require("npmlog")
+log.pause() // will be unpaused when config is loaded.
+log.info("it worked if it ends with", "ok")
var fs = require("graceful-fs")
, path = require("path")
process.argv.splice(1, 1, "npm", "-g")
}
-log.verbose(process.argv, "cli")
+log.verbose("cli", process.argv)
var conf = nopt(types, shorthands)
npm.argv = conf.argv.remain
return
}
-log.info("npm@"+npm.version, "using")
-log.info("node@"+process.version, "using")
+log.info("using", "npm@%s", npm.version)
+log.info("using", "node@%s", process.version)
// make sure that this version of node works with this version of npm.
var semver = require("semver")
var fs = require("fs")
, file = argv[2]
- , readJson = require("../lib/utils/read-json")
+ , readJson = require("read-package-json")
readJson(file, function (er, data) {
if (er) throw er
Always create a new Error object with your message. Don't just return a
string message to the callback. Stack traces are handy.
-Use the `require("./utils/log").er` function. It takes a callback and an
-error message, and returns an object that will report the message in the
-event of a failure. It's quite handy.
-
- function myThing (args, cb) {
- getData(args, function (er, data) {
- if (er) return log.er(cb, "Couldn't get data")(er)
- doSomethingElse(data, cb)
- })
- }
- function justHasToWork (cb) {
- doSomething(log.er(cb, "the doSomething failed."))
- }
-
## Logging
+Logging is done using the [npmlog](https://github.com/isaacs/npmlog)
+utility.
+
Please clean up logs when they are no longer helpful. In particular,
logging the same object over and over again is not helpful. Logs should
report what's happening so that it's easier to track down where a fault
occurs.
-Use appropriate log levels. The default log() function logs at the
-"info" level. See `npm-config(1)` and search for "loglevel".
+Use appropriate log levels. See `npm-config(1)` and search for
+"loglevel".
## Case, naming, etc.
* `-S`: `--save`
* `-D`: `--save-dev`
* `-O`: `--save-optional`
+* `-B`: `--save-bundle`
* `-y`: `--yes`
* `-n`: `--yes false`
* `ll` and `la` commands: `ls --long`
Force npm to always require authentication when accessing the registry,
even for `GET` requests.
-### bin-publish
-
-* Default: false
-* Type: Boolean
-
-If set to true, then binary packages will be created on publish.
-
-This is the way to opt into the "bindist" behavior described below.
-
-### bindist
-
-* Default: Unstable node versions, `null`, otherwise
- `"<node version>-<platform>-<os release>"`
-* Type: String or `null`
-
-Experimental: on stable versions of node, binary distributions will be
-created with this tag. If a user then installs that package, and their
-`bindist` tag is found in the list of binary distributions, they will
-get that prebuilt version.
-
-Pre-build node packages have their preinstall, install, and postinstall
-scripts stripped (since they are run prior to publishing), and do not
-have their `build` directories automatically ignored.
-
-It's yet to be seen if this is a good idea.
-
### browser
* Default: OS X: `"open"`, others: `"google-chrome"`
The location of npm's cache directory. See `npm-cache(1)`
+### cache-lock-stale
+
+* Default: 60000 (1 minute)
+* Type: Number
+
+The number of ms before cache folder lockfiles are considered stale.
+
+### cache-lock-retries
+
+* Default: 10
+* Type: Number
+
+Number of times to retry to acquire a lock on cache folder lockfiles.
+
+### cache-lock-wait
+
+* Default: 10000 (10 seconds)
+* Type: Number
+
+Number of ms to wait for cache lock files to expire.
+
### cache-max
* Default: Infinity
The command to run for `npm edit` or `npm config edit`.
+### engine-strict
+
+* Default: false
+* Type: Boolean
+
+If set to true, then npm will stubbornly refuse to install (or even
+consider installing) any package that claims to not be compatible with
+the current Node.js version.
+
### force
* Default: false
* skips cache when requesting from the registry.
* prevents checks against clobbering non-npm files.
+### fetch-retries
+
+* Default: 2
+* Type: Number
+
+The "retries" config for the `retry` module to use when fetching
+packages from the registry.
+
+### fetch-retry-factor
+
+* Default: 10
+* Type: Number
+
+The "factor" config for the `retry` module to use when fetching
+packages.
+
+### fetch-retry-mintimeout
+
+* Default: 10000 (10 seconds)
+* Type: Number
+
+The "minTimeout" config for the `retry` module to use when fetching
+packages.
+
+### fetch-retry-maxtimeout
+
+* Default: 60000 (1 minute)
+* Type: Number
+
+The "maxTimeout" config for the `retry` module to use when fetching
+packages.
+
### git
* Default: `"git"`
A white-space separated list of glob patterns of files to always exclude
from packages when building tarballs.
+### init-module
+
+* Default: ~/.npm-init.js
+* Type: path
+
+A module that will be loaded by the `npm init` command. See the
+documentation for the
+[init-package-json](https://github.com/isaacs/init-package-json) module
+for more information, or npm-init(1).
+
### init.version
* Default: "0.0.0"
* the globally installed version is identical to the version that is
being installed locally.
-### logfd
-
-* Default: stderr file descriptor
-* Type: Number or Stream
-
-The location to write log output.
-
### loglevel
* Default: "http"
Any logs of a higher level than the setting are shown.
The default is "http", which shows http, warn, and error output.
-### logprefix
+### logstream
-* Default: true on Posix, false on Windows
-* Type: Boolean
+* Default: process.stderr
+* Type: Stream
+
+This is the stream that is passed to the
+[npmlog](https://github.com/isaacs/npmlog) module at run time.
-Whether or not to prefix log messages with "npm" and the log level. See
-also "color" and "loglevel".
+It cannot be set from the command line, but if you are using npm
+programmatically, you may wish to send logs to somewhere other than
+stderr.
### long
A node module to `require()` when npm loads. Useful for programmatic
usage.
-### outfd
-
-* Default: standard output file descriptor
-* Type: Number or Stream
-
-Where to write "normal" output. This has no effect on log output.
-
### parseable
* Default: false
Save installed packages to a package.json file as dependencies.
+When used with the `npm rm` command, it removes it from the dependencies
+hash.
+
Only works if there is already a package.json file present.
+### save-bundle
+
+* Default: false
+* Type: Boolean
+
+If a package would be saved at install time by the use of `--save`,
+`--save-dev`, or `--save-optional`, then also put it in the
+`bundleDependencies` list.
+
+When used with the `npm rm` command, it removes it from the
+bundledDependencies list.
+
### save-dev
* Default: false
Save installed packages to a package.json file as devDependencies.
+When used with the `npm rm` command, it removes it from the devDependencies
+hash.
+
Only works if there is already a package.json file present.
### save-optional
Save installed packages to a package.json file as optionalDependencies.
+When used with the `npm rm` command, it removes it from the devDependencies
+hash.
+
Only works if there is already a package.json file present.
### searchopts
## SEE ALSO
+* <https://github.com/isaacs/init-package-json>
* npm-json(1)
* npm-version(1)
npm install sax --force
The `--global` argument will cause npm to install the package globally
-rather than locally. See `npm-global(1)`.
+rather than locally. See `npm-folders(1)`.
The `--link` argument will cause npm to link global installs into the
local space in some cases.
## engines
-You can specify the version of
-node that your stuff works on:
+You can specify the version of node that your stuff works on:
{ "engines" : { "node" : ">=0.1.27 <0.1.30" } }
{ "engines" : { "npm" : "~1.0.20" } }
+Note that, unless the user has set the `engine-strict` config flag, this
+field is advisory only.
+
+## engineStrict
+
+If you are sure that your module will *definitely not* run properly on
+versions of Node/npm other than those specified in the `engines` hash,
+then you can set `"engineStrict": true` in your package.json file.
+This will override the user's `engine-strict` config setting.
+
+Please do not do this unless you are really very very sure. If your
+engines hash is something overly restrictive, you can quite easily and
+inadvertently lock yourself into obscurity and prevent your users from
+updating to new versions of Node. Consider this choice carefully. If
+people abuse it, it will be removed in a future version of npm.
+
## os
You can specify which operating systems your
## SYNOPSIS
- npm list
- npm ls
- npm la
- npm ll
+ npm list [<pkg> ...]
+ npm ls [<pkg> ...]
+ npm la [<pkg> ...]
+ npm ll [<pkg> ...]
## DESCRIPTION
This command will print to stdout all the versions of packages that are
installed, as well as their dependencies, in a tree-structure.
-It does not take positional arguments, though you may set config flags
-like with any other command, such as `-g` to list global packages.
+Positional arguments are `name@version-range` identifiers, which will
+limit the results to only the paths to the packages named. Note that
+nested packages will *also* show the paths to the specified packages.
+For example, running `npm ls promzard` in npm's source tree will show:
-It will print out extraneous, missing, and invalid packages.
+ npm@@VERSION@ /path/to/npm
+ └─┬ init-package-json@0.0.4
+ └── promzard@0.1.5
+
+It will show print out extraneous, missing, and invalid packages.
When run as `ll` or `la`, it shows extended information by default.
<p>This function should not be used programmatically. Instead, just refer
to the <code>npm.bin</code> member.</p>
</div>
-<p id="footer">bin — npm@1.1.24</p>
+<p id="footer">bin — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This command will launch a browser, so this command may not be the most
friendly for programmatic use.</p>
</div>
-<p id="footer">bugs — npm@1.1.24</p>
+<p id="footer">bugs — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer">commands — npm@1.1.24</p>
+<p id="footer">commands — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/npm.html">npm(3)</a></li></ul>
</div>
-<p id="footer">config — npm@1.1.24</p>
+<p id="footer">config — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/publish.html">publish(3)</a></li><li><a href="../api/unpublish.html">unpublish(3)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">deprecate — npm@1.1.24</p>
+<p id="footer">deprecate — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This command will launch a browser, so this command may not be the most
friendly for programmatic use.</p>
</div>
-<p id="footer">docs — npm@1.1.24</p>
+<p id="footer">docs — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Since this command opens an editor in a new process, be careful about where
and how this is used.</p>
</div>
-<p id="footer">edit — npm@1.1.24</p>
+<p id="footer">edit — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The first element in the 'args' parameter must be a package name. After that is the optional command, which can be any number of strings. All of the strings will be combined into one, space-delimited command.</p>
</div>
-<p id="footer">explore — npm@1.1.24</p>
+<p id="footer">explore — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The silent parameter is not neccessary not used, but it may in the future.</p>
</div>
-<p id="footer">help-search — npm@1.1.24</p>
+<p id="footer">help-search — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p><a href="../doc/json.html">json(1)</a></p>
</div>
-<p id="footer">init — npm@1.1.24</p>
+<p id="footer">init — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when all packages have been
installed or when an error has been encountered.</p>
</div>
-<p id="footer">install — npm@1.1.24</p>
+<p id="footer">install — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Now, any changes to the redis package will be reflected in
the package in the current working directory</p>
</div>
-<p id="footer">link — npm@1.1.24</p>
+<p id="footer">link — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>For a list of all the available command-line configs, see <code>npm help config</code></p>
</div>
-<p id="footer">load — npm@1.1.24</p>
+<p id="footer">load — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
This means that if a submodule a same dependency as a parent module, then the
dependency will only be output once.</p>
</div>
-<p id="footer">ls — npm@1.1.24</p>
+<p id="footer">ls — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="VERSION">VERSION</h2>
-<p>1.1.24</p>
+<p>1.1.37</p>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<pre><code>var cmd = npm.deref("unp") // cmd === "unpublish"</code></pre>
</div>
-<p id="footer">npm — npm@1.1.24</p>
+<p id="footer">npm — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If the 'packages' parameter is left out, npm will check all packages.</p>
</div>
-<p id="footer">outdated — npm@1.1.24</p>
+<p id="footer">outdated — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/publish.html">publish(3)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">owner — npm@1.1.24</p>
+<p id="footer">owner — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If no arguments are supplied, then npm packs the current package folder.</p>
</div>
-<p id="footer">pack — npm@1.1.24</p>
+<p id="footer">pack — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically</p>
</div>
-<p id="footer">prefix — npm@1.1.24</p>
+<p id="footer">prefix — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Extraneous packages are packages that are not listed on the parent
package's dependencies list.</p>
</div>
-<p id="footer">prune — npm@1.1.24</p>
+<p id="footer">prune — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../api/owner.html">owner(3)</a></li></ul>
</div>
-<p id="footer">publish — npm@1.1.24</p>
+<p id="footer">publish — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>See <code>npm help build</code></p>
</div>
-<p id="footer">rebuild — npm@1.1.24</p>
+<p id="footer">rebuild — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/start.html">start(3)</a></li><li><a href="../api/stop.html">stop(3)</a></li></ul>
</div>
-<p id="footer">restart — npm@1.1.24</p>
+<p id="footer">restart — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically.</p>
</div>
-<p id="footer">root — npm@1.1.24</p>
+<p id="footer">root — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../api/test.html">test(3)</a></li><li><a href="../api/start.html">start(3)</a></li><li><a href="../api/restart.html">restart(3)</a></li><li><a href="../api/stop.html">stop(3)</a></li></ul>
</div>
-<p id="footer">run-script — npm@1.1.24</p>
+<p id="footer">run-script — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
and doesn't try to read your mind (it doesn't do any verb tense matching or the
like).</p>
</div>
-<p id="footer">search — npm@1.1.24</p>
+<p id="footer">search — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when the shrinkwrap has
been saved.</p>
</div>
-<p id="footer">shrinkwrap — npm@1.1.24</p>
+<p id="footer">shrinkwrap — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run tests on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">start — npm@1.1.24</p>
+<p id="footer">start — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run stop on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">stop — npm@1.1.24</p>
+<p id="footer">stop — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li>npm help json</li><li>git help submodule</li></ul>
</div>
-<p id="footer">submodule — npm@1.1.24</p>
+<p id="footer">submodule — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
used. For more information about how to set this config, check
<code>man 3 npm-config</code> for programmatic usage or <code>man npm-config</code> for cli usage.</p>
</div>
-<p id="footer">tag — npm@1.1.24</p>
+<p id="footer">tag — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run tests on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">test — npm@1.1.24</p>
+<p id="footer">test — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when all packages have been
uninstalled or when an error has been encountered.</p>
</div>
-<p id="footer">uninstall — npm@1.1.24</p>
+<p id="footer">uninstall — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If no version is specified, or if all versions are removed then
the root package entry is removed from the registry entirely.</p>
</div>
-<p id="footer">unpublish — npm@1.1.24</p>
+<p id="footer">unpublish — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The 'packages' argument is an array of packages to update. The 'callback' parameter will be called when done or when an error occurs.</p>
</div>
-<p id="footer">update — npm@1.1.24</p>
+<p id="footer">update — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
parameter. The difference, however, is this function will fail if it does
not have exactly one element. The only element should be a version number.</p>
</div>
-<p id="footer">version — npm@1.1.24</p>
+<p id="footer">version — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>corresponding to the list of fields selected.</p>
</div>
-<p id="footer">view — npm@1.1.24</p>
+<p id="footer">view — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically</p>
</div>
-<p id="footer">whoami — npm@1.1.24</p>
+<p id="footer">whoami — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<pre><code>git clone https://github.com/isaacs/npm.git
cd npm
-git submodule update --init --recursive
sudo make install # (or: `node cli.js install -gf`)</code></pre>
<p>If you're sitting in the code folder reading this document in your
terminal, then you've already got the code. Just do:</p>
-<pre><code>git submodule update --init --recursive
-sudo make install</code></pre>
+<pre><code>sudo make install</code></pre>
<p>and npm will install itself.</p>
-<p>Note that github tarballs <strong>do not contain submodules</strong>, so
-those won't work. You'll have to also fetch the appropriate submodules
-listed in the .gitmodules file.</p>
-
<h2 id="Permissions-when-Using-npm-to-Install-Other-Stuff">Permissions when Using npm to Install Other Stuff</h2>
<p><strong>tl;dr</strong></p>
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer"><a href="../doc/README.html">README</a> — npm@1.1.24</p>
+<p id="footer"><a href="../doc/README.html">README</a> — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li><li><a href="../doc/whoami.html">whoami(1)</a></li></ul>
</div>
-<p id="footer">adduser — npm@1.1.24</p>
+<p id="footer">adduser — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prefix.html">prefix(1)</a></li><li><a href="../doc/root.html">root(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">bin — npm@1.1.24</p>
+<p id="footer">bin — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/docs.html">docs(1)</a></li><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">bugs — npm@1.1.24</p>
+<p id="footer">bugs — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">build — npm@1.1.24</p>
+<p id="footer">build — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">bundle — npm@1.1.24</p>
+<p id="footer">bundle — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/pack.html">pack(1)</a></li></ul>
</div>
-<p id="footer">cache — npm@1.1.24</p>
+<p id="footer">cache — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li></ul>
</div>
-<p id="footer">changelog — npm@1.1.24</p>
+<p id="footer">changelog — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Always create a new Error object with your message. Don't just return a
string message to the callback. Stack traces are handy.</p>
-<p>Use the <code>require("./utils/log").er</code> function. It takes a callback and an
-error message, and returns an object that will report the message in the
-event of a failure. It's quite handy.</p>
-
-<pre><code>function myThing (args, cb) {
- getData(args, function (er, data) {
- if (er) return log.er(cb, "Couldn't get data")(er)
- doSomethingElse(data, cb)
- })
-}
-function justHasToWork (cb) {
- doSomething(log.er(cb, "the doSomething failed."))
-}</code></pre>
-
<h2 id="Logging">Logging</h2>
+<p>Logging is done using the <a href="https://github.com/isaacs/npmlog">npmlog</a>
+utility.</p>
+
<p>Please clean up logs when they are no longer helpful. In particular,
logging the same object over and over again is not helpful. Logs should
report what's happening so that it's easier to track down where a fault
occurs.</p>
-<p>Use appropriate log levels. The default log() function logs at the
-"info" level. See <code><a href="../doc/config.html">config(1)</a></code> and search for "loglevel".</p>
+<p>Use appropriate log levels. See <code><a href="../doc/config.html">config(1)</a></code> and search for
+"loglevel".</p>
<h2 id="Case-naming-etc">Case, naming, etc.</h2>
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">coding-style — npm@1.1.24</p>
+<p id="footer">coding-style — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">completion — npm@1.1.24</p>
+<p id="footer">completion — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The following shorthands are parsed on the command-line:</p>
-<ul><li><code>-v</code>: <code>--version</code></li><li><code>-h</code>, <code>-?</code>, <code>--help</code>, <code>-H</code>: <code>--usage</code></li><li><code>-s</code>, <code>--silent</code>: <code>--loglevel silent</code></li><li><code>-q</code>, <code>--quiet</code>: <code>--loglevel warn</code></li><li><code>-d</code>: <code>--loglevel info</code></li><li><code>-dd</code>, <code>--verbose</code>: <code>--loglevel verbose</code></li><li><code>-ddd</code>: <code>--loglevel silly</code></li><li><code>-g</code>: <code>--global</code></li><li><code>-l</code>: <code>--long</code></li><li><code>-m</code>: <code>--message</code></li><li><code>-p</code>, <code>--porcelain</code>: <code>--parseable</code></li><li><code>-reg</code>: <code>--registry</code></li><li><code>-v</code>: <code>--version</code></li><li><code>-f</code>: <code>--force</code></li><li><code>-l</code>: <code>--long</code></li><li><code>-desc</code>: <code>--description</code></li><li><code>-S</code>: <code>--save</code></li><li><code>-D</code>: <code>--save-dev</code></li><li><code>-O</code>: <code>--save-optional</code></li><li><code>-y</code>: <code>--yes</code></li><li><code>-n</code>: <code>--yes false</code></li><li><code>ll</code> and <code>la</code> commands: <code>ls --long</code></li></ul>
+<ul><li><code>-v</code>: <code>--version</code></li><li><code>-h</code>, <code>-?</code>, <code>--help</code>, <code>-H</code>: <code>--usage</code></li><li><code>-s</code>, <code>--silent</code>: <code>--loglevel silent</code></li><li><code>-q</code>, <code>--quiet</code>: <code>--loglevel warn</code></li><li><code>-d</code>: <code>--loglevel info</code></li><li><code>-dd</code>, <code>--verbose</code>: <code>--loglevel verbose</code></li><li><code>-ddd</code>: <code>--loglevel silly</code></li><li><code>-g</code>: <code>--global</code></li><li><code>-l</code>: <code>--long</code></li><li><code>-m</code>: <code>--message</code></li><li><code>-p</code>, <code>--porcelain</code>: <code>--parseable</code></li><li><code>-reg</code>: <code>--registry</code></li><li><code>-v</code>: <code>--version</code></li><li><code>-f</code>: <code>--force</code></li><li><code>-l</code>: <code>--long</code></li><li><code>-desc</code>: <code>--description</code></li><li><code>-S</code>: <code>--save</code></li><li><code>-D</code>: <code>--save-dev</code></li><li><code>-O</code>: <code>--save-optional</code></li><li><code>-B</code>: <code>--save-bundle</code></li><li><code>-y</code>: <code>--yes</code></li><li><code>-n</code>: <code>--yes false</code></li><li><code>ll</code> and <code>la</code> commands: <code>ls --long</code></li></ul>
<p>If the specified configuration param resolves unambiguously to a known
configuration parameter, then it is expanded to that configuration
<p>Force npm to always require authentication when accessing the registry,
even for <code>GET</code> requests.</p>
-<h3 id="bin-publish">bin-publish</h3>
-
-<ul><li>Default: false</li><li>Type: Boolean</li></ul>
-
-<p>If set to true, then binary packages will be created on publish.</p>
-
-<p>This is the way to opt into the "bindist" behavior described below.</p>
-
-<h3 id="bindist">bindist</h3>
-
-<ul><li>Default: Unstable node versions, <code>null</code>, otherwise
-<code>"<node version>-<platform>-<os release>"</code></li><li>Type: String or <code>null</code></li></ul>
-
-<p>Experimental: on stable versions of node, binary distributions will be
-created with this tag. If a user then installs that package, and their
-<code>bindist</code> tag is found in the list of binary distributions, they will
-get that prebuilt version.</p>
-
-<p>Pre-build node packages have their preinstall, install, and postinstall
-scripts stripped (since they are run prior to publishing), and do not
-have their <code>build</code> directories automatically ignored.</p>
-
-<p>It's yet to be seen if this is a good idea.</p>
-
<h3 id="browser">browser</h3>
<ul><li>Default: OS X: <code>"open"</code>, others: <code>"google-chrome"</code></li><li>Type: String</li></ul>
<p>The location of npm's cache directory. See <code><a href="../doc/cache.html">cache(1)</a></code></p>
+<h3 id="cache-lock-stale">cache-lock-stale</h3>
+
+<ul><li>Default: 60000 (1 minute)</li><li>Type: Number</li></ul>
+
+<p>The number of ms before cache folder lockfiles are considered stale.</p>
+
+<h3 id="cache-lock-retries">cache-lock-retries</h3>
+
+<ul><li>Default: 10</li><li>Type: Number</li></ul>
+
+<p>Number of times to retry to acquire a lock on cache folder lockfiles.</p>
+
+<h3 id="cache-lock-wait">cache-lock-wait</h3>
+
+<ul><li>Default: 10000 (10 seconds)</li><li>Type: Number</li></ul>
+
+<p>Number of ms to wait for cache lock files to expire.</p>
+
<h3 id="cache-max">cache-max</h3>
<ul><li>Default: Infinity</li><li>Type: Number</li></ul>
<p>The command to run for <code>npm edit</code> or <code>npm config edit</code>.</p>
+<h3 id="engine-strict">engine-strict</h3>
+
+<ul><li>Default: false</li><li>Type: Boolean</li></ul>
+
+<p>If set to true, then npm will stubbornly refuse to install (or even
+consider installing) any package that claims to not be compatible with
+the current Node.js version.</p>
+
<h3 id="force">force</h3>
<ul><li>Default: false</li><li>Type: Boolean</li></ul>
<ul><li>lifecycle script failure does not block progress.</li><li>publishing clobbers previously published versions.</li><li>skips cache when requesting from the registry.</li><li>prevents checks against clobbering non-npm files.</li></ul>
+<h3 id="fetch-retries">fetch-retries</h3>
+
+<ul><li>Default: 2</li><li>Type: Number</li></ul>
+
+<p>The "retries" config for the <code>retry</code> module to use when fetching
+packages from the registry.</p>
+
+<h3 id="fetch-retry-factor">fetch-retry-factor</h3>
+
+<ul><li>Default: 10</li><li>Type: Number</li></ul>
+
+<p>The "factor" config for the <code>retry</code> module to use when fetching
+packages.</p>
+
+<h3 id="fetch-retry-mintimeout">fetch-retry-mintimeout</h3>
+
+<ul><li>Default: 10000 (10 seconds)</li><li>Type: Number</li></ul>
+
+<p>The "minTimeout" config for the <code>retry</code> module to use when fetching
+packages.</p>
+
+<h3 id="fetch-retry-maxtimeout">fetch-retry-maxtimeout</h3>
+
+<ul><li>Default: 60000 (1 minute)</li><li>Type: Number</li></ul>
+
+<p>The "maxTimeout" config for the <code>retry</code> module to use when fetching
+packages.</p>
+
<h3 id="git">git</h3>
<ul><li>Default: <code>"git"</code></li><li>Type: String</li></ul>
<p>A white-space separated list of glob patterns of files to always exclude
from packages when building tarballs.</p>
+<h3 id="init-module">init-module</h3>
+
+<ul><li>Default: ~/.npm-init.js</li><li>Type: path</li></ul>
+
+<p>A module that will be loaded by the <code>npm init</code> command. See the
+documentation for the
+<a href="https://github.com/isaacs/init-package-json">init-package-json</a> module
+for more information, or <a href="../doc/init.html">init(1)</a>.</p>
+
<h3 id="init-version">init.version</h3>
<ul><li>Default: "0.0.0"</li><li>Type: semver</li></ul>
<ul><li>The package is not already installed globally, or</li><li>the globally installed version is identical to the version that is
being installed locally.</li></ul>
-<h3 id="logfd">logfd</h3>
-
-<ul><li>Default: stderr file descriptor</li><li>Type: Number or Stream</li></ul>
-
-<p>The location to write log output.</p>
-
<h3 id="loglevel">loglevel</h3>
<ul><li>Default: "http"</li><li>Type: String</li><li>Values: "silent", "win", "error", "warn", "http", "info", "verbose", "silly"</li></ul>
<p>Any logs of a higher level than the setting are shown.
The default is "http", which shows http, warn, and error output.</p>
-<h3 id="logprefix">logprefix</h3>
+<h3 id="logstream">logstream</h3>
+
+<ul><li>Default: process.stderr</li><li>Type: Stream</li></ul>
-<ul><li>Default: true on Posix, false on Windows</li><li>Type: Boolean</li></ul>
+<p>This is the stream that is passed to the
+<a href="https://github.com/isaacs/npmlog">npmlog</a> module at run time.</p>
-<p>Whether or not to prefix log messages with "npm" and the log level. See
-also "color" and "loglevel".</p>
+<p>It cannot be set from the command line, but if you are using npm
+programmatically, you may wish to send logs to somewhere other than
+stderr.</p>
<h3 id="long">long</h3>
<p>A node module to <code>require()</code> when npm loads. Useful for programmatic
usage.</p>
-<h3 id="outfd">outfd</h3>
-
-<ul><li>Default: standard output file descriptor</li><li>Type: Number or Stream</li></ul>
-
-<p>Where to write "normal" output. This has no effect on log output.</p>
-
<h3 id="parseable">parseable</h3>
<ul><li>Default: false</li><li>Type: Boolean</li></ul>
<p>Save installed packages to a package.json file as dependencies.</p>
+<p>When used with the <code>npm rm</code> command, it removes it from the dependencies
+hash.</p>
+
<p>Only works if there is already a package.json file present.</p>
+<h3 id="save-bundle">save-bundle</h3>
+
+<ul><li>Default: false</li><li>Type: Boolean</li></ul>
+
+<p>If a package would be saved at install time by the use of <code>--save</code>,
+<code>--save-dev</code>, or <code>--save-optional</code>, then also put it in the
+<code>bundleDependencies</code> list.</p>
+
+<p>When used with the <code>npm rm</code> command, it removes it from the
+bundledDependencies list.</p>
+
<h3 id="save-dev">save-dev</h3>
<ul><li>Default: false</li><li>Type: Boolean</li></ul>
<p>Save installed packages to a package.json file as devDependencies.</p>
+<p>When used with the <code>npm rm</code> command, it removes it from the devDependencies
+hash.</p>
+
<p>Only works if there is already a package.json file present.</p>
<h3 id="save-optional">save-optional</h3>
<p>Save installed packages to a package.json file as optionalDependencies.</p>
+<p>When used with the <code>npm rm</code> command, it removes it from the devDependencies
+hash.</p>
+
<p>Only works if there is already a package.json file present.</p>
<h3 id="searchopts">searchopts</h3>
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">config — npm@1.1.24</p>
+<p id="footer">config — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">deprecate — npm@1.1.24</p>
+<p id="footer">deprecate — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">developers — npm@1.1.24</p>
+<p id="footer">developers — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li></ul>
</div>
-<p id="footer">disputes — npm@1.1.24</p>
+<p id="footer">disputes — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">docs — npm@1.1.24</p>
+<p id="footer">docs — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/explore.html">explore(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">edit — npm@1.1.24</p>
+<p id="footer">edit — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/submodule.html">submodule(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/edit.html">edit(1)</a></li><li><a href="../doc/rebuild.html">rebuild(1)</a></li><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">explore — npm@1.1.24</p>
+<p id="footer">explore — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li></ul>
</div>
-<p id="footer">faq — npm@1.1.24</p>
+<p id="footer">faq — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/pack.html">pack(1)</a></li><li><a href="../doc/cache.html">cache(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li></ul>
</div>
-<p id="footer">folders — npm@1.1.24</p>
+<p id="footer">folders — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/help.html">help(1)</a></li></ul>
</div>
-<p id="footer">help-search — npm@1.1.24</p>
+<p id="footer">help-search — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/README.html">README</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/help-search.html">help-search(1)</a></li><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer">help — npm@1.1.24</p>
+<p id="footer">help — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p> Display npm username</p>
</div>
-<p id="footer">index — npm@1.1.24</p>
+<p id="footer">index — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="SEE-ALSO">SEE ALSO</h2>
-<ul><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/version.html">version(1)</a></li></ul>
+<ul><li><a href="https://github.com/isaacs/init-package-json">https://github.com/isaacs/init-package-json</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/version.html">version(1)</a></li></ul>
</div>
-<p id="footer">init — npm@1.1.24</p>
+<p id="footer">init — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<pre><code>npm install sax --force</code></pre>
<p>The <code>--global</code> argument will cause npm to install the package globally
-rather than locally. See <code><a href="../doc/global.html">global(1)</a></code>.</p>
+rather than locally. See <code><a href="../doc/folders.html">folders(1)</a></code>.</p>
<p>The <code>--link</code> argument will cause npm to link global installs into the
local space in some cases.</p>
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/update.html">update(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/rebuild.html">rebuild(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/tag.html">tag(1)</a></li><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/shrinkwrap.html">shrinkwrap(1)</a></li></ul>
</div>
-<p id="footer">install — npm@1.1.24</p>
+<p id="footer">install — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="engines">engines</h2>
-<p>You can specify the version of
-node that your stuff works on:</p>
+<p>You can specify the version of node that your stuff works on:</p>
<pre><code>{ "engines" : { "node" : ">=0.1.27 <0.1.30" } }</code></pre>
<pre><code>{ "engines" : { "npm" : "~1.0.20" } }</code></pre>
+<p>Note that, unless the user has set the <code>engine-strict</code> config flag, this
+field is advisory only.</p>
+
+<h2 id="engineStrict">engineStrict</h2>
+
+<p>If you are sure that your module will <em>definitely not</em> run properly on
+versions of Node/npm other than those specified in the <code>engines</code> hash,
+then you can set <code>"engineStrict": true</code> in your package.json file.
+This will override the user's <code>engine-strict</code> config setting.</p>
+
+<p>Please do not do this unless you are really very very sure. If your
+engines hash is something overly restrictive, you can quite easily and
+inadvertently lock yourself into obscurity and prevent your users from
+updating to new versions of Node. Consider this choice carefully. If
+people abuse it, it will be removed in a future version of npm.</p>
+
<h2 id="os">os</h2>
<p>You can specify which operating systems your
<ul><li><a href="../doc/semver.html">semver(1)</a></li><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/version.html">version(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/rm.html">rm(1)</a></li></ul>
</div>
-<p id="footer">json — npm@1.1.24</p>
+<p id="footer">json — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">link — npm@1.1.24</p>
+<p id="footer">link — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="SYNOPSIS">SYNOPSIS</h2>
-<pre><code>npm list
-npm ls
-npm la
-npm ll</code></pre>
+<pre><code>npm list [<pkg> ...]
+npm ls [<pkg> ...]
+npm la [<pkg> ...]
+npm ll [<pkg> ...]</code></pre>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<p>This command will print to stdout all the versions of packages that are
installed, as well as their dependencies, in a tree-structure.</p>
-<p>It does not take positional arguments, though you may set config flags
-like with any other command, such as <code>-g</code> to list global packages.</p>
+<p>Positional arguments are <code>name@version-range</code> identifiers, which will
+limit the results to only the paths to the packages named. Note that
+nested packages will <em>also</em> show the paths to the specified packages.
+For example, running <code>npm ls promzard</code> in npm's source tree will show:</p>
-<p>It will print out extraneous, missing, and invalid packages.</p>
+<pre><code>npm@1.1.37 /path/to/npm
+└─┬ init-package-json@0.0.4
+ └── promzard@0.1.5</code></pre>
+
+<p>It will show print out extraneous, missing, and invalid packages.</p>
<p>When run as <code>ll</code> or <code>la</code>, it shows extended information by default.</p>
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/prune.html">prune(1)</a></li><li><a href="../doc/outdated.html">outdated(1)</a></li><li><a href="../doc/update.html">update(1)</a></li></ul>
</div>
-<p id="footer">list — npm@1.1.24</p>
+<p id="footer">list — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="VERSION">VERSION</h2>
-<p>1.1.24</p>
+<p>1.1.37</p>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<ul><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/README.html">README</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/index.html">index(1)</a></li><li><a href="../api/npm.html">npm(3)</a></li></ul>
</div>
-<p id="footer">npm — npm@1.1.24</p>
+<p id="footer">npm — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/update.html">update(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li></ul>
</div>
-<p id="footer">outdated — npm@1.1.24</p>
+<p id="footer">outdated — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/disputes.html">disputes(1)</a></li></ul>
</div>
-<p id="footer">owner — npm@1.1.24</p>
+<p id="footer">owner — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/cache.html">cache(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">pack — npm@1.1.24</p>
+<p id="footer">pack — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/root.html">root(1)</a></li><li><a href="../doc/bin.html">bin(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">prefix — npm@1.1.24</p>
+<p id="footer">prefix — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">prune — npm@1.1.24</p>
+<p id="footer">prune — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li><li><a href="../doc/deprecate.html">deprecate(1)</a></li><li><a href="../doc/tag.html">tag(1)</a></li></ul>
</div>
-<p id="footer">publish — npm@1.1.24</p>
+<p id="footer">publish — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">rebuild — npm@1.1.24</p>
+<p id="footer">rebuild — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/disputes.html">disputes(1)</a></li></ul>
</div>
-<p id="footer">registry — npm@1.1.24</p>
+<p id="footer">registry — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/README.html">README</a></li><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/prune.html">prune(1)</a></li></ul>
</div>
-<p id="footer">removing-npm — npm@1.1.24</p>
+<p id="footer">removing-npm — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">restart — npm@1.1.24</p>
+<p id="footer">restart — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prefix.html">prefix(1)</a></li><li><a href="../doc/bin.html">bin(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">root — npm@1.1.24</p>
+<p id="footer">root — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">run-script — npm@1.1.24</p>
+<p id="footer">run-script — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">scripts — npm@1.1.24</p>
+<p id="footer">scripts — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/view.html">view(1)</a></li></ul>
</div>
-<p id="footer">search — npm@1.1.24</p>
+<p id="footer">search — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">semver — npm@1.1.24</p>
+<p id="footer">semver — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">shrinkwrap — npm@1.1.24</p>
+<p id="footer">shrinkwrap — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/whoami.html">whoami(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li></ul>
</div>
-<p id="footer">star — npm@1.1.24</p>
+<p id="footer">star — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">start — npm@1.1.24</p>
+<p id="footer">start — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li></ul>
</div>
-<p id="footer">stop — npm@1.1.24</p>
+<p id="footer">stop — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/json.html">json(1)</a></li><li>git help submodule</li></ul>
</div>
-<p id="footer">submodule — npm@1.1.24</p>
+<p id="footer">submodule — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">tag — npm@1.1.24</p>
+<p id="footer">tag — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">test — npm@1.1.24</p>
+<p id="footer">test — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prune.html">prune(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">uninstall — npm@1.1.24</p>
+<p id="footer">uninstall — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/deprecate.html">deprecate(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li></ul>
</div>
-<p id="footer">unpublish — npm@1.1.24</p>
+<p id="footer">unpublish — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/outdated.html">outdated(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">update — npm@1.1.24</p>
+<p id="footer">update — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/semver.html">semver(1)</a></li></ul>
</div>
-<p id="footer">version — npm@1.1.24</p>
+<p id="footer">version — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/search.html">search(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/docs.html">docs(1)</a></li></ul>
</div>
-<p id="footer">view — npm@1.1.24</p>
+<p id="footer">view — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li></ul>
</div>
-<p id="footer">whoami — npm@1.1.24</p>
+<p id="footer">whoami — npm@1.1.37</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
-/* @license
- * MyFonts Webfont Build ID 2303021, 2012-06-04T14:43:21-0400
- *
- * The fonts listed in this notice are subject to the End User License
- * Agreement(s) entered into by the website owner. All other parties are
- * explicitly restricted from using the Licensed Webfonts(s).
- *
- * You may obtain a valid license at the URLs below.
- *
- * Webfont: Gubblebum Blocky by Jelloween
- * URL: http://www.myfonts.com/fonts/jelloween/gubblebum/blocky/
- * Licensed pageviews: unspecified
- *
- *
- * License: http://www.myfonts.com/viewlicense?type=web&buildid=2303021
- * Webfonts copyright: Copyright (c) 2007 by Tjarda Koster. All rights reserved.
- *
- * © 2012 Bitstream Inc
+/*
+
+Webfont: Gubblebum Blocky by Jelloween
+License: http://www.myfonts.com/viewlicense?type=web&buildid=2303021
+Webfonts copyright: Copyright (c) 2007 by Tjarda Koster. All rights reserved.
+
+"Gubblebum Blocky" font
+Copyright (c) 2007 by Tjarda Koster, http://jelloween.deviantart.com
+included for use in the npm website and documentation,
+used with permission.
+
*/
@font-face {
module.exports = adduser
-var registry = require("./utils/npm-registry-client/index.js")
- , ini = require("./utils/ini.js")
- , log = require("./utils/log.js")
+var ini = require("./utils/ini.js")
+ , log = require("npmlog")
, npm = require("./npm.js")
+ , registry = npm.registry
, read = require("read")
- , promiseChain = require("./utils/promise-chain.js")
, crypto
try {
if (!crypto) return cb(new Error(
"You must compile node with ssl support to use the adduser feature"))
- var u = { u : npm.config.get("username")
+ var c = { u : npm.config.get("username")
, p : npm.config.get("_password")
, e : npm.config.get("email")
}
, changed = false
+ , u = {}
+ , fns = [readUsername, readPassword, readEmail, save]
- promiseChain(cb)
- (read, [{prompt: "Username: ", default: u.u}], function (un) {
- changed = u.u !== un
- u.u = un
- })
- (function (cb) {
- if (u.p && !changed) return cb(null, u.p)
- read({prompt: "Password: ", default: u.p, silent: true}, cb)
- }, [], function (pw) { u.p = pw })
- (read, [{prompt: "Email: ", default: u.e}], function (em) { u.e = em })
- (function (cb) {
- if (changed) npm.config.del("_auth")
- registry.adduser(u.u, u.p, u.e, function (er) {
- if (er) return cb(er)
- ini.set("username", u.u, "user")
- ini.set("_password", u.p, "user")
- ini.set("email", u.e, "user")
- log("Authorized user " + u.u, "adduser")
- ini.save("user", cb)
- })
- })
- ()
+ loop()
+ function loop (er) {
+ if (er) return cb(er)
+ var fn = fns.shift()
+ if (fn) return fn(c, u, loop)
+ cb()
+ }
+}
+
+function readUsername (c, u, cb) {
+ read({prompt: "Username: ", default: c.u}, function (er, un) {
+ c.changed = c.u !== un
+ u.u = un
+ cb(er)
+ })
+}
+
+function readPassword (c, u, cb) {
+ if (!c.changed) {
+ u.p = c.p
+ return cb()
+ }
+ read({prompt: "Password: ", silent: true}, function (er, pw) {
+ u.p = pw
+ cb(er)
+ })
+}
+
+function readEmail (c, u, cb) {
+ read({prompt: "Email: ", default: c.e}, function (er, em) {
+ u.e = em
+ cb(er)
+ })
+}
+
+function save (c, u, cb) {
+ if (c.changed) {
+ delete registry.auth
+ delete registry.username
+ delete registry.password
+ registry.username = u.u
+ registry.password = u.p
+ }
+
+ // save existing configs, but yank off for this PUT
+ registry.adduser(u.u, u.p, u.e, function (er) {
+ if (er) return cb(er)
+ registry.username = u.u
+ registry.password = u.p
+ registry.email = u.e
+ ini.set("username", u.u, "user")
+ ini.set("_password", u.p, "user")
+ ini.set("email", u.e, "user")
+ log.info("adduser", "Authorized user %s", u.u)
+ ini.save("user", cb)
+ })
}
bugs.usage = "npm bugs <pkgname>"
+var exec = require("./utils/exec.js")
+ , npm = require("./npm.js")
+ , registry = npm.registry
+ , log = require("npmlog")
+
bugs.completion = function (opts, cb) {
if (opts.conf.argv.remain.length > 2) return cb()
- registry.get("/-/short", null, 60000, function (er, list) {
+ registry.get("/-/short", 60000, function (er, list) {
return cb(null, list || [])
})
}
-var exec = require("./utils/exec.js")
- , registry = require("./utils/npm-registry-client/index.js")
- , npm = require("./npm.js")
- , log = require("./utils/log.js")
-
function bugs (args, cb) {
if (!args.length) return cb(bugs.usage)
var n = args[0].split("@").shift()
- registry.get(n, "latest", 3600, function (er, d) {
+ registry.get(n + "/latest", 3600, function (er, d) {
if (er) return cb(er)
var bugs = d.bugs
, repo = d.repository || d.repositories
if (repo) {
if (Array.isArray(repo)) repo = repo.shift()
if (repo.hasOwnProperty("url")) repo = repo.url
- log.verbose(repo, "repository")
+ log.verbose("repository", repo)
if (repo && repo.match(/^(https?:\/\/|git(:\/\/|@))github.com/)) {
return open(repo.replace(/^git(@|:\/\/)/, "http://")
.replace(/^https?:\/\/github.com:/, "github.com/")
// This runs AFTER install or link are completed.
var npm = require("./npm.js")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, chain = require("slide").chain
, fs = require("graceful-fs")
, path = require("path")
, lifecycle = require("./utils/lifecycle.js")
- , readJson = require("./utils/read-json.js")
+ , readJson = require("read-package-json")
, link = require("./utils/link.js")
, linkIfExists = link.ifExists
, cmdShim = require("./utils/cmd-shim.js")
function build_ (global, didPre, didRB) { return function (folder, cb) {
folder = path.resolve(folder)
build._didBuild[folder] = true
- log.info(folder, "build")
+ log.info("build", folder)
readJson(path.resolve(folder, "package.json"), function (er, pkg) {
if (er) return cb(er)
chain
, top = parent === npm.dir
, gtop = parent === gnm
- log.verbose([global, gnm, gtop, parent], "linkStuff")
- log(pkg._id, "linkStuff")
+ log.verbose("linkStuff", [global, gnm, gtop, parent])
+ log.info("linkStuff", pkg._id)
if (top && pkg.preferGlobal && !global) {
- log.warn(pkg._id + " should be installed with -g", "prefer global")
+ log.warn("prefer global", pkg._id + " should be installed with -g")
}
asyncMap( [linkBins, linkMans, !didRB && rebuildBundles]
, function (fn, cb) {
if (!fn) return cb()
- log.verbose(pkg._id, fn.name)
+ log.verbose(fn.name, pkg._id)
fn(pkg, folder, parent, gtop, cb)
}, cb)
}
// error means no bundles
if (er) return cb()
- log.verbose(files, "rebuildBundles")
+ log.verbose("rebuildBundles", files)
// don't asyncMap these, because otherwise build script output
// gets interleaved and is impossible to read
chain(files.filter(function (file) {
file = path.resolve(folder, "node_modules", file)
return function (cb) {
if (build._didBuild[file]) return cb()
- log.verbose(file, "rebuild bundle")
+ log.verbose("rebuild bundle", file)
// if file is not a package dir, then don't do it.
fs.lstat(path.resolve(file, "package.json"), function (er, st) {
if (er) return cb()
}
var binRoot = gtop ? npm.globalBin
: path.resolve(parent, ".bin")
- log.verbose([pkg.bin, binRoot, gtop], "bins linking")
+ log.verbose("link bins", [pkg.bin, binRoot, gtop])
asyncMap(Object.keys(pkg.bin), function (b, cb) {
linkBin( path.resolve(folder, pkg.bin[b])
// XXX lib/utils/tar.js and this file need to be rewritten.
+// URL-to-cache folder mapping:
+// : -> !
+// @ -> _
+// http://registry.npmjs.org/foo/version -> cache/http!/...
+//
+
/*
+fetching a url:
+1. Check for url in inFlightUrls. If present, add cb, and return.
+2. create inFlightURL list
+3. Acquire lock at {cache}/{sha(url)}.lock
+ retries = {cache-lock-retries, def=3}
+ stale = {cache-lock-stale, def=30000}
+ wait = {cache-lock-wait, def=100}
+4. if lock can't be acquired, then fail
+5. fetch url, clear lock, call cbs
+
+cache folders:
+1. urls: http!/server.com/path/to/thing
+2. c:\path\to\thing: file!/c!/path/to/thing
+3. /path/to/thing: file!/path/to/thing
+4. git@ private: git_github.com!isaacs/npm
+5. git://public: git!/github.com/isaacs/npm
+6. git+blah:// git-blah!/server.com/foo/bar
+
adding a folder:
1. tar into tmp/random/package.tgz
2. untar into tmp/random/contents/package, stripping one dir piece
2. goto folder(2)
adding a name@version:
-1. registry.get(name, version)
+1. registry.get(name/version)
2. if response isn't 304, add url(dist.tarball)
adding a name@range:
exports.read = read
exports.clean = clean
exports.unpack = unpack
+exports.lock = lock
+exports.unlock = unlock
var mkdir = require("mkdirp")
, exec = require("./utils/exec.js")
, npm = require("./npm.js")
, fs = require("graceful-fs")
, rm = require("rimraf")
- , readJson = require("./utils/read-json.js")
- , registry = require("./utils/npm-registry-client/index.js")
- , log = require("./utils/log.js")
+ , readJson = require("read-package-json")
+ , registry = npm.registry
+ , log = require("npmlog")
, path = require("path")
, output
, sha = require("./utils/sha.js")
- , find = require("./utils/find.js")
, asyncMap = require("slide").asyncMap
, semver = require("semver")
, tar = require("./utils/tar.js")
, fileCompletion = require("./utils/completion/file-completion.js")
, url = require("url")
, chownr = require("chownr")
+ , lockFile = require("lockfile")
+ , crypto = require("crypto")
+ , retry = require("retry")
cache.usage = "npm cache add <tarball file>"
+ "\nnpm cache add <folder>"
}
if (forceBypass && npm.config.get("force")) {
- log.verbose(true, "force found, skipping cache")
+ log.verbose("using force", "skipping cache")
return addNamed(name, ver, c)
}
add([pkg, ver], cb)
})
}
- log.verbose([pkg, ver], "cache add")
+ log.verbose("cache add", [pkg, ver])
return add([pkg, ver], cb)
}
spec = args[0]
}
- log.silly([name, spec, args], "cache add: name, spec, args")
+ log.silly("cache add", "name=%j spec=%j args=%j", name, spec, args)
+
if (!name && !spec) return cb(usage)
// see if the spec is a url
// otherwise, treat as name@version
var p = url.parse(spec) || {}
- log.verbose(p, "parsed url")
+ log.verbose("parsed url", p)
// it could be that we got name@http://blah
// in that case, we will not have a protocol now, but if we
default:
// if we have a name and a spec, then try name@spec
// if not, then try just spec (which may try name@"" if not found)
- return name ? addNamed(name, spec, cb) : addLocal(spec, cb)
+ if (name) {
+ addNamed(name, spec, cb)
+ } else {
+ addLocal(spec, cb)
+ }
}
}
+function fetchAndShaCheck (u, tmp, shasum, cb) {
+ fetch(u, tmp, function (er, response) {
+ if (er) {
+ log.error("fetch failed", u)
+ return cb(er, response)
+ }
+ if (!shasum) return cb()
+ // validate that the url we just downloaded matches the expected shasum.
+ sha.check(tmp, shasum, cb)
+ })
+}
+
// Only have a single download action at once for a given url
// additional calls stack the callbacks.
var inFlightURLs = {}
if (iF.length > 1) return
function cb (er, data) {
- var c
- while (c = iF.shift()) c(er, data)
- delete inFlightURLs[u]
+ unlock(u, function () {
+ var c
+ while (c = iF.shift()) c(er, data)
+ delete inFlightURLs[u]
+ })
}
- log.verbose([u, shasum], "addRemoteTarball")
- var tmp = path.join(npm.tmp, Date.now()+"-"+Math.random(), "tmp.tgz")
- mkdir(path.dirname(tmp), function (er) {
+ lock(u, function (er) {
if (er) return cb(er)
- fetch(u, tmp, function (er) {
- if (er) return log.er(cb, "failed to fetch "+u)(er)
- if (!shasum) return done()
- // validate that the url we just downloaded matches the expected shasum.
- sha.check(tmp, shasum, done)
+
+ log.verbose("addRemoteTarball", [u, shasum])
+ var tmp = path.join(npm.tmp, Date.now()+"-"+Math.random(), "tmp.tgz")
+ mkdir(path.dirname(tmp), function (er) {
+ if (er) return cb(er)
+ // Tuned to spread 3 attempts over about a minute.
+ // See formula at <https://github.com/tim-kos/node-retry>.
+ var operation = retry.operation
+ ( { retries: npm.config.get("fetch-retries")
+ , factor: npm.config.get("fetch-retry-factor")
+ , minTimeout: npm.config.get("fetch-retry-mintimeout")
+ , maxTimeout: npm.config.get("fetch-retry-maxtimeout") })
+
+ operation.attempt(function (currentAttempt) {
+ log.info("retry", "fetch attempt " + currentAttempt
+ + " at " + (new Date()).toLocaleTimeString())
+ fetchAndShaCheck(u, tmp, shasum, function (er, response) {
+ // Only retry on 408, 5xx or no `response`.
+ var statusCode = response && response.statusCode
+ var statusRetry = !statusCode || (statusCode === 408 || statusCode >= 500)
+ if (er && statusRetry && operation.retry(er)) {
+ log.info("retry", "will retry, error on last attempt: " + er)
+ return
+ }
+ done(er)
+ })
+ })
})
+ function done (er) {
+ if (er) return cb(er)
+ addLocalTarball(tmp, name, cb)
+ }
})
- function done (er) {
- if (er) return cb(er)
- addLocalTarball(tmp, name, cb)
- }
}
// For now, this is kind of dumb. Just basically treat git as
if (iF.length > 1) return
function cb (er, data) {
- var c
- while (c = iF.shift()) c(er, data)
- delete inFlightURLs[u]
+ unlock(u, function () {
+ var c
+ while (c = iF.shift()) c(er, data)
+ delete inFlightURLs[u]
+ })
}
- // figure out what we should check out.
- var co = parsed.hash && parsed.hash.substr(1) || "master"
- // git is so tricky!
- // if the path is like ssh://foo:22/some/path then it works, but
- // it needs the ssh://
- // If the path is like ssh://foo:some/path then it works, but
- // only if you remove the ssh://
- u = u.replace(/^git\+/, "")
- .replace(/#.*$/, "")
-
- // ssh paths that are scp-style urls don't need the ssh://
- if (parsed.pathname.match(/^\/?:/)) {
- u = u.replace(/^ssh:\/\//, "")
- }
+ lock(u, function (er) {
+ if (er) return cb(er)
- log.verbose([u, co], "addRemoteGit")
+ // figure out what we should check out.
+ var co = parsed.hash && parsed.hash.substr(1) || "master"
+ // git is so tricky!
+ // if the path is like ssh://foo:22/some/path then it works, but
+ // it needs the ssh://
+ // If the path is like ssh://foo:some/path then it works, but
+ // only if you remove the ssh://
+ u = u.replace(/^git\+/, "")
+ .replace(/#.*$/, "")
+
+ // ssh paths that are scp-style urls don't need the ssh://
+ if (parsed.pathname.match(/^\/?:/)) {
+ u = u.replace(/^ssh:\/\//, "")
+ }
- var tmp = path.join(npm.tmp, Date.now()+"-"+Math.random())
- mkdir(path.dirname(tmp), function (er) {
- if (er) return cb(er)
- exec( npm.config.get("git"), ["clone", u, tmp], null, false
- , function (er, code, stdout, stderr) {
- stdout = (stdout + "\n" + stderr).trim()
- if (er) {
- log.error(stdout, "git clone "+u)
- return cb(er)
- }
- log.verbose(stdout, "git clone "+u)
- exec( npm.config.get("git"), ["checkout", co], null, false, tmp
+ log.verbose("addRemoteGit", [u, co])
+
+ var tmp = path.join(npm.tmp, Date.now()+"-"+Math.random())
+ mkdir(path.dirname(tmp), function (er) {
+ if (er) return cb(er)
+ exec( npm.config.get("git"), ["clone", u, tmp], null, false
, function (er, code, stdout, stderr) {
stdout = (stdout + "\n" + stderr).trim()
if (er) {
- log.error(stdout, "git checkout "+co)
+ log.error("git clone " + u, stdout)
return cb(er)
}
- log.verbose(stdout, "git checkout "+co)
- addLocalDirectory(tmp, cb)
+ log.verbose("git clone "+u, stdout)
+ exec( npm.config.get("git"), ["checkout", co], null, false, tmp
+ , function (er, code, stdout, stderr) {
+ stdout = (stdout + "\n" + stderr).trim()
+ if (er) {
+ log.error("git checkout " + co, stdout)
+ return cb(er)
+ }
+ log.verbose("git checkout " + co, stdout)
+ addLocalDirectory(tmp, cb)
+ })
})
})
})
// only have one request in flight for a given
// name@blah thing.
var inFlightNames = {}
-function addNamed (name, x, cb_) {
- log.verbose([name, x], "addNamed")
+function addNamed (name, x, data, cb_) {
+ if (typeof cb_ !== "function") cb_ = data, data = null
+ log.verbose("addNamed", [name, x])
+
var k = name + "@" + x
if (!inFlightNames[k]) inFlightNames[k] = []
var iF = inFlightNames[k]
if (iF.length > 1) return
function cb (er, data) {
- var c
- while (c = iF.shift()) c(er, data)
- delete inFlightNames[k]
+ unlock(k, function () {
+ var c
+ while (c = iF.shift()) c(er, data)
+ delete inFlightNames[k]
+ })
}
- log.verbose([semver.valid(x), semver.validRange(x)], "addNamed")
- return ( null !== semver.valid(x) ? addNameVersion
- : null !== semver.validRange(x) ? addNameRange
- : addNameTag
- )(name, x, cb)
+ log.verbose("addNamed", [semver.valid(x), semver.validRange(x)])
+ lock(k, function (er, fd) {
+ if (er) return cb(er)
+
+ var fn = ( null !== semver.valid(x) ? addNameVersion
+ : null !== semver.validRange(x) ? addNameRange
+ : addNameTag
+ )
+ fn(name, x, data, cb)
+ })
}
-function addNameTag (name, tag, cb) {
- log([name, tag], "addNameTag")
+function addNameTag (name, tag, data, cb) {
+ if (typeof cb !== "function") cb = data, data = null
+ log.info("addNameTag", [name, tag])
var explicit = true
if (!tag) {
explicit = false
if (data["dist-tags"] && data["dist-tags"][tag]
&& data.versions[data["dist-tags"][tag]]) {
var ver = data["dist-tags"][tag]
- return addNameVersion(name, ver, data.versions[ver], cb)
+ return addNamed(name, ver, data.versions[ver], cb)
}
if (!explicit && Object.keys(data.versions).length) {
- return addNameRange(name, "*", data, cb)
+ return addNamed(name, "*", data, cb)
}
return cb(installTargetsError(tag, data))
})
function engineFilter (data) {
var npmv = npm.version
, nodev = npm.config.get("node-version")
+ , strict = npm.config.get("engine-strict")
if (!nodev || npm.config.get("force")) return data
Object.keys(data.versions || {}).forEach(function (v) {
var eng = data.versions[v].engines
if (!eng) return
+ if (!strict && !data.versions[v].engineStrict) return
if (eng.node && !semver.satisfies(nodev, eng.node)
|| eng.npm && !semver.satisfies(npmv, eng.npm)) {
delete data.versions[v]
if (range === null) return cb(new Error(
"Invalid version range: "+range))
- log.silly([name, range, !!data], "name, range, hasData")
+ log.silly("addNameRange", {name:name, range:range, hasData:!!data})
if (data) return next()
registry.get(name, function (er, d, json, response) {
})
function next () {
- log.silly([name, range, !!data], "name, range, hasData 2")
+ log.silly( "addNameRange", "number 2"
+ , {name:name, range:range, hasData:!!data})
engineFilter(data)
if (npm.config.get("registry")) return next_()
}
function next_ () {
- log.silly([data.name, Object.keys(data.versions)], "versions")
+ log.silly("addNameRange", "versions"
+ , [data.name, Object.keys(data.versions || {})])
+
// if the tagged version satisfies, then use that.
var tagged = data["dist-tags"][npm.config.get("tag")]
if (tagged && data.versions[tagged] && semver.satisfies(tagged, range)) {
- return addNameVersion(name, tagged, data.versions[tagged], cb)
+ return addNamed(name, tagged, data.versions[tagged], cb)
}
// find the max satisfying version.
// if we don't have a registry connection, try to see if
// there's a cached copy that will be ok.
- addNameVersion(name, ms, data.versions[ms], cb)
+ addNamed(name, ms, data.versions[ms], cb)
}
}
// filter the versions down based on what's already in cache.
function cachedFilter (data, range, cb) {
- log.silly(data.name, "cachedFilter")
+ log.silly("cachedFilter", data.name)
ls_(data.name, 1, function (er, files) {
- if (er) return log.er(cb, "Not in cache, can't fetch: "+data.name)(er)
+ if (er) {
+ log.error("cachedFilter", "Not in cache, can't fetch", data.name)
+ return cb(er)
+ }
files = files.map(function (f) {
return path.basename(f.replace(/(\\|\/)$/, ""))
}).filter(function (f) {
return cb(new Error("Not in cache, can't fetch: "+data.name+"@"+range))
}
- log.silly([data.name, files], "cached")
+ log.silly("cached", [data.name, files])
Object.keys(data.versions).forEach(function (v) {
if (files.indexOf(v) === -1) delete data.versions[v]
})
if (Object.keys(data.versions).length === 0) {
- return log.er(cb, "Not in cache, can't fetch: "+data.name)(er)
+ log.error("cachedFilter", "Not in cache, can't fetch", data.name)
+ return cb(new Error("Not in cache, can't fetch: "+data.name+"@"+range))
}
- log.silly([data.name, Object.keys(data.versions)], "filtered")
+ log.silly("filtered", [data.name, Object.keys(data.versions)])
cb(null, data)
})
}
response = null
return next()
}
- registry.get(name, ver, function (er, d, json, resp) {
+ registry.get(name + "/" + ver, function (er, d, json, resp) {
if (er) return cb(er)
data = d
response = resp
if (!dist) return cb(new Error("No dist in "+data._id+" package"))
- var bd = npm.config.get("bindist")
- , b = dist.bin && bd && dist.bin[bd]
- log.verbose([bd, dist], "bin dist")
- if (b && b.tarball && b.shasum) {
- log.info(data._id, "prebuilt")
- log.verbose(b, "prebuilt "+data._id)
- dist = b
- }
-
if (!dist.tarball) return cb(new Error(
"No dist.tarball in " + data._id + " package"))
if (typeof cb_ !== "function") cb_ = name, name = ""
function cb (er, data) {
- if (er) {
- // if it doesn't have a / in it, it might be a
- // remote thing.
- if (p.indexOf("/") === -1 && p.charAt(0) !== "."
- && (process.platform !== "win32" || p.indexOf("\\") === -1)) {
- return addNamed(p, "", cb_)
+ unlock(p, function () {
+ if (er) {
+ // if it doesn't have a / in it, it might be a
+ // remote thing.
+ if (p.indexOf("/") === -1 && p.charAt(0) !== "."
+ && (process.platform !== "win32" || p.indexOf("\\") === -1)) {
+ return addNamed(p, "", cb_)
+ }
+ log.error("addLocal", "Could not install %s", p)
+ return cb_(er)
}
- return log.er(cb_, "Could not install: "+p)(er)
- }
- return cb_(er, data)
+ return cb_(er, data)
+ })
}
- // figure out if this is a folder or file.
- fs.stat(p, function (er, s) {
+ lock(p, function (er) {
if (er) return cb(er)
- if (s.isDirectory()) addLocalDirectory(p, name, cb)
- else addLocalTarball(p, name, cb)
+ // figure out if this is a folder or file.
+ fs.stat(p, function (er, s) {
+ if (er) return cb(er)
+ if (s.isDirectory()) addLocalDirectory(p, name, cb)
+ else addLocalTarball(p, name, cb)
+ })
})
}
to.on("error", errHandler)
to.on("close", function () {
if (errState) return
- log.verbose(npm.modes.file.toString(8), "chmod "+tmp)
+ log.verbose("chmod", tmp, npm.modes.file.toString(8))
fs.chmod(tmp, npm.modes.file, function (er) {
if (er) return cb(er)
addTmpTarball(tmp, name, cb)
fs.stat(npm.cache, function (er, st) {
if (er) return makeCacheDir(cb)
if (!st.isDirectory()) {
- return log.er(cb, "invalid cache directory: "+npm.cache)(er)
+ log.error("getCacheStat", "invalid cache dir %j", npm.cache)
+ return cb(er)
}
return cb(null, cacheStat = st)
})
}
fs.stat(process.env.HOME, function (er, st) {
- if (er) return log.er(cb, "homeless?")(er)
+ if (er) {
+ log.error("makeCacheDir", "homeless?")
+ return cb(er)
+ }
cacheStat = st
- log.silly([st.uid, st.gid], "uid, gid for cache dir")
+ log.silly("makeCacheDir", "cache dir uid, gid", [st.uid, st.gid])
return mkdir(npm.cache, afterMkdir)
})
var target = path.dirname(p)
, folder = path.join(target, "package")
- rm(folder, function (er) {
- if (er) return log.er(cb, "Could not remove "+folder)(er)
+ lock(folder, function (er) {
+ if (er) return cb(er)
+ rmUnpack()
+ })
+
+ function rmUnpack () {
+ rm(folder, function (er) {
+ unlock(folder, function () {
+ if (er) {
+ log.error("addPlacedTarball", "Could not remove %j", folder)
+ return cb(er)
+ }
+ thenUnpack()
+ })
+ })
+ }
+
+ function thenUnpack () {
tar.unpack(p, folder, null, null, uid, gid, function (er) {
- if (er) return log.er(cb, "Could not unpack "+p+" to "+target)(er)
+ if (er) {
+ log.error("addPlacedTarball", "Could not unpack %j to %j", p, target)
+ return cb(er)
+ }
// calculate the sha of the file that we just unpacked.
// this is so that the data is available when publishing.
sha.get(p, function (er, shasum) {
- if (er) return log.er(cb, "couldn't validate shasum of "+p)(er)
+ if (er) {
+ log.error("addPlacedTarball", "shasum fail", p)
+ return cb(er)
+ }
readJson(path.join(folder, "package.json"), function (er, data) {
- if (er) return log.er(cb, "couldn't read json in "+folder)(er)
+ if (er) {
+ log.error("addPlacedTarball", "Couldn't read json in %j"
+ , folder)
+ return cb(er)
+ }
data.dist = data.dist || {}
if (shasum) data.dist.shasum = shasum
deprCheck(data)
asyncMap([p], function (f, cb) {
- log.verbose(npm.modes.file.toString(8), "chmod "+f)
+ log.verbose("chmod", f, npm.modes.file.toString(8))
fs.chmod(f, npm.modes.file, cb)
}, function (f, cb) {
if (process.platform === "win32") {
- log.silly(f, "skipping chown for windows")
+ log.silly("chown", "skipping for windows", f)
cb()
} else if (typeof uid === "number"
&& typeof gid === "number"
&& parseInt(uid, 10) === uid
&& parseInt(gid, 10) === gid) {
- log.verbose([f, uid, gid], "chown")
+ log.verbose("chown", f, [uid, gid])
fs.chown(f, uid, gid, cb)
} else {
- log.verbose([f, uid, gid], "not chowning, invalid uid/gid")
+ log.verbose("chown", "skip for invalid uid/gid", [f, uid, gid])
cb()
}
}, function (er) {
})
})
})
- })
+ }
}
function addLocalDirectory (p, name, cb) {
mkdir(path.dirname(tgz), function (er, made) {
if (er) return cb(er)
tar.pack(tgz, p, data, doFancyCrap, function (er) {
- if (er) return log.er(cb,"couldn't pack "+p+ " to "+tgz)(er)
+ if (er) {
+ log.error( "addLocalDirectory", "Could not pack %j to %j"
+ , p, tgz )
+ return cb(er)
+ }
- if (er || !cs || isNaN(cs.uid) || isNaN(cs.gid)) return cb()
+ // if we don't get a cache stat, or if the gid/uid is not
+ // a number, then just move on. chown would fail anyway.
+ if (!cs || isNaN(cs.uid) || isNaN(cs.gid)) return cb()
chownr(made || tgz, cs.uid, cs.gid, function (er) {
if (er) return cb(er)
read(pkg, ver, false, function (er, data) {
if (er) {
- log.error("Could not read data for "+pkg+"@"+ver)
+ log.error("unpack", "Could not read data for %s", pkg + "@" + ver)
return cb(er)
}
npm.commands.unbuild([unpackTarget], function (er) {
else return
if (!deprWarned[data._id]) {
deprWarned[data._id] = true
- log.warn(data._id+": "+data.deprecated, "deprecated")
+ log.warn("deprecated", "%s: %s", data._id, data.deprecated)
+ }
+}
+
+function lockFileName (u) {
+ var c = u.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "")
+ , h = crypto.createHash("sha1").update(u).digest("hex")
+ h = h.substr(0, 8)
+ c = c.substr(-32)
+ log.silly("lockFile", h + "-" + c, u)
+ return path.resolve(npm.config.get("cache"), h + "-" + c + ".lock")
+}
+
+var madeCache = false
+function lock (u, cb) {
+ // the cache dir needs to exist already for this.
+ if (madeCache) then()
+ else mkdir(npm.config.get("cache"), function (er) {
+ if (er) return cb(er)
+ madeCache = true
+ then()
+ })
+ function then () {
+ var opts = { stale: npm.config.get("cache-lock-stale")
+ , retries: npm.config.get("cache-lock-retries")
+ , wait: npm.config.get("cache-lock-wait") }
+ var lf = lockFileName(u)
+ log.verbose("lock", u, lf)
+ lockFile.lock(lf, opts, cb)
}
}
+
+function unlock (u, cb) {
+ lockFile.unlock(lockFileName(u), cb)
+}
+ "\nnpm get [<key>]"
var ini = require("./utils/ini.js")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, npm = require("./npm.js")
, exec = require("./utils/exec.js")
, fs = require("graceful-fs")
}
key = key.trim()
val = val.trim()
- log("set "+key+" "+val, "config")
+ log.info("config", "set %j %j", key, val)
var where = ini.get("global") ? "global" : "user"
ini.set(key, val, where)
ini.save(where, cb)
if (cliKeys.length) {
msg += "; cli configs" + eol
cliKeys.forEach(function (k) {
+ if (cli[k] && typeof cli[k] === 'object') return
if (k === "argv") return
+ if (typeof cli[k] === 'object') return
msg += k + " = " + JSON.stringify(cli[k]) + eol
})
msg += eol
if (envKeys.length) {
msg += "; environment configs" + eol
envKeys.forEach(function (k) {
+ if (env[k] && typeof env[k] === 'object') return
if (env[k] !== ini.get(k)) {
if (!long) return
msg += "; " + k + " = " + JSON.stringify(env[k])
if (uconfKeys.length) {
msg += "; userconfig " + ini.get("userconfig") + eol
uconfKeys.forEach(function (k) {
+ if (uconf[k] && typeof uconf[k] === 'object') return
var val = (k.charAt(0) === "_")
? "---sekretz---"
: JSON.stringify(uconf[k])
if (gconfKeys.length) {
msg += "; globalconfig " + ini.get("globalconfig") + eol
gconfKeys.forEach(function (k) {
+ if (gconf[k] && typeof gconf[k] === 'object') return
var val = (k.charAt(0) === "_")
? "---sekretz---"
: JSON.stringify(gconf[k])
var path = require("path")
msg += "; builtin config " + path.resolve(__dirname, "../npmrc") + eol
bconfKeys.forEach(function (k) {
+ if (bconf[k] && typeof bconf[k] === 'object') return
var val = (k.charAt(0) === "_")
? "---sekretz---"
: JSON.stringify(bconf[k])
, defKeys = Object.keys(defaults)
msg += "; default values" + eol
defKeys.forEach(function (k) {
+ if (defaults[k] && typeof defaults[k] === 'object') return
var val = JSON.stringify(defaults[k])
if (defaults[k] !== ini.get(k)) {
if (!long) return
})
}
-var registry = require("./utils/npm-registry-client/index.js")
- , semver = require("semver")
- , log = require("./utils/log.js")
+var semver = require("semver")
, npm = require("./npm.js")
+ , registry = npm.registry
function deprecate (args, cb) {
var pkg = args[0]
data.versions[v].deprecated = msg
})
// now update the doc on the registry
- registry.request.PUT(data._id, data, cb)
+ registry.request('PUT', data._id, data, cb)
})
}
docs.completion = function (opts, cb) {
if (opts.conf.argv.remain.length > 2) return cb()
- registry.get("/-/short", null, 60000, function (er, list) {
+ registry.get("/-/short", 60000, function (er, list) {
return cb(null, list || [])
})
}
var exec = require("./utils/exec.js")
- , registry = require("./utils/npm-registry-client/index.js")
, npm = require("./npm.js")
- , log = require("./utils/log.js")
+ , registry = npm.registry
+ , log = require("npmlog")
function docs (args, cb) {
if (!args.length) return cb(docs.usage)
var n = args[0].split("@").shift()
- registry.get(n, "latest", 3600, function (er, d) {
+ registry.get(n + "/latest", 3600, function (er, d) {
if (er) return cb(er)
var homepage = d.homepage
, repo = d.repository || d.repositories
if (repo) {
if (Array.isArray(repo)) repo = repo.shift()
if (repo.hasOwnProperty("url")) repo = repo.url
- log.verbose(repo, "repository")
+ log.verbose("repository", repo)
if (repo) {
return open(repo.replace(/^git(@|:\/\/)/, 'http://')
.replace(/\.git$/, '')+"#readme", cb)
, exec = require("./utils/exec.js")
, path = require("path")
, fs = require("graceful-fs")
- , log = require("./utils/log.js")
function edit (args, cb) {
var p = args[0]
, asyncMap = require("slide").asyncMap
, cliDocsPath = path.join(__dirname, "..", "doc", "cli")
, apiDocsPath = path.join(__dirname, "..", "doc", "api")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, npm = require("./npm.js")
helpSearch.usage = "npm help-search <text>"
}
fs.readdir(docsPath, function(er, files) {
- if (er) return log.er(cb, "Could not load documentation")(er)
+ if (er) {
+ log.error("helpSearch", "Could not load documentation")
+ return cb(er)
+ }
var search = args.join(" ")
, results = []
, exec = require("./utils/exec.js")
, npm = require("./npm.js")
, output = require("./utils/output.js")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
function help (args, cb) {
var num = 1
&& npm.commands[section].usage
) {
npm.config.set("loglevel", "silent")
+ log.level = "silent"
return output.write(npm.commands[section].usage, cb)
}
} else getSections(function (er, sections) {
if (er) return cb(er)
npm.config.set("loglevel", "silent")
+ log.level = "silent"
output.write
( ["\nUsage: npm <command>"
, ""
module.exports = init
-var read = require("read")
- , path = require("path")
- , readJson = require("./utils/read-json.js")
- , fs = require("graceful-fs")
- , promiseChain = require("./utils/promise-chain.js")
- , exec = require("./utils/exec.js")
- , semver = require("semver")
- , log = require("./utils/log.js")
+var log = require("npmlog")
, npm = require("./npm.js")
- , output = require("./utils/output.js")
+ , initJson = require("init-package-json")
-init.usage = "npm init [folder]"
+init.usage = "npm init"
function init (args, cb) {
- var folder = args[0] || "."
- , ll = npm.config.get("loglevel")
- npm.config.set("loglevel", "paused")
- if (folder.charAt(0) !== "/") folder = path.join(process.cwd(), folder)
+ var dir = process.cwd()
+ log.pause()
+ var initFile = npm.config.get('init-module')
- readJson(path.join(folder, "package.json"), function (er, data) {
- if (er) data = {}
-
- data.author = data.author ||
- { name: npm.config.get("init.author.name")
- , email: npm.config.get("init.author.email")
- , url: npm.config.get("init.author.url") }
-
- init_(data, folder, function (er) {
- npm.config.set("loglevel", ll)
- if (!er) log(path.resolve(folder, "package.json"), "written")
- cb(er)
- })
- })
-}
-
-function init_ (data, folder, cb) {
- var nv = npm.config.get("node-version")
- , p = semver.parse(nv)
- , eng = ""
-
- if (!p[5]) eng = "~" + nv
- else eng = "~" + [p[1], p[2], p[3]].join(".") + " || " + nv
-
- // node version 0.n is api-compatible with 0.(n+1) when n is odd.
- if (p[2] % 2) {
- eng += " || " + [p[1], +(p[2]) + 1].join(".")
- }
-
-
- output.write(
+ console.log(
["This utility will walk you through creating a package.json file."
,"It only covers the most common items, and tries to guess sane defaults."
,""
,"save it as a dependency in the package.json file."
,""
,"Press ^C at any time to quit."
- ,""
].join("\n"))
- promiseChain(cb)
- ( read
- , [{prompt: "Package name: ", default: defaultName(folder, data)}]
- , function (n) { data.name = n }
- )
- ( read
- , [{prompt: "Description: ", default: data.description}]
- , function (d) { data.description = d }
- )
- ( defaultVersion, [folder, data], function (v) { data.version = v } )
- (function (cb) {
- read( { prompt: "Package version: ", default: data.version }
- , function (er, v) {
- if (er) return cb(er)
- data.version = v
- cb()
- })
- }, [])
- ( read
- , [ { prompt: "Project homepage: "
- , default: data.homepage || data.url || "none" } ]
- , function (u) {
- if (u === "none") return
- data.homepage = u
- delete data.url
- }
- )
- ( defaultRepo, [folder, data], function (r) { data.repository = r } )
- (function (cb) {
- read( { prompt: "Project git repository: "
- , default: data.repository && data.repository.url || "none" }
- , function (er, r) {
- if (er) return cb(er)
- if (r !== "none") {
- data.repository = (data.repository || {})
- data.repository.url = r
- } else {
- delete data.repository
- }
- cb()
- }
- )
- }, [])
- ( read
- , [{ prompt: "Author name: ", default: data.author && data.author.name }]
- , function (n) {
- if (!n) return
- (data.author = data.author || {}).name = n
- }
- )
- ( read
- , [ { prompt: "Author email: "
- , default: data.author && data.author.email || "none" } ]
- , function (n) {
- if (n === "none") return
- (data.author = data.author || {}).email = n
- }
- )
- ( read
- , [ { prompt: "Author url: "
- , default: data.author && data.author.url || "none" } ]
- , function (n) {
- if (n === "none") return
- (data.author = data.author || {}).url = n
- }
- )
- ( read
- , [ { prompt: "Main module/entry point: ", default: data.main || "none" } ]
- , function (m) {
- if (m === "none") {
- delete data.main
- return
- }
- data.main = m
- }
- )
- ( read
- , [ { prompt: "Test command: "
- , default: data.scripts && data.scripts.test || "none" } ]
- , function (t) {
- if (t === "none") return
- (data.scripts = data.scripts || {}).test = t
- }
- )
- (cleanupPaths, [data, folder])
- (function (cb) {
- try { data = readJson.processJson(data) }
- catch (er) { return cb(er) }
- Object.keys(data)
- .filter(function (k) { return k.match(/^_/) })
- .forEach(function (k) { delete data[k] })
- readJson.unParsePeople(data)
- var str = JSON.stringify(data, null, 2)
- , msg = "About to write to "
- + path.join(folder, "package.json")
- + "\n\n"
- + str
- + "\n\n"
- output.write(msg, cb)
- })
- (function (cb) {
- read({ prompt: "\nIs this ok? ", default: "yes" }, function (er, ok) {
- if (er) return cb(er)
- if (ok.toLowerCase().charAt(0) !== "y") {
- return cb(new Error("cancelled"))
- }
- return cb()
- })
- })
- (function (cb) {
- fs.writeFile( path.join(folder, "package.json")
- , JSON.stringify(data, null, 2) + "\n"
- , cb )
- })
- ()
-}
-
-// sync - no io
-function defaultName (folder, data) {
- if (data.name) return data.name
- return path.basename(folder)
- .replace(/^node[-\._]?|([-\._]node)[-\._]?(js)?$/g, "")
-}
-
-function defaultVersion (folder, data, cb) {
- if (data.version) return cb(null, data.version)
- exec(npm.config.get("git"), ["describe", "--tags"], process.env, false, folder,
- function (er, code, out) {
- out = (out || "").trim()
- if (semver.valid(out)) return cb(null, out)
- out = npm.config.get("init.version")
- if (semver.valid(out)) return cb(null, out)
- return cb(null, "0.0.0")
- })
-}
-function defaultRepo (folder, data, cb) {
- if (data.repository) return cb(null, data.repository)
- exec( npm.config.get("git"), ["remote", "-v"], process.env, false, folder
- , function (er, code, out) {
- out = (out || "")
- .trim()
- .split("\n").filter(function (line) {
- return line.search(/^origin/) !== -1
- })[0]
- if (!out) return cb(null, {})
- var repo =
- { type: "git"
- , url: out.split(/\s/)[1]
- .replace("git@github.com:", "git://github.com/")
- }
- return cb(null, repo)
+ initJson(dir, initFile, npm.config.get(), function (er, data) {
+ log.resume()
+ log.silly('package data', data)
+ log.info('init', 'written successfully')
+ cb(er, data)
})
}
-
-function cleanupPaths (data, folder, cb) {
- if (data.main) {
- data.main = cleanupPath(data.main, folder)
- }
- var dirs = data.directories
- if (dirs) {
- Object.keys(dirs).forEach(function (dir) {
- dirs[dir] = cleanupPath(dirs[dir], folder)
- })
- }
- cb()
-}
-
-function cleanupPath (m, folder) {
- if (m.indexOf(folder) === 0) m = path.join(".", m.substr(folder.length))
- return m
-}
// if it has a slash, then it's gotta be a folder
// if it starts with https?://, then just give up, because it's a url
// for now, not yet implemented.
- var registry = require("./utils/npm-registry-client/index.js")
+ var registry = npm.registry
registry.get("/-/short", function (er, pkgs) {
if (er) return cb()
if (!opts.partialWord) return cb(null, pkgs)
var npm = require("./npm.js")
, semver = require("semver")
- , readJson = require("./utils/read-json.js")
- , log = require("./utils/log.js")
+ , readJson = require("read-package-json")
+ , log = require("npmlog")
, path = require("path")
, fs = require("graceful-fs")
, cache = require("./cache.js")
, asyncMap = require("slide").asyncMap
, chain = require("slide").chain
- , relativize = require("./utils/relativize.js")
, output
, url = require("url")
, mkdir = require("mkdirp")
where = args
args = [].concat(cb_) // pass in [] to do default dep-install
cb_ = arguments[2]
- log.verbose([where, args], "install(where, what)")
+ log.verbose("install", "where,what", [where, args])
}
if (!npm.config.get("global")) {
, where
, { dev: !npm.config.get("production") }
, function (er, data) {
- if (er) return log.er(cb, "Couldn't read dependencies.")(er)
+ if (er) {
+ log.error("install", "Couldn't read dependencies")
+ return cb(er)
+ }
var deps = Object.keys(data.dependencies || {})
- log.verbose([where, deps], "where, deps")
+ log.verbose("install", "where, deps", [where, deps])
var context = { family: {}
, ancestors: {}
, explicit: false
, parent: data
, wrap: null }
- context.family[data.name] = context.ancestors[data.name] = data.version
+
+ if (data.name === path.basename(where) &&
+ path.basename(path.dirname(where)) === "node_modules") {
+ // Only include in ancestry if it can actually be required.
+ // Otherwise, it does not count.
+ context.family[data.name] =
+ context.ancestors[data.name] = data.version
+ }
+
installManyTop(deps.map(function (dep) {
var target = data.dependencies[dep]
, parsed = url.parse(target.replace(/^git\+/, "git"))
var wrap = context ? context.wrap : null
readJson( path.resolve(where, "package.json")
- , opts
, function (er, data) {
if (er) return cb(er)
+ if (opts && opts.dev) {
+ if (!data.dependencies) data.dependencies = {};
+ Object.keys(data.devDependencies || {}).forEach(function (k) {
+ data.dependencies[k] = data.devDependencies[k]
+ })
+ }
+
if (wrap) {
- log.verbose([where, wrap], "readDependencies: using existing wrap")
+ log.verbose("readDependencies: using existing wrap", [where, wrap])
var rv = {}
Object.keys(data).forEach(function (key) {
rv[key] = data[key]
})
rv.dependencies = {}
Object.keys(wrap).forEach(function (key) {
- log.verbose([key, wrap[key]], "from wrap")
+ log.verbose("from wrap", [key, wrap[key]])
var w = wrap[key]
rv.dependencies[key] = w.from || w.version
})
- log.verbose([rv.dependencies], "readDependencies: returned deps")
+ log.verbose("readDependencies returned deps", rv.dependencies)
return cb(null, rv, wrap)
}
fs.readFile(wrapfile, "utf8", function (er, wrapjson) {
if (er) {
- log.verbose("readDependencies: using package.json deps")
+ log.verbose("readDependencies", "using package.json deps")
return cb(null, data, null)
}
return cb(ex)
}
- log.info(wrapfile, "using shrinkwrap file")
+ log.info("shrinkwrap", "file %j", wrapfile)
var rv = {}
Object.keys(data).forEach(function (key) {
rv[key] = data[key]
var w = newwrap.dependencies[key]
rv.dependencies[key] = w.from || w.version
})
- log.verbose([rv.dependencies], "readDependencies: returned deps")
+ log.verbose("readDependencies returned deps", rv.dependencies)
return cb(null, rv, newwrap.dependencies)
})
})
return cb(null, installed, tree, pretty)
}
+ var saveBundle = npm.config.get('save-bundle')
+
// each item in the tree is a top-level thing that should be saved
// to the package.json file.
// The relevant tree shape is { <folder>: {what:<pkg>} }
: npm.config.get("save-dev") ? "devDependencies"
: "dependencies"
+ if (saveBundle) {
+ var bundle = data.bundleDependencies || data.bundledDependencies
+ delete data.bundledDependencies
+ if (!Array.isArray(bundle)) bundle = []
+ data.bundleDependencies = bundle
+ }
+
data[deps] = data[deps] || {}
Object.keys(things).forEach(function (t) {
data[deps][t] = things[t]
+ if (saveBundle) {
+ var i = bundle.indexOf(t)
+ if (i === -1) bundle.push(t)
+ }
})
+
data = JSON.stringify(data, null, 2) + "\n"
fs.writeFile(saveTarget, data, function (er) {
cb(er, installed, tree, pretty)
return l
}, {})
- //log.warn(whatWhere, "whatWhere")
+ //log.warn("install", whatWhere, "whatWhere")
return Object.keys(whatWhere).reduce(function (l, r) {
var ww = whatWhere[r]
- //log.warn([r, ww], "r, ww")
+ //log.warn("r, ww", [r, ww])
if (!ww.parent) {
l[r] = ww
} else {
targets.forEach(function (t) {
newPrev[t.name] = t.version
})
- log.silly(targets, "resolved")
+ log.silly("resolved", targets)
targets.filter(function (t) { return t }).forEach(function (t) {
- log.info(t._id, "into "+where)
+ log.info("install", "%s into %s", t._id, where)
})
asyncMap(targets, function (target, cb) {
- log.info(target._id, "installOne")
+ log.info("installOne", target._id)
var newWrap = wrap ? wrap[target.name].dependencies || {} : null
var newContext = { family: newPrev
, ancestors: newAnc
// now we know what's been installed here manually,
// or tampered with in some way that npm doesn't want to overwrite.
if (alreadyInstalledManually.indexOf(what.split("@").shift()) !== -1) {
- log.verbose("skipping "+what, "already installed in "+where)
+ log.verbose("already installed", "skipping %s %s", what, where)
return cb(null, [])
}
// If installing from a shrinkwrap, it must match exactly.
if (context.family[what]) {
if (wrap && wrap[what].version === context.family[what]) {
- log.verbose(what, "using existing (matches shrinkwrap)")
+ log.verbose("shrinkwrap", "use existing", what)
return cb(null, [])
}
}
// doing `npm install foo` inside of the foo project. Print
// a warning, and skip it.
if (parent && parent.name === what && !npm.config.get("force")) {
- log.warn("Refusing to install "+what+" as a dependency of itself"
- ,"install")
+ log.warn("install", "Refusing to install %s as a dependency of itself"
+ , what)
return cb(null, [])
}
name = what.split(/@/).shift()
if (wrap[name]) {
var wrapTarget = wrap[name].from || wrap[name].version
- log.verbose("resolving "+what+" to "+wrapTarget, "shrinkwrap")
+ log.verbose("shrinkwrap", "resolving %s to %s", wrapTarget, what)
what = name + "@" + wrapTarget
} else {
- log.verbose("skipping "+what+" (not in shrinkwrap)", "shrinkwrap")
+ log.verbose("shrinkwrap", "skipping %s (not in shrinkwrap)", what)
}
} else if (deps[what]) {
what = what + "@" + deps[what]
cache.add(what, function (er, data) {
if (er && parent && parent.optionalDependencies &&
parent.optionalDependencies.hasOwnProperty(what.split("@")[0])) {
- log.warn(what, "optional dependency failed, continuing")
- log.verbose([what, er], "optional dependency failed, continuing")
+ log.warn("optional dep failed, continuing", what)
+ log.verbose("optional dep failed, continuing", [what, er])
return cb(null, [])
}
!context.explicit &&
context.family[data.name] === data.version &&
!npm.config.get("force")) {
- log.info(data.name + "@" + data.version, "already installed")
+ log.info("already installed", data.name + "@" + data.version)
return cb(null, [])
}
// check if this one is optional to its parent.
if (er && context.parent && context.parent.optionalDependencies &&
context.parent.optionalDependencies.hasOwnProperty(target.name)) {
- log.warn(target._id, "optional dependency failed, continuing")
- log.verbose([target._id, er], "optional dependency failed, continuing")
+ log.warn("optional dep failed, continuing", target._id)
+ log.verbose("optional dep failed, continuing", [target._id, er])
er = null
}
}
function localLink (target, where, context, cb) {
- log.verbose(target._id, "try to link")
+ log.verbose("localLink", target._id)
var jsonFile = path.resolve( npm.globalDir, target.name
, "package.json" )
, parent = context.parent
function thenLink () {
npm.commands.link([target.name], function (er, d) {
- log.silly([er, d], "back from link")
+ log.silly("localLink", "back from link", [er, d])
cb(er, [resultList(target, where, parent && parent._id)])
})
}
} else {
- log.verbose(target._id, "install locally (no link)")
+ log.verbose("localLink", "install locally (no link)", target._id)
installOne_(target, where, context, cb)
}
})
function resultList (target, where, parentId) {
var nm = path.resolve(where, "node_modules")
, targetFolder = path.resolve(nm, target.name)
- , prettyWhere = relativize(where, process.cwd() + "/x")
+ , prettyWhere = path.relative(process.cwd(), where)
if (prettyWhere === ".") prettyWhere = null
if (!npm.config.get("global")) {
// print out the folder relative to where we are right now.
- // relativize isn't really made for dirs, so you need this hack
- targetFolder = relativize(targetFolder, process.cwd()+"/x")
+ targetFolder = path.relative(process.cwd(), targetFolder)
}
return [ target._id
function installOne_ (target, where, context, cb) {
var nm = path.resolve(where, "node_modules")
, targetFolder = path.resolve(nm, target.name)
- , prettyWhere = relativize(where, process.cwd() + "/x")
+ , prettyWhere = path.relative(process.cwd, where)
, parent = context.parent
if (prettyWhere === ".") prettyWhere = null
var npmv = npm.version
, force = npm.config.get("force")
, nodev = force ? null : npm.config.get("node-version")
+ , strict = npm.config.get("engine-strict") || target.engineStrict
, eng = target.engines
if (!eng) return cb()
if (nodev && eng.node && !semver.satisfies(nodev, eng.node)
|| eng.npm && !semver.satisfies(npmv, eng.npm)) {
- var er = new Error("Unsupported")
- er.errno = npm.ENOTSUP
- er.required = eng
- er.pkgid = target._id
- return cb(er)
+ if (strict) {
+ var er = new Error("Unsupported")
+ er.code = "ENOTSUP"
+ er.required = eng
+ er.pkgid = target._id
+ return cb(er)
+ } else {
+ log.warn( "engine", "%s: wanted: %j (current: %j)"
+ , target._id, eng, {node: nodev, npm: npm.version} )
+ }
}
return cb()
}
}
if (!osOk || !cpuOk) {
var er = new Error("Unsupported")
- er.errno = npm.EBADPLATFORM
+ er.code = "EBADPLATFORM"
er.os = target.os || ['any']
er.cpu = target.cpu || ['any']
er.pkgid = target._id
tree.push(JSON.parse(JSON.stringify(t)))
t = Object.getPrototypeOf(t)
}
- log.verbose(tree, "unresolvable dependency tree")
+ log.verbose("unresolvable dependency tree", tree)
er.pkgid = target._id
- er.errno = npm.ECYCLE
+ er.code = "ECYCLE"
return cb(er)
}
if (!er && s.isDirectory()) {
var e = new Error("Appears to be a git repo or submodule.")
e.path = folder
- e.errno = npm.EISGIT
+ e.code = "EISGIT"
return cb(e)
}
cb()
if (false === npm.config.get("rollback")) return cb_(er)
npm.commands.unbuild([targetFolder], function (er2) {
- if (er2) log.error(er2, "error rolling back "+target._id)
+ if (er2) log.error("error rolling back", target._id, er2)
return cb_(er, data)
})
}
t = d + "@" + t
return t
}), targetFolder, newcontext, function (er, d) {
- log.verbose(targetFolder, "about to build")
+ log.verbose("about to build", targetFolder)
if (er) return cb(er)
npm.commands.build( [targetFolder]
, npm.config.get("global")
-
// link with no args: symlink the folder to the global location
// link with package arg: symlink the global to the local
var npm = require("./npm.js")
, symlink = require("./utils/link.js")
, fs = require("graceful-fs")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, asyncMap = require("slide").asyncMap
, chain = require("slide").chain
, path = require("path")
- , relativize = require("./utils/relativize.js")
, rm = require("rimraf")
, output = require("./utils/output.js")
, build = require("./build.js")
function link (args, cb) {
if (process.platform === "win32") {
- var e = new Error("npm link not supported on windows")
- e.code = "ENOTSUP"
- e.errno = require("constants").ENOTSUP
- return cb(e)
+ var semver = require("semver")
+ if (!semver.satisfies(process.version, ">=0.7.9")) {
+ var msg = "npm link not supported on windows prior to node 0.7.9"
+ , e = new Error(msg)
+ e.code = "ENOTSUP"
+ e.errno = require("constants").ENOTSUP
+ return cb(e)
+ }
}
if (npm.config.get("global")) {
return cb(new Error("link should never be --global.\n"
+"Please re-run this command with --local"))
}
+
if (args.length === 1 && args[0] === ".") args = []
if (args.length) return linkInstall(args, cb)
linkPkg(npm.prefix, cb)
next()
} else {
return fs.realpath(pp, function (er, real) {
- if (er) log.warn(pkg, "invalid symbolic link")
+ if (er) log.warn("invalid symbolic link", pkg)
else rp = real
next()
})
function next () {
chain
( [ [npm.commands, "unbuild", [target]]
- , [log.verbose, "symlinking " + pp + " to "+target, "link"]
+ , [function (cb) {
+ log.verbose("link", "symlinking %s to %s", pp, target)
+ cb()
+ }]
, [symlink, pp, target]
// do run lifecycle scripts - full build here.
, rp && [build, [target]]
function linkPkg (folder, cb_) {
var me = folder || npm.prefix
- , readJson = require("./utils/read-json.js")
- readJson( path.resolve(me, "package.json")
- , { dev: true }
- , function (er, d) {
+ , readJson = require("read-package-json")
+
+ log.verbose("linkPkg", folder)
+
+ readJson(path.resolve(me, "package.json"), function (er, d) {
function cb (er) {
return cb_(er, [[d && d._id, target, null, null]])
}
if (er) return cb(er)
symlink(me, target, function (er) {
if (er) return cb(er)
- log.verbose(target, "link: build target")
+ log.verbose("link", "build target", target)
// also install missing dependencies.
npm.commands.install(me, [], function (er, installed) {
if (er) return cb(er)
function resultPrinter (pkg, src, dest, rp, cb) {
if (typeof cb !== "function") cb = rp, rp = null
- var where = relativize(dest, path.resolve(process.cwd(),"x"))
+ var where = dest
rp = (rp || "").trim()
src = (src || "").trim()
// XXX If --json is set, then look up the data from the package.json
module.exports = exports = ls
var npm = require("./npm.js")
- , readInstalled = require("./utils/read-installed.js")
+ , readInstalled = require("read-installed")
, output = require("./utils/output.js")
- , log = require("./utils/log.js")
- , relativize = require("./utils/relativize.js")
+ , log = require("npmlog")
, path = require("path")
, archy = require("archy")
+ , semver = require("semver")
ls.usage = "npm ls"
+ls.completion = require("./utils/completion/installed-deep.js")
+
function ls (args, silent, cb) {
if (typeof cb !== "function") cb = silent, silent = false
- if (args.length) {
- log.warn("ls doesn't take positional args. Try the 'search' command")
- }
-
var dir = path.resolve(npm.dir, "..")
- readInstalled(dir, function (er, data) {
- var lite = getLite(bfsify(data))
+ // npm ls 'foo@~1.3' bar 'baz@<2'
+ if (!args) args = []
+ else args = args.map(function (a) {
+ var nv = a.split("@")
+ , name = nv.shift()
+ , ver = semver.validRange(nv.join("@")) || ""
+
+ return [ name, ver ]
+ })
+
+ readInstalled(dir, npm.config.get("depth"), function (er, data) {
+ var bfs = bfsify(data, args)
+ , lite = getLite(bfs)
if (er || silent) return cb(er, data, lite)
var long = npm.config.get("long")
, out
if (json) {
var seen = []
- var d = long ? bfsify(data) : lite
+ var d = long ? bfs : lite
// the raw data can be circular
out = JSON.stringify(d, function (k, o) {
if (typeof o === "object") {
return o
}, 2)
} else if (npm.config.get("parseable")) {
- out = makeParseable(bfsify(data), long, dir)
+ out = makeParseable(bfs, long, dir)
} else if (data) {
- out = makeArchy(bfsify(data), long, dir)
+ out = makeArchy(bfs, long, dir)
}
output.write(out, function (er) { cb(er, data, lite) })
})
}
+// only include
+function filter (data, args) {
+
+}
+
function alphasort (a, b) {
a = a.toLowerCase()
b = b.toLowerCase()
return lite
}
-function bfsify (root, current, queue, seen) {
+function bfsify (root, args, current, queue, seen) {
// walk over the data, and turn it from this:
// +-- a
// | `-- b
// +-- a
// `-- b
// which looks nicer
+ args = args || []
current = current || root
queue = queue || []
seen = seen || [root]
queue.push(dep)
seen.push(dep)
})
- if (!queue.length) return root
- return bfsify(root, queue.shift(), queue, seen)
+
+ if (!queue.length) {
+ // if there were args, then only show the paths to found nodes.
+ return filterFound(root, args)
+ }
+ return bfsify(root, args, queue.shift(), queue, seen)
}
+function filterFound (root, args) {
+ if (!args.length) return root
+ var deps = root.dependencies
+ if (deps) Object.keys(deps).forEach(function (d) {
+ var dep = filterFound(deps[d], args)
+
+ // see if this one itself matches
+ var found = false
+ for (var i = 0; !found && i < args.length; i ++) {
+ if (d === args[i][0]) {
+ found = semver.satisfies(dep.version, args[i][1])
+ }
+ }
+ // included explicitly
+ if (found) dep._found = true
+ // included because a child was included
+ if (dep._found && !root._found) root._found = 1
+ // not included
+ if (!dep._found) delete deps[d]
+ })
+ if (!root._found) root._found = false
+ return root
+}
function makeArchy (data, long, dir) {
var out = makeArchy_(data, long, dir, 0)
if (depth < npm.config.get("depth")) {
// just missing
var p = parent.link || parent.path
- log.warn("Unmet dependency in "+p, d+" "+data)
+ log.warn("unmet dependency", "%s in %s", d+" "+data, p)
data = "\033[31;40mUNMET DEPENDENCY\033[0m " + d + " " + data
} else {
- data = d+"@'"+ data +"' (max depth reached)"
+ data = d+"@"+ data +" (max depth reached)"
}
return data
}
var out = {}
// the top level is a bit special.
- out.label = data._id ? data._id + " " : ""
- if (data.link) out.label += "-> " + data.link
+ out.label = data._id || ""
+ if (data._found === true && data._id) {
+ out.label = "\033[33;40m" + out.label.trim() + "\033[m "
+ }
+ if (data.link) out.label += " -> " + data.link
if (data.invalid) {
if (data.realName !== data.name) out.label += " ("+data.realName+")"
function getExtras (data, dir) {
var extras = []
- , rel = relativize(data.path || "", dir)
, url = require("url")
if (data.description) extras.push(data.description)
.sort(alphasort).map(function (d) {
return makeParseable(data.dependencies[d], long, dir, depth + 1, data, d)
}))
+ .filter(function (x) { return x })
.join("\n")
}
function makeParseable_ (data, long, dir, depth, parent, d) {
+ if (data.hasOwnProperty("_found") && data._found !== true) return ""
+
if (typeof data === "string") {
if (data.depth < npm.config.get("depth")) {
var p = parent.link || parent.path
- log.warn("Unmet dependency in "+p, d+" "+data)
+ log.warn("unmet dependency", "%s in %s", d+" "+data, p)
data = npm.config.get("long")
? path.resolve(parent.path, "node_modules", d)
+ ":"+d+"@"+JSON.stringify(data)+":INVALID:MISSING"
, npm = module.exports = new EventEmitter
, config = require("./config.js")
, ini = require("./utils/ini.js")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, fs = require("graceful-fs")
, path = require("path")
, abbrev = require("abbrev")
, mkdirp = require("mkdirp")
, slide = require("slide")
, chain = slide.chain
+ , RegClient = require("npm-registry-client")
// /usr/local is often a read-only fs, which is not
// well handled by node or mkdirp. Just double-check
}
npm.commands = {}
-npm.ELIFECYCLE = {}
-npm.E404 = {}
-npm.EPUBLISHCONFLICT = {}
-npm.EJSONPARSE = {}
-npm.EISGIT = {}
-npm.ECYCLE = {}
-npm.ENOTSUP = {}
-npm.EBADPLATFORM = {}
try {
// startup, ok to do this synchronously
npm.version = j.version
npm.nodeVersionRequired = j.engines.node
if (!semver.satisfies(process.version, j.engines.node)) {
- log.error([""
- ,"npm requires node version: "+j.engines.node
- ,"And you have: "+process.version
- ,"which is not satisfactory."
- ,""
- ,"Bad things will likely happen. You have been warned."
- ,""].join("\n"), "unsupported version")
+ log.warn("unsupported version", [""
+ ,"npm requires node version: "+j.engines.node
+ ,"And you have: "+process.version
+ ,"which is not satisfactory."
+ ,""
+ ,"Bad things will likely happen. You have been warned."
+ ,""].join("\n"))
}
} catch (ex) {
try {
- log(ex, "error reading version")
+ log.info("error reading version", ex)
} catch (er) {}
npm.version = ex
}
, "apihelp" : "help"
, "login": "adduser"
, "add-user": "adduser"
+ , "tst": "test"
}
, aliasNames = Object.keys(aliases)
}
}
- log.waitForConfig()
+ log.pause()
load(npm, conf, cb)
}
//console.error("about to look up configs")
ini.resolveConfigs(conf, function (er) {
- //console.error("back from config lookup", er && er.stack)
+ log.level = npm.config.get("loglevel")
+ log.heading = "npm"
+ log.stream = npm.config.get("logstream")
+ switch (npm.config.get("color")) {
+ case "always": log.enableColor(); break
+ case false: log.disableColor(); break
+ }
+ log.resume()
+
if (er) return cb(er)
+ // at this point the configs are all set.
+ // go ahead and spin up the registry client.
+ var token
+ try { token = JSON.parse(npm.config.get("_token")) }
+ catch (er) { token = null }
+
+ npm.registry = new RegClient(
+ { registry: npm.config.get("registry")
+ , cache: npm.config.get("cache")
+ , auth: npm.config.get("_auth")
+ , token: token
+ , alwaysAuth: npm.config.get("always-auth")
+ , email: npm.config.get("email")
+ , proxy: npm.config.get("proxy")
+ , tag: npm.config.get("tag")
+ , ca: npm.config.get("ca")
+ , strictSSL: npm.config.get("strict-ssl")
+ , userAgent: npm.config.get("user-agent")
+ , E404: npm.E404
+ , EPUBLISHCONFLICT: npm.EPUBLISHCONFLICT
+ , log: log
+ , retries: npm.config.get("fetch-retries")
+ , retryFactor: npm.config.get("fetch-retry-factor")
+ , retryMinTimeout: npm.config.get("fetch-retry-mintimeout")
+ , retryMaxTimeout: npm.config.get("fetch-retry-maxtimeout")
+ })
+
+ // save the token cookie in the config file
+ if (npm.registry.couchLogin) {
+ npm.registry.couchLogin.tokenSet = function (tok, cb) {
+ ini.set("_token", JSON.stringify(tok), "user")
+ // ignore save error. best effort.
+ ini.save("user", function () {})
+ }
+ }
+
var umask = parseInt(conf.umask, 8)
npm.modes = { exec: 0777 & (~umask)
, file: 0666 & (~umask)
var prefix = path.resolve(cl.get("prefix"))
mkdir(prefix, function (er) {
if (er) {
- log.error(prefix, "could not create prefix directory")
+ log.error("could not create prefix dir", prefix)
return cb(er)
}
fs.stat(prefix, function (er, st) {
var tmpFolder
Object.defineProperty(npm, "tmp",
{ get : function () {
- if (!tmpFolder) tmpFolder = "npm-"+Date.now()
+ if (!tmpFolder) tmpFolder = "npm-" + process.pid
return path.resolve(npm.config.get("tmp"), tmpFolder)
}
, enumerable : true
var path = require("path")
, fs = require("graceful-fs")
- , readJson = require("./utils/read-json.js")
+ , readJson = require("read-package-json")
, cache = require("./cache.js")
, asyncMap = require("slide").asyncMap
, npm = require("./npm.js")
- , log = require("./utils/log.js")
, semver = require("semver")
- , relativize = require("./utils/relativize.js")
function outdated (args, silent, cb) {
if (typeof cb !== "function") cb = silent, silent = false
if (er || silent) return cb_(er)
var outList = list.map(makePretty)
- require("./utils/output.js").write(outList.join("\n"), cb_)
+ require("./utils/output.js").write(outList.join("\n"))
+ cb_()
})
}
}
if (!npm.config.get("global")) {
- dir = relativize(dir, process.cwd()+"/x")
+ dir = path.relative(process.cwd(), dir)
}
return dep + "@" + want + " " + dir
+ " current=" + (has || "MISSING")
var deps = null
readJson(path.resolve(dir, "package.json"), function (er, d) {
- deps = (er) ? true : d.dependencies
+ deps = (er) ? true : (d.dependencies || {})
return next()
})
return next()
}
asyncMap(pkgs, function (pkg, cb) {
- readJson( path.resolve(dir, "node_modules", pkg, "package.json")
- , function (er, d) {
+ var jsonFile = path.resolve(dir, "node_modules", pkg, "package.json")
+ readJson(jsonFile, function (er, d) {
cb(null, er ? [] : [[d.name, d.version]])
})
}, function (er, pvs) {
pvs.forEach(function (pv) {
has[pv[0]] = pv[1]
})
+
next()
})
})
return l
}, {})
}
+
// now get what we should have, based on the dep.
// if has[dep] !== shouldHave[dep], then cb with the data
// otherwise dive into the folder
}
}
-var registry = require("./utils/npm-registry-client/index.js")
- , get = registry.request.GET
- , put = registry.request.PUT
- , log = require("./utils/log.js")
+var npm = require("./npm.js")
+ , registry = npm.registry
+ , log = require("npmlog")
, output
- , npm = require("./npm.js")
+ , readJson = require("read-package-json")
function owner (args, cb) {
var action = args.shift()
function ls (pkg, cb) {
if (!pkg) return cb(owner.usage)
- get(pkg, function (er, data) {
+ registry.get(pkg, function (er, data) {
var msg = ""
- if (er) return log.er(cb, "Couldn't get owner data for "+pkg)(er)
+ if (er) {
+ log.error("owner ls", "Couldn't get owner data", pkg)
+ return cb(er)
+ }
var owners = data.maintainers
if (!owners || !owners.length) msg = "admin party!"
else msg = owners.map(function (o) { return o.name +" <"+o.email+">" }).join("\n")
add(user, pkg, cb)
})
- log.verbose(user+" to "+pkg, "owner add")
+ log.verbose("owner add", "%s to %s", user, pkg)
mutate(pkg, user, function (u, owners) {
if (!owners) owners = []
for (var i = 0, l = owners.length; i < l; i ++) {
var o = owners[i]
if (o.name === u.name) {
- log( "Already a package owner: "+o.name+" <"+o.email+">"
- , "owner add"
- )
+ log.info( "owner add"
+ , "Already a package owner: "+o.name+" <"+o.email+">")
return false
}
}
rm(user, pkg, cb)
})
- log.verbose(user+" from "+pkg, "owner rm")
+ log.verbose("owner rm", "%s from %s", user, pkg)
mutate(pkg, null, function (u, owners) {
var found = false
, m = owners.filter(function (o) {
return !match
})
if (!found) {
- log("Not a package owner: "+user, "owner rm")
+ log.info("owner rm", "Not a package owner: "+user)
return false
}
if (!m.length) return new Error(
function mutate (pkg, user, mutation, cb) {
if (user) {
- get("/-/user/org.couchdb.user:"+user, mutate_)
+ registry.get("/-/user/org.couchdb.user:"+user, mutate_)
} else {
mutate_(null, null)
}
function mutate_ (er, u) {
- if (er) return log.er(cb, "Error getting user data for "+user)(er)
- if (user && (!u || u.error)) return cb(new Error(
- "Couldn't get user data for "+user+": "+JSON.stringify(u)))
+ if (!er && user && (!u || u.error)) er = new Error(
+ "Couldn't get user data for "+user+": "+JSON.stringify(u))
+
+ if (er) {
+ log.error("owner mutate", "Error getting user data for %s", user)
+ return cb(er)
+ }
+
if (u) u = { "name" : u.name, "email" : u.email }
- get("/"+pkg, function (er, data) {
- if (er) return log.er(cb, "Couldn't get package data for "+pkg)(er)
+ registry.get(pkg, function (er, data) {
+ if (er) {
+ log.error("owner mutate", "Error getting package data for %s", pkg)
+ return cb(er)
+ }
var m = mutation(u, data.maintainers)
if (!m) return cb() // handled
if (m instanceof Error) return cb(m) // error
, _rev : data._rev
, maintainers : m
}
- put("/"+pkg+"/-rev/"+data._rev, data, function (er, data) {
- if (er) return log.er(cb, "Failed to update package metadata")(er)
- if (data.error) return cb(new Error(
- "Failed to update package metadata: "+JSON.stringify(data)))
- cb(null, data)
+ registry.request("PUT"
+ , pkg+"/-rev/"+data._rev, data
+ , function (er, data) {
+ if (!er && data.error) er = new Error(
+ "Failed to update package metadata: "+JSON.stringify(data))
+ if (er) {
+ log.error("owner mutate", "Failed to update package metadata")
+ }
+ cb(er, data)
})
})
}
function readLocalPkg (cb) {
if (npm.config.get("global")) return cb()
var path = require("path")
- , readJson = require("./utils/read-json.js")
readJson(path.resolve(npm.prefix, "package.json"), function (er, d) {
return cb(er, d && d.name)
})
, fs = require("graceful-fs")
, chain = require("slide").chain
, path = require("path")
- , relativize = require("./utils/relativize.js")
, cwd = process.cwd()
pack.usage = "npm pack <pkg>"
function printFiles (files, cb) {
files = files.map(function (file) {
- return relativize(file, cwd)
+ return path.relative(cwd, file)
})
output.write(files.join("\n"), cb)
}
prune.usage = "npm prune"
-var readInstalled = require("./utils/read-installed.js")
+var readInstalled = require("read-installed")
, npm = require("./npm.js")
prune.completion = require("./utils/completion/installed-deep.js")
function prune (args, cb) {
- readInstalled(npm.prefix, function (er, data) {
+ readInstalled(npm.prefix, npm.config.get("depth"), function (er, data) {
if (er) return cb(er)
prune_(args, data, cb)
})
module.exports = publish
var npm = require("./npm.js")
- , registry = require("./utils/npm-registry-client/index.js")
- , log = require("./utils/log.js")
+ , registry = npm.registry
+ , log = require("npmlog")
, tar = require("./utils/tar.js")
- , sha = require("./utils/sha.js")
, path = require("path")
- , readJson = require("./utils/read-json.js")
+ , readJson = require("read-package-json")
, fs = require("graceful-fs")
, lifecycle = require("./utils/lifecycle.js")
, chain = require("slide").chain
if (args.length === 0) args = ["."]
if (args.length !== 1) return cb(publish.usage)
- log.verbose(args, "publish")
+ log.verbose("publish", args)
var arg = args[0]
// if it's a local folder, then run the prepublish there, first.
readJson(path.resolve(arg, "package.json"), function (er, data) {
// to the cache.
if (er) return cacheAddPublish(arg, false, isRetry, cb)
+ data._npmUser = { name: npm.config.get("username")
+ , email: npm.config.get("email") }
cacheAddPublish(arg, true, isRetry, cb)
})
}
function cacheAddPublish (arg, didPre, isRetry, cb) {
npm.commands.cache.add(arg, function (er, data) {
if (er) return cb(er)
- log.silly(data, "publish")
+ log.silly("publish", data)
var cachedir = path.resolve( npm.cache
, data.name
, data.version
// check for publishConfig hash
if (data.publishConfig) {
Object.keys(data.publishConfig).forEach(function (k) {
- log.info(k + "=" + data.publishConfig[k], "publishConfig")
+ log.info("publishConfig", k + "=" + data.publishConfig[k])
npm.config.set(k, data.publishConfig[k])
})
}
("This package has been marked as private\n"
+"Remove the 'private' field from the package.json to publish it."))
- // pre-build
- var bd = data.scripts
- && ( data.scripts.preinstall
- || data.scripts.install
- || data.scripts.postinstall )
- && npm.config.get("bindist")
- && npm.config.get("bin-publish")
- preBuild(data, bd, function (er, tb) {
- if (er) return cb(er)
- return regPublish(data, tb, isRetry, arg, cachedir, cb)
- })
+ regPublish(data, isRetry, arg, cachedir, cb)
}
-
-function preBuild (data, bd, cb) {
- if (!bd) return cb()
- // unpack to cache/n/v/build
- // build there
- // pack to cache/package-<bd>.tgz
- var cf = path.resolve(npm.cache, data.name, data.version)
- var pb = path.resolve(cf, "build")
- , buildTarget = path.resolve(pb, "node_modules", data.name)
- , tb = path.resolve(cf, "package-"+bd+".tgz")
- , sourceBall = path.resolve(cf, "package.tgz")
-
- log.verbose("about to cache unpack")
- log.verbose(sourceBall, "the tarball")
- npm.commands.install(pb, sourceBall, function (er) {
- log.info(data._id, "prebuild done")
- // build failure just means that we can't prebuild
- if (er) {
- log.warn(er.message, "prebuild failed "+bd)
- return cb()
- }
- // now strip the preinstall/install scripts
- // they've already been run.
- var pbj = path.resolve(buildTarget, "package.json")
- readJson(pbj, function (er, pbo) {
- if (er) return cb(er)
- if (pbo.scripts) {
- delete pbo.scripts.preinstall
- delete pbo.scripts.install
- delete pbo.scripts.postinstall
- }
- pbo.prebuilt = bd
- pbo.files = pbo.files || []
- pbo.files.push("build")
- pbo.files.push("build/")
- pbo.files.push("*.node")
- pbo.files.push("*.js")
- fs.writeFile(pbj, JSON.stringify(pbo, null, 2), function (er) {
- if (er) return cb(er)
- tar.pack(tb, buildTarget, pbo, true, function (er) {
- if (er) return cb(er)
- // try to validate the shasum, too
- sha.get(tb, function (er, shasum) {
- if (er) return cb(er)
- // binary distribution requires shasum checking.
- if (!shasum) return cb()
- data.dist.bin = data.dist.bin || {}
- data.dist.bin[bd] = data.dist.bin[bd] || {}
- data.dist.bin[bd].shasum = shasum
- return cb(null, tb)
- })
- })
- })
- })
- })
-}
-
-function regPublish (data, prebuilt, isRetry, arg, cachedir, cb) {
+function regPublish (data, isRetry, arg, cachedir, cb) {
// check to see if there's a README.md in there.
var readme = path.resolve(cachedir, "README.md")
+ , tarball = cachedir + ".tgz"
+
fs.readFile(readme, function (er, readme) {
// ignore error. it's an optional feature
- registry.publish(data, prebuilt, readme, function (er) {
- if (er && er.errno === npm.EPUBLISHCONFLICT
+
+ registry.publish(data, tarball, readme, function (er) {
+ if (er && er.code === "EPUBLISHCONFLICT"
&& npm.config.get("force") && !isRetry) {
- log.warn("Forced publish over "+data._id, "publish")
+ log.warn("publish", "Forced publish over "+data._id)
return npm.commands.unpublish([data._id], function (er) {
// ignore errors. Use the force. Reach out with your feelings.
publish([arg], true, cb)
module.exports = rebuild
-var readInstalled = require("./utils/read-installed.js")
+var readInstalled = require("read-installed")
, semver = require("semver")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, path = require("path")
, npm = require("./npm.js")
, output = require("./utils/output.js")
rebuild.completion = require("./utils/completion/installed-deep.js")
function rebuild (args, cb) {
- readInstalled(npm.prefix, function (er, data) {
- log(typeof data, "read Installed")
+ readInstalled(npm.prefix, npm.config.get("depth"), function (er, data) {
+ log.info("readInstalled", typeof data)
if (er) return cb(er)
var set = filter(data, args)
, folders = Object.keys(set).filter(function (f) {
return f !== npm.prefix
})
if (!folders.length) return cb()
- log.silly(folders, "rebuild set")
+ log.silly("rebuild set", folders)
cleanBuild(folders, set, cb)
})
}
}
}
if (pass && data._id) {
- log.verbose([data.path, data._id], "path id")
+ log.verbose("rebuild", "path, id", [data.path, data._id])
set[data.path] = data._id
}
// need to also dive through kids, always.
var npm = require("./npm.js")
, output = require("./utils/output.js")
- , log = require("./utils/log.js")
root.usage = "npm root\nnpm root -g\n(just prints the root folder)"
var lifecycle = require("./utils/lifecycle.js")
, npm = require("./npm.js")
, path = require("path")
- , readJson = require("./utils/read-json.js")
- , log = require("./utils/log.js")
+ , readJson = require("read-package-json")
+ , log = require("npmlog")
, chain = require("slide").chain
, fs = require("graceful-fs")
, asyncMap = require("slide").asyncMap
: npm.prefix
var pkgDir = path.resolve( pref, "node_modules"
, argv[2], "package.json" )
- console.error("global?", npm.config.get("global"))
- console.error(pkgDir, "package dir")
readJson(pkgDir, function (er, d) {
if (er) d = {}
var scripts = Object.keys(d.scripts || {})
if (!cmd.match(/^(pre|post)/)) {
cmds = ["pre"+cmd].concat(cmds).concat("post"+cmd)
}
- log.verbose(cmds, "run-script")
+ log.verbose("run-script", cmds)
chain(cmds.map(function (c) {
// when running scripts explicitly, assume that they're trusted.
return [lifecycle, pkg, c, wd, true]
module.exports = exports = search
var npm = require("./npm.js")
- , registry = require("./utils/npm-registry-client/index.js")
+ , registry = npm.registry
, semver = require("semver")
, output
- , log = require("./utils/log.js")
search.usage = "npm search [some search terms ...]"
}
function getFilteredData (staleness, args, notArgs, cb) {
- registry.get( "/-/all", null, staleness, false
+ registry.get( "/-/all", staleness, false
, true, function (er, data) {
if (er) return cb(er)
return cb(null, filter(data, args, notArgs))
var npm = require("./npm.js")
, output = require("./utils/output.js")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, fs = require("fs")
, path = require("path")
if (typeof cb !== "function") cb = silent, silent = false
if (args.length) {
- log.warn("shrinkwrap doesn't take positional args.")
+ log.warn("shrinkwrap", "doesn't take positional args")
}
npm.commands.ls([], true, function (er, _, pkginfo) {
try {
var swdata = JSON.stringify(pkginfo, null, 2) + "\n"
} catch (er) {
- log.error("Error converting package info to json")
+ log.error("shrinkwrap", "Error converting package info to json")
return cb(er)
}
module.exports = star
var npm = require("./npm.js")
- , registry = require("./utils/npm-registry-client/index.js")
- , log = require("./utils/log.js")
+ , registry = npm.registry
+ , log = require("npmlog")
, asyncMap = require("slide").asyncMap
, output = require("./utils/output.js")
+ "npm unstar <package> [pkg, pkg, ...]"
star.completion = function (opts, cb) {
- registry.get("/-/short", null, 60000, function (er, list) {
+ registry.get("/-/short", 60000, function (er, list) {
return cb(null, list || [])
})
}
registry.star(pkg, using, function (er, data, raw, req) {
if (!er) {
output.write(s + " "+pkg, npm.config.get("outfd"))
- log.verbose(data, "back from star/unstar")
+ log.verbose("star", data)
}
cb(er, data, raw, req)
})
module.exports = submodule
var npm = require("./npm.js")
- , readJson = require("./utils/read-json.js")
, exec = require("./utils/exec.js")
, output = require("./utils/output.js")
, cache = require("./cache.js")
module.exports = substack
var npm = require("./npm.js")
- , log = require("./utils/log.js")
+
+var isms =
+ [ "\033[32mbeep \033[35mboop\033[m"
+ , "Replace your configs with services"
+ , "SEPARATE ALL THE CONCERNS!"
+ , "MODULE ALL THE THINGS!"
+ , "\\o/"
+ , "but first, burritos"
+ , "full time mad scientist here"
+ , "c/,,\\" ]
function substack (args, cb) {
- console.log("\033[32mbeep \033[35mboop\033[m")
+ var i = Math.floor(Math.random() * isms.length)
+ console.log(isms[i])
var c = args.shift()
if (c) npm.commands[c](args, cb)
else cb()
tag.completion = require("./unpublish.js").completion
var npm = require("./npm.js")
+ , registry = npm.registry
function tag (args, cb) {
var thing = (args.shift() || "").split("@")
, version = thing.join("@")
, t = args.shift() || npm.config.get("tag")
if (!project || !version || !t) return cb("Usage:\n"+tag.usage)
- require("./utils/npm-registry-client/index.js").tag(project, version, t, cb)
+ registry.tag(project, version, t, cb)
}
-module.exports = require("./utils/lifecycle.js").cmd("test")
+module.exports = test
+
+var testCmd = require("./utils/lifecycle.js").cmd("test")
+ , log = require("npmlog")
+
+function test (args, cb) {
+ testCmd(args, function (er) {
+ if (!er) return cb()
+ if (er.code === "ELIFECYCLE") {
+ return cb("Test failed. See above for more details.")
+ }
+ return cb(er)
+ })
+}
module.exports = unbuild
unbuild.usage = "npm unbuild <folder>\n(this is plumbing)"
-var readJson = require("./utils/read-json.js")
+var readJson = require("read-package-json")
, rm = require("rimraf")
, gentlyRm = require("./utils/gently-rm.js")
, npm = require("./npm.js")
, lifecycle = require("./utils/lifecycle.js")
, asyncMap = require("slide").asyncMap
, chain = require("slide").chain
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, build = require("./build.js")
// args is a list of folders.
readJson(path.resolve(folder, "package.json"), function (er, pkg) {
// if no json, then just trash it, but no scripts or whatever.
if (er) return rm(folder, cb)
- readJson.clearCache(folder)
+ readJson.cache.del(folder)
chain
( [ [lifecycle, pkg, "preuninstall", folder, false, true]
, [lifecycle, pkg, "uninstall", folder, false, true]
, gnm = npm.dir
, top = gnm === parent
+ readJson.cache.del(path.resolve(folder, "package.json"))
+
log.verbose([top, gnm, parent], "unbuild "+pkg._id)
asyncMap([rmBins, rmMans], function (fn, cb) {
fn(pkg, folder, parent, top, cb)
log.verbose([binRoot, pkg.bin], "binRoot")
asyncMap(Object.keys(pkg.bin), function (b, cb) {
if (process.platform === "win32") {
- rm(path.resolve(binRoot, b) + ".cmd", cb)
+ chain([ [rm, path.resolve(binRoot, b) + ".cmd"]
+ , [rm, path.resolve(binRoot, b) ] ], cb)
} else {
gentlyRm( path.resolve(binRoot, b)
, !npm.config.get("force") && folder
uninstall.completion = require("./utils/completion/installed-shallow.js")
var fs = require("graceful-fs")
- , log = require("./utils/log.js")
- , readJson = require("./utils/read-json.js")
+ , log = require("npmlog")
+ , readJson = require("read-package-json")
, path = require("path")
, npm = require("./npm.js")
, semver = require("semver")
}
function uninstall_ (args, nm, cb) {
+ // if we've been asked to --save or --save-dev or --save-optional,
+ // then also remove it from the associated dependencies hash.
+ var s = npm.config.get('save')
+ , d = npm.config.get('save-dev')
+ , o = npm.config.get('save-optional')
+ if (s || d || o) {
+ cb = saver(args, nm, cb)
+ }
+
asyncMap(args, function (arg, cb) {
// uninstall .. should not delete /usr/local/lib/node_modules/..
var p = path.join(path.resolve(nm), path.join("/", arg))
if (path.resolve(p) === nm) {
- log.warn(arg, "uninstall: invalid argument")
+ log.warn("uninstall", "invalid argument: %j", arg)
return cb(null, [])
}
fs.lstat(p, function (er) {
if (er) {
- log.warn(arg, "Not installed in "+nm)
+ log.warn("uninstall", "not installed in %s: %j", nm, arg)
return cb(null, [])
}
cb(null, p)
asyncMap(folders, npm.commands.unbuild, cb)
})
}
+
+function saver (args, nm, cb_) {
+ return cb
+ function cb (er, data) {
+ var s = npm.config.get('save')
+ , d = npm.config.get('save-dev')
+ , o = npm.config.get('save-optional')
+ if (er || !(s || d || o)) return cb_(er, data)
+ var pj = path.resolve(nm, '..', 'package.json')
+ // don't use readJson here, because we don't want all the defaults
+ // filled in, for mans and other bs.
+ fs.readFile(pj, 'utf8', function (er, json) {
+ try {
+ var pkg = JSON.parse(json)
+ } catch (_) {}
+ if (!pkg) return cb_(null, data)
+
+ var bundle
+ if (npm.config.get('save-bundle')) {
+ var bundle = pkg.bundleDependencies || pkg.bundledDependencies
+ if (!Array.isArray(bundle)) bundle = undefined
+ }
+
+ var changed = false
+ args.forEach(function (a) {
+ ; [ [s, 'dependencies']
+ , [o, 'optionalDependencies']
+ , [d, 'devDependencies'] ].forEach(function (f) {
+ var flag = f[0]
+ , field = f[1]
+ if (!flag || !pkg[field] || !pkg[field].hasOwnProperty(a)) return
+ changed = true
+
+ if (bundle) {
+ var i = bundle.indexOf(a)
+ if (i !== -1) bundle.splice(i, 1)
+ }
+
+ delete pkg[field][a]
+ })
+ })
+ if (!changed) return cb_(null, data)
+
+ if (bundle) {
+ delete pkg.bundledDependencies
+ if (bundle.length) {
+ pkg.bundleDependencies = bundle
+ } else {
+ delete pkg.bundleDependencies
+ }
+ }
+
+ fs.writeFile(pj, JSON.stringify(pkg, null, 2) + "\n", function (er) {
+ return cb_(er, data)
+ })
+ })
+ }
+}
module.exports = unpublish
-var registry = require("./utils/npm-registry-client/index.js")
- , log = require("./utils/log.js")
+var log = require("npmlog")
, npm = require("./npm.js")
- , readJson = require("./utils/read-json.js")
+ , registry = npm.registry
+ , readJson = require("read-package-json")
, path = require("path")
, output = require("./utils/output.js")
// remove from the cache first
npm.commands.cache(["clean", project, version], function (er) {
- if (er) return log.er(cb, "Failed to clean cache")(er)
+ if (er) {
+ log.error("unpublish", "Failed to clean cache")
+ return cb(er)
+ }
+
registry.unpublish(project, version, cb)
})
}
var npm = require("./npm.js")
, lifecycle = require("./utils/lifecycle.js")
, asyncMap = require("slide").asyncMap
- , log = require("./utils/log.js")
+ , log = require("npmlog")
// load these, just so that we know that they'll be available, in case
// npm itself is getting overwritten.
function update (args, cb) {
npm.commands.outdated(args, true, function (er, outdated) {
- log(outdated, "outdated updating")
+ log.info("outdated", "updating", outdated)
if (er) return cb(er)
asyncMap(outdated, function (ww, cb) {
, chain = require("slide").chain
, mkdir = require("mkdirp")
, rm = require("rimraf")
- , log = require("./log.js")
+ , log = require("npmlog")
, path = require("path")
- , relativize = require("./relativize.js")
, npm = require("../npm.js")
, shebangExpr = /^#\!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+)(.*)$/
}
function writeShim_ (from, to, prog, args, cb) {
- var shTarget = relativize(from, to)
+ var shTarget = path.relative(path.dirname(to), from)
, target = shTarget.split("/").join("\\")
, longProg
- , shProg = prog
+ , shProg = prog && prog.split("\\").join("/")
, shLongProg
+ shTarget = shTarget.split("\\").join("/")
args = args || ""
if (!prog) {
prog = "\"%~dp0\\" + target + "\""
- shProg = "\"`dirname \"$0\"`/" + shTarget + "\""
+ shProg = "\"$basedir/" + shTarget + "\""
args = ""
target = ""
shTarget = ""
} else {
longProg = "\"%~dp0\\" + prog + ".exe\""
- shLongProg = "\"`dirname \"$0\"`/" + prog + "\""
+ shLongProg = "\"$basedir/" + prog + "\""
target = "\"%~dp0\\" + target + "\""
- shTarget = "\"`dirname \"$0\"`/" + shTarget + "\""
+ shTarget = "\"$basedir/" + shTarget + "\""
}
// @IF EXIST "%~dp0\node.exe" (
cmd = ":: Created by npm, please don't edit manually.\r\n" + cmd
// #!/bin/sh
- // if [ -x "`dirname "$0"`/node.exe" ]; then
- // "`dirname "$0"`/node.exe" "`dirname "$0"`/node_modules/npm/bin/npm-cli.js" "$@"
+ // basedir=`dirname "$0"`
+ //
+ // case `uname` in
+ // *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
+ // esac
+ //
+ // if [ -x "$basedir/node.exe" ]; then
+ // "$basedir/node.exe" "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
+ // ret=$?
// else
- // node "`dirname "$0"`/node_modules/npm/bin/npm-cli.js" "$@"
+ // node "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
+ // ret=$?
// fi
+ // exit $ret
+
var sh = "#!/bin/sh\n"
if (shLongProg) {
sh = sh
+ + "basedir=`dirname \"$0\"`\n"
+ + "\n"
+ + "case `uname` in\n"
+ + " *CYGWIN*) basedir=`cygpath -w \"$basedir\"`;;\n"
+ + "esac\n"
+ + "\n"
+
+ sh = sh
+ "if [ -x "+shLongProg+" ]; then\n"
+ " " + shLongProg + " " + args + " " + shTarget + " \"$@\"\n"
+ " ret=$?\n"
fs.writeFile(to + ".cmd", cmd, "utf8", function (er) {
if (er) {
- log.warn("Could not write "+to+".cmd", "cmdShim")
+ log.warn("cmdShim", "Could not write "+to+".cmd")
return cb(er)
}
fs.writeFile(to, sh, "utf8", function (er) {
if (er) {
- log.warn("Could not write "+to, "shShim")
+ log.warn("shShim", "Could not write "+to)
return cb(er)
}
fs.chmod(to, 0755, cb)
COMP_WORDBREAKS=${COMP_WORDBREAKS/@/}
export COMP_WORDBREAKS
-if complete &>/dev/null; then
+if type complete &>/dev/null; then
_npm_completion () {
local si="$IFS"
IFS=$'\n' COMPREPLY=($(COMP_CWORD="$COMP_CWORD" \
IFS="$si"
}
complete -F _npm_completion npm
-elif compctl &>/dev/null; then
+elif type compdef &>/dev/null; then
+ _npm_completion() {
+ si=$IFS
+ compadd -- $(COMP_CWORD=$((CURRENT-1)) \
+ COMP_LINE=$BUFFER \
+ COMP_POINT=0 \
+ npm completion -- "${words[@]}" \
+ 2>/dev/null)
+ IFS=$si
+ }
+ compdef _npm_completion npm
+elif type compctl &>/dev/null; then
_npm_completion () {
local cword line point words si
read -Ac words
module.exports = fileCompletion
-var find = require("../find.js")
- , mkdir = require("mkdirp")
+var mkdir = require("mkdirp")
, path = require("path")
+ , fs = require("graceful-fs")
+ , glob = require("glob")
function fileCompletion (root, req, depth, cb) {
if (typeof cb !== "function") cb = depth, depth = Infinity
mkdir(root, function (er) {
if (er) return cb(er)
- function dirFilter (f, type) {
- // return anything that is a file,
- // or not exactly the req.
- return type !== "dir" ||
- ( f && f !== path.join(root, req)
- && f !== path.join(root, req) + "/" )
- }
- find(path.join(root, req), dirFilter, depth, function (er, files) {
+
+ // can be either exactly the req, or a descendent
+ var pattern = root + "/{" + req + "," + req + "/**/*}"
+ , opts = { mark: true, dot: true, maxDepth: depth }
+ glob(pattern, opts, function (er, files) {
if (er) return cb(er)
return cb(null, (files || []).map(function (f) {
return path.join(req, f.substr(root.length + 1)
})
})
}
-
module.exports = installedDeep
var npm = require("../../npm.js")
- , readInstalled = require("../read-installed.js")
+ , readInstalled = require("read-installed")
function installedDeep (opts, cb) {
var local
, global
if (npm.config.get("global")) local = [], next()
- else readInstalled(npm.prefix, function (er, data) {
+ else readInstalled(npm.prefix, npm.config.get("depth"), function (er, data) {
local = getNames(data || {})
next()
})
- readInstalled(npm.config.get("prefix"), function (er, data) {
+ readInstalled(npm.config.get("prefix"), npm.config.get("depth"), function (er, data) {
global = getNames(data || {})
next()
})
var npm = require("../../npm.js")
, fs = require("graceful-fs")
, path = require("path")
- , readJson = require("../read-json.js")
+ , readJson = require("read-package-json")
, asyncMap = require("slide").asyncMap
function installedShallow (opts, filter, cb) {
module.exports = remotePackages
-var registry = require("../npm-registry-client/index.js")
+var npm = require("../../npm.js")
+ , registry = npm.registry
, containsSingleMatch = require("./contains-single-match.js")
, getCompletions = require("./get-completions.js")
if (name === undefined) name = ""
if (name.indexOf("/") !== -1) return cb(null, [])
// use up-to 1 hour stale cache. not super urgent.
- registry.get("/", null, 3600, function (er, d) {
+ registry.get("/", 3600, function (er, d) {
if (er) return cb(er)
var remoteList = Object.keys(d)
, found = remoteList.indexOf(name)
module.exports = users
-var registry = require("../npm-registry-client/index.js")
+var npm = require("../../npm")
+ , registry = npm.registry
, containsSingleMatch = require("./contains-single-match.js")
, getCompletions = require("./get-completions.js")
- , log = require("../log.js")
+ , log = require("npmlog")
function users (args, index, cb) {
var name = (args.length + 1 === index) ? args[args.length - 1] : ""
if (name === undefined) name = ""
// use up-to 1 day stale cache. doesn't change much
- log.warn("About to fetch", "users completion")
- registry.get("/-/users", null, 24*60*60, function (er, d) {
- log.warn(d, "userdata")
- log.warn(name, "name")
+ log.warn("users completion", "About to fetch")
+ registry.get("/-/users", 24*60*60, function (er, d) {
+ log.warn("userdata", d)
+ log.warn("name", name)
if (er) return cb(er)
var remoteList = Object.keys(d)
, simpleMatches = getCompletions(name, remoteList)
, Stream = require("stream").Stream
, semver = require("semver")
, stableFamily = semver.parse(process.version)
- , os = require("os")
, nopt = require("nopt")
- , log = require("./log.js")
+ , log = require("npmlog")
, npm = require("../npm.js")
+ , osenv = require("osenv")
function Octal () {}
function validateOctal (data, k, val) {
data[k] = semver.valid(val)
}
+function validateStream (data, k, val) {
+ if (!(val instanceof Stream)) return false
+ data[k] = val
+}
+
nopt.typeDefs.semver = { type: semver, validate: validateSemver }
nopt.typeDefs.Octal = { type: Octal, validate: validateOctal }
+nopt.typeDefs.Stream = { type: Stream, validate: validateStream }
nopt.invalidHandler = function (k, val, type, data) {
- log.warn(k + "=" + JSON.stringify(val), "invalid config")
+ log.warn("invalid config", k + "=" + JSON.stringify(val))
if (Array.isArray(type)) {
if (type.indexOf(url) !== -1) type = url
switch (type) {
case Octal:
- log.warn("Must be octal number, starting with 0", "invalid config")
+ log.warn("invalid config", "Must be octal number, starting with 0")
break
case url:
- log.warn("Must be a full url with 'http://'", "invalid config")
+ log.warn("invalid config", "Must be a full url with 'http://'")
break
case path:
- log.warn("Must be a valid filesystem path", "invalid config")
+ log.warn("invalid config", "Must be a valid filesystem path")
break
case Number:
- log.warn("Must be a numeric value", "invalid config")
+ log.warn("invalid config", "Must be a numeric value")
+ break
+ case Stream:
+ log.warn("invalid config", "Must be an instance of the Stream class")
break
}
}
if (!stableFamily || (+stableFamily[2] % 2)) stableFamily = null
else stableFamily = stableFamily[1] + "." + stableFamily[2]
-var httpsOk = semver.satisfies(process.version, ">=0.4.9")
-var winColor = semver.satisfies(process.version, ">=0.5.9")
-
var defaults
-var temp = process.env.TMPDIR
- || process.env.TMP
- || process.env.TEMP
- || ( process.platform === "win32"
- ? "c:\\windows\\temp"
- : "/tmp" )
-
-var home = ( process.platform === "win32"
- ? process.env.USERPROFILE
- : process.env.HOME )
+var temp = osenv.tmpdir()
+var home = osenv.home()
if (home) process.env.HOME = home
else home = temp
globalPrefix = process.env.PREFIX
} else if (process.platform === "win32") {
// c:\node\node.exe --> prefix=c:\node\
- globalPrefix = path.join(process.execPath, "..")
+ globalPrefix = path.dirname(process.execPath)
} else {
// /usr/local/bin/node --> prefix=/usr/local
- globalPrefix = path.join(process.execPath, "..", "..")
+ globalPrefix = path.dirname(path.dirname(process.execPath))
// destdir only is respected on Unix
if (process.env.DESTDIR) {
- globalPrefix = process.env.DESTDIR + "/" + globalPrefix
+ globalPrefix = path.join(process.env.DESTDIR, globalPrefix)
}
}
return defaults =
{ "always-auth" : false
- // Disable bindist publishing for now. Too problematic.
- // Revisit when we have a less crappy approach, or just make
- // bindist be a thing that only dedicated build-farms will enable.
- , "bin-publish" : false
-
- , bindist : stableFamily
- && ( stableFamily + "-"
- + "ares" + process.versions.ares + "-"
- + "ev" + process.versions.ev + "-"
- + "openssl" + process.versions.openssl + "-"
- + "v8" + process.versions.v8 + "-"
- + process.platform + "-"
- + (process.arch ? process.arch + "-" : "")
- + os.release() )
-
// are there others?
, browser : process.platform === "darwin" ? "open"
: process.platform === "win32" ? "start"
, cache : process.platform === "win32"
? path.resolve(process.env.APPDATA || home || temp, "npm-cache")
: path.resolve( home || temp, ".npm")
+
+ , "cache-lock-stale": 60000
+ , "cache-lock-retries": 10
+ , "cache-lock-wait": 10000
+
, "cache-max": Infinity
, "cache-min": 0
- , color : process.platform !== "win32" || winColor
+ , color : true
, coverage: false
, depth: Infinity
, description : true
, dev : false
- , editor : process.env.EDITOR ||
- ( process.platform === "win32" ? "notepad" : "vi" )
+ , editor : osenv.editor()
+ , "engine-strict": false
, force : false
+ , "fetch-retries": 2
+ , "fetch-retry-factor": 10
+ , "fetch-retry-mintimeout": 10000
+ , "fetch-retry-maxtimeout": 60000
+
, git: "git"
, global : false
, group : process.platform === "win32" ? 0
: process.env.SUDO_GID || (process.getgid && process.getgid())
, ignore: ""
+ , "init-module": path.resolve(home, '.npm-init.js')
, "init.version" : "0.0.0"
, "init.author.name" : ""
, "init.author.email" : ""
, "init.author.url" : ""
, json: false
, link: false
- , logfd : 2
, loglevel : "http"
- , logprefix : process.platform !== "win32" || winColor
+ , logstream : process.stderr
, long : false
, message : "%s"
, "node-version" : process.version
, parseable : false
, pre: false
, prefix : globalPrefix
- , production: false
+ , production: process.env.NODE_ENV === "production"
, "proprietary-attribs": true
, proxy : process.env.HTTP_PROXY || process.env.http_proxy || null
, "https-proxy" : process.env.HTTPS_PROXY || process.env.https_proxy ||
process.env.HTTP_PROXY || process.env.http_proxy || null
, "user-agent" : "npm/" + npm.version + " node/" + process.version
, "rebuild-bundle" : true
- , registry : "http" + (httpsOk ? "s" : "") + "://registry.npmjs.org/"
+ , registry : "https://registry.npmjs.org/"
, rollback : true
, save : false
+ , "save-bundle": false
, "save-dev" : false
, "save-optional" : false
, searchopts: ""
, searchexclude: null
, searchsort: "name"
- , shell : process.platform === "win32"
- ? process.env.ComSpec || "cmd"
- : process.env.SHELL || "bash"
+ , shell : osenv.shell()
, "strict-ssl": true
, tag : "latest"
, tmp : temp
exports.types =
{ "always-auth" : Boolean
- , "bin-publish" : Boolean
- , bindist : [null, String]
, browser : String
, ca: [null, String]
, cache : path
+ , "cache-lock-stale": Number
+ , "cache-lock-retries": Number
+ , "cache-lock-wait": Number
, "cache-max": Number
, "cache-min": Number
, color : ["always", Boolean]
, description : Boolean
, dev : Boolean
, editor : String
+ , "engine-strict": Boolean
, force : Boolean
+ , "fetch-retries": Number
+ , "fetch-retry-factor": Number
+ , "fetch-retry-mintimeout": Number
+ , "fetch-retry-maxtimeout": Number
, git: String
, global : Boolean
, globalconfig : path
, "https-proxy" : [null, url]
, "user-agent" : String
, ignore : String
+ , "init-module": path
, "init.version" : [null, semver]
, "init.author.name" : String
, "init.author.email" : String
, "init.author.url" : ["", url]
, json: Boolean
, link: Boolean
- , logfd : [Number, Stream]
, loglevel : ["silent","win","error","warn","http","info","verbose","silly"]
- , logprefix : Boolean
+ , logstream : Stream
, long : Boolean
, message: String
, "node-version" : [null, semver]
, registry : [null, url]
, rollback : Boolean
, save : Boolean
+ , "save-bundle": Boolean
, "save-dev" : Boolean
, "save-optional" : Boolean
, searchopts : String
, O : ["--save-optional"]
, y : ["--yes"]
, n : ["--no-yes"]
+ , B : ["--save-bundle"]
}
module.exports = errorHandler
var cbCalled = false
- , log = require("./log.js")
+ , log = require("npmlog")
, npm = require("../npm.js")
, rm = require("rimraf")
- , constants = require("constants")
, itWorked = false
, path = require("path")
, ini = require("./ini.js")
if (itWorked) log.info("ok")
else {
if (!cbCalled) {
- log.error("cb() never called!\n ")
+ log.error("", "cb() never called!")
}
+
if (wroteLogFile) {
- log.error([""
+ log.error("", [""
,"Additional logging details can be found in:"
," " + path.resolve("npm-debug.log")
].join("\n"))
wroteLogFile = false
}
- log.win("not ok")
+ log.error("not ok", "code", code)
}
var doExit = npm.config.get("_exit")
exitCode = exitCode || code
var doExit = npm.config.get("_exit")
- log.verbose([code, doExit], "exit")
- if (log.level === log.LEVEL.silent) noLog = true
+ log.verbose("exit", [code, doExit])
+ if (log.level === "silent") noLog = true
if (code && !noLog) writeLogFile(reallyExit)
else rm("npm-debug.log", function () { rm(npm.tmp, reallyExit) })
function reallyExit() {
+ // truncate once it's been written.
+ log.record.length = 0
+
itWorked = !code
// just emit a fake exit event.
cbCalled = true
if (!er) return exit(0)
- if (!(er instanceof Error)) {
- log.error(er)
+ if (typeof er === "string") {
+ log.error("", er)
+ return exit(1, true)
+ } else if (!(er instanceof Error)) {
+ log.error("weird error", er)
return exit(1, true)
}
var m = er.code || er.message.match(/^(?:Error: )?(E[A-Z]+)/)
- if (m) {
- m = m[1]
- if (!constants[m] && !npm[m]) constants[m] = {}
- er.errno = npm[m] || constants[m]
- }
+ if (m && !er.code) er.code = m
- console.error("")
- switch (er.code || er.errno) {
+ switch (er.code) {
case "ECONNREFUSED":
- case constants.ECONNREFUSED:
- log.error(er)
- log.error(["\nIf you are behind a proxy, please make sure that the"
+ log.error("", er)
+ log.error("", ["\nIf you are behind a proxy, please make sure that the"
,"'proxy' config is set properly. See: 'npm help config'"
].join("\n"))
break
case "EACCES":
case "EPERM":
- case constants.EACCES:
- case constants.EPERM:
- log.error(er)
- log.error(["\nPlease try running this command again as root/Administrator."
+ log.error("", er)
+ log.error("", ["\nPlease try running this command again as root/Administrator."
].join("\n"))
break
- case npm.ELIFECYCLE:
+ case "ELIFECYCLE":
er.code = "ELIFECYCLE"
- log.error(er.message)
- log.error(["","Failed at the "+er.pkgid+" "+er.stage+" script."
+ log.error("", er.message)
+ log.error("", ["","Failed at the "+er.pkgid+" "+er.stage+" script."
,"This is most likely a problem with the "+er.pkgname+" package,"
,"not with npm itself."
,"Tell the author that this fails on your system:"
].join("\n"))
break
- case npm.EJSONPARSE:
+ case "EJSONPARSE":
er.code = "EJSONPARSE"
- log.error(er.message)
- log.error("File: "+er.file)
- log.error(["Failed to parse package.json data."
+ log.error("", er.message)
+ log.error("", "File: "+er.file)
+ log.error("", ["Failed to parse package.json data."
,"package.json must be actual JSON, not just JavaScript."
,"","This is not a bug in npm."
,"Tell the package author to fix their package.json file."
].join("\n"), "JSON.parse")
break
- case npm.E404:
+ case "E404":
er.code = "E404"
if (er.pkgid && er.pkgid !== "-") {
var msg = ["'"+er.pkgid+"' is not in the npm registry."
}
msg.push("\nNote that you can also install from a"
,"tarball, folder, or http url, or git url.")
- log.error(msg.join("\n"), "404")
+ log.error("404", msg.join("\n"))
}
break
- case npm.EPUBLISHCONFLICT:
+ case "EPUBLISHCONFLICT":
er.code = "EPUBLISHCONFLICT"
- log.error(["Cannot publish over existing version."
+ log.error("publish fail", ["Cannot publish over existing version."
,"Bump the 'version' field, set the --force flag, or"
," npm unpublish '"+er.pkgid+"'"
,"and try again"
- ].join("\n"), "publish fail" )
+ ].join("\n"))
break
- case npm.EISGIT:
+ case "EISGIT":
er.code = "EISGIT"
- log.error([er.message
+ log.error("git", [er.message
," "+er.path
,"Refusing to remove it. Update manually,"
,"or move it out of the way first."
- ].join("\n"), "git" )
+ ].join("\n"))
break
- case npm.ECYCLE:
+ case "ECYCLE":
er.code = "ECYCLE"
- log.error([er.message
+ log.error("cycle", [er.message
,"While installing: "+er.pkgid
,"Found a pathological dependency case that npm cannot solve."
,"Please report this to the package author."
].join("\n"))
break
- case npm.ENOTSUP:
- er.code = "ENOTSUP"
- log.error([er.message
- ,"Not compatible with your version of node/npm: "+er.pkgid
- ,"Required: "+JSON.stringify(er.required)
- ,"Actual: "
- +JSON.stringify({npm:npm.version
- ,node:npm.config.get("node-version")})
- ].join("\n"))
- break
-
- case npm.EBADPLATFORM:
+ case "EBADPLATFORM":
er.code = "EBADPLATFORM"
- log.error([er.message
+ log.error("notsup", [er.message
,"Not compatible with your operating system or architecture: "+er.pkgid
,"Valid OS: "+er.os.join(",")
,"Valid Arch: "+er.cpu.join(",")
break
case "EEXIST":
- case constants.EEXIST:
log.error([er.message
,"File exists: "+er.path
,"Move it away, and try again."].join("\n"))
break
+ case "ENOTSUP":
+ if (er.required) {
+ log.error("notsup", [er.message
+ ,"Not compatible with your version of node/npm: "+er.pkgid
+ ,"Required: "+JSON.stringify(er.required)
+ ,"Actual: "
+ +JSON.stringify({npm:npm.version
+ ,node:npm.config.get("node-version")})
+ ].join("\n"))
+ break
+ } // else passthrough
+
default:
- log.error(er)
- log.error(["You may report this log at:"
+ log.error("", er)
+ log.error("", ["You may report this log at:"
," <http://github.com/isaacs/npm/issues>"
,"or email it to:"
," <npm-@googlegroups.com>"
}
var os = require("os")
- log.error("")
- log.error(os.type() + " " + os.release(), "System")
- log.error(process.argv
- .map(JSON.stringify).join(" "), "command")
- log.error(process.cwd(), "cwd")
- log.error(process.version, "node -v")
- log.error(npm.version, "npm -v")
+ // just a line break
+ console.error("")
+ log.error("System", os.type() + " " + os.release())
+ log.error("command", process.argv
+ .map(JSON.stringify).join(" "))
+ log.error("cwd", process.cwd())
+ log.error("node -v", process.version)
+ log.error("npm -v", npm.version)
; [ "file"
, "path"
, "fstream_class"
, "fstream_finish_call"
, "fstream_linkpath"
- , "arguments"
, "code"
, "message"
, "errno"
].forEach(function (k) {
- if (er[k]) log.error(er[k], k)
+ if (er[k]) log.error(k, er[k])
})
if (er.fstream_stack) {
- log.error(er.fstream_stack.join("\n"), "fstream_stack")
+ log.error("fstream_stack", er.fstream_stack.join("\n"))
}
if (er.errno && typeof er.errno !== "object") log.error(er.errno, "errno")
var fs = require("graceful-fs")
, fstr = fs.createWriteStream("npm-debug.log")
, util = require("util")
-
- log.history.forEach(function (m) {
- var lvl = log.LEVEL[m.level]
- , pref = m.pref ? " " + m.pref : ""
- , b = lvl + pref + " "
- , eol = process.platform === "win32" ? "\r\n" : "\n"
- , msg = typeof m.msg === "string" ? m.msg
- : msg instanceof Error ? msg.stack || msg.message
- : util.inspect(m.msg, 0, 4)
- fstr.write(new Buffer(b
- +(msg.split(/\r?\n+/).join(eol+b))
- + eol))
+ , eol = process.platform === "win32" ? "\r\n" : "\n"
+ , out = ""
+
+ log.record.forEach(function (m) {
+ var pref = [m.id, m.level]
+ if (m.prefix) pref.push(m.prefix)
+ pref = pref.join(' ')
+
+ m.message.trim().split(/\r?\n/).map(function (line) {
+ return (pref + ' ' + line).trim()
+ }).forEach(function (line) {
+ out += line + eol
+ })
})
- fstr.end()
+
+ fstr.end(out)
fstr.on("close", cb)
}
module.exports = exec
exec.spawn = spawn
-exec.pipe = pipe
-var log = require("./log.js")
+var log = require("npmlog")
, child_process = require("child_process")
, util = require("util")
, npm = require("../npm.js")
}
}
if (uid !== myUID) {
- log.verbose(uid, "Setting uid from "+myUID)
- log.verbose(new Error().stack, "stack at uid setting")
+ log.verbose("set uid", "from=%s to=%s", myUID, uid)
}
if (uid && gid && (isNaN(uid) || isNaN(gid))) {
})
}
- log.silly(cmd+" "+args.map(JSON.stringify).join(" "), "exec")
+ log.silly("exec", cmd+" "+args.map(JSON.stringify).join(" "))
var stdout = ""
, stderr = ""
, cp = spawn(cmd, args, env, takeOver, cwd, uid, gid)
return cp
}
-function logger (d) { if (d) process.stderr.write(d+"") }
-function pipe (cp1, cp2, cb) {
- util.pump(cp1.stdout, cp2.stdin)
- var errState = null
- , buff1 = ""
- , buff2 = ""
- if (log.level <= log.LEVEL.silly) {
- cp1.stderr.on("data", logger)
- cp2.stderr.on("data", logger)
- } else {
- cp1.stderr.on("data", function (d) { buff1 += d })
- cp2.stderr.on("data", function (d) { buff2 += d })
- }
-
- cp1.on("exit", function (code) {
- if (!code) return log.verbose(cp1.name || "<unknown>", "success")
- if (!cp2._exited) cp2.kill()
- log.error(buff1, cp1.name || "<unknown>")
- cb(errState = new Error(
- "Failed "+(cp1.name || "<unknown>")+"\nexited with "+code))
- })
-
- cp2.on("exit", function (code) {
- cp2._exited = true
- if (errState) return
- if (!code) return log.verbose(cp2.name || "<unknown>", "success", cb)
- log.error(buff2, cp2.name || "<unknown>")
- cb(new Error( "Failed "
- + (cp2.name || "<unknown>")
- + "\nexited with " + code ))
- })
-}
function spawn (c, a, env, takeOver, cwd, uid, gid) {
var fds = [ 0, 1, 2 ]
, fs = require("graceful-fs")
, npm = require("../npm.js")
, url = require("url")
- , log = require("./log.js")
+ , log = require("npmlog")
, path = require("path")
, mkdir = require("mkdirp")
, chownr = require("chownr")
function fetch (remote, local, headers, cb) {
if (typeof cb !== "function") cb = headers, headers = {}
- log.verbose(local, "fetch to")
+ log.verbose("fetch", "to=", local)
mkdir(path.dirname(local), function (er, made) {
if (er) return cb(er)
fetch_(remote, local, headers, cb)
function fetch_ (remote, local, headers, cb) {
var fstr = fs.createWriteStream(local, { mode : npm.modes.file })
+ var response = null
+ var calledback = false
fstr.on("error", function (er) {
fs.close(fstr.fd, function () {})
- if (fstr._ERROR) return
+ if (calledback) return
+ calledback = true
cb(fstr._ERROR = er)
})
fstr.on("open", function () {
- makeRequest(remote, fstr, headers)
+ var req = makeRequest(remote, fstr, headers)
+ req.on("response", function (res) {
+ log.http(res.statusCode, remote)
+ response = res
+ })
})
fstr.on("close", function () {
- if (fstr._ERROR) return
- cb()
+ if (calledback) return
+ calledback = true
+ if (response && response.statusCode && response.statusCode >= 400) {
+ var er = new Error(response.statusCode + " "
+ + require("http").STATUS_CODES[response.statusCode])
+ cb(fstr._ERROR = er, response)
+ } else {
+ cb(null, response)
+ }
})
}
function makeRequest (remote, fstr, headers) {
remote = url.parse(remote)
- log.http(remote.href, "GET")
+ log.http("GET", remote.href)
regHost = regHost || url.parse(npm.config.get("registry")).host
if (remote.host === regHost && npm.config.get("always-auth")) {
? "https-proxy"
: "proxy")
- request({ url: remote
- , proxy: proxy
- , strictSSL: npm.config.get("strict-ssl")
- , ca: remote.host === regHost ? npm.config.get("ca") : undefined
- , headers: { "user-agent": npm.config.get("user-agent") }
- , onResponse: onResponse }).pipe(fstr)
- function onResponse (er, res) {
- if (er) return fstr.emit("error", er)
- log.http(res.statusCode + " " + remote.href)
- }
+ var opts = { url: remote
+ , proxy: proxy
+ , strictSSL: npm.config.get("strict-ssl")
+ , ca: remote.host === regHost ? npm.config.get("ca") : undefined
+ , headers: { "user-agent": npm.config.get("user-agent") }}
+ var req = request(opts)
+ req.on("error", function (er) {
+ fstr.emit("error", er)
+ })
+ req.pipe(fstr)
+ return req;
}
+++ /dev/null
-
-// walks a set of directories recursively, and returns
-// the list of files that match the filter, if one is
-// provided.
-
-module.exports = find
-var fs = require("graceful-fs")
- , asyncMap = require("slide").asyncMap
- , path = require("path")
-
-function find (dir, filter, depth, cb) {
- if (typeof cb !== "function") cb = depth, depth = Infinity
- if (typeof cb !== "function") cb = filter, filter = null
- if (filter instanceof RegExp) filter = reFilter(filter)
- if (typeof filter === "string") filter = strFilter(filter)
- if (!Array.isArray(dir)) dir = [dir]
- if (!filter) filter = nullFilter
- asyncMap(dir, findDir(filter, depth), cb)
-}
-function findDir (filter, depth) { return function (dir, cb) {
- fs.lstat(dir, function (er, stats) {
- // don't include missing files, but don't abort either
- if (er) return cb()
- if (!stats.isDirectory()) return findFile(dir, filter, depth)("", cb)
- var found = []
- if (!filter || filter(dir, "dir")) found.push(dir+"/")
- if (depth <= 0) return cb(null, found)
- cb = (function (cb) { return function (er, f) {
- cb(er, found.concat(f))
- }})(cb)
- fs.readdir(dir, function (er, files) {
- if (er) return cb(er)
- asyncMap(files, findFile(dir, filter, depth - 1), cb)
- })
- })
-}}
-function findFile (dir, filter, depth) { return function (f, cb) {
- f = path.join(dir, f)
- fs.lstat(f, function (er, s) {
- // don't include missing files, but don't abort either
- if (er) return cb(null, [])
- if (s.isDirectory()) return find(f, filter, depth, cb)
- if (!filter || filter(f, "file")) cb(null, f)
- else cb(null, [])
- })
-}}
-function reFilter (re) { return function (f, type) {
- return nullFilter(f, type) && f.match(re)
-}}
-function strFilter (s) { return function (f, type) {
- return nullFilter(f, type) && f.indexOf(s) === 0
-}}
-function nullFilter (f, type) { return type === "file" && f }
, mkdir = require("mkdirp")
, npm = require("../npm.js")
- , log = require("./log.js")
+ , log = require("npmlog")
, configDefs = require("./config-defs.js")
, myUid = process.env.SUDO_UID !== undefined
// just put this here for a moment, so that the logs
// in the config-loading phase don't cause it to blow up.
-configList.push({loglevel:"warn"})
function resolveConfigs (cli, cb_) {
defaultConfig = defaultConfig || configDefs.defaults
if (er) return cb(er)
if (conf.hasOwnProperty("prefix")) {
- log.warn("Cannot set prefix in globalconfig file"
+ log.warn( "globalconfig"
+ , "Cannot set prefix in globalconfig file"
, cl.get("globalconfig"))
delete conf.prefix
}
function parseFile (file, cb) {
if (!file) return cb(null, {})
- log.verbose(file, "config file")
+ log.verbose("config file", file)
fs.readFile(file, function (er, data) {
// treat all errors as just an empty file
if (er) return cb(null, {})
exports = module.exports = lifecycle
exports.cmd = cmd
-var log = require("./log.js")
+var log = require("npmlog")
, exec = require("./exec.js")
, npm = require("../npm.js")
, path = require("path")
- , readJson = require("./read-json.js")
, fs = require("graceful-fs")
, chain = require("slide").chain
, constants = require("constants")
, output = require("./output.js")
+ , Stream = require("stream").Stream
, PATH = "PATH"
// windows calls it's path "Path" usually, but this is not guaranteed.
while (pkg && pkg._data) pkg = pkg._data
if (!pkg) return cb(new Error("Invalid package data"))
- log(pkg._id, stage)
+ log.info(stage, pkg._id)
if (!pkg.scripts) pkg.scripts = {}
validWd(wd || path.resolve(npm.dir, pkg.name), function (er, wd) {
if ((wd.indexOf(npm.dir) !== 0 || path.basename(wd) !== pkg.name)
&& !unsafe && pkg.scripts[stage]) {
- log.warn(pkg._id+" "+pkg.scripts[stage], "skipping, cannot run in "+wd)
+ log.warn( "cannot run in wd", "%s %s (wd=%s)"
+ , pkg._id, pkg.scripts[stage], wd)
return cb()
}
if (failOk) {
cb = (function (cb_) { return function (er) {
- if (er) log.warn(er.message, "continuing anyway")
+ if (er) log.warn("continuing anyway", er.message)
cb_()
}})(cb)
}
if (npm.config.get("force")) {
cb = (function (cb_) { return function (er) {
- if (er) log(er, "forced, continuing")
+ if (er) log.info("forced, continuing", er)
cb_()
}})(cb)
}
shFlag = "/c"
}
- log.verbose(unsafe, "unsafe-perm in lifecycle")
+ log.verbose("unsafe-perm in lifecycle", unsafe)
var note = "\n> " + pkg._id + " " + stage + " " + wd
+ "\n> " + cmd + "\n"
, user, group
, function (er, code, stdout, stderr) {
if (er && !npm.ROLLBACK) {
- log("Failed to exec "+stage+" script", pkg._id)
+ log.info(pkg._id, "Failed to exec "+stage+" script")
er.message = pkg._id + " "
+ stage + ": `" + env.npm_lifecycle_script+"`\n"
+ er.message
- if (er.errno !== constants.EPERM) {
- er.errno = npm.ELIFECYCLE
+ if (er.code !== "EPERM") {
+ er.code = "ELIFECYCLE"
}
er.pkgid = pkg._id
er.stage = stage
er.pkgname = pkg.name
return cb(er)
} else if (er) {
- log.error(er, pkg._id+"."+stage)
- log.error("failed, but continuing anyway", pkg._id+"."+stage)
+ log.error(pkg._id+"."+stage, er)
+ log.error(pkg._id+"."+stage, "continuing anyway")
return cb()
}
cb(er)
, function (er) {
if (er) {
er.message += "\nFailed to exec "+stage+" hook script"
- log(er, pkg._id)
+ log.info(pkg._id, er)
}
if (npm.ROLLBACK) return cb()
cb(er)
return
}
var value = ini.get(i)
- if (/^(log|out)fd$/.test(i) && typeof value === "object") {
- // not an fd, a stream
- return
- }
+ if (value instanceof Stream) return
if (!value) value = ""
else if (typeof value !== "string") value = JSON.stringify(value)
, chain = require("slide").chain
, mkdir = require("mkdirp")
, rm = require("./gently-rm.js")
- , log = require("./log.js")
, path = require("path")
- , relativize = require("./relativize.js")
, npm = require("../npm.js")
function linkIfExists (from, to, gently, cb) {
function link (from, to, gently, cb) {
if (typeof cb !== "function") cb = gently, gently = null
if (npm.config.get("force")) gently = false
+
+ to = path.resolve(to)
+ var target = from = path.resolve(from)
+ if (process.platform !== "win32") {
+ // junctions on windows must be absolute
+ target = path.relative(path.dirname(to), from)
+ // if there is no folder in common, then it will be much
+ // longer, and using a relative link is dumb.
+ if (target.length >= from.length) target = from
+ }
+
chain
( [ [fs, "stat", from]
, [rm, to, gently]
, [mkdir, path.dirname(to)]
- , [fs, "symlink", relativize(from, to), to] ]
+ , [fs, "symlink", target, to, "junction"] ]
, cb)
}
+++ /dev/null
-
-module.exports = loadPackageDefaults
-
-var path = require("path")
- , log = require("./log.js")
- , find = require("./find.js")
- , asyncMap = require("slide").asyncMap
- , npm = require("../npm.js")
- , fs = require("graceful-fs")
-
-function loadPackageDefaults (pkg, pkgDir, cb) {
- if (!pkg) return cb(new Error("no package to load defaults from!"))
- if (typeof pkgDir === "function") {
- cb = pkgDir
- pkgDir = path.join(npm.dir, pkg.name, pkg.version, "package")
- }
- if (!pkgDir) pkgDir = "."
-
- if (pkg._defaultsLoaded) return cb(null, pkg)
-
- pkg._defaultsLoaded = true
- asyncMap
- ( [pkg]
- , function (pkg, cb) { log.verbose(pkg._id, "loadDefaults", cb) }
- , readDefaultBins(pkgDir)
- , readDefaultMans(pkgDir)
- , function (er) { cb(er, pkg) } )
-}
-
-function objectForEach (obj, fn) {
- Object.keys(obj).forEach(function (k) {
- fn(obj[k])
- })
-}
-
-function readDefaultMans (pkgDir) { return function (pkg, cb) {
- var man = pkg.directories && pkg.directories.man
- , manDir = path.join(pkgDir, man)
- if (pkg.man && !Array.isArray(pkg.man)) pkg.man = [pkg.man]
- if (pkg.man || !man) return cb(null, pkg)
- find(manDir, /\.[0-9]+(\.gz)?$/, function (er, filenames) {
- if (er) return cb(er)
- var cut = pkgDir === "." ? 0 : pkgDir.length + 1
- pkg.man = (filenames || []).map(function (filename) {
- return filename.substr(cut)
- }).filter(function (f) {
- return !f.match(/(^|\/)\./)
- })
- cb(null,pkg)
- })
-}}
-
-function readDefaultBins (pkgDir) { return function (pkg, cb) {
- var bin = pkg.directories && pkg.directories.bin
- if (pkg.bins) pkg.bin = pkg.bins, delete pkg.bins
- if (pkg.bin || !bin) return cb(null, pkg)
- log.verbose("linking default bins", pkg._id)
- var binDir = path.join(pkgDir, bin)
- pkg.bin = {}
- find(binDir, function (er, filenames) {
- if (er || !filenames || !filenames.length) return cb(er, pkg)
- var cut = pkgDir === "." ? 0 : pkgDir.length + 1
- , binCut = pkgDir === "." ? bin.length - 1 : binDir.length + 1
- filenames.filter(function (f) {
- return !f.substr(binCut).match(/(^|\/)\./)
- }).forEach(function (filename) {
- var key = filename.substr(binCut)
- .replace(/\.(js|node)$/, '')
- , val = filename.substr(cut)
- if (key.length && val.length) pkg.bin[key] = val
- })
- log.silly(pkg.bin, pkg._id+".bin")
- cb(null, pkg)
- })
-}}
+++ /dev/null
-
-module.exports = log
-
-var output = require("./output.js")
-
-function colorize (msg, color) {
- return msg ? "\033["+color+"m"+msg+"\033[0m" : ""
-}
-
-var l = -1
- , LEVEL = { silly : l++
- , verbose : l++
- , info : l++
- , "http" : l++
- , WARN : l++
- , "ERR!" : l++
- , ERROR : "ERR!"
- , ERR : "ERR!"
- , win : 0x15AAC5
- , paused : 0x19790701
- , silent : 0xDECAFBAD
- }
- , COLOR = {}
- , SHOWLEVEL = null
- , normalNames = {}
-log.LEVEL = LEVEL
-normalNames[LEVEL["ERR!"]] = "error"
-normalNames[LEVEL.WARN] = "warn"
-normalNames[LEVEL.info] = "info"
-normalNames[LEVEL.verbose] = "verbose"
-normalNames[LEVEL.silly] = "silly"
-normalNames[LEVEL.win] = "win"
-
-Object.keys(LEVEL).forEach(function (l) {
- if (typeof LEVEL[l] === "string") LEVEL[l] = LEVEL[LEVEL[l]]
- else LEVEL[LEVEL[l]] = l
- LEVEL[l.toLowerCase()] = LEVEL[l]
- if (l === "silent" || l === "paused") return
- log[l] = log[l.toLowerCase()] =
- function (msg, pref, cb) { return log(msg, pref, l, cb) }
-})
-
-COLOR[LEVEL.silly] = 30
-COLOR[LEVEL.verbose] = "34;40"
-COLOR[LEVEL.info] = 32
-COLOR[LEVEL.http] = "32;40"
-COLOR[LEVEL.warn] = "30;41"
-COLOR[LEVEL.error] = "31;40"
-for (var c in COLOR) COLOR[LEVEL[c]] = COLOR[c]
-COLOR.npm = "37;40"
-COLOR.pref = 35
-
-var logBuffer = []
- , ini = require("./ini.js")
- , waitForConfig
-log.waitForConfig = function () { waitForConfig = true }
-
-// now the required stuff has been loaded,
-// so the transitive module dep will work
-var util = require("util")
- , npm = require("../npm.js")
- , net = require("net")
-
-Object.defineProperty(log, "level",
- { get : function () {
- if (SHOWLEVEL !== null) return SHOWLEVEL
- var show = npm.config && npm.config.get("loglevel") || ''
- show = show.split(",")[0]
- if (!isNaN(show)) show = +show
- else if (!LEVEL.hasOwnProperty(show)) {
- util.error("Invalid loglevel config: "+JSON.stringify(show))
- show = "info"
- }
- if (isNaN(show)) show = LEVEL[show]
- else show = +show
- if (!waitForConfig || ini.resolved) SHOWLEVEL = show
- return show
- }
- , set : function (l) {
- SHOWLEVEL = null
- npm.config.set("showlevel", l)
- }
- })
-
-function log (msg, pref, level, cb) {
- if (typeof level === "function") cb = level, level = null
- var show = log.level
- if (show === LEVEL.silent || show === LEVEL.paused) return cb && cb()
- if (level == null) level = LEVEL.info
- if (isNaN(level)) level = LEVEL[level]
- else level = +level
-
- // logging just undefined is almost never the right thing.
- // a lot of these are kicking around throughout the codebase
- // with relatively unhelpful prefixes.
- if (msg === undefined && level > LEVEL.silly) {
- msg = new Error("undefined log message")
- }
- if (typeof msg === "object" && (msg instanceof Error)) level = LEVEL.error
- if (!ini.resolved && waitForConfig || level === LEVEL.paused) {
- return logBuffer.push([msg, pref, level, cb])
- }
- if (logBuffer.length && !logBuffer.discharging) {
- logBuffer.push([msg, pref, level, cb])
- logBuffer.discharging = true
- logBuffer.forEach(function (l) { log.apply(null, l) })
- logBuffer.length = 0
- delete logBuffer.discharging
- return
- }
- log.level = show
- npm.emit("log", { level : level, msg : msg, pref : pref, cb : cb })
- npm.emit("log."+normalNames[level], { msg : msg, pref : pref, cb : cb })
-}
-
-var loglog = log.history = []
- , loglogLen = 0
-npm.on("log", function (logData) {
- var level = logData.level
- , msg = logData.msg
- , pref = logData.pref
- , cb = logData.cb || function () {}
- , show = log.level
- , spaces = " "
- , logFD = npm.config.get("logfd")
- if (msg instanceof Error) {
- msg = logData.msg = msg.stack || msg.toString()
- }
- loglog.push(logData)
- loglogLen ++
- if (loglogLen > 2000) {
- loglog = loglog.slice(loglogLen - 1000)
- loglogLen = 1000
- }
- if (!isFinite(level) || level < show) return cb()
- if (typeof msg !== "string" && !(msg instanceof Error)) {
- msg = util.inspect(msg, 0, 4, true)
- }
-
- // console.error("level, showlevel, show", level, show, (level >= show))
- if (pref && COLOR.pref) {
- pref = colorize(pref, COLOR.pref)
- }
- if (!pref) pref = ""
-
- if (npm.config.get("logprefix")) {
- pref = colorize("npm", COLOR.npm)
- + (COLOR[level] ? " "+colorize(
- (LEVEL[level]+spaces).substr(0,4), COLOR[level]) : "")
- + (pref ? (" " + pref) : "")
- }
- if (pref) pref += " "
-
-
-
- if (msg.indexOf("\n") !== -1) {
- msg = msg.split(/\n/).join("\n"+pref)
- }
- msg = pref+msg
- return output.write(msg, logFD, cb)
-})
-
-log.er = function (cb, msg) {
- if (!msg) throw new Error(
- "Why bother logging it if you're not going to print a message?")
- return function (er) {
- if (er) log.error(msg)
- cb.apply(this, arguments)
- }
-}
+++ /dev/null
-
-module.exports = adduser
-
-var uuid = require("node-uuid")
- , request = require("./request.js")
- , log = require("../log.js")
- , npm = require("../../npm.js")
- , crypto
-
-try {
- crypto = process.binding("crypto") && require("crypto")
-} catch (ex) {}
-
-function sha (s) {
- return crypto.createHash("sha1").update(s).digest("hex")
-}
-
-function adduser (username, password, email, cb) {
- if (!crypto) return cb(new Error(
- "You must compile node with ssl support to use the adduser feature"))
-
- password = ("" + (password || "")).trim()
- if (!password) return cb(new Error("No password supplied."))
-
- email = ("" + (email || "")).trim()
- if (!email) return cb(new Error("No email address supplied."))
- if (!email.match(/^[^@]+@[^\.]+\.[^\.]+/)) {
- return cb(new Error("Please use a real email address."))
- }
-
- if (password.indexOf(":") !== -1) return cb(new Error(
- "Sorry, ':' chars are not allowed in passwords.\n"+
- "See <https://issues.apache.org/jira/browse/COUCHDB-969> for why."))
- var salt = uuid()
- , userobj =
- { name : username
- , salt : salt
- , password_sha : sha(password + salt)
- , email : email
- , _id : 'org.couchdb.user:'+username
- , type : "user"
- , roles : []
- , date: new Date().toISOString()
- }
- cb = done(cb)
- log.verbose(userobj, "before first PUT")
- request.PUT
- ( '/-/user/org.couchdb.user:'+encodeURIComponent(username)
- , userobj
- , function (error, data, json, response) {
- // if it worked, then we just created a new user, and all is well.
- // but if we're updating a current record, then it'll 409 first
- if (error && !npm.config.get("_auth")) {
- // must be trying to re-auth on a new machine.
- // use this info as auth
- npm.config.set("username", username)
- npm.config.set("_password", password)
- var b = new Buffer(username + ":" + password)
- npm.config.set("_auth", b.toString("base64"))
- }
- if (!error || !response || response.statusCode !== 409) {
- return cb(error, data, json, response)
- }
- log.verbose("update existing user", "adduser")
- return request.GET
- ( '/-/user/org.couchdb.user:'+encodeURIComponent(username)
- , function (er, data, json, response) {
- userobj._rev = data._rev
- userobj.roles = data.roles
- log.verbose(userobj, "userobj")
- request.PUT
- ( '/-/user/org.couchdb.user:'+encodeURIComponent(username)
- + "/-rev/" + userobj._rev
- , userobj
- , cb )
- }
- )
- }
- )
-}
-
-function done (cb) { return function (error, data, json, response) {
- if (!error && (!response || response.statusCode === 201)) {
- return cb(error, data, json, response)
- }
- log.verbose([error, data, json], "back from adduser")
- if (!error) {
- error = new Error( (response && response.statusCode || "") + " "+
- "Could not create user\n"+JSON.stringify(data))
- }
- if (response
- && (response.statusCode === 401 || response.statusCode === 403)) {
- log.warn("Incorrect username or password\n"
- +"You can reset your account by visiting:\n"
- +"\n"
- +" http://admin.npmjs.org/reset\n")
- }
-
- return cb(error)
-}}
+++ /dev/null
-
-// utilities for working with the js-registry site.
-
-var cached = {}
-function lazyGet (p) { return function () {
- return cached[p] || (cached[p] = require("./"+p+".js"))
-}}
-
-function setLazyGet (p) {
- Object.defineProperty(exports, p,
- { get : lazyGet(p)
- , enumerable : true })
-}
-
-; ["publish", "unpublish", "tag", "adduser", "get", "request", "star"]
- .forEach(setLazyGet)
+++ /dev/null
-
-module.exports = publish
-
-var request = require("./request.js")
- , GET = request.GET
- , PUT = request.PUT
- , DELETE = request.DELETE
- , reg = request.reg
- , upload = request.upload
- , log = require("../log.js")
- , path = require("path")
- , npm = require("../../npm.js")
- , url = require("url")
-
-function publish (data, prebuilt, readme, cb) {
- if (typeof readme === "function") cb = readme, readme = ""
- if (typeof prebuilt === "function") cb = prebuilt, prebuilt = null
- // add the dist-url to the data, pointing at the tarball.
- // if the {name} isn't there, then create it.
- // if the {version} is already there, then fail.
- // then:
- // PUT the data to {config.registry}/{data.name}/{data.version}
- var registry = reg()
- if (registry instanceof Error) return cb(registry)
-
- readme = readme ? "" + readme : ""
-
- var fullData =
- { _id : data.name
- , name : data.name
- , description : data.description
- , "dist-tags" : {}
- , versions : {}
- , readme: readme
- , maintainers :
- [ { name : npm.config.get("username")
- , email : npm.config.get("email")
- }
- ]
- }
-
- var tbName = data.name + "-" + data.version + ".tgz"
- , bd = npm.config.get("bindist")
- , pbName = data.name + "-" + data.version + "-" + bd + ".tgz"
- , tbURI = data.name + "/-/" + tbName
- , pbURI = data.name + "/-/" + pbName
-
- data._id = data.name+"@"+data.version
- data.dist = data.dist || {}
- data.dist.tarball = url.resolve(registry, tbURI)
- .replace(/^https:\/\//, "http://")
-
- if (prebuilt && bd) {
- data.dist.bin[bd] = data.dist.bin[bd] || {}
- data.dist.bin[bd].tarball = url.resolve(registry, pbURI)
- .replace(/^https:\/\//, "http://")
- }
-
-
-
-
- // first try to just PUT the whole fullData, and this will fail if it's
- // already there, because it'll be lacking a _rev, so couch'll bounce it.
- PUT(encodeURIComponent(data.name), fullData,
- function (er, parsed, json, response) {
- // get the rev and then upload the attachment
- // a 409 is expected here, if this is a new version of an existing package.
- if (er
- && !(response && response.statusCode === 409)
- && !( parsed
- && parsed.reason ===
- "must supply latest _rev to update existing package" )) {
- return log.er(cb, "Failed PUT response "
- +(response && response.statusCode))(er)
- }
- var dataURI = encodeURIComponent(data.name)
- + "/" + encodeURIComponent(data.version)
-
- var tag = data.tag || npm.config.get("tag")
- if (npm.config.get("pre")) dataURI += "/-pre/true"
- else if (tag) dataURI += "/-tag/" + tag
- else dataURI += "/-tag/latest"
-
- // let's see what verions are already published.
- // could be that we just need to update the bin dist values.
- GET(data.name, function (er, fullData) {
- if (er) return cb(er)
-
- var exists = fullData.versions && fullData.versions[data.version]
- if (exists) {
- log(exists._id, "Already published")
- var ebin = exists.dist.bin || {}
- , nbin = data.dist.bin || {}
- , needs = Object.keys(nbin).filter(function (bd) {
- return !ebin.hasOwnProperty(bd)
- })
- log.verbose(needs, "uploading bin dists")
- if (!needs.length) return cb(conflictError(data._id))
- // attach the needed bindists, upload the new metadata
- exists.dist.bin = ebin
- needs.forEach(function (bd) { exists.dist.bin[bd] = nbin[bd] })
- return PUT(dataURI + "/-rev/" + fullData._rev, exists, function (er) {
- if (er) return cb(er)
- attach(data.name, prebuilt, pbName, cb)
- })
- }
-
- // this way, it'll also get attached to packages that were previously
- // published with a version of npm that lacked this feature.
- if (!fullData.readme) {
- data.readme = readme
- }
- PUT(dataURI, data, function (er) {
- if (er) {
- if (er.message.indexOf("conflict Document update conflict.") === 0) {
- return cb(conflictError(data._id))
- }
- return log.er(cb, "Error sending version data")(er)
- }
-
- var c = path.resolve(npm.cache, data.name, data.version)
- , tb = path.resolve(c, "package.tgz")
-
- cb = rollbackFailure(data, cb)
-
- log.verbose([data.name, tb, tbName], "attach 2")
- attach(data.name, tb, tbName, function (er) {
- log.verbose([er, data.name, prebuilt, pbName], "attach 3")
- if (er || !prebuilt) return cb(er)
- attach(data.name, prebuilt, pbName, cb)
- })
- })
- })
- })
-}
-
-function conflictError (pkgid) {
- var e = new Error("publish fail")
- e.errno = npm.EPUBLISHCONFLICT
- e.pkgid = pkgid
- return e
-}
-
-function attach (doc, file, filename, cb) {
- doc = encodeURIComponent(doc)
- GET(doc, function (er, d) {
- if (er) return cb(er)
- if (!d) return cb(new Error(
- "Attempting to upload to invalid doc "+doc))
- var rev = "-rev/"+d._rev
- , attURI = doc + "/-/" + encodeURIComponent(filename) + "/" + rev
- log.verbose([attURI, file], "uploading")
- upload(attURI, file, cb)
- })
-}
-
-function rollbackFailure (data, cb) { return function (er) {
- if (!er) return cb()
- npm.ROLLBACK = true
- log.error(er, "publish failed")
- log("rollback", "publish failed")
- npm.commands.unpublish([data.name+"@"+data.version], function (er_) {
- if (er_) {
- log.error(er_, "rollback failed")
- log.error( "Invalid data in registry! Please report this."
- , "rollback failed" )
- } else log("rolled back", "publish failed")
- cb(er)
- })
-}}
+++ /dev/null
-module.exports = regRequest
-
-regRequest.GET = GET
-regRequest.PUT = PUT
-regRequest.reg = reg
-regRequest.upload = upload
-
-var npm = require("../../npm.js")
- , url = require("url")
- , log = require("../log.js")
- , fs = require("graceful-fs")
- , rm = require("rimraf")
- , asyncMap = require("slide").asyncMap
- , warnedAuth = false
- , newloctimeout = 0
- , stream = require("stream")
- , Stream = stream.Stream
- , request = require("request")
-
-function regRequest (method, where, what, etag, nofollow, cb_) {
- if (typeof cb_ !== "function") cb_ = nofollow, nofollow = false
- if (typeof cb_ !== "function") cb_ = etag, etag = null
- if (typeof cb_ !== "function") cb_ = what, what = null
-
- // Since there are multiple places where an error could occur,
- // don't let the cb be called more than once.
- var errState = null
- function cb (er) {
- if (errState) return
- if (er) errState = er
- cb_.apply(null, arguments)
- }
-
- if (where.match(/^\/?favicon.ico/)) {
- return cb(new Error("favicon.ico isn't a package, it's a picture."))
- }
-
- var registry = reg()
- if (registry instanceof Error) return cb(registry)
-
- var adduserChange = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)\/-rev/
- , adduserNew = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)/
- , authRequired = (what || npm.config.get("always-auth"))
- && !where.match(adduserNew)
- || where.match(adduserChange)
- || method === "DELETE"
-
- // resolve to a full url on the registry
- if (!where.match(/^https?:\/\//)) {
- log.verbose(where, "raw, before any munging")
-
- var q = where.split("?")
- where = q.shift()
- q = q.join("?")
-
- if (where.charAt(0) !== "/") where = "/" + where
- where = "." + where.split("/").map(function (p) {
- p = p.trim()
- if (p.match(/^org.couchdb.user/)) {
- return p.replace(/\//g, encodeURIComponent("/"))
- }
- return encodeURIComponent(p)
- }).join("/")
- if (q) where += "?" + q
- log.verbose([registry, where], "url resolving")
- where = url.resolve(registry, where)
- log.verbose(where, "url resolved")
- }
-
- var remote = url.parse(where)
- , auth = authRequired && npm.config.get("_auth")
-
- if (authRequired && !auth) {
- return cb(new Error(
- "Cannot insert data into the registry without authorization\n"
- + "See: npm-adduser(1)"))
- }
-
- if (auth) remote.auth = new Buffer(auth, "base64").toString("utf8")
-
- makeRequest(method, remote, where, what, etag, nofollow, cb)
-}
-
-function makeRequest (method, remote, where, what, etag, nofollow, cb) {
- var opts = { url: remote
- , method: method
- , ca: npm.config.get("ca")
- , strictSSL: npm.config.get("strict-ssl") }
- , headers = opts.headers = {}
- if (etag) {
- log.verbose(etag, "etag")
- headers[method === "GET" ? "if-none-match" : "if-match"] = etag
- }
-
- headers.accept = "application/json"
-
- headers["user-agent"] = npm.config.get("user-agent")
-
- opts.proxy = npm.config.get( remote.protocol === "https:"
- ? "https-proxy" : "proxy" )
-
- // figure out wth 'what' is
- if (what) {
- if (Buffer.isBuffer(what) || typeof what === "string") {
- opts.body = what
- headers["content-type"] = "application/json"
- headers["content-length"] = Buffer.byteLength(what)
- } else if (what instanceof Stream) {
- headers["content-type"] = "application/octet-stream"
- if (what.size) headers["content-length"] = what.size
- } else {
- delete what._etag
- opts.json = what
- }
- }
-
- if (nofollow) {
- opts.followRedirect = false
- }
-
- log.http(remote.href || "/", method)
-
- var req = request(opts, requestDone(method, where, cb))
- var r = npm.config.get("registry")
- if (!r) {
- return new Error("Must define registry URL before accessing registry.")
- }
-
- req.on("error", cb)
-
- if (what && (what instanceof Stream)) {
- what.pipe(req)
- }
-}
-
-// cb(er, parsed, raw, response)
-function requestDone (method, where, cb) { return function (er, response, data) {
- if (er) return cb(er)
-
- log.http(response.statusCode + " " + url.parse(where).href)
-
- var parsed
-
- if (Buffer.isBuffer(data)) {
- data = data.toString()
- }
-
- if (data && typeof data === "string" && response.statusCode !== 304) {
- try {
- parsed = JSON.parse(data)
- } catch (ex) {
- ex.message += "\n" + data
- log.verbose(data, "bad json")
- log.error("error parsing json", "registry")
- return cb(ex, null, data, response)
- }
- } else if (data) {
- parsed = data
- data = JSON.stringify(parsed)
- }
-
- // expect data with any error codes
- if (!data && response.statusCode >= 400) {
- return cb( response.statusCode + " "
- + require("http").STATUS_CODES[response.statusCode]
- , null, data, response )
- }
-
- var er = null
- if (parsed && response.headers.etag) {
- parsed._etag = response.headers.etag
- }
-
- if (parsed && parsed.error && response.statusCode >= 400) {
- var w = url.parse(where).pathname.substr(1)
- if (!w.match(/^-/) && parsed.error === "not_found") {
- w = w.split("/")
- name = w[w.indexOf("_rewrite") + 1]
- er = new Error("404 Not Found: "+name)
- er.errno = npm.E404
- er.pkgid = name
- } else {
- er = new Error(
- parsed.error + " " + (parsed.reason || "") + ": " + w)
- }
- } else if (method !== "HEAD" && method !== "GET") {
- // invalidate cache
- // This is irrelevant for commands that do etag caching, but
- // ls and view also have a timed cache, so this keeps the user
- // from thinking that it didn't work when it did.
- // Note that failure is an acceptable option here, since the
- // only result will be a stale cache for some helper commands.
- var path = require("path")
- , p = url.parse(where).pathname.split("/")
- , _ = "/"
- , caches = p.map(function (part) {
- return _ = path.join(_, part)
- }).map(function (cache) {
- return path.join(npm.cache, cache, ".cache.json")
- })
-
- // if the method is DELETE, then also remove the thing itself.
- // Note that the search index is probably invalid. Whatever.
- // That's what you get for deleting stuff. Don't do that.
- if (method === "DELETE") {
- p = p.slice(0, p.indexOf("-rev"))
- caches.push(path.join(npm.cache, p.join("/")))
- }
-
- asyncMap(caches, rm, function () {})
- }
- return cb(er, parsed, data, response)
-}}
-
-function GET (where, etag, nofollow, cb) {
- regRequest("GET", where, null, etag, nofollow, cb)
-}
-
-function PUT (where, what, etag, nofollow, cb) {
- regRequest("PUT", where, what, etag, nofollow, cb)
-}
-
-function upload (where, filename, etag, nofollow, cb) {
- if (typeof nofollow === "function") cb = nofollow, nofollow = false
- if (typeof etag === "function") cb = etag, etag = null
-
- fs.stat(filename, function (er, stat) {
- if (er) return cb(er)
- var s = fs.createReadStream(filename)
- s.size = stat.size
- s.on("error", cb)
-
- PUT(where, s, etag, nofollow, cb)
- })
-}
-
-function reg () {
- var r = npm.config.get("registry")
- if (!r) {
- return new Error("Must define registry URL before accessing registry.")
- }
- if (r.substr(-1) !== "/") r += "/"
- npm.config.set("registry", r)
- return r
-}
+++ /dev/null
-
-module.exports = star
-
-var request = require("./request.js")
- , GET = request.GET
- , PUT = request.PUT
- , log = require("../log.js")
- , npm = require("../../npm.js")
-
-function star (package, starred, cb) {
- var users = {}
-
- GET(package, function (er, fullData) {
- if (er) return cb(er)
-
- fullData = { _id: fullData._id
- , _rev: fullData._rev
- , users: fullData.users || {} }
-
- if (starred) {
- log.info("starring", fullData._id)
- fullData.users[npm.config.get("username")] = true
- log.verbose(fullData)
- } else {
- delete fullData.users[npm.config.get("username")]
- log.info("unstarring", fullData._id)
- log.verbose(fullData)
- }
-
- return PUT(package, fullData, cb)
- })
-}
+++ /dev/null
-
-module.exports = tag
-
-var PUT = require("./request.js").PUT
-
-function tag (project, version, tag, cb) {
- PUT(project+"/"+tag, JSON.stringify(version), cb)
-}
+++ /dev/null
-
-// fetch the data
-// modify to remove the version in question
-// If no versions remaining, then DELETE
-// else, PUT the modified data
-// delete the tarball
-
-module.exports = unpublish
-
-var request = require("./request.js")
- , log = require("../log.js")
- , get = require("./get.js")
- , semver = require("semver")
- , url = require("url")
- , chain = require("slide").chain
-
-function unpublish (name, ver, cb) {
- if (!cb) cb = ver, ver = null
- if (!cb) throw new Error(
- "Not enough arguments for registry unpublish")
-
- get(name, null, -1, true, function (er, data) {
- if (er) return log(name+" not published", "unpublish", cb)
- // remove all if no version specified
- if (!ver) {
- log("No version specified, removing all", "unpublish")
- return request("DELETE", name+'/-rev/'+data._rev, cb)
- }
-
- var versions = data.versions || {}
- , versionPublic = versions.hasOwnProperty(ver)
-
- if (!versionPublic) log(name+"@"+ver+" not published", "unpublish")
- else {
- var dist = versions[ver].dist
- log.verbose(dist, "removing attachments")
- }
-
- delete versions[ver]
- // if it was the only version, then delete the whole package.
- if (!Object.keys(versions).length) {
- log("No versions remain, removing entire package", "unpublish")
- return request("DELETE", name+"/-rev/"+data._rev, cb)
- }
-
- if (!versionPublic) return cb()
-
- var latestVer = data["dist-tags"].latest
- for (var tag in data["dist-tags"]) {
- if (data["dist-tags"][tag] === ver) delete data["dist-tags"][tag]
- }
-
- if (latestVer === ver) {
- data["dist-tags"].latest =
- Object.getOwnPropertyNames(versions).sort(semver.compare).pop()
- }
-
- var rev = data._rev
- delete data._revisions
- delete data._attachments
- // log(data._rev, "rev")
- request.PUT(name+"/-rev/"+rev, data,
- log.er(detacher(data, dist, cb), "Failed to update the data"))
- })
-}
-
-function detacher (data, dist, cb) { return function (er) {
- if (er) return cb(er)
- get(data.name, function (er, data) {
- if (er) return cb(er)
-
- var tb = url.parse(dist.tarball)
-
- detach(data, tb.pathname, data._rev, function (er) {
- if (er || !dist.bin) return cb(er)
- chain(Object.keys(dist.bin).map(function (bt) {
- return function (cb) {
- var d = dist.bin[bt]
- detach(data, url.parse(d.tarball).pathname, null, cb)
- }
- }), cb)
- })
- })
-}}
-
-function detach (data, path, rev, cb) {
- if (rev) {
- path += "/-rev/" + rev
- log(path, "detach")
- return request("DELETE", path, cb)
- }
- get(data.name, function (er, data) {
- rev = data._rev
- if (!rev) return cb(new Error(
- "No _rev found in "+data._id))
- detach(data, path, rev, cb)
- })
-}
+++ /dev/null
-
-module.exports = promiseChain
-
-// usage:
-//
-// promiseChain(cb) <-- this is the callback for eventual success or error
-// ( fn, [arg, arg, arg], function (a,b,c) { success(a,b,c) })
-// ( fn2, [args] )
-// () <-- this kicks it off.
-//
-// promiseChain.call(someObj, cb) <-- bind this-context for all functions
-
-function promiseChain (cb) {
- var steps = []
- , vals = []
- , context = this
- function go () {
- var step = steps.shift()
- if (!step) return cb()
- try { step[0].apply(context, step[1]) }
- catch (ex) { cb(ex) }
- }
- return function pc (fn, args, success) {
- if (arguments.length === 0) return go()
- // add the step
- steps.push
- ( [ fn
- , (args || []).concat([ function (er) {
- if (er) return cb(er)
- var a = Array.prototype.slice.call(arguments, 1)
- try { success && success.apply(context, a) }
- catch (ex) { return cb(ex) }
- go()
- }])
- ]
- )
- return pc
- }
-}
+++ /dev/null
-
-module.exports = readJson
-readJson.processJson = processJson
-readJson.unParsePeople = unParsePeople
-readJson.parsePeople = parsePeople
-readJson.clearCache = clearCache
-
-var fs = require("graceful-fs")
- , semver = require("semver")
- , path = require("path")
- , log = require("./log.js")
- , npm = require("../npm.js")
- , cache = {}
- , timers = {}
- , loadPackageDefaults = require("./load-package-defaults.js")
-
-function readJson (jsonFile, opts, cb) {
- if (typeof cb !== "function") cb = opts, opts = {}
- if (cache.hasOwnProperty(jsonFile)) {
- log.verbose(jsonFile, "from cache")
- return cb(null, cache[jsonFile])
- }
- opts.file = jsonFile
- if (!opts.tag) {
- var parsedPath = jsonFile.indexOf(npm.dir) === 0 && jsonFile.match(
- /\/([^\/]+)\/([^\/]+)\/package\/package\.json$/)
- if (parsedPath && semver.valid(parsedPath[2])) {
- // this is a package.json in some installed package.
- // infer the opts.tag so that linked packages behave right.
- opts.tag = parsedPath[2]
- }
- }
-
- var wscript = null
- , contributors = null
- , serverjs = null
- , gypfile = null
-
- if (opts.gypfile !== null && opts.gypfile !== undefined) {
- gypfile = opts.gypfile
- next()
- } else {
- var pkgdir = path.dirname(jsonFile)
-
- function hasGyp (has) {
- gypfile = opts.gypfile = has
- next()
- }
-
- fs.readdir(pkgdir, function (er, gf) {
- // this would be weird.
- if (er) return hasGyp(false)
-
- // see if there are any *.gyp files in there.
- gf = gf.filter(function (f) {
- return f.match(/\.gyp$/)
- })
- gf = gf[0]
- return hasGyp(!!gf)
- })
- }
-
- if (opts.wscript !== null && opts.wscript !== undefined) {
- wscript = opts.wscript
- next()
- } else fs.readFile( path.join(path.dirname(jsonFile), "wscript")
- , function (er, data) {
- if (er) opts.wscript = false
- else opts.wscript = !!(data.toString().match(/(^|\n)def build\b/)
- && data.toString().match(/(^|\n)def configure\b/))
- wscript = opts.wscript
- next()
- })
-
- if (opts.contributors !== null && opts.contributors !== undefined) {
- contributors = opts.contributors
- next()
- } else fs.readFile( path.join(path.dirname(jsonFile), "AUTHORS")
- , function (er, data) {
- if (er) opts.contributors = false
- else {
- data = data.toString().split(/\r?\n/).map(function (l) {
- l = l.trim().split("#").shift()
- return l
- }).filter(function (l) { return l })
- opts.contributors = data
- }
- contributors = opts.contributors
- next()
- })
-
- if (opts.serverjs !== null && opts.serverjs !== undefined) {
- serverjs = opts.serverjs
- next()
- } else fs.stat( path.join(path.dirname(jsonFile), "server.js")
- , function (er, st) {
- if (er) opts.serverjs = false
- else opts.serverjs = st.isFile()
- serverjs = opts.serverjs
- next()
- })
-
- function next () {
- if (wscript === null ||
- contributors === null ||
- gypfile === null ||
- serverjs === null) {
- return
- }
-
- // XXX this api here is insane. being internal is no excuse.
- // please refactor.
- var thenLoad = processJson(opts, function (er, data) {
- if (er) return cb(er)
- var doLoad = !(jsonFile.indexOf(npm.cache) === 0 &&
- path.basename(path.dirname(jsonFile)) !== "package")
- if (!doLoad) return cb(er, data)
- loadPackageDefaults(data, path.dirname(jsonFile), cb)
- })
-
- fs.readFile(jsonFile, function (er, data) {
- if (er && er.code === "ENOENT") {
- // single-file module, maybe?
- // check index.js for a /**package { ... } **/ section.
- var indexFile = path.resolve(path.dirname(jsonFile), "index.js")
- return fs.readFile(indexFile, function (er2, data) {
- // if this doesn't work, then die with the original error.
- if (er2) return cb(er)
- data = parseIndex(data)
- if (!data) return cb(er)
- thenLoad(null, data)
- })
- }
- thenLoad(er, data)
- })
- }
-}
-
-// sync. no io.
-// /**package { "name": "foo", "version": "1.2.3", ... } **/
-function parseIndex (data) {
- data = data.toString()
- data = data.split(/^\/\*\*package(?:\s|$)/m)
- if (data.length < 2) return null
- data = data[1]
- data = data.split(/\*\*\/$/m)
- if (data.length < 2) return null
- data = data[0]
- data = data.replace(/^\s*\*/mg, "")
- return data
-}
-
-function processJson (opts, cb) {
- if (typeof cb !== "function") cb = opts, opts = {}
- if (typeof cb !== "function") {
- var thing = cb, cb = null
- return P(null, thing)
- } else return P
-
- function P (er, thing) {
- if (er) {
- if (cb) return cb(er, thing)
- throw er
- }
- if (typeof thing === "object" && !Buffer.isBuffer(thing)) {
- return processObject(opts, cb)(er, thing)
- } else {
- return processJsonString(opts, cb)(er, thing)
- }
- }
-}
-
-function processJsonString (opts, cb) { return function (er, jsonString) {
- if (er) return cb(er, jsonString)
- jsonString += ""
- var json
- try {
- json = JSON.parse(jsonString)
- } catch (ex) {
- if (opts.file && opts.file.indexOf(npm.dir) === 0) {
- try {
- json = require("vm").runInNewContext("(\n"+jsonString+"\n)")
- log.error(opts.file, "Error parsing json")
- log.error(ex, "parse error ")
- } catch (ex2) {
- return jsonParseFail(ex, opts.file, cb)
- }
- } else {
- return jsonParseFail(ex, opts.file, cb)
- }
- }
- return processObject(opts, cb)(er, json)
-}}
-
-
-function jsonParseFail (ex, file, cb) {
- var e = new Error(
- "Failed to parse json\n"+ex.message)
- e.errno = npm.EJSONPARSE
- e.file = file
- if (cb) return cb(e)
- throw e
-}
-
-// a warning for deprecated or likely-incorrect fields
-var typoWarned = {}
-function typoWarn (json) {
- if (typoWarned[json._id]) return
- typoWarned[json._id] = true
-
- if (json.modules) {
- log.warn("package.json: 'modules' object is deprecated", json._id)
- delete json.modules
- }
-
- // http://registry.npmjs.org/-/fields
- var typos = { "dependancies": "dependencies"
- , "dependecies": "dependencies"
- , "depdenencies": "dependencies"
- , "devEependencies": "devDependencies"
- , "depends": "dependencies"
- , "dev-dependencies": "devDependencies"
- , "devDependences": "devDependencies"
- , "devDepenencies": "devDependencies"
- , "devdependencies": "devDependencies"
- , "repostitory": "repository"
- , "prefereGlobal": "preferGlobal"
- , "hompage": "homepage"
- , "hampage": "homepage" // XXX maybe not a typo, just delicious?
- , "autohr": "author"
- , "autor": "author"
- , "contributers": "contributors"
- , "publicationConfig": "publishConfig"
- }
-
- Object.keys(typos).forEach(function (d) {
- if (json.hasOwnProperty(d)) {
- log.warn( "package.json: '" + d + "' should probably be '"
- + typos[d] + "'", json._id)
- }
- })
-
- // bugs typos
- var bugsTypos = { "web": "url"
- , "name": "url"
- }
-
- if (typeof json.bugs === "object") {
- // just go ahead and correct these.
- Object.keys(bugsTypos).forEach(function (d) {
- if (json.bugs.hasOwnProperty(d)) {
- json.bugs[ bugsTypos[d] ] = json.bugs[d]
- delete json.bugs[d]
- }
- })
- }
-
- // script typos
- var scriptTypos = { "server": "start" }
- if (json.scripts) Object.keys(scriptTypos).forEach(function (d) {
- if (json.scripts.hasOwnProperty(d)) {
- log.warn( "package.json: scripts['" + d + "'] should probably be "
- + "scripts['" + scriptTypos[d] + "']", json._id)
- }
- })
-}
-
-
-function processObject (opts, cb) { return function (er, json) {
- // json._npmJsonOpts = opts
- // log.warn(json, "processing json")
- if (npm.config.get("username")) {
- json._npmUser = { name: npm.config.get("username")
- , email: npm.config.get("email") }
- }
-
- // slashes would be a security risk.
- // anything else will just fail harmlessly.
- if (!json.name) {
- var e = new Error("No 'name' field found in package.json")
- if (cb) return cb(e)
- throw e
- }
- json.name = json.name.trim()
- if (json.name.charAt(0) === "." || json.name.match(/[\/@\s\+%:]/)) {
- var msg = "Invalid name: "
- + JSON.stringify(json.name)
- + " may not start with '.' or contain %/@+: or whitespace"
- , e = new Error(msg)
- if (cb) return cb(e)
- throw e
- }
- if (json.name.toLowerCase() === "node_modules") {
- var msg = "Invalid package name: node_modules"
- , e = new Error(msg)
- if (cb) return cb(e)
- throw e
- }
- if (json.name.toLowerCase() === "favicon.ico") {
- var msg = "Sorry, favicon.ico is a picture, not a package."
- , e = new Error(msg)
- if (cb) return cb(e)
- throw e
- }
-
- if (json.repostories) {
- var msg = "'repositories' (plural) No longer supported.\n"
- + "Please pick one, and put it in the 'repository' field."
- , e = new Error(msg)
- // uncomment once this is no longer an issue.
- // if (cb) return cb(e)
- // throw e
- log.error(msg, "incorrect json: "+json.name)
- json.repostory = json.repositories[0]
- delete json.repositories
- }
-
- if (json.repository) {
- if (typeof json.repository === "string") {
- json.repository = { type : "git"
- , url : json.repository }
- }
- var repo = json.repository.url || ""
- repo = repo.replace(/^(https?|git):\/\/[^\@]+\@github.com/
- ,'$1://github.com')
- if (json.repository.type === "git"
- && ( repo.match(/^https?:\/\/github.com/)
- || repo.match(/github.com\/[^\/]+\/[^\/]+\/?$/)
- && !repo.match(/\.git$/)
- )) {
- repo = repo.replace(/^https?:\/\/github.com/, 'git://github.com')
- if (!repo.match(/\.git$/)) {
- repo = repo.replace(/\/?$/, '.git')
- }
- }
- if (repo.match(/github\.com\/[^\/]+\/[^\/]+\/?$/)
- && repo.match(/\.git\.git$/)) {
- log.warn(repo, "Probably broken git url")
- }
- json.repository.url = repo
- }
-
- var files = json.files
- if (files && !Array.isArray(files)) {
- log.warn(files, "Invalid 'files' member. See 'npm help json'")
- delete json.files
- }
-
- var kw = json.keywords
- if (typeof kw === "string") {
- kw = kw.split(/,\s+/)
- json.keywords = kw
- }
-
- json._id = json.name+"@"+json.version
-
- var tag = opts.tag
- if (tag) json.version = tag
-
- var scripts = json.scripts || {}
-
- // if it has a bindings.gyp, then build with node-gyp
- if (opts.gypfile && !json.prebuilt) {
- log.verbose([json.prebuilt, opts], "has bindings.gyp")
- if (!scripts.install && !scripts.preinstall) {
- scripts.install = "node-gyp rebuild"
- json.scripts = scripts
- }
- }
-
- // if it has a wscript, then build it.
- if (opts.wscript && !json.prebuilt) {
- log.verbose([json.prebuilt, opts], "has wscript")
- if (!scripts.install && !scripts.preinstall) {
- // don't fail if it was unexpected, just try.
- scripts.preinstall = "node-waf clean || (exit 0); node-waf configure build"
- json.scripts = scripts
- }
- }
-
- // if it has an AUTHORS, then credit them
- if (opts.contributors && Array.isArray(opts.contributors)
- && opts.contributors.length) {
- json.contributors = opts.contributors
- }
-
- // if it has a server.js, then start it.
- if (opts.serverjs && !scripts.start) {
- scripts.start = "node server.js"
- json.scripts = scripts
- }
-
- if (!(semver.valid(json.version))) {
- var m
- if (!json.version) {
- m = "'version' field missing\n"
- } else {
- m = "Invalid 'version' field: "+json.version+"\n"
- }
-
- m += "'version' Must be X.Y.Z, with an optional trailing tag.\n"
- + "See the section on 'version' in `npm help json`"
-
- var e = new Error(m)
- if (cb) return cb(e)
- throw e
- }
- json.version = semver.clean(json.version)
-
- if (json.bin && typeof json.bin === "string") {
- var b = {}
- b[ json.name ] = json.bin
- json.bin = b
- }
-
- if (json.bundledDependencies && !json.bundleDependencies) {
- json.bundleDependencies = json.bundledDependencies
- delete json.bundledDependencies
- }
-
- if (json.bundleDependencies && !Array.isArray(json.bundleDependencies)) {
- var e = new Error("bundleDependencies must be an array.\n"
- +"See `npm help json`")
- if (cb) return cb(e)
- throw e
- }
-
- if (json["dev-dependencies"] && !json.devDependencies) {
- json.devDependencies = json["dev-dependencies"]
- delete json["dev-dependencies"]
- }
-
- ; [ "dependencies"
- , "devDependencies"
- , "optionalDependencies"
- ].forEach(function (d) {
- json[d] = json.hasOwnProperty(d)
- ? depObjectify(json[d], d, json._id)
- : {}
- })
-
- // always merge optionals into deps
- Object.keys(json.optionalDependencies).forEach(function (d) {
- json.dependencies[d] = json.optionalDependencies[d]
- })
-
- if (opts.dev
- || npm.config.get("dev")
- || npm.config.get("npat")) {
- Object.keys(json.devDependencies || {}).forEach(function (d) {
- json.dependencies[d] = json.devDependencies[d]
- })
- }
-
- typoWarn(json)
-
- json = testEngine(json)
- json = parsePeople(unParsePeople(json))
- if ( json.bugs ) json.bugs = parsePerson(unParsePerson(json.bugs))
- json._npmVersion = npm.version
- json._nodeVersion = process.version
- if (opts.file) {
- log.verbose(opts.file, "caching")
- cache[opts.file] = json
- // arbitrary
- var keys = Object.keys(cache)
- , l = keys.length
- if (l > 10000) for (var i = 0; i < l - 5000; i ++) {
- delete cache[keys[i]]
- }
- }
- if (cb) cb(null,json)
- return json
-}}
-
-var depObjectifyWarn = {}
-function depObjectify (deps, d, id) {
- if (!deps) return {}
- if (typeof deps === "string") {
- deps = deps.trim().split(/[\n\r\s\t ,]+/)
- }
- if (!Array.isArray(deps)) return deps
- var o = {}
- deps.forEach(function (d) {
- d = d.trim().split(/(:?[@\s><=])/)
- o[d.shift()] = d.join("").trim().replace(/^@/, "")
- })
- return o
-}
-
-function testEngine (json) {
- // if engines is empty, then assume that node is allowed.
- if ( !json.engines
- || Array.isArray(json.engines)
- && !json.engines.length
- || typeof json.engines === "object"
- && !Object.keys(json.engines).length
- ) {
- json.engines = { "node" : "*" }
- }
- if (typeof json.engines === "string") {
- if (semver.validRange(json.engines) !== null) {
- json.engines = { "node" : json.engines }
- } else json.engines = [ json.engines ]
- }
-
- var nodeVer = npm.config.get("node-version")
- , ok = false
- if (nodeVer) nodeVer = nodeVer.replace(/\+$/, '')
- if (Array.isArray(json.engines)) {
- // Packages/1.0 commonjs style, with an array.
- // hack it to just hang a "node" member with the version range,
- // then do the npm-style check below.
- for (var i = 0, l = json.engines.length; i < l; i ++) {
- var e = json.engines[i].trim()
- if (e.substr(0, 4) === "node") {
- json.engines.node = e.substr(4)
- } else if (e.substr(0, 3) === "npm") {
- json.engines.npm = e.substr(3)
- }
- }
- }
- if (json.engines.node === "") json.engines.node = "*"
- if (json.engines.node && null === semver.validRange(json.engines.node)) {
- log.warn( json.engines.node
- , "Invalid range in engines.node. Please see `npm help json`" )
- }
-
- if (nodeVer) {
- json._engineSupported = semver.satisfies( nodeVer
- , json.engines.node || "null" )
- }
- if (json.engines.hasOwnProperty("npm") && json._engineSupported) {
- json._engineSupported = semver.satisfies(npm.version, json.engines.npm)
- }
- return json
-}
-
-function unParsePeople (json) { return parsePeople(json, true) }
-
-function parsePeople (json, un) {
- var fn = un ? unParsePerson : parsePerson
- if (json.author) json.author = fn(json.author)
- ;["maintainers", "contributors"].forEach(function (set) {
- if (Array.isArray(json[set])) json[set] = json[set].map(fn)
- })
- return json
-}
-
-function unParsePerson (person) {
- if (typeof person === "string") return person
- var name = person.name || ""
- , u = person.url || person.web
- , url = u ? (" ("+u+")") : ""
- , e = person.email || person.mail
- , email = e ? (" <"+e+">") : ""
- return name+email+url
-}
-
-function parsePerson (person) {
- if (typeof person !== "string") return person
- var name = person.match(/^([^\(<]+)/)
- , url = person.match(/\(([^\)]+)\)/)
- , email = person.match(/<([^>]+)>/)
- , obj = {}
- if (name && name[0].trim()) obj.name = name[0].trim()
- if (email) obj.email = email[1]
- if (url) obj.url = url[1]
- return obj
-}
-
-function clearCache (prefix) {
- if (!prefix) {
- cache = {}
- return
- }
- Object.keys(cache).forEach(function (c) {
- if (c.indexOf(prefix) === 0) delete cache[c]
- })
-}
+++ /dev/null
-
-module.exports = relativize
-
-// return the shortest path between two folders.
-// if the original path is shorter, then use that,
-// unless forceRelative is set to true.
-var path = require("path")
-function relativize (dest, src, forceRelative) {
- var orig = dest
- if (!isAbsolute(src)) forceRelative = true
- else if (!isAbsolute(dest)) return false
- src = path.resolve(src)
- dest = path.resolve(dest)
- if (src === dest) return "."
- src = src.split(split)
- dest = dest.split(split)
- var i = 0
- while (src[i] === dest[i]) i++
- if (!forceRelative && i === 1) return orig // nothing in common
- src.splice(0, i + 1)
- var dots = [0, i, "."]
- for (var i = 0, l = src.length; i < l; i ++) dots.push("..")
- dest.splice.apply(dest, dots)
- if (dest[0] === "." && dest[1] === "..") dest.shift()
- dest = dest.join("/")
- return !forceRelative && orig.length < dest.length ? orig : dest
-}
-
-var split = process.platform === "win32" ? /[\/\\]/ : "/"
-
-function isAbsolute (p) {
- if (process.platform !== "win32") return p.charAt(0) === "/"
-
-
- // yanked from node/lib/path.js
- var splitDeviceRe =
- /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?([\s\S]*?)$/
-
- var result = p.match(splitDeviceRe)
- , device = result[1] || ""
- , isUnc = device && device.charAt(1) !== ":"
- , isAbs = !!result[2] || isUnc // UNC always absolute
-
- return isAbs
-}
-
-if (module === require.main) {
- // from, to, result, relativeForced
- var assert = require("assert")
-
- ; [ ["/bar" ,"/foo" ,"/bar" ,"./bar" ]
- , ["/foo/baz" ,"/foo/bar/baz" ,"../baz" ,"../baz" ]
- , ["/a/d" ,"/a/b/c/d/e/f" ,"/a/d" ,"../../../../d" ]
- // trailing slashes are ignored.
- , ["/a/d" ,"/a/b/c/d/e/" ,"/a/d" ,"../../../d" ]
- , ["./foo/bar" ,"./foo/baz" ,"./bar" ,"./bar" ]
- // force relative when the src is relative.
- , ["./d" ,"./a/b/c/d/e" ,"../../../../d" ,"../../../../d" ]
- // if src is abs and dest is relative, then fail
- , ["./d" ,"/a/b" ,false ,false ]
- ].forEach(function (test) {
- var d = test[0]
- , s = test[1]
- , r = test[2]
- , rr = test[3]
- , ra = relativize(d, s)
- , rra = relativize(d, s, true)
- console.log([d, s, r, rr], [ra, rra], [r === ra, rr === rra])
- assert.equal(r, ra)
- assert.equal(rr, rra)
- if (!r) return
- // contract: this is the relative path from absolute A to absolute B
- var ad = path.resolve(d)
- , as = path.resolve(s)
- , dir = path.dirname(as)
- assert.equal(path.resolve(dir, rr), ad)
- assert.equal(path.resolve(dir, r), ad)
- })
-
- console.log("ok")
-}
var fs = require("graceful-fs")
, crypto = require("crypto")
- , log = require("./log.js")
+ , log = require("npmlog")
, binding
try { binding = process.binding("crypto") }
function check (file, sum, cb) {
if (!binding) {
- log.warn("crypto binding not found. Cannot verify shasum.", "shasum")
+ log.warn("shasum", "crypto binding not found. Cannot verify shasum.")
return cb()
}
get(file, function (er, actual) {
- if (er) return log.er(cb, "Error getting shasum")(er)
+ if (er) {
+ log.error("shasum", "error getting shasum")
+ return cb(er)
+ }
var expected = sum.toLowerCase().trim()
, ok = actual === expected
cb(ok ? null : new Error(
function get (file, cb) {
if (!binding) {
- log.warn("crypto binding not found. Cannot verify shasum.", "shasum")
+ log.warn("shasum", "crypto binding not found. Cannot verify shasum.")
return cb()
}
var h = crypto.createHash("sha1")
, errState = null
s.on("error", function (er) {
if (errState) return
- log.silly(er.stack || er.message, "sha error")
+ log.silly("shasum", "error", er)
return cb(errState = er)
}).on("data", function (chunk) {
if (errState) return
- log.silly(chunk.length, "updated sha bytes")
+ log.silly("shasum", "updated bytes", chunk.length)
h.update(chunk)
}).on("end", function () {
if (errState) return
var actual = h.digest("hex").toLowerCase().trim()
- log(actual+"\n"+file, "shasum")
+ log.info("shasum", actual+"\n"+file)
cb(null, actual)
})
}
var npm = require("../npm.js")
, fs = require("graceful-fs")
, path = require("path")
- , log = require("./log.js")
+ , log = require("npmlog")
, uidNumber = require("uid-number")
, rm = require("rimraf")
- , readJson = require("./read-json.js")
- , relativize = require("./relativize.js")
+ , readJson = require("read-package-json")
, cache = require("../cache.js")
+ , lock = cache.lock
+ , unlock = cache.unlock
, myUid = process.getuid && process.getuid()
, myGid = process.getgid && process.getgid()
, tar = require("tar")
exports.unpack = unpack
function pack (targetTarball, folder, pkg, dfc, cb) {
- log.verbose([targetTarball, folder], "tar.pack")
+ log.verbose("tar pack", [targetTarball, folder])
if (typeof cb !== "function") cb = dfc, dfc = false
- log.verbose(targetTarball, "tarball")
- log.verbose(folder, "folder")
+ log.verbose("tarball", targetTarball)
+ log.verbose("folder", folder)
if (dfc) {
// do fancy crap
}
}
-function pack_ (targetTarball, folder, pkg, cb) {
- new Packer({ path: folder, type: "Directory", isDirectory: true })
- .on("error", log.er(cb, "error reading "+folder))
-
- // By default, npm includes some proprietary attributes in the
- // package tarball. This is sane, and allowed by the spec.
- // However, npm *itself* excludes these from its own package,
- // so that it can be more easily bootstrapped using old and
- // non-compliant tar implementations.
- .pipe(tar.Pack({ noProprietary: !npm.config.get("proprietary-attribs") }))
- .on("error", log.er(cb, "tar creation error "+targetTarball))
- .pipe(zlib.Gzip())
- .on("error", log.er(cb, "gzip error "+targetTarball))
- .pipe(fstream.Writer({ type: "File", path: targetTarball }))
- .on("error", log.er(cb, "Could not write "+targetTarball))
- .on("close", function () {
- cb()
+function pack_ (targetTarball, folder, pkg, cb_) {
+ function cb (er) {
+ unlock(targetTarball, function () {
+ return cb_(er)
})
+ }
+ lock(targetTarball, function (er) {
+ if (er) return cb(er)
+
+ new Packer({ path: folder, type: "Directory", isDirectory: true })
+ .on("error", function (er) {
+ if (er) log.error("tar pack", "Error reading " + folder)
+ return cb(er)
+ })
+
+ // By default, npm includes some proprietary attributes in the
+ // package tarball. This is sane, and allowed by the spec.
+ // However, npm *itself* excludes these from its own package,
+ // so that it can be more easily bootstrapped using old and
+ // non-compliant tar implementations.
+ .pipe(tar.Pack({ noProprietary: !npm.config.get("proprietary-attribs") }))
+ .on("error", function (er) {
+ if (er) log.error("tar.pack", "tar creation error", targetTarball)
+ cb(er)
+ })
+ .pipe(zlib.Gzip())
+ .on("error", function (er) {
+ if (er) log.error("tar.pack", "gzip error "+targetTarball)
+ cb(er)
+ })
+ .pipe(fstream.Writer({ type: "File", path: targetTarball }))
+ .on("error", function (er) {
+ if (er) log.error("tar.pack", "Could not write "+targetTarball)
+ cb(er)
+ })
+ .on("close", cb)
+ })
}
function unpack (tarball, unpackTarget, dMode, fMode, uid, gid, cb) {
- log.verbose(tarball, "unpack")
+ log.verbose("tar unpack", tarball)
if (typeof cb !== "function") cb = gid, gid = null
if (typeof cb !== "function") cb = uid, uid = null
if (typeof cb !== "function") cb = fMode, fMode = npm.modes.file
})
}
-function unpack_ ( tarball, unpackTarget, dMode, fMode, uid, gid, cb ) {
+function unpack_ ( tarball, unpackTarget, dMode, fMode, uid, gid, cb_ ) {
var parent = path.dirname(unpackTarget)
, base = path.basename(unpackTarget)
- rm(unpackTarget, function (er) {
+ function cb (er) {
+ unlock(unpackTarget, function () {
+ return cb_(er)
+ })
+ }
+
+ lock(unpackTarget, function (er) {
if (er) return cb(er)
+ rmGunz()
+ })
+ function rmGunz () {
+ rm(unpackTarget, function (er) {
+ if (er) return cb(er)
+ gtp()
+ })
+ }
+
+ function gtp () {
// gzip {tarball} --decompress --stdout \
// | tar -mvxpf - --strip-components=1 -C {unpackTarget}
gunzTarPerm( tarball, unpackTarget
if (er) return cb(er)
readJson(path.resolve(folder, "package.json"), cb)
})
- })
+ }
}
function gunzTarPerm (tarball, target, dMode, fMode, uid, gid, cb_) {
if (!dMode) dMode = npm.modes.exec
if (!fMode) fMode = npm.modes.file
- log.silly([dMode.toString(8), fMode.toString(8)], "gunzTarPerm modes")
+ log.silly("gunzTarPerm", "modes", [dMode.toString(8), fMode.toString(8)])
var cbCalled = false
function cb (er) {
}
function extractEntry (entry) {
- log.silly(entry.path, "extracting entry")
+ log.silly("gunzTarPerm", "extractEntry", entry.path)
// never create things that are user-unreadable,
// or dirs that are user-un-listable. Only leads to headaches.
var originalMode = entry.mode = entry.mode || entry.props.mode
entry.mode = entry.mode & (~npm.modes.umask)
entry.props.mode = entry.mode
if (originalMode !== entry.mode) {
- log.silly([entry.path, originalMode, entry.mode], "modified mode")
+ log.silly( "gunzTarPerm", "modified mode"
+ , [entry.path, originalMode, entry.mode])
}
// if there's a specific owner uid/gid that we want, then set that
extractOpts.filter = function () {
// symbolic links are not allowed in packages.
if (this.type.match(/^.*Link$/)) {
- log.warn( this.path.substr(target.length + 1)
- + ' -> ' + this.linkpath
- , "excluding symbolic link")
+ log.warn( "excluding symbolic link"
+ , this.path.substr(target.length + 1)
+ + ' -> ' + this.linkpath )
return false
}
return true
}
- fst.on("error", log.er(cb, "error reading "+tarball))
+ fst.on("error", function (er) {
+ if (er) log.error("tar.unpack", "error reading "+tarball)
+ cb(er)
+ })
fst.on("data", function OD (c) {
// detect what it is.
// Then, depending on that, we'll figure out whether it's
c[2] === 0x08) {
fst
.pipe(zlib.Unzip())
- .on("error", log.er(cb, "unzip error "+tarball))
+ .on("error", function (er) {
+ if (er) log.error("tar.unpack", "unzip error "+tarball)
+ cb(er)
+ })
.pipe(tar.Extract(extractOpts))
.on("entry", extractEntry)
- .on("error", log.er(cb, "untar error "+tarball))
+ .on("error", function (er) {
+ if (er) log.error("tar.unpack", "untar error "+tarball)
+ cb(er)
+ })
.on("close", cb)
} else if (c.toString().match(/^package\//)) {
// naked tar
fst
.pipe(tar.Extract(extractOpts))
.on("entry", extractEntry)
- .on("error", log.er(cb, "untar error "+tarball))
+ .on("error", function (er) {
+ if (er) log.error("tar.unpack", "untar error "+tarball)
+ cb(er)
+ })
.on("close", cb)
} else {
// naked js file
fst
.pipe(fstream.Writer(jsOpts))
- .on("error", log.er(cb, "copy error "+tarball))
+ .on("error", function (er) {
+ if (er) log.error("tar.unpack", "copy error "+tarball)
+ cb(er)
+ })
.on("close", function () {
var j = path.resolve(target, "package.json")
readJson(j, function (er, d) {
if (er) {
- log.error(tarball, "Not a package")
+ log.error("not a package", tarball)
return cb(er)
}
fs.writeFile(j, JSON.stringify(d) + "\n", cb)
module.exports = version
var exec = require("./utils/exec.js")
- , readJson = require("./utils/read-json.js")
, semver = require("semver")
, path = require("path")
, fs = require("graceful-fs")
, chain = require("slide").chain
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, npm = require("./npm.js")
version.usage = "npm version <newversion> [--message commit-message]"
function version (args, cb) {
if (args.length !== 1) return cb(version.usage)
- readJson(path.join(process.cwd(), "package.json"), function (er, data) {
- if (er) return log.er(cb, "No package.json found")(er)
+ fs.readFile(path.join(process.cwd(), "package.json"), function (er, data) {
+ if (er) {
+ log.error("version", "No package.json found")
+ return cb(er)
+ }
+
+ try {
+ data = JSON.parse(data)
+ } catch (er) {
+ log.error("version", "Bad package.json data")
+ return cb(er)
+ }
+
var newVer = semver.valid(args[0])
if (!newVer) newVer = semver.inc(data.version, args[0])
if (!newVer) return cb(version.usage)
if (data.version === newVer) return cb(new Error("Version not changed"))
data.version = newVer
- Object.keys(data).forEach(function (k) {
- if (k.charAt(0) === "_") delete data[k]
- })
- readJson.unParsePeople(data)
+
fs.stat(path.join(process.cwd(), ".git"), function (er, s) {
var doGit = !er && s.isDirectory()
if (!doGit) return write(data, cb)
})
})
}
+
function checkGit (data, cb) {
exec( npm.config.get("git"), ["status", "--porcelain"], process.env, false
, function (er, code, stdout, stderr) {
})
})
}
+
function write (data, cb) {
fs.writeFile( path.join(process.cwd(), "package.json")
, new Buffer(JSON.stringify(data, null, 2) + "\n")
}
}
-var registry = require("./utils/npm-registry-client/index.js")
+var npm = require("./npm.js")
+ , registry = npm.registry
, ini = require("ini")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
, util = require("util")
, output
- , npm = require("./npm.js")
, semver = require("semver")
- , readJson = require("./utils/read-json.js")
function view (args, silent, cb) {
if (typeof cb !== "function") cb = silent, silent = false
if (name === ".") return cb(view.usage)
// get the data about this package
- registry.get(name, null, 600, function (er, data) {
+ registry.get(name, 600, function (er, data) {
if (er) return cb(er)
if (data["dist-tags"].hasOwnProperty(version)) {
version = data["dist-tags"][version]
}
Object.keys(versions).forEach(function (v) {
- try {
- versions[v] = readJson.processJson(versions[v])
- } catch (ex) {
- delete versions[v]
- }
if (semver.satisfies(v, version)) args.forEach(function (args) {
// remove readme unless we asked for it
if (-1 === args.indexOf("readme")) {
if (args.length === 1 && args[0] === "") {
retval = cleanBlanks(retval)
- log.silly(retval, "cleanup")
+ log.silly("cleanup", retval)
}
if (error || silent) cb(error, retval)
var npm = require("./npm.js")
, output = require("./utils/output.js")
- , log = require("./utils/log.js")
whoami.usage = "npm whoami\n(just prints the 'username' config)"
// happy xmas
var npm = require("./npm.js")
- , log = require("./utils/log.js")
+ , log = require("npmlog")
module.exports = function (args, cb) {
-npm.config.set("loglevel", "win")
var s = process.platform === "win32" ? " *" : " \u2605"
, f = "\uFF0F"
, b = "\uFF3C"
}
})(20)
w("\n\n")
-log.win("Happy Xmas, Noders!", "loves you", cb)
+log.heading = ''
+log.addLevel('npm', 100000, log.headingStyle)
+log.npm("loves you", "Happy Xmas, Noders!")
+cb()
}
var dg=false
Object.defineProperty(module.exports, "usage", {get:function () {
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM" "1" "June 2012" "" ""
+.TH "NPM" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm\fR \-\- node package manager
.nf
git clone https://github\.com/isaacs/npm\.git
cd npm
-git submodule update \-\-init \-\-recursive
sudo make install # (or: `node cli\.js install \-gf`)
.
.fi
.IP "" 4
.
.nf
-git submodule update \-\-init \-\-recursive
sudo make install
.
.fi
.P
and npm will install itself\.
.
-.P
-Note that github tarballs \fBdo not contain submodules\fR, so
-those won\'t work\. You\'ll have to also fetch the appropriate submodules
-listed in the \.gitmodules file\.
-.
.SH "Permissions when Using npm to Install Other Stuff"
\fBtl;dr\fR
.
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-ADDUSER" "1" "June 2012" "" ""
+.TH "NPM\-ADDUSER" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-adduser\fR \-\- Add a registry user account
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BIN" "1" "June 2012" "" ""
+.TH "NPM\-BIN" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-bin\fR \-\- Display npm bin folder
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BUGS" "1" "June 2012" "" ""
+.TH "NPM\-BUGS" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-bugs\fR \-\- Bugs for a package in a web browser maybe
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BUILD" "1" "June 2012" "" ""
+.TH "NPM\-BUILD" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-build\fR \-\- Build a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BUNDLE" "1" "June 2012" "" ""
+.TH "NPM\-BUNDLE" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-bundle\fR \-\- REMOVED
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CACHE" "1" "June 2012" "" ""
+.TH "NPM\-CACHE" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-cache\fR \-\- Manipulates packages cache
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CHANGELOG" "1" "June 2012" "" ""
+.TH "NPM\-CHANGELOG" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-changelog\fR \-\- Changes
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CODING\-STYLE" "1" "June 2012" "" ""
+.TH "NPM\-CODING\-STYLE" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-coding-style\fR \-\- npm\'s "funny" coding style
Always create a new Error object with your message\. Don\'t just return a
string message to the callback\. Stack traces are handy\.
.
-.P
-Use the \fBrequire("\./utils/log")\.er\fR function\. It takes a callback and an
-error message, and returns an object that will report the message in the
-event of a failure\. It\'s quite handy\.
-.
-.IP "" 4
-.
-.nf
-function myThing (args, cb) {
- getData(args, function (er, data) {
- if (er) return log\.er(cb, "Couldn\'t get data")(er)
- doSomethingElse(data, cb)
- })
-}
-function justHasToWork (cb) {
- doSomething(log\.er(cb, "the doSomething failed\."))
-}
-.
-.fi
-.
-.IP "" 0
-.
.SH "Logging"
+Logging is done using the npmlog \fIhttps://github\.com/isaacs/npmlog\fR
+utility\.
+.
+.P
Please clean up logs when they are no longer helpful\. In particular,
logging the same object over and over again is not helpful\. Logs should
report what\'s happening so that it\'s easier to track down where a fault
occurs\.
.
.P
-Use appropriate log levels\. The default log() function logs at the
-"info" level\. See \fBnpm help config\fR and search for "loglevel"\.
+Use appropriate log levels\. See \fBnpm help config\fR and search for
+"loglevel"\.
.
.SH "Case, naming, etc\."
Use \fBlowerCamelCase\fR for multiword identifiers when they refer to objects,
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-COMPLETION" "1" "June 2012" "" ""
+.TH "NPM\-COMPLETION" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-completion\fR \-\- Tab Completion for npm
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CONFIG" "1" "June 2012" "" ""
+.TH "NPM\-CONFIG" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-config\fR \-\- Manage the npm configuration file
\fB\-O\fR: \fB\-\-save\-optional\fR
.
.IP "\(bu" 4
+\fB\-B\fR: \fB\-\-save\-bundle\fR
+.
+.IP "\(bu" 4
\fB\-y\fR: \fB\-\-yes\fR
.
.IP "\(bu" 4
Force npm to always require authentication when accessing the registry,
even for \fBGET\fR requests\.
.
-.SS "bin\-publish"
+.SS "browser"
.
.IP "\(bu" 4
-Default: false
+Default: OS X: \fB"open"\fR, others: \fB"google\-chrome"\fR
.
.IP "\(bu" 4
-Type: Boolean
+Type: String
.
.IP "" 0
.
.P
-If set to true, then binary packages will be created on publish\.
-.
-.P
-This is the way to opt into the "bindist" behavior described below\.
+The browser that is called by the \fBnpm docs\fR command to open websites\.
.
-.SS "bindist"
+.SS "ca"
.
.IP "\(bu" 4
-Default: Unstable node versions, \fBnull\fR, otherwise \fB"<node version>\-<platform>\-<os release>"\fR
+Default: The npm CA certificate
.
.IP "\(bu" 4
-Type: String or \fBnull\fR
+Type: String or null
.
.IP "" 0
.
.P
-Experimental: on stable versions of node, binary distributions will be
-created with this tag\. If a user then installs that package, and their \fBbindist\fR tag is found in the list of binary distributions, they will
-get that prebuilt version\.
+The Certificate Authority signing certificate that is trusted for SSL
+connections to the registry\.
.
.P
-Pre\-build node packages have their preinstall, install, and postinstall
-scripts stripped (since they are run prior to publishing), and do not
-have their \fBbuild\fR directories automatically ignored\.
+Set to \fBnull\fR to only allow "known" registrars, or to a specific CA cert
+to trust only that specific signing authority\.
.
.P
-It\'s yet to be seen if this is a good idea\.
+See also the \fBstrict\-ssl\fR config\.
.
-.SS "browser"
+.SS "cache"
.
.IP "\(bu" 4
-Default: OS X: \fB"open"\fR, others: \fB"google\-chrome"\fR
+Default: Windows: \fB%APPDATA%\\npm\-cache\fR, Posix: \fB~/\.npm\fR
.
.IP "\(bu" 4
-Type: String
+Type: path
.
.IP "" 0
.
.P
-The browser that is called by the \fBnpm docs\fR command to open websites\.
+The location of npm\'s cache directory\. See \fBnpm help cache\fR
.
-.SS "ca"
+.SS "cache\-lock\-stale"
.
.IP "\(bu" 4
-Default: The npm CA certificate
+Default: 60000 (1 minute)
.
.IP "\(bu" 4
-Type: String or null
+Type: Number
.
.IP "" 0
.
.P
-The Certificate Authority signing certificate that is trusted for SSL
-connections to the registry\.
+The number of ms before cache folder lockfiles are considered stale\.
.
-.P
-Set to \fBnull\fR to only allow "known" registrars, or to a specific CA cert
-to trust only that specific signing authority\.
+.SS "cache\-lock\-retries"
+.
+.IP "\(bu" 4
+Default: 10
+.
+.IP "\(bu" 4
+Type: Number
+.
+.IP "" 0
.
.P
-See also the \fBstrict\-ssl\fR config\.
+Number of times to retry to acquire a lock on cache folder lockfiles\.
.
-.SS "cache"
+.SS "cache\-lock\-wait"
.
.IP "\(bu" 4
-Default: Windows: \fB%APPDATA%\\npm\-cache\fR, Posix: \fB~/\.npm\fR
+Default: 10000 (10 seconds)
.
.IP "\(bu" 4
-Type: path
+Type: Number
.
.IP "" 0
.
.P
-The location of npm\'s cache directory\. See \fBnpm help cache\fR
+Number of ms to wait for cache lock files to expire\.
.
.SS "cache\-max"
.
.P
The command to run for \fBnpm edit\fR or \fBnpm config edit\fR\|\.
.
+.SS "engine\-strict"
+.
+.IP "\(bu" 4
+Default: false
+.
+.IP "\(bu" 4
+Type: Boolean
+.
+.IP "" 0
+.
+.P
+If set to true, then npm will stubbornly refuse to install (or even
+consider installing) any package that claims to not be compatible with
+the current Node\.js version\.
+.
.SS "force"
.
.IP "\(bu" 4
.
.IP "" 0
.
+.SS "fetch\-retries"
+.
+.IP "\(bu" 4
+Default: 2
+.
+.IP "\(bu" 4
+Type: Number
+.
+.IP "" 0
+.
+.P
+The "retries" config for the \fBretry\fR module to use when fetching
+packages from the registry\.
+.
+.SS "fetch\-retry\-factor"
+.
+.IP "\(bu" 4
+Default: 10
+.
+.IP "\(bu" 4
+Type: Number
+.
+.IP "" 0
+.
+.P
+The "factor" config for the \fBretry\fR module to use when fetching
+packages\.
+.
+.SS "fetch\-retry\-mintimeout"
+.
+.IP "\(bu" 4
+Default: 10000 (10 seconds)
+.
+.IP "\(bu" 4
+Type: Number
+.
+.IP "" 0
+.
+.P
+The "minTimeout" config for the \fBretry\fR module to use when fetching
+packages\.
+.
+.SS "fetch\-retry\-maxtimeout"
+.
+.IP "\(bu" 4
+Default: 60000 (1 minute)
+.
+.IP "\(bu" 4
+Type: Number
+.
+.IP "" 0
+.
+.P
+The "maxTimeout" config for the \fBretry\fR module to use when fetching
+packages\.
+.
.SS "git"
.
.IP "\(bu" 4
A white\-space separated list of glob patterns of files to always exclude
from packages when building tarballs\.
.
+.SS "init\-module"
+.
+.IP "\(bu" 4
+Default: ~/\.npm\-init\.js
+.
+.IP "\(bu" 4
+Type: path
+.
+.IP "" 0
+.
+.P
+A module that will be loaded by the \fBnpm init\fR command\. See the
+documentation for the init\-package\-json \fIhttps://github\.com/isaacs/init\-package\-json\fR module
+for more information, or npm help init\.
+.
.SS "init\.version"
.
.IP "\(bu" 4
.
.IP "" 0
.
-.SS "logfd"
-.
-.IP "\(bu" 4
-Default: stderr file descriptor
-.
-.IP "\(bu" 4
-Type: Number or Stream
-.
-.IP "" 0
-.
-.P
-The location to write log output\.
-.
.SS "loglevel"
.
.IP "\(bu" 4
Any logs of a higher level than the setting are shown\.
The default is "http", which shows http, warn, and error output\.
.
-.SS "logprefix"
+.SS "logstream"
.
.IP "\(bu" 4
-Default: true on Posix, false on Windows
+Default: process\.stderr
.
.IP "\(bu" 4
-Type: Boolean
+Type: Stream
.
.IP "" 0
.
.P
-Whether or not to prefix log messages with "npm" and the log level\. See
-also "color" and "loglevel"\.
+This is the stream that is passed to the npmlog \fIhttps://github\.com/isaacs/npmlog\fR module at run time\.
+.
+.P
+It cannot be set from the command line, but if you are using npm
+programmatically, you may wish to send logs to somewhere other than
+stderr\.
.
.SS "long"
.
A node module to \fBrequire()\fR when npm loads\. Useful for programmatic
usage\.
.
-.SS "outfd"
-.
-.IP "\(bu" 4
-Default: standard output file descriptor
-.
-.IP "\(bu" 4
-Type: Number or Stream
-.
-.IP "" 0
-.
-.P
-Where to write "normal" output\. This has no effect on log output\.
-.
.SS "parseable"
.
.IP "\(bu" 4
Save installed packages to a package\.json file as dependencies\.
.
.P
+When used with the \fBnpm rm\fR command, it removes it from the dependencies
+hash\.
+.
+.P
Only works if there is already a package\.json file present\.
.
+.SS "save\-bundle"
+.
+.IP "\(bu" 4
+Default: false
+.
+.IP "\(bu" 4
+Type: Boolean
+.
+.IP "" 0
+.
+.P
+If a package would be saved at install time by the use of \fB\-\-save\fR, \fB\-\-save\-dev\fR, or \fB\-\-save\-optional\fR, then also put it in the \fBbundleDependencies\fR list\.
+.
+.P
+When used with the \fBnpm rm\fR command, it removes it from the
+bundledDependencies list\.
+.
.SS "save\-dev"
.
.IP "\(bu" 4
Save installed packages to a package\.json file as devDependencies\.
.
.P
+When used with the \fBnpm rm\fR command, it removes it from the devDependencies
+hash\.
+.
+.P
Only works if there is already a package\.json file present\.
.
.SS "save\-optional"
Save installed packages to a package\.json file as optionalDependencies\.
.
.P
+When used with the \fBnpm rm\fR command, it removes it from the devDependencies
+hash\.
+.
+.P
Only works if there is already a package\.json file present\.
.
.SS "searchopts"
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DEPRECATE" "1" "June 2012" "" ""
+.TH "NPM\-DEPRECATE" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-deprecate\fR \-\- Deprecate a version of a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DEVELOPERS" "1" "June 2012" "" ""
+.TH "NPM\-DEVELOPERS" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-developers\fR \-\- Developer Guide
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DISPUTES" "1" "June 2012" "" ""
+.TH "NPM\-DISPUTES" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-disputes\fR \-\- Handling Module Name Disputes
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DOCS" "1" "June 2012" "" ""
+.TH "NPM\-DOCS" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-docs\fR \-\- Docs for a package in a web browser maybe
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-EDIT" "1" "June 2012" "" ""
+.TH "NPM\-EDIT" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-edit\fR \-\- Edit an installed package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-EXPLORE" "1" "June 2012" "" ""
+.TH "NPM\-EXPLORE" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-explore\fR \-\- Browse an installed package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-FAQ" "1" "June 2012" "" ""
+.TH "NPM\-FAQ" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-faq\fR \-\- Frequently Asked Questions
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-FOLDERS" "1" "June 2012" "" ""
+.TH "NPM\-FOLDERS" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-folders\fR \-\- Folder Structures Used by npm
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-HELP\-SEARCH" "1" "June 2012" "" ""
+.TH "NPM\-HELP\-SEARCH" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-help-search\fR \-\- Search npm help documentation
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-HELP" "1" "June 2012" "" ""
+.TH "NPM\-HELP" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-help\fR \-\- Get help on npm
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-INDEX" "1" "June 2012" "" ""
+.TH "NPM\-INDEX" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-index\fR \-\- Index of all npm documentation
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-INIT" "1" "June 2012" "" ""
+.TH "NPM\-INIT" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-init\fR \-\- Interactively create a package\.json file
.SH "SEE ALSO"
.
.IP "\(bu" 4
+\fIhttps://github\.com/isaacs/init\-package\-json\fR
+.
+.IP "\(bu" 4
npm help json
.
.IP "\(bu" 4
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-INSTALL" "1" "June 2012" "" ""
+.TH "NPM\-INSTALL" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-install\fR \-\- Install a package
.
.P
The \fB\-\-global\fR argument will cause npm to install the package globally
-rather than locally\. See \fBnpm help global\fR\|\.
+rather than locally\. See \fBnpm help folders\fR\|\.
.
.P
The \fB\-\-link\fR argument will cause npm to link global installs into the
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-JSON" "1" "June 2012" "" ""
+.TH "NPM\-JSON" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-json\fR \-\- Specifics of npm\'s package\.json handling
Entries in \fBoptionalDependencies\fR will override entries of the same name in \fBdependencies\fR, so it\'s usually best to only put in one place\.
.
.SH "engines"
-You can specify the version of
-node that your stuff works on:
+You can specify the version of node that your stuff works on:
.
.IP "" 4
.
.
.IP "" 0
.
+.P
+Note that, unless the user has set the \fBengine\-strict\fR config flag, this
+field is advisory only\.
+.
+.SH "engineStrict"
+If you are sure that your module will \fIdefinitely not\fR run properly on
+versions of Node/npm other than those specified in the \fBengines\fR hash,
+then you can set \fB"engineStrict": true\fR in your package\.json file\.
+This will override the user\'s \fBengine\-strict\fR config setting\.
+.
+.P
+Please do not do this unless you are really very very sure\. If your
+engines hash is something overly restrictive, you can quite easily and
+inadvertently lock yourself into obscurity and prevent your users from
+updating to new versions of Node\. Consider this choice carefully\. If
+people abuse it, it will be removed in a future version of npm\.
+.
.SH "os"
You can specify which operating systems your
module will run on:
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LINK" "1" "June 2012" "" ""
+.TH "NPM\-LINK" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-link\fR \-\- Symlink a package folder
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LS" "1" "June 2012" "" ""
+.TH "NPM\-LS" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-ls\fR \-\- List installed packages
.SH "SYNOPSIS"
.
.nf
-npm list
-npm ls
-npm la
-npm ll
+npm list [<pkg> \.\.\.]
+npm ls [<pkg> \.\.\.]
+npm la [<pkg> \.\.\.]
+npm ll [<pkg> \.\.\.]
.
.fi
.
installed, as well as their dependencies, in a tree\-structure\.
.
.P
-It does not take positional arguments, though you may set config flags
-like with any other command, such as \fB\-g\fR to list global packages\.
+Positional arguments are \fBname@version\-range\fR identifiers, which will
+limit the results to only the paths to the packages named\. Note that
+nested packages will \fIalso\fR show the paths to the specified packages\.
+For example, running \fBnpm ls promzard\fR in npm\'s source tree will show:
+.
+.IP "" 4
+.
+.nf
+npm@1.1.37 /path/to/npm
+└─┬ init\-package\-json@0\.0\.4
+ └── promzard@0\.1\.5
+.
+.fi
+.
+.IP "" 0
.
.P
-It will print out extraneous, missing, and invalid packages\.
+It will show print out extraneous, missing, and invalid packages\.
.
.P
When run as \fBll\fR or \fBla\fR, it shows extended information by default\.
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM" "1" "June 2012" "" ""
+.TH "NPM" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm\fR \-\- node package manager
.fi
.
.SH "VERSION"
-1.1.24
+1.1.37
.
.SH "DESCRIPTION"
npm is the package manager for the Node JavaScript platform\. It puts
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-OUTDATED" "1" "June 2012" "" ""
+.TH "NPM\-OUTDATED" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-outdated\fR \-\- Check for outdated packages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-OWNER" "1" "June 2012" "" ""
+.TH "NPM\-OWNER" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-owner\fR \-\- Manage package owners
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PACK" "1" "June 2012" "" ""
+.TH "NPM\-PACK" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-pack\fR \-\- Create a tarball from a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PREFIX" "1" "June 2012" "" ""
+.TH "NPM\-PREFIX" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-prefix\fR \-\- Display prefix
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PRUNE" "1" "June 2012" "" ""
+.TH "NPM\-PRUNE" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-prune\fR \-\- Remove extraneous packages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PUBLISH" "1" "June 2012" "" ""
+.TH "NPM\-PUBLISH" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-publish\fR \-\- Publish a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-REBUILD" "1" "June 2012" "" ""
+.TH "NPM\-REBUILD" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-rebuild\fR \-\- Rebuild a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-REGISTRY" "1" "June 2012" "" ""
+.TH "NPM\-REGISTRY" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-registry\fR \-\- The JavaScript Package Registry
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-REMOVAL" "1" "June 2012" "" ""
+.TH "NPM\-REMOVAL" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-removal\fR \-\- Cleaning the Slate
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RESTART" "1" "June 2012" "" ""
+.TH "NPM\-RESTART" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-restart\fR \-\- Start a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-ROOT" "1" "June 2012" "" ""
+.TH "NPM\-ROOT" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-root\fR \-\- Display npm root
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RUN\-SCRIPT" "1" "June 2012" "" ""
+.TH "NPM\-RUN\-SCRIPT" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-run-script\fR \-\- Run arbitrary package scripts
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SCRIPTS" "1" "June 2012" "" ""
+.TH "NPM\-SCRIPTS" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-scripts\fR \-\- How npm handles the "scripts" field
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SEARCH" "1" "June 2012" "" ""
+.TH "NPM\-SEARCH" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-search\fR \-\- Search for packages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SEMVER" "1" "June 2012" "" ""
+.TH "NPM\-SEMVER" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-semver\fR \-\- The semantic versioner for npm
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SHRINKWRAP" "1" "June 2012" "" ""
+.TH "NPM\-SHRINKWRAP" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-shrinkwrap\fR \-\- Lock down dependency versions
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-STAR" "1" "June 2012" "" ""
+.TH "NPM\-STAR" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-star\fR \-\- Mark your favorite packages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-START" "1" "June 2012" "" ""
+.TH "NPM\-START" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-start\fR \-\- Start a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-STOP" "1" "June 2012" "" ""
+.TH "NPM\-STOP" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-stop\fR \-\- Stop a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SUBMODULE" "1" "June 2012" "" ""
+.TH "NPM\-SUBMODULE" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-submodule\fR \-\- Add a package as a git submodule
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-TAG" "1" "June 2012" "" ""
+.TH "NPM\-TAG" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-tag\fR \-\- Tag a published version
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-TEST" "1" "June 2012" "" ""
+.TH "NPM\-TEST" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-test\fR \-\- Test a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RM" "1" "June 2012" "" ""
+.TH "NPM\-RM" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-rm\fR \-\- Remove a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UNPUBLISH" "1" "June 2012" "" ""
+.TH "NPM\-UNPUBLISH" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-unpublish\fR \-\- Remove a package from the registry
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UPDATE" "1" "June 2012" "" ""
+.TH "NPM\-UPDATE" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-update\fR \-\- Update a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-VERSION" "1" "June 2012" "" ""
+.TH "NPM\-VERSION" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-version\fR \-\- Bump a package version
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-VIEW" "1" "June 2012" "" ""
+.TH "NPM\-VIEW" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-view\fR \-\- View registry info
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-WHOAMI" "1" "June 2012" "" ""
+.TH "NPM\-WHOAMI" "1" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-whoami\fR \-\- Display npm username
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BIN" "3" "June 2012" "" ""
+.TH "NPM\-BIN" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-bin\fR \-\- Display npm bin folder
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BUGS" "3" "June 2012" "" ""
+.TH "NPM\-BUGS" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-bugs\fR \-\- Bugs for a package in a web browser maybe
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-COMMANDS" "3" "June 2012" "" ""
+.TH "NPM\-COMMANDS" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-commands\fR \-\- npm commands
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CONFIG" "3" "June 2012" "" ""
+.TH "NPM\-CONFIG" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-config\fR \-\- Manage the npm configuration files
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DEPRECATE" "3" "June 2012" "" ""
+.TH "NPM\-DEPRECATE" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-deprecate\fR \-\- Deprecate a version of a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DOCS" "3" "June 2012" "" ""
+.TH "NPM\-DOCS" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-docs\fR \-\- Docs for a package in a web browser maybe
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-EDIT" "3" "June 2012" "" ""
+.TH "NPM\-EDIT" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-edit\fR \-\- Edit an installed package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-EXPLORE" "3" "June 2012" "" ""
+.TH "NPM\-EXPLORE" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-explore\fR \-\- Browse an installed package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-HELP\-SEARCH" "3" "June 2012" "" ""
+.TH "NPM\-HELP\-SEARCH" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-help-search\fR \-\- Search the help pages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "INIT" "3" "June 2012" "" ""
+.TH "INIT" "3" "July 2012" "" ""
.
.SH "NAME"
\fBinit\fR \-\- Interactively create a package\.json file
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-INSTALL" "3" "June 2012" "" ""
+.TH "NPM\-INSTALL" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-install\fR \-\- install a package programmatically
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LINK" "3" "June 2012" "" ""
+.TH "NPM\-LINK" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-link\fR \-\- Symlink a package folder
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LOAD" "3" "June 2012" "" ""
+.TH "NPM\-LOAD" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-load\fR \-\- Load config settings
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LS" "3" "June 2012" "" ""
+.TH "NPM\-LS" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-ls\fR \-\- List installed packages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM" "3" "June 2012" "" ""
+.TH "NPM" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm\fR \-\- node package manager
.fi
.
.SH "VERSION"
-1.1.24
+1.1.37
.
.SH "DESCRIPTION"
This is the API documentation for npm\.
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-OUTDATED" "3" "June 2012" "" ""
+.TH "NPM\-OUTDATED" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-outdated\fR \-\- Check for outdated packages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-OWNER" "3" "June 2012" "" ""
+.TH "NPM\-OWNER" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-owner\fR \-\- Manage package owners
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PACK" "3" "June 2012" "" ""
+.TH "NPM\-PACK" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-pack\fR \-\- Create a tarball from a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PREFIX" "3" "June 2012" "" ""
+.TH "NPM\-PREFIX" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-prefix\fR \-\- Display prefix
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PRUNE" "3" "June 2012" "" ""
+.TH "NPM\-PRUNE" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-prune\fR \-\- Remove extraneous packages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PUBLISH" "3" "June 2012" "" ""
+.TH "NPM\-PUBLISH" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-publish\fR \-\- Publish a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-REBUILD" "3" "June 2012" "" ""
+.TH "NPM\-REBUILD" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-rebuild\fR \-\- Rebuild a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RESTART" "3" "June 2012" "" ""
+.TH "NPM\-RESTART" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-restart\fR \-\- Start a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-ROOT" "3" "June 2012" "" ""
+.TH "NPM\-ROOT" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-root\fR \-\- Display npm root
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RUN\-SCRIPT" "3" "June 2012" "" ""
+.TH "NPM\-RUN\-SCRIPT" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-run-script\fR \-\- Run arbitrary package scripts
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SEARCH" "3" "June 2012" "" ""
+.TH "NPM\-SEARCH" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-search\fR \-\- Search for packages
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SHRINKWRAP" "3" "June 2012" "" ""
+.TH "NPM\-SHRINKWRAP" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-shrinkwrap\fR \-\- programmatically generate package shrinkwrap file
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-START" "3" "June 2012" "" ""
+.TH "NPM\-START" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-start\fR \-\- Start a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-STOP" "3" "June 2012" "" ""
+.TH "NPM\-STOP" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-stop\fR \-\- Stop a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SUBMODULE" "3" "June 2012" "" ""
+.TH "NPM\-SUBMODULE" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-submodule\fR \-\- Add a package as a git submodule
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-TAG" "3" "June 2012" "" ""
+.TH "NPM\-TAG" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-tag\fR \-\- Tag a published version
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-TEST" "3" "June 2012" "" ""
+.TH "NPM\-TEST" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-test\fR \-\- Test a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UNINSTALL" "3" "June 2012" "" ""
+.TH "NPM\-UNINSTALL" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-uninstall\fR \-\- uninstall a package programmatically
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UNPUBLISH" "3" "June 2012" "" ""
+.TH "NPM\-UNPUBLISH" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-unpublish\fR \-\- Remove a package from the registry
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UPDATE" "3" "June 2012" "" ""
+.TH "NPM\-UPDATE" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-update\fR \-\- Update a package
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-VERSION" "3" "June 2012" "" ""
+.TH "NPM\-VERSION" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-version\fR \-\- Bump a package version
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-VIEW" "3" "June 2012" "" ""
+.TH "NPM\-VIEW" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-view\fR \-\- View registry info
.\" Generated with Ronnjs/v0.1
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-WHOAMI" "3" "June 2012" "" ""
+.TH "NPM\-WHOAMI" "3" "July 2012" "" ""
.
.SH "NAME"
\fBnpm-whoami\fR \-\- Display npm username
fancy things in a terminal window, like render text in colors, delete characters,
lines, the entire window, or hide and show the cursor, and lots more!
-The code for the example in the screenshot above can be found in the `examples`
-directory.
+The code for the example in the screenshot above can be found in the
+`examples/imgcat` directory.
#### Features:
* 256 color support for the terminal!
+ * Make a beep sound from your terminal!
* Works with *any* writable `Stream` instance.
* Allows you to move the cursor anywhere on the terminal window.
* Allows you to delete existing contents from the terminal window.
* Allows you to hide and show the cursor.
* Converts CSS color codes and RGB values into ANSI escape codes.
* Low-level; you are in control of when escape codes are used, it's not abstracted.
- * Optional automatic cleanup of stream by before closing (still TODO).
Installation
, cursor = ansi(process.stdout)
// You can chain your calls forever:
-cursor.red() // Set font color to red
- .bg.blue() // Set background color to blue
- .write('Hello World!') // Write 'Hello World!' to stdout
- .reset() // When a bg color is set, call reset() before
- // writing the trailing \n, to avoid Terminal glitches
- .write('\n') // And a final \n to wrap things up
+cursor
+ .red() // Set font color to red
+ .bg.grey() // Set background color to grey
+ .write('Hello World!') // Write 'Hello World!' to stdout
+ .bg.reset() // Reset the bgcolor before writing the trailing \n,
+ // to avoid Terminal glitches
+ .write('\n') // And a final \n to wrap things up
// Rendering modes are persistent:
-cursor.green().bold()
+cursor.hex('#660000').bold().underline()
// You can use the regular logging functions, text will be green
-console.log('This is green, bold text')
+console.log('This is blood red, bold text')
// To reset just the foreground color:
cursor.fg.reset()
console.log('This will still be bold')
+
+// Clean up after yourself!
+cursor.reset()
```
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.
+
+----------
+
+Additionally:
+
+ * The `yoshi.png` file inside `examples/imgcat` is copyright to Nintendo, Inc.
--- /dev/null
+
+/**
+ * References:
+ *
+ * - http://en.wikipedia.org/wiki/ANSI_escape_code
+ * - http://www.termsys.demon.co.uk/vtansi.htm
+ *
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var emitNewlineEvents = require('./newlines')
+ , prefix = '\033[' // For all escape codes
+ , suffix = 'm' // Only for color codes
+
+/**
+ * The ANSI escape sequences.
+ */
+
+var codes = {
+ up: 'A'
+ , down: 'B'
+ , forward: 'C'
+ , back: 'D'
+ , nextLine: 'E'
+ , previousLine: 'F'
+ , horizontalAbsolute: 'G'
+ , eraseData: 'J'
+ , eraseLine: 'K'
+ , scrollUp: 'S'
+ , scrollDown: 'T'
+ , savePosition: 's'
+ , restorePosition: 'u'
+ , queryPosition: '6n'
+ , hide: '?25l'
+ , show: '?25h'
+}
+
+/**
+ * Rendering ANSI codes.
+ */
+
+var styles = {
+ bold: 1
+ , italic: 3
+ , underline: 4
+ , inverse: 7
+}
+
+/**
+ * The negating ANSI code for the rendering modes.
+ */
+
+var reset = {
+ bold: 22
+ , italic: 23
+ , underline: 24
+ , inverse: 27
+}
+
+/**
+ * The standard, styleable ANSI colors.
+ */
+
+var colors = {
+ white: 37
+ , black: 30
+ , blue: 34
+ , cyan: 36
+ , green: 32
+ , magenta: 35
+ , red: 31
+ , yellow: 33
+ , grey: 90
+ , brightBlack: 90
+ , brightRed: 91
+ , brightGreen: 92
+ , brightYellow: 93
+ , brightBlue: 94
+ , brightMagenta: 95
+ , brightCyan: 96
+ , brightWhite: 97
+}
+
+
+/**
+ * Creates a Cursor instance based off the given `writable stream` instance.
+ */
+
+function ansi (stream, options) {
+ if (stream._ansicursor) {
+ return stream._ansicursor
+ } else {
+ return stream._ansicursor = new Cursor(stream, options)
+ }
+}
+module.exports = exports = ansi
+
+/**
+ * The `Cursor` class.
+ */
+
+function Cursor (stream, options) {
+ if (!(this instanceof Cursor)) {
+ return new Cursor(stream, options)
+ }
+ if (typeof stream != 'object' || typeof stream.write != 'function') {
+ throw new Error('a valid Stream instance must be passed in')
+ }
+
+ // the stream to use
+ this.stream = stream
+
+ // when 'enabled' is false then all the functions are no-ops except for write()
+ this.enabled = options && options.enabled
+ if (typeof this.enabled === 'undefined') {
+ this.enabled = stream.isTTY
+ }
+ this.enabled = !!this.enabled
+
+ // controls the foreground and background colors
+ this.fg = this.foreground = new Colorer(this, 0)
+ this.bg = this.background = new Colorer(this, 10)
+
+ // defaults
+ this.Bold = false
+ this.Italic = false
+ this.Underline = false
+ this.Inverse = false
+
+ // keep track of the number of "newlines" that get encountered
+ this.newlines = 0
+ emitNewlineEvents(stream)
+ stream.on('newline', function () {
+ this.newlines++
+ }.bind(this))
+}
+exports.Cursor = Cursor
+
+/**
+ * Helper function that calls `write()` on the underlying Stream.
+ * Returns `this` instead of the write() return value to keep
+ * the chaining going.
+ */
+
+Cursor.prototype.write = function () {
+ this.stream.write.apply(this.stream, arguments)
+ return this
+}
+
+
+/**
+ * The `Colorer` class manages both the background and foreground colors.
+ */
+
+function Colorer (cursor, base) {
+ this.current = null
+ this.cursor = cursor
+ this.base = base
+}
+exports.Colorer = Colorer
+
+/**
+ * Write an ANSI color code, ensuring that the same code doesn't get rewritten.
+ */
+
+Colorer.prototype._setColorCode = function setColorCode (code) {
+ var c = String(code)
+ if (this.current === c) return
+ this.cursor.enabled && this.cursor.write(prefix + c + suffix)
+ this.current = c
+ return this
+}
+
+
+/**
+ * Set up the positional ANSI codes.
+ */
+
+Object.keys(codes).forEach(function (name) {
+ var code = String(codes[name])
+ Cursor.prototype[name] = function () {
+ var c = code
+ if (arguments.length > 0) {
+ c = toArray(arguments).map(Math.round).join(';') + code
+ }
+ this.enabled && this.write(prefix + c)
+ return this
+ }
+})
+
+/**
+ * Set up the functions for the rendering ANSI codes.
+ */
+
+Object.keys(styles).forEach(function (style) {
+ var name = style[0].toUpperCase() + style.substring(1)
+ , c = styles[style]
+ , r = reset[style]
+
+ Cursor.prototype[style] = function () {
+ if (this[name]) return
+ this.enabled && this.write(prefix + c + suffix)
+ this[name] = true
+ return this
+ }
+
+ Cursor.prototype['reset' + name] = function () {
+ if (!this[name]) return
+ this.enabled && this.write(prefix + r + suffix)
+ this[name] = false
+ return this
+ }
+})
+
+/**
+ * Setup the functions for the standard colors.
+ */
+
+Object.keys(colors).forEach(function (color) {
+ var code = colors[color]
+
+ Colorer.prototype[color] = function () {
+ this._setColorCode(this.base + code)
+ return this.cursor
+ }
+
+ Cursor.prototype[color] = function () {
+ return this.foreground[color]()
+ }
+})
+
+/**
+ * Makes a beep sound!
+ */
+
+Cursor.prototype.beep = function () {
+ this.enabled && this.write('\007')
+ return this
+}
+
+/**
+ * Moves cursor to specific position
+ */
+
+Cursor.prototype.goto = function (x, y) {
+ x = x | 0
+ y = y | 0
+ this.enabled && this.write(prefix + y + ';' + x + 'H')
+ return this
+}
+
+/**
+ * Resets the color.
+ */
+
+Colorer.prototype.reset = function () {
+ this._setColorCode(this.base + 39)
+ return this.cursor
+}
+
+/**
+ * Resets all ANSI formatting on the stream.
+ */
+
+Cursor.prototype.reset = function () {
+ this.enabled && this.write(prefix + '0' + suffix)
+ this.Bold = false
+ this.Italic = false
+ this.Underline = false
+ this.Inverse = false
+ this.foreground.current = null
+ this.background.current = null
+ return this
+}
+
+/**
+ * Sets the foreground color with the given RGB values.
+ * The closest match out of the 216 colors is picked.
+ */
+
+Colorer.prototype.rgb = function (r, g, b) {
+ var base = this.base + 38
+ , code = rgb(r, g, b)
+ this._setColorCode(base + ';5;' + code)
+ return this.cursor
+}
+
+/**
+ * Same as `cursor.fg.rgb(r, g, b)`.
+ */
+
+Cursor.prototype.rgb = function (r, g, b) {
+ return this.foreground.rgb(r, g, b)
+}
+
+/**
+ * Accepts CSS color codes for use with ANSI escape codes.
+ * For example: `#FF000` would be bright red.
+ */
+
+Colorer.prototype.hex = function (color) {
+ return this.rgb.apply(this, hex(color))
+}
+
+/**
+ * Same as `cursor.fg.hex(color)`.
+ */
+
+Cursor.prototype.hex = function (color) {
+ return this.foreground.hex(color)
+}
+
+
+// UTIL FUNCTIONS //
+
+/**
+ * Translates a 255 RGB value to a 0-5 ANSI RGV value,
+ * then returns the single ANSI color code to use.
+ */
+
+function rgb (r, g, b) {
+ var red = r / 255 * 5
+ , green = g / 255 * 5
+ , blue = b / 255 * 5
+ return rgb5(red, green, blue)
+}
+
+/**
+ * Turns rgb 0-5 values into a single ANSI color code to use.
+ */
+
+function rgb5 (r, g, b) {
+ var red = Math.round(r)
+ , green = Math.round(g)
+ , blue = Math.round(b)
+ return 16 + (red*36) + (green*6) + blue
+}
+
+/**
+ * Accepts a hex CSS color code string (# is optional) and
+ * translates it into an Array of 3 RGB 0-255 values, which
+ * can then be used with rgb().
+ */
+
+function hex (color) {
+ var c = color[0] === '#' ? color.substring(1) : color
+ , r = c.substring(0, 2)
+ , g = c.substring(2, 4)
+ , b = c.substring(4, 6)
+ return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)]
+}
+
+/**
+ * Turns an array-like object into a real array.
+ */
+
+function toArray (a) {
+ var i = 0
+ , l = a.length
+ , rtn = []
+ for (; i<l; i++) {
+ rtn.push(a[i])
+ }
+ return rtn
+}
--- /dev/null
+
+/**
+ * Accepts any node Stream instance and hijacks its "write()" function,
+ * so that it can count any newlines that get written to the output.
+ *
+ * When a '\n' byte is encountered, then a "newline" event will be emitted
+ * on the stream, with no arguments. It is up to the listeners to determine
+ * any necessary deltas required for their use-case.
+ *
+ * Ex:
+ *
+ * var cursor = ansi(process.stdout)
+ * , ln = 0
+ * process.stdout.on('newline', function () {
+ * ln++
+ * })
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var assert = require('assert')
+var NEWLINE = '\n'.charCodeAt(0)
+
+function emitNewlineEvents (stream) {
+ if (stream._emittingNewlines) {
+ // already emitting newline events
+ return
+ }
+
+ var write = stream.write
+
+ stream.write = function (data) {
+ // first write the data
+ var rtn = write.apply(stream, arguments)
+
+ if (stream.listeners('newline').length > 0) {
+ var len = data.length
+ , i = 0
+ // now try to calculate any deltas
+ if (typeof data == 'string') {
+ for (; i<len; i++) {
+ processByte(stream, data.charCodeAt(i))
+ }
+ } else {
+ // buffer
+ for (; i<len; i++) {
+ processByte(stream, data[i])
+ }
+ }
+ }
+
+ return rtn
+ }
+
+ stream._emittingNewlines = true
+}
+module.exports = emitNewlineEvents
+
+
+/**
+ * Processes an individual byte being written to a stream
+ */
+
+function processByte (stream, b) {
+ assert.equal(typeof b, 'number')
+ if (b === NEWLINE) {
+ stream.emit('newline')
+ }
+}
"256",
"stream"
],
- "version": "0.0.4",
+ "version": "0.1.2",
"author": {
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",
"url": "git://github.com/TooTallNate/ansi.js.git"
},
"main": "./lib/ansi.js",
+ "bin": {
+ "beep": "./examples/beep/index.js",
+ "clear": "./examples/clear/index.js",
+ "imgcat": "./examples/imgcat/index.js",
+ "starwars": "./examples/starwars.js"
+ },
"scripts": {
- "test": "mocha --reporter spec",
- "start": "node server.js"
+ "test": "mocha --reporter spec"
},
"devDependencies": {
"mocha": "*",
"canvas": "*"
},
"engines": {
- "node": ">= 0.4.0 && < 0.9.0"
+ "node": "*"
},
"_npmUser": {
"name": "isaacs",
"email": "i@izs.me"
},
- "_id": "ansi@0.0.4",
+ "_id": "ansi@0.1.2",
"dependencies": {},
"optionalDependencies": {},
"_engineSupported": true,
- "_npmVersion": "1.1.23",
+ "_npmVersion": "1.1.24",
"_nodeVersion": "v0.7.10-pre",
"_defaultsLoaded": true,
- "_from": "ansi@0.0.x"
+ "dist": {
+ "shasum": "6139b23459bcd74b04572cf56b36102983d0a7d4"
+ },
+ "_from": "ansi@~0.1.2"
}
--- /dev/null
+# couch-login
+
+This module lets you log into couchdb to get a session token, then make
+requests using that session. It is basically just a thin wrapper around
+[@mikeal's request module](https://github.com/mikeal/request).
+
+This is handy if you want a user to take actions in a couchdb database
+on behalf of a user, without having to store their couchdb username and
+password anywhere. (You do need to store the AuthSession token
+somewhere, though.)
+
+## Usage
+
+```javascript
+var CouchLogin = require('couch-login')
+
+// Nothing about this module is http-server specific of course.
+// You could also use it to do authenticated requests against
+// a couchdb using sessions and storing the token somewhere else.
+
+http.createServer(function (req, res) {
+ var couch = new CouchLogin('http://my-couch.iriscouch.com:5984/')
+
+ // .. look up the token in the user's session or whatever ..
+ // Look at couch.decorate(req, res) for more on doing that
+ // automatically, below.
+
+ if (sessionToken) {
+ // this user already logged in.
+ couch.token = sessionToken
+
+ // now we can do things on their behalf, like:
+ // 1. View their session info.
+ // like doing request.get({ uri: couch + '/_session', ... })
+ // but with the cookie and whatnot
+
+ couch.get('/_session', function (er, resp, data) {
+ // er = some kind of communication error.
+ // resp = response object from the couchdb request.
+ // data = parsed JSON response body.
+ if (er || resp.statusCode !== 200) {
+ res.statusCode = resp.statusCode || 403
+ return res.end('Invalid login or something')
+ }
+
+ // now we have the session info, we know who this user is.
+ // hitting couchdb for this on every request is kinda costly,
+ // so maybe you should store the username wherever you're storing
+ // the sessionToken. RedSess is a good util for this, if you're
+ // into redis. And if you're not into redis, you're crazy,
+ // because it is awesome.
+
+ // now let's get the user record.
+ // note that this will 404 for anyone other than the user,
+ // unless they're a server admin.
+ couch.get('/_users/org.couchdb.user:' + data.userCtx.name, etc)
+
+ // PUTs and DELETEs will also use their session, of course, so
+ // your validate_doc_update's will see their info in userCtx
+ })
+
+ } else {
+ // don't have a sessionToken.
+ // get a username and password from the post body or something.
+ // maybe redirect to a /login page or something to ask for that.
+ var login = { name: name, password: password }
+ couch.login(login, function (er, resp, data) {
+ // again, er is an error, resp is the response obj, data is the json
+ if (er || resp.statusCode !== 200) {
+ res.statusCode = resp.statusCode || 403
+ return res.end('Invalid login or something')
+ }
+
+ // the data is something like
+ // {"ok":true,"name":"testuser","roles":[]}
+ // and couch.token is the token you'll need to save somewhere.
+
+ // at this point, you can start making authenticated requests to
+ // couchdb, or save data in their session, or do whatever it is
+ // that you need to do.
+
+ res.statusCode = 200
+ res.write("Who's got two thumbs and just logged you into couch?\n")
+ setTimeout(function () {
+ res.end("THIS GUY!")
+ }, 500)
+ })
+ }
+})
+```
+
+## Class: CouchLogin
+### new CouchLogin(couchdbUrl, token)
+
+Create a new CouchLogin object bound to the couchdb url.
+
+In addition to these, the `get`, `post`, `put`, and `del` methods all
+proxy to the associated method on [request](https://github.com/mikeal/request).
+
+However, as you'll note in the example above, only the pathname portion
+of the url is required. Urls will be appended to the couchdb url passed
+into the constructor.
+
+If you have to talk to more than one couchdb, then you'll need more than
+one CouchLogin object, for somewhat obvious reasons.
+
+All callbacks get called with the following arguments, which are exactly
+identical to the arguments passed to a `request` callback.
+
+* `er` {Error | null} Set if a communication error happens.
+* `resp` {HTTP Response} The response from the request to couchdb
+* `data` {Object} The parsed JSON data from couch
+
+If the token is the string "anonymous", then it will not attempt to log
+in before making requests. If the token is not "anonymous", then it
+must be an object with the appropriate fields.
+
+### couch.token
+
+* {Object}
+
+An object representing the couchdb session token. (Basically just a
+cookie and a timeout.)
+
+If the token has already timed out, then setting it will have no effect.
+
+### couch.tokenSet
+
+If set, this method is called whenever the token is saved.
+
+For example, you could assign a function to this method to save the
+token into a redis session, a cookie, or in some other database.
+
+Takes a callback which should be called when the token is saved.
+
+### couch.tokenGet
+
+If set, this method is called to look up the token on demand.
+
+The inverse of couch.tokenSet. Takes a callback which is called with
+the `cb(er || null, token)`.
+
+### couch.tokenDel
+
+If set, this method is called to delete the token when it should be
+discarded.
+
+Related to tokenGet and tokenSet. Takes a callback which should be
+called when the token is deleted.
+
+### couch.anonymous()
+
+Return a new CouchLogin object that points at the same couchdb server,
+but doesn't try to log in before making requests.
+
+This is handy for situations where the user is not logged in at the
+moment, but a request needs to be made anyway, and does not require
+authorization.
+
+### couch.login(auth, callback)
+
+* `auth` {Object} The login details
+ * `name` {String}
+ * `password` {String}
+* `callback` {Function}
+
+When the callback is called, the `couch.token` will already have been
+set (assuming it worked!), so subsequent requests will be done as that
+user.
+
+### couch.get(path, callback)
+
+GET the supplied path from the couchdb using the credentials on the
+token.
+
+Fails if the token is invalid or expired.
+
+### couch.del(path, callback)
+
+DELETE the supplied path from the couchdb using the credentials on the
+token.
+
+Fails if the token is invalid or expired.
+
+### couch.post(path, data, callback)
+
+POST the data to the supplied path in the couchdb, using the credentials
+on the token.
+
+Fails if the token is invalid or expired.
+
+### couch.put(path, data, callback)
+
+PUT the data to the supplied path in the couchdb, using the credentials
+on the token.
+
+Fails if the token is invalid or expired.
+
+### couch.changePass(newAuth, callback)
+
+Must already be logged in. Updates the `_users` document with new salt
+and hash, and re-logs in with the new credentials. Callback is called
+with the same arguments as login, or the first step of the process that
+failed.
+
+### couch.signup(userData, callback)
+
+Create a new user account. The userData must contain at least a `name`
+and `password` field. Any additional data will be copied to the user
+record. The `_id`, `name`, `roles`, `type`, `password_sha`, `salt`, and
+`date` fields are generated.
+
+Also signs in as the newly created user, on successful account creation.
+
+### couch.deleteAccount(name, callback)
+
+Deletes a user account. If not logged in as the user, or a server
+admin, then the request will fail.
+
+Note that this immediately invalidates any session tokens for the
+deleted user account. If you are deleting the user's record, then you
+ought to follow this with `couch.logout(callback)` so that it won't try
+to re-use the invalid session.
+
+### couch.logout(callback)
+
+Delete the session out of couchdb. This makes the token permanently
+invalid, and deletes it.
+
+### couch.decorate(req, res)
+
+Set up `req.couch` and `res.couch` as references to this couch login
+instance.
+
+Additionall, if `req.session` or `res.session` is set, then it'll call
+`session.get('couch_token', cb)` as the tokenGet method,
+`session.set('couch_token', token, cb)` as the tokenSet method, and
+`session.del('couch_token', cb)` as the tokenDel method.
+
+This works really nice with
+[RedSess](https://github.com/isaacs/redsess).
--- /dev/null
+var request = require('request')
+, url = require('url')
+, crypto = require('crypto')
+
+module.exports = CouchLogin
+
+function CouchLogin (couch, tok) {
+ if (!(this instanceof CouchLogin)) {
+ return new CouchLogin(couch)
+ }
+
+ if (!couch) throw new Error(
+ "Need to pass a couch url to CouchLogin constructor")
+
+ // having auth completely defeats the purpose
+ couch = url.parse(couch)
+ delete couch.auth
+
+ if (tok === 'anonymous') tok = NaN
+ this.token = tok
+ this.couch = url.format(couch)
+}
+
+CouchLogin.prototype =
+{ get: makeReq('GET')
+, del: makeReq('DELETE')
+, put: makeReq('PUT', true)
+, post: makeReq('POST', true)
+, login: login
+, logout: logout
+, decorate: decorate
+, changePass: changePass
+, signup: signup
+, deleteAccount: deleteAccount
+, anon: anon
+, anonymous: anon
+, valid: valid
+}
+
+Object.defineProperty(CouchLogin.prototype, 'constructor',
+ { value: CouchLogin, enumerable: false })
+
+function decorate (req, res) {
+ req.couch = res.couch = this
+
+ // backed by some sort of set(k,v,cb), get(k,cb) session storage.
+ var session = req.session || res.session || null
+ if (session) {
+ this.tokenGet = function (cb) {
+ session.get('couch_token', cb)
+ }
+
+ // don't worry about it failing. it'll just mean a login next time.
+ this.tokenSet = function (tok, cb) {
+ session.set('couch_token', tok, cb || function () {})
+ }
+
+ this.tokenDel = function (cb) {
+ session.del('couch_token', cb || function () {})
+ }
+ }
+
+ return this
+}
+
+function anon () {
+ return new CouchLogin(this.couch, NaN)
+}
+
+function makeReq (meth, body, f) { return function madeReq (p, d, cb) {
+ f = f || (this.token !== this.token)
+ if (!f && !valid(this.token)) {
+ // lazily get the token.
+ if (this.tokenGet) return this.tokenGet(function (er, tok) {
+ if (er || !valid(tok)) {
+ if (!body) cb = d, d = null
+ return cb(new Error('auth token expired or invalid'))
+ }
+ this.token = tok
+ return madeReq.call(this, p, d, cb)
+ }.bind(this))
+
+ // no getter, no token, no business.
+ return process.nextTick(function () {
+ if (!body) cb = d, d = null
+ cb(new Error('auth token expired or invalid'))
+ })
+ }
+
+ if (!body) cb = d, d = null
+
+ var h = {}
+ , u = url.resolve(this.couch, p)
+ , req = { uri: u, headers: h, json: true, body: d, method: meth }
+
+ if (this.token) {
+ h.cookie = 'AuthSession=' + this.token.AuthSession
+ }
+
+ request(req, function (er, res, data) {
+ // update cookie.
+ if (er || res.statusCode !== 200) return cb(er, res, data)
+ addToken.call(this, res)
+ return cb(er, res, data)
+ }.bind(this))
+}}
+
+function login (auth, cb) {
+ var a = { name: auth.name, password: auth.password }
+ makeReq('post', true, true).call(this, '/_session', a, cb)
+}
+
+function changePass (auth, cb) {
+ if (!auth.name || !auth.password) return cb(new Error('invalid auth'))
+
+ var u = '/_users/org.couchdb.user:' + auth.name
+ this.get(u, function (er, res, data) {
+ if (er || res.statusCode !== 200) return cb(er, res, data)
+
+ // copy any other keys we're setting here.
+ // note that name, password_sha, salt, and date
+ // are all set explicitly below.
+ Object.keys(auth).filter(function (k) {
+ return k.charAt(0) !== '_'
+ && k !== 'password'
+ && k !== 'password_sha'
+ && k !== 'salt'
+ }).forEach(function (k) {
+ data[k] = auth[k]
+ })
+
+ var newSalt = crypto.randomBytes(30).toString('hex')
+ , newPass = auth.password
+ , newSha = sha(newPass + newSalt)
+
+ data.password_sha = newSha
+ data.salt = newSalt
+ data.date = new Date().toISOString()
+
+ this.put(u + '?rev=' + data._rev, data, function (er, res, data) {
+ if (er || res.statusCode >= 400) return cb(er, res, data)
+ this.login(auth, cb)
+ }.bind(this))
+ }.bind(this))
+}
+
+// They said that there should probably be a warning before
+// deleting the user's whole account, so here it is:
+//
+// WATCH OUT!
+function deleteAccount (name, cb) {
+ var u = '/_users/org.couchdb.user:' + name
+ this.get(u, thenPut.bind(this))
+
+ function thenPut (er, res, data) {
+ if (er || res.statusCode !== 200) {
+ return cb(er, res, data)
+ }
+
+ // user accts can't be just DELETE'd by non-admins
+ // so we take the existing doc and just slap a _deleted
+ // flag on it to fake it. Works the same either way
+ // in couch.
+ data._deleted = true
+ this.put(u + '?rev=' + data._rev, data, cb)
+ }
+}
+
+
+
+function signup (auth, cb) {
+ if (this.token) return this.logout(function (er, res, data) {
+ if (er || res.statusCode !== 200) {
+ return cb(er, res, data)
+ }
+
+ if (this.token) {
+ return cb(new Error('failed to delete token'), res, data)
+ }
+
+ this.signup(auth, cb)
+ }.bind(this))
+
+ // make a new user record.
+ var newSalt = crypto.randomBytes(30).toString('hex')
+ , newSha = sha(auth.password + newSalt)
+ , user = { _id: 'org.couchdb.user:' + auth.name
+ , name: auth.name
+ , roles: []
+ , type: 'user'
+ , password_sha: newSha
+ , salt: newSalt
+ , date: new Date().toISOString() }
+
+ Object.keys(auth).forEach(function (k) {
+ if (k === 'name' || k === 'password') return
+ user[k] = auth[k]
+ })
+
+ var u = '/_users/' + user._id
+ makeReq('put', true, true).call(this, u, user, function (er, res, data) {
+ if (er || res.statusCode >= 400) {
+ return cb(er, res, data)
+ }
+
+ // it worked! log in as that user and get their record
+ this.login(auth, function (er, res, data) {
+ if (er || (res && res.statusCode >= 400) || data && data.error) {
+ return cb(er, res, data)
+ }
+ this.get(u, cb)
+ }.bind(this))
+ }.bind(this))
+}
+
+function addToken (res) {
+ // attach the token, if a new one was provided.
+ var sc = res.headers['set-cookie']
+ if (!sc) return
+ if (!Array.isArray(sc)) sc = [sc]
+
+ sc = sc.filter(function (c) {
+ return c.match(/^AuthSession=/)
+ })[0]
+
+ if (!sc.length) return
+
+ sc = sc.split(/\s*;\s*/).map(function (p) {
+ return p.split('=')
+ }).reduce(function (set, p) {
+ var k = p[0] === 'AuthSession' ? p[0] : p[0].toLowerCase()
+ , v = k === 'expires' ? Date.parse(p[1])
+ : p[1] === '' || p[1] === undefined ? true // HttpOnly
+ : p[1]
+ set[k] = v
+ return set
+ }, {})
+
+ if (sc.hasOwnProperty('max-age')) {
+ var ma = sc['max-age']
+ sc.expires = (ma <= 0) ? 0 : Date.now() + (ma * 1000)
+ delete sc['max-age']
+ }
+
+ this.token = sc
+ if (this.tokenSet) this.tokenSet(this.token)
+}
+
+
+function logout (cb) {
+ if (!this.token && this.tokenGet) {
+ return this.tokenGet(function (er, tok) {
+ if (er || !tok) return cb()
+ this.token = tok
+ this.logout(cb)
+ }.bind(this))
+ }
+
+ if (!valid(this.token)) {
+ this.token = null
+ if (this.tokenDel) this.tokenDel()
+ return process.nextTick(cb)
+ }
+
+ var h = { cookie: 'AuthSession=' + this.token.AuthSession }
+ , u = url.resolve(this.couch, '/_session')
+ , req = { uri: u, headers: h, json: true }
+
+ request.del(req, function (er, res, data) {
+ if (er || res.statusCode !== 200) {
+ return cb(er, res, data)
+ }
+
+ this.token = null
+ if (this.tokenDel) this.tokenDel()
+ cb(er, res, data)
+ }.bind(this))
+}
+
+function valid (token) {
+ var d = token && token.expires
+ return token && token.expires > Date.now()
+}
+
+function sha (s) {
+ return crypto.createHash("sha1").update(s).digest("hex")
+}
--- /dev/null
+{
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "name": "couch-login",
+ "description": "A module for doing logged-in requests to a couchdb server",
+ "version": "0.1.6",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/couch-login.git"
+ },
+ "main": "couch-login.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {
+ "request": "~2.9.202"
+ },
+ "devDependencies": {
+ "tap": "~0.2.4"
+ },
+ "readme": "# couch-login\n\nThis module lets you log into couchdb to get a session token, then make\nrequests using that session. It is basically just a thin wrapper around\n[@mikeal's request module](https://github.com/mikeal/request).\n\nThis is handy if you want a user to take actions in a couchdb database\non behalf of a user, without having to store their couchdb username and\npassword anywhere. (You do need to store the AuthSession token\nsomewhere, though.)\n\n## Usage\n\n```javascript\nvar CouchLogin = require('couch-login')\n\n// Nothing about this module is http-server specific of course.\n// You could also use it to do authenticated requests against\n// a couchdb using sessions and storing the token somewhere else.\n\nhttp.createServer(function (req, res) {\n var couch = new CouchLogin('http://my-couch.iriscouch.com:5984/')\n\n // .. look up the token in the user's session or whatever ..\n // Look at couch.decorate(req, res) for more on doing that\n // automatically, below.\n\n if (sessionToken) {\n // this user already logged in.\n couch.token = sessionToken\n\n // now we can do things on their behalf, like:\n // 1. View their session info.\n // like doing request.get({ uri: couch + '/_session', ... })\n // but with the cookie and whatnot\n\n couch.get('/_session', function (er, resp, data) {\n // er = some kind of communication error.\n // resp = response object from the couchdb request.\n // data = parsed JSON response body.\n if (er || resp.statusCode !== 200) {\n res.statusCode = resp.statusCode || 403\n return res.end('Invalid login or something')\n }\n\n // now we have the session info, we know who this user is.\n // hitting couchdb for this on every request is kinda costly,\n // so maybe you should store the username wherever you're storing\n // the sessionToken. RedSess is a good util for this, if you're\n // into redis. And if you're not into redis, you're crazy,\n // because it is awesome.\n\n // now let's get the user record.\n // note that this will 404 for anyone other than the user,\n // unless they're a server admin.\n couch.get('/_users/org.couchdb.user:' + data.userCtx.name, etc)\n\n // PUTs and DELETEs will also use their session, of course, so\n // your validate_doc_update's will see their info in userCtx\n })\n\n } else {\n // don't have a sessionToken.\n // get a username and password from the post body or something.\n // maybe redirect to a /login page or something to ask for that.\n var login = { name: name, password: password }\n couch.login(login, function (er, resp, data) {\n // again, er is an error, resp is the response obj, data is the json\n if (er || resp.statusCode !== 200) {\n res.statusCode = resp.statusCode || 403\n return res.end('Invalid login or something')\n }\n\n // the data is something like\n // {\"ok\":true,\"name\":\"testuser\",\"roles\":[]}\n // and couch.token is the token you'll need to save somewhere.\n\n // at this point, you can start making authenticated requests to\n // couchdb, or save data in their session, or do whatever it is\n // that you need to do.\n\n res.statusCode = 200\n res.write(\"Who's got two thumbs and just logged you into couch?\\n\")\n setTimeout(function () {\n res.end(\"THIS GUY!\")\n }, 500)\n })\n }\n})\n```\n\n## Class: CouchLogin\n### new CouchLogin(couchdbUrl, token)\n\nCreate a new CouchLogin object bound to the couchdb url.\n\nIn addition to these, the `get`, `post`, `put`, and `del` methods all\nproxy to the associated method on [request](https://github.com/mikeal/request).\n\nHowever, as you'll note in the example above, only the pathname portion\nof the url is required. Urls will be appended to the couchdb url passed\ninto the constructor.\n\nIf you have to talk to more than one couchdb, then you'll need more than\none CouchLogin object, for somewhat obvious reasons.\n\nAll callbacks get called with the following arguments, which are exactly\nidentical to the arguments passed to a `request` callback.\n\n* `er` {Error | null} Set if a communication error happens.\n* `resp` {HTTP Response} The response from the request to couchdb\n* `data` {Object} The parsed JSON data from couch\n\nIf the token is the string \"anonymous\", then it will not attempt to log\nin before making requests. If the token is not \"anonymous\", then it\nmust be an object with the appropriate fields.\n\n### couch.token\n\n* {Object}\n\nAn object representing the couchdb session token. (Basically just a\ncookie and a timeout.)\n\nIf the token has already timed out, then setting it will have no effect.\n\n### couch.tokenSet\n\nIf set, this method is called whenever the token is saved.\n\nFor example, you could assign a function to this method to save the\ntoken into a redis session, a cookie, or in some other database.\n\nTakes a callback which should be called when the token is saved.\n\n### couch.tokenGet\n\nIf set, this method is called to look up the token on demand.\n\nThe inverse of couch.tokenSet. Takes a callback which is called with\nthe `cb(er || null, token)`.\n\n### couch.tokenDel\n\nIf set, this method is called to delete the token when it should be\ndiscarded.\n\nRelated to tokenGet and tokenSet. Takes a callback which should be\ncalled when the token is deleted.\n\n### couch.anonymous()\n\nReturn a new CouchLogin object that points at the same couchdb server,\nbut doesn't try to log in before making requests.\n\nThis is handy for situations where the user is not logged in at the\nmoment, but a request needs to be made anyway, and does not require\nauthorization.\n\n### couch.login(auth, callback)\n\n* `auth` {Object} The login details\n * `name` {String}\n * `password` {String}\n* `callback` {Function}\n\nWhen the callback is called, the `couch.token` will already have been\nset (assuming it worked!), so subsequent requests will be done as that\nuser.\n\n### couch.get(path, callback)\n\nGET the supplied path from the couchdb using the credentials on the\ntoken.\n\nFails if the token is invalid or expired.\n\n### couch.del(path, callback)\n\nDELETE the supplied path from the couchdb using the credentials on the\ntoken.\n\nFails if the token is invalid or expired.\n\n### couch.post(path, data, callback)\n\nPOST the data to the supplied path in the couchdb, using the credentials\non the token.\n\nFails if the token is invalid or expired.\n\n### couch.put(path, data, callback)\n\nPUT the data to the supplied path in the couchdb, using the credentials\non the token.\n\nFails if the token is invalid or expired.\n\n### couch.changePass(newAuth, callback)\n\nMust already be logged in. Updates the `_users` document with new salt\nand hash, and re-logs in with the new credentials. Callback is called\nwith the same arguments as login, or the first step of the process that\nfailed.\n\n### couch.signup(userData, callback)\n\nCreate a new user account. The userData must contain at least a `name`\nand `password` field. Any additional data will be copied to the user\nrecord. The `_id`, `name`, `roles`, `type`, `password_sha`, `salt`, and\n`date` fields are generated.\n\nAlso signs in as the newly created user, on successful account creation.\n\n### couch.deleteAccount(name, callback)\n\nDeletes a user account. If not logged in as the user, or a server\nadmin, then the request will fail.\n\nNote that this immediately invalidates any session tokens for the\ndeleted user account. If you are deleting the user's record, then you\nought to follow this with `couch.logout(callback)` so that it won't try\nto re-use the invalid session.\n\n### couch.logout(callback)\n\nDelete the session out of couchdb. This makes the token permanently\ninvalid, and deletes it.\n\n### couch.decorate(req, res)\n\nSet up `req.couch` and `res.couch` as references to this couch login\ninstance.\n\nAdditionall, if `req.session` or `res.session` is set, then it'll call\n`session.get('couch_token', cb)` as the tokenGet method,\n`session.set('couch_token', token, cb)` as the tokenSet method, and\n`session.del('couch_token', cb)` as the tokenDel method.\n\nThis works really nice with\n[RedSess](https://github.com/isaacs/redsess).\n",
+ "_id": "couch-login@0.1.6",
+ "_from": "couch-login@~0.1.6"
+}
--- /dev/null
+Copyright (c) Isaac Z. Schlueter
+All rights reserved.
+
+The BSD License
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+Copyright (c) Isaac Z. Schlueter
+All rights reserved.
+
+The BSD License
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
return m.replace(/\/$/, "")
}
return m
- })
+ }, this)
}
if (this.debug) console.error("emitting end", all)
--- /dev/null
+{
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "name": "glob",
+ "description": "a little globber",
+ "version": "3.1.10",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/node-glob.git"
+ },
+ "main": "glob.js",
+ "engines": {
+ "node": "*"
+ },
+ "dependencies": {
+ "minimatch": "0.2",
+ "graceful-fs": "~1.1.2",
+ "inherits": "1"
+ },
+ "devDependencies": {
+ "tap": "~0.2.3",
+ "mkdirp": "0",
+ "rimraf": "1"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "license": "BSD",
+ "readme": "# Glob\n\nThis is a glob implementation in JavaScript. It uses the `minimatch`\nlibrary to do its matching.\n\n## Attention: node-glob users!\n\nThe API has changed dramatically between 2.x and 3.x. This library is\nnow 100% JavaScript, and the integer flags have been replaced with an\noptions object.\n\nAlso, there's an event emitter class, proper tests, and all the other\nthings you've come to expect from node modules.\n\nAnd best of all, no compilation!\n\n## Usage\n\n```javascript\nvar glob = require(\"glob\")\n\n// options is optional\nglob(\"**/*.js\", options, function (er, files) {\n // files is an array of filenames.\n // If the `nonull` option is set, and nothing\n // was found, then files is [\"**/*.js\"]\n // er is an error object or null.\n})\n```\n\n## Features\n\nPlease see the [minimatch\ndocumentation](https://github.com/isaacs/minimatch) for more details.\n\nSupports these glob features:\n\n* Brace Expansion\n* Extended glob matching\n* \"Globstar\" `**` matching\n\nSee:\n\n* `man sh`\n* `man bash`\n* `man 3 fnmatch`\n* `man 5 gitignore`\n* [minimatch documentation](https://github.com/isaacs/minimatch)\n\n## glob(pattern, [options], cb)\n\n* `pattern` {String} Pattern to be matched\n* `options` {Object}\n* `cb` {Function}\n * `err` {Error | null}\n * `matches` {Array<String>} filenames found matching the pattern\n\nPerform an asynchronous glob search.\n\n## glob.sync(pattern, [options]\n\n* `pattern` {String} Pattern to be matched\n* `options` {Object}\n* return: {Array<String>} filenames found matching the pattern\n\nPerform a synchronous glob search.\n\n## Class: glob.Glob\n\nCreate a Glob object by instanting the `glob.Glob` class.\n\n```javascript\nvar Glob = require(\"glob\").Glob\nvar mg = new Glob(pattern, options, cb)\n```\n\nIt's an EventEmitter, and starts walking the filesystem to find matches\nimmediately.\n\n### new glob.Glob(pattern, [options], [cb])\n\n* `pattern` {String} pattern to search for\n* `options` {Object}\n* `cb` {Function} Called when an error occurs, or matches are found\n * `err` {Error | null}\n * `matches` {Array<String>} filenames found matching the pattern\n\nNote that if the `sync` flag is set in the options, then matches will\nbe immediately available on the `g.found` member.\n\n### Properties\n\n* `minimatch` The minimatch object that the glob uses.\n* `options` The options object passed in.\n* `error` The error encountered. When an error is encountered, the\n glob object is in an undefined state, and should be discarded.\n* `aborted` Boolean which is set to true when calling `abort()`. There\n is no way at this time to continue a glob search after aborting, but\n you can re-use the statCache to avoid having to duplicate syscalls.\n\n### Events\n\n* `end` When the matching is finished, this is emitted with all the\n matches found. If the `nonull` option is set, and no match was found,\n then the `matches` list contains the original pattern. The matches\n are sorted, unless the `nosort` flag is set.\n* `match` Every time a match is found, this is emitted with the matched.\n* `error` Emitted when an unexpected error is encountered, or whenever\n any fs error occurs if `options.strict` is set.\n* `abort` When `abort()` is called, this event is raised.\n\n### Methods\n\n* `abort` Stop the search.\n\n### Options\n\nAll the options that can be passed to Minimatch can also be passed to\nGlob to change pattern matching behavior. Also, some have been added,\nor have glob-specific ramifications.\n\nAll options are false by default, unless otherwise noted.\n\nAll options are added to the glob object, as well.\n\n* `cwd` The current working directory in which to search. Defaults\n to `process.cwd()`.\n* `root` The place where patterns starting with `/` will be mounted\n onto. Defaults to `path.resolve(options.cwd, \"/\")` (`/` on Unix\n systems, and `C:\\` or some such on Windows.)\n* `nomount` By default, a pattern starting with a forward-slash will be\n \"mounted\" onto the root setting, so that a valid filesystem path is\n returned. Set this flag to disable that behavior.\n* `mark` Add a `/` character to directory matches. Note that this\n requires additional stat calls.\n* `nosort` Don't sort the results.\n* `stat` Set to true to stat *all* results. This reduces performance\n somewhat, and is completely unnecessary, unless `readdir` is presumed\n to be an untrustworthy indicator of file existence. It will cause\n ELOOP to be triggered one level sooner in the case of cyclical\n symbolic links.\n* `silent` When an unusual error is encountered\n when attempting to read a directory, a warning will be printed to\n stderr. Set the `silent` option to true to suppress these warnings.\n* `strict` When an unusual error is encountered\n when attempting to read a directory, the process will just continue on\n in search of other matches. Set the `strict` option to raise an error\n in these cases.\n* `statCache` A cache of results of filesystem information, to prevent\n unnecessary stat calls. While it should not normally be necessary to\n set this, you may pass the statCache from one glob() call to the\n options object of another, if you know that the filesystem will not\n change between calls. (See \"Race Conditions\" below.)\n* `sync` Perform a synchronous glob search.\n* `nounique` In some cases, brace-expanded patterns can result in the\n same file showing up multiple times in the result set. By default,\n this implementation prevents duplicates in the result set.\n Set this flag to disable that behavior.\n* `nonull` Set to never return an empty set, instead returning a set\n containing the pattern itself. This is the default in glob(3).\n* `nocase` Perform a case-insensitive match. Note that case-insensitive\n filesystems will sometimes result in glob returning results that are\n case-insensitively matched anyway, since readdir and stat will not\n raise an error.\n* `debug` Set to enable debug logging in minimatch and glob.\n* `globDebug` Set to enable debug logging in glob, but not minimatch.\n\n## Comparisons to other fnmatch/glob implementations\n\nWhile strict compliance with the existing standards is a worthwhile\ngoal, some discrepancies exist between node-glob and other\nimplementations, and are intentional.\n\nIf the pattern starts with a `!` character, then it is negated. Set the\n`nonegate` flag to suppress this behavior, and treat leading `!`\ncharacters normally. This is perhaps relevant if you wish to start the\npattern with a negative extglob pattern like `!(a|B)`. Multiple `!`\ncharacters at the start of a pattern will negate the pattern multiple\ntimes.\n\nIf a pattern starts with `#`, then it is treated as a comment, and\nwill not match anything. Use `\\#` to match a literal `#` at the\nstart of a line, or set the `nocomment` flag to suppress this behavior.\n\nThe double-star character `**` is supported by default, unless the\n`noglobstar` flag is set. This is supported in the manner of bsdglob\nand bash 4.1, where `**` only has special significance if it is the only\nthing in a path part. That is, `a/**/b` will match `a/x/y/b`, but\n`a/**b` will not. **Note that this is different from the way that `**` is\nhandled by ruby's `Dir` class.**\n\nIf an escaped pattern has no matches, and the `nonull` flag is set,\nthen glob returns the pattern as-provided, rather than\ninterpreting the character escapes. For example,\n`glob.match([], \"\\\\*a\\\\?\")` will return `\"\\\\*a\\\\?\"` rather than\n`\"*a?\"`. This is akin to setting the `nullglob` option in bash, except\nthat it does not resolve escaped pattern characters.\n\nIf brace expansion is not disabled, then it is performed before any\nother interpretation of the glob pattern. Thus, a pattern like\n`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded\n**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are\nchecked for validity. Since those two are valid, matching proceeds.\n\n## Windows\n\n**Please only use forward-slashes in glob expressions.**\n\nThough windows uses either `/` or `\\` as its path separator, only `/`\ncharacters are used by this glob implementation. You must use\nforward-slashes **only** in glob expressions. Back-slashes will always\nbe interpreted as escape characters, not path separators.\n\nResults from absolute patterns such as `/foo/*` are mounted onto the\nroot setting using `path.join`. On windows, this will by default result\nin `/foo/*` matching `C:\\foo\\bar.txt`.\n\n## Race Conditions\n\nGlob searching, by its very nature, is susceptible to race conditions,\nsince it relies on directory walking and such.\n\nAs a result, it is possible that a file that exists when glob looks for\nit may have been deleted or modified by the time it returns the result.\n\nAs part of its internal implementation, this program caches all stat\nand readdir calls that it makes, in order to cut down on system\noverhead. However, this also makes it even more susceptible to races,\nespecially if the statCache object is reused between glob calls.\n\nUsers are thus advised not to use a glob result as a\nguarantee of filesystem state in the face of rapid changes.\nFor the vast majority of operations, this is never a problem.\n",
+ "_id": "glob@3.1.10",
+ "_from": "glob@~3.1.9"
+}
--- /dev/null
+# init-package-json
+
+A node module to get your node module started.
+
+## Usage
+
+```javascript
+var init = require('init-package-json')
+var path = require('path')
+
+// a path to a promzard module. In the event that this file is
+// not found, one will be provided for you.
+var initFile = path.resolve(process.env.HOME, '.npm-init')
+
+// the dir where we're doin stuff.
+var dir = process.cwd()
+
+// extra stuff that gets put into the PromZard module's context.
+// In npm, this is the resolved config object. Exposed as 'config'
+// Optional.
+var configData = { some: 'extra stuff' }
+
+// Any existing stuff from the package.json file is also exposed in the
+// PromZard module as the `package` object. There will also be free
+// vars for:
+// * `filename` path to the package.json file
+// * `basename` the tip of the package dir
+// * `dirname` the parent of the package dir
+
+init(dir, initFile, configData, function (er, data) {
+ // the data's already been written to {dir}/package.json
+ // now you can do stuff with it
+})
+```
+
+Or from the command line:
+
+```
+$ npm-init
+```
+
+See [PromZard](https://github.com/isaacs/promzard) for details about
+what can go in the config file.
--- /dev/null
+var fs = require('fs')
+var path = require('path')
+var glob = require('glob')
+
+// more popular packages should go here, maybe?
+function isTestPkg (p) {
+ return !!p.match(/^(expresso|mocha|tap|coffee-script|coco|streamline)$/)
+}
+
+function niceName (n) {
+ return n.replace(/^node-|[.-]js$/g, '')
+}
+
+function readDeps (test) { return function (cb) {
+ fs.readdir('node_modules', function (er, dir) {
+ if (er) return cb()
+ var deps = {}
+ var n = dir.length
+ dir.forEach(function (d) {
+ if (d.match(/^\./)) return next()
+ if (test !== isTestPkg(d))
+ return next()
+
+ var dp = path.join(dirname, 'node_modules', d, 'package.json')
+ fs.readFile(dp, 'utf8', function (er, p) {
+ if (er) return next()
+ try { p = JSON.parse(p) }
+ catch (e) { return next() }
+ if (!p.version) return next()
+ deps[d] = '~' + p.version
+ return next()
+ })
+ })
+ function next () {
+ if (--n === 0) return cb(null, deps)
+ }
+ })
+}}
+
+
+exports.name = prompt('name', package.name || basename)
+exports.version = prompt('version', package.version || '0.0.0')
+if (!package.description) {
+ exports.description = prompt('description')
+}
+
+if (!package.main) {
+ exports.main = function (cb) {
+ fs.readdir(dirname, function (er, f) {
+ if (er) f = []
+
+ f = f.filter(function (f) {
+ return f.match(/\.js$/)
+ })
+
+ if (f.indexOf('index.js') !== -1)
+ f = 'index.js'
+ else if (f.indexOf('main.js') !== -1)
+ f = 'main.js'
+ else if (f.indexOf(basename + '.js') !== -1)
+ f = basename + '.js'
+ else
+ f = f[0]
+
+ return cb(null, prompt('entry point', f || 'index.js'))
+ })
+ }
+}
+
+if (!package.bin) {
+ exports.bin = function (cb) {
+ fs.readdir(path.resolve(dirname, 'bin'), function (er, d) {
+ // no bins
+ if (er) return cb()
+ // just take the first js file we find there, or nada
+ return cb(null, d.filter(function (f) {
+ return f.match(/\.js$/)
+ })[0])
+ })
+ }
+}
+
+exports.directories = function (cb) {
+ fs.readdir(dirname, function (er, dirs) {
+ if (er) return cb(er)
+ var res = {}
+ dirs.forEach(function (d) {
+ switch (d) {
+ case 'example': case 'examples': return res.example = d
+ case 'test': case 'tests': return res.test = d
+ case 'doc': case 'docs': return res.doc = d
+ case 'man': return res.man = d
+ }
+ })
+ if (Object.keys(res).length === 0) res = undefined
+ return cb(null, res)
+ })
+}
+
+if (!package.dependencies) {
+ exports.dependencies = readDeps(false)
+}
+
+if (!package.devDependencies) {
+ exports.devDependencies = readDeps(true)
+}
+
+// MUST have a test script!
+var s = package.scripts || {}
+var notest = 'echo "Error: no test specified" && exit 1'
+if (!package.scripts) {
+ exports.scripts = function (cb) {
+ fs.readdir(path.join(dirname, 'node_modules'), function (er, d) {
+ setupScripts(d || [], cb)
+ })
+ }
+}
+function setupScripts (d, cb) {
+ // check to see what framework is in use, if any
+ function tx (test) {
+ return test || notest
+ }
+
+ if (!s.test || s.test === notest) {
+ if (d.indexOf('tap') !== -1)
+ s.test = prompt('test command', 'tap test/*.js', tx)
+ else if (d.indexOf('expresso') !== -1)
+ s.test = prompt('test command', 'expresso test', tx)
+ else if (d.indexOf('mocha') !== -1)
+ s.test = prompt('test command', 'mocha', tx)
+ else
+ s.test = prompt('test command', tx)
+ }
+
+ return cb(null, s)
+}
+
+if (!package.repository) {
+ exports.repository = function (cb) {
+ fs.readFile('.git/config', 'utf8', function (er, gconf) {
+ if (er || !gconf) return cb(null, prompt('git repository'))
+
+ gconf = gconf.split(/\r?\n/)
+ var i = gconf.indexOf('[remote "origin"]')
+ if (i !== -1) {
+ var u = gconf[i + 1]
+ if (!u.match(/^\s*url =/)) u = gconf[i + 2]
+ if (!u.match(/^\s*url =/)) u = null
+ else u = u.replace(/^\s*url = /, '')
+ }
+ if (u && u.match(/^git@github.com:/))
+ u = u.replace(/^git@github.com:/, 'git://github.com/')
+
+ return cb(null, prompt('git repository', u))
+ })
+ }
+}
+
+if (!package.keywords) {
+ exports.keywords = prompt('keywords', function (s) {
+ if (!s) return undefined
+ if (Array.isArray(s)) s = s.join(' ')
+ if (typeof s !== 'string') return s
+ return s.split(/[\s,]+/)
+ })
+}
+
+if (!package.author) {
+ exports.author = config['init.author.name']
+ ? {
+ "name" : config['init.author.name'],
+ "email" : config['init.author.email'],
+ "url" : config['init.author.url']
+ }
+ : prompt('author')
+}
+
+exports.license = prompt('license', 'BSD')
--- /dev/null
+
+module.exports = init
+
+var PZ = require('promzard').PromZard
+var path = require('path')
+var def = require.resolve('./default-input.js')
+
+var fs = require('fs')
+var semver = require('semver')
+var read = require('read')
+
+// to validate the data object at the end as a worthwhile package
+// and assign default values for things.
+// readJson.extras(file, data, cb)
+var readJson = require('read-package-json')
+
+function init (dir, input, config, cb) {
+ if (typeof config === 'function')
+ cb = config, config = {}
+ var package = path.resolve(dir, 'package.json')
+ input = path.resolve(input)
+ var pkg
+ var ctx = {}
+
+ var es = readJson.extraSet
+ readJson.extraSet = es.filter(function (fn) {
+ return fn.name !== 'authors' && fn.name !== 'mans'
+ })
+ readJson(package, function (er, d) {
+ readJson.extraSet = es
+
+ if (er) pkg = {}
+ else pkg = d
+
+ ctx.filename = package
+ ctx.dirname = path.dirname(package)
+ ctx.basename = path.basename(ctx.dirname)
+ if (!pkg.version || !semver.valid(pkg.version))
+ delete pkg.version
+
+ ctx.package = pkg
+ ctx.config = config || {}
+
+ // make sure that the input is valid.
+ // if not, use the default
+ var pz = new PZ(input, ctx)
+ pz.backupFile = def
+ pz.on('error', cb)
+ pz.on('data', function (data) {
+ Object.keys(data).forEach(function (k) {
+ if (data[k] !== undefined && data[k] !== null) pkg[k] = data[k]
+ })
+
+ // only do a few of these.
+ // no need for mans or contributors if they're in the files
+ var es = readJson.extraSet
+ readJson.extraSet = es.filter(function (fn) {
+ return fn.name !== 'authors' && fn.name !== 'mans'
+ })
+ readJson.extras(package, pkg, function (er, pkg) {
+ readJson.extraSet = es
+ if (er) return cb(er, pkg)
+ pkg = unParsePeople(pkg)
+ // no need for the readme now.
+ delete pkg.readme
+ // really don't want to have this lying around in the file
+ delete pkg._id
+ var d = JSON.stringify(pkg, null, 2) + '\n'
+ console.log('About to write to %s:\n\n%s\n', package, d)
+ read({prompt:'Is this ok? ', default: 'yes'}, function (er, ok) {
+ if (!ok || ok.toLowerCase().charAt(0) !== 'y') {
+ console.log('Aborted.')
+ } else {
+ fs.writeFile(package, d, 'utf8', function (er) {
+ return cb(er, pkg)
+ })
+ }
+ })
+ })
+ })
+ })
+
+}
+
+// turn the objects into somewhat more humane strings.
+function unParsePeople (data) {
+ if (data.author) data.author = unParsePerson(data.author)
+ ;["maintainers", "contributors"].forEach(function (set) {
+ if (!Array.isArray(data[set])) return;
+ data[set] = data[set].map(unParsePerson)
+ })
+ return data
+}
+
+function unParsePerson (person) {
+ if (typeof person === "string") return person
+ var name = person.name || ""
+ var u = person.url || person.web
+ var url = u ? (" ("+u+")") : ""
+ var e = person.email || person.mail
+ var email = e ? (" <"+e+">") : ""
+ return name+email+url
+}
+
--- /dev/null
+example/npm-init/package.json
--- /dev/null
+# promzard
+
+A reimplementation of @SubStack's
+[prompter](https://github.com/substack/node-prompter), which does not
+use AST traversal.
+
+From another point of view, it's a reimplementation of
+[@Marak](https://github.com/marak)'s
+[wizard](https://github.com/Marak/wizard) which doesn't use schemas.
+
+The goal is a nice drop-in enhancement for `npm init`.
+
+## Usage
+
+```javascript
+var promzard = require('promzard')
+promzard(inputFile, optionalContextAdditions, function (er, data) {
+ // .. you know what you doing ..
+})
+```
+
+In the `inputFile` you can have something like this:
+
+```javascript
+var fs = require('fs')
+module.exports = {
+ "greeting": prompt("Who shall you greet?", "world", function (who) {
+ return "Hello, " + who
+ }),
+ "filename": __filename,
+ "directory": function (cb) {
+ fs.readdir(__dirname, cb)
+ }
+}
+```
+
+When run, promzard will display the prompts and resolve the async
+functions in order, and then either give you an error, or the resolved
+data, ready to be dropped into a JSON file or some other place.
+
+
+### promzard(inputFile, ctx, callback)
+
+The inputFile is just a node module. You can require() things, set
+module.exports, etc. Whatever that module exports is the result, and it
+is walked over to call any functions as described below.
+
+The only caveat is that you must give PromZard the full absolute path
+to the module (you can get this via Node's `require.resolve`.) Also,
+the `prompt` function is injected into the context object, so watch out.
+
+Whatever you put in that `ctx` will of course also be available in the
+module. You can get quite fancy with this, passing in existing configs
+and so on.
+
+### Class: promzard.PromZard(file, ctx)
+
+Just like the `promzard` function, but the EventEmitter that makes it
+all happen. Emits either a `data` event with the data, or a `error`
+event if it blows up.
+
+If `error` is emitted, then `data` never will be.
+
+### prompt(...)
+
+In the promzard input module, you can call the `prompt` function.
+This prompts the user to input some data. The arguments are interpreted
+based on type:
+
+1. `string` The first string encountered is the prompt. The second is
+ the default value.
+2. `function` A transformer function which receives the data and returns
+ something else. More than meets the eye.
+3. `object` The `prompt` member is the prompt, the `default` member is
+ the default value, and the `transform` is the transformer.
+
+Whatever the final value is, that's what will be put on the resulting
+object.
+
+### Functions
+
+If there are any functions on the promzard input module's exports, then
+promzard will call each of them with a callback. This way, your module
+can do asynchronous actions if necessary to validate or ascertain
+whatever needs verification.
+
+The functions are called in the context of the ctx object, and are given
+a single argument, which is a callback that should be called with either
+an error, or the result to assign to that spot.
+
+In the async function, you can also call prompt() and return the result
+of the prompt in the callback.
+
+For example, this works fine in a promzard module:
+
+```
+exports.asyncPrompt = function (cb) {
+ fs.stat(someFile, function (er, st) {
+ // if there's an error, no prompt, just error
+ // otherwise prompt and use the actual file size as the default
+ cb(er, prompt('file size', st.size))
+ })
+}
+```
+
+You can also return other async functions in the async function
+callback. Though that's a bit silly, it could be a handy way to reuse
+functionality in some cases.
+
+### Sync vs Async
+
+The `prompt()` function is not synchronous, though it appears that way.
+It just returns a token that is swapped out when the data object is
+walked over asynchronously later, and returns a token.
+
+For that reason, prompt() calls whose results don't end up on the data
+object are never shown to the user. For example, this will only prompt
+once:
+
+```
+exports.promptThreeTimes = prompt('prompt me once', 'shame on you')
+exports.promptThreeTimes = prompt('prompt me twice', 'um....')
+exports.promptThreeTimes = prompt('you cant prompt me again')
+```
+
+### Isn't this exactly the sort of 'looks sync' that you said was bad about other libraries?
+
+Yeah, sorta. I wouldn't use promzard for anything more complicated than
+a wizard that spits out prompts to set up a config file or something.
+Maybe there are other use cases I haven't considered.
--- /dev/null
+var pz = require('../promzard')
+
+var path = require('path')
+var file = path.resolve(__dirname, 'substack-input.js')
+var ctx = { basename: path.basename(path.dirname(file)) }
+
+pz(file, ctx, function (er, res) {
+ if (er)
+ throw er
+ console.error(JSON.stringify(res, null, 2))
+})
--- /dev/null
+# npm-init
+
+An initter you init wit, innit?
+
+## More stuff here
+
+Blerp derp herp lerg borgle pop munch efemerate baz foo a gandt synergy
+jorka chatt slurm.
--- /dev/null
+var fs = require('fs')
+var path = require('path');
+
+module.exports = {
+ "name" : prompt('name',
+ typeof name === 'undefined'
+ ? basename.replace(/^node-|[.-]js$/g, ''): name),
+ "version" : prompt('version', typeof version !== "undefined"
+ ? version : '0.0.0'),
+ "description" : (function () {
+ if (typeof description !== 'undefined' && description) {
+ return description
+ }
+ var value;
+ try {
+ var src = fs.readFileSync('README.md', 'utf8');
+ value = src.split('\n').filter(function (line) {
+ return /\s+/.test(line)
+ && line.trim() !== basename.replace(/^node-/, '')
+ && !line.trim().match(/^#/)
+ ;
+ })[0]
+ .trim()
+ .replace(/^./, function (c) { return c.toLowerCase() })
+ .replace(/\.$/, '')
+ ;
+ }
+ catch (e) {
+ try {
+ // Wouldn't it be nice if that file mattered?
+ var d = fs.readFileSync('.git/description', 'utf8')
+ } catch (e) {}
+ if (d.trim() && !value) value = d
+ }
+ return prompt('description', value);
+ })(),
+ "main" : (function () {
+ var f
+ try {
+ f = fs.readdirSync(dirname).filter(function (f) {
+ return f.match(/\.js$/)
+ })
+ if (f.indexOf('index.js') !== -1)
+ f = 'index.js'
+ else if (f.indexOf('main.js') !== -1)
+ f = 'main.js'
+ else if (f.indexOf(basename + '.js') !== -1)
+ f = basename + '.js'
+ else
+ f = f[0]
+ } catch (e) {}
+
+ return prompt('entry point', f || 'index.js')
+ })(),
+ "bin" : function (cb) {
+ fs.readdir(dirname + '/bin', function (er, d) {
+ // no bins
+ if (er) return cb()
+ // just take the first js file we find there, or nada
+ return cb(null, d.filter(function (f) {
+ return f.match(/\.js$/)
+ })[0])
+ })
+ },
+ "directories" : function (cb) {
+ fs.readdir('.', function (er, dirs) {
+ if (er) return cb(er)
+ var res = {}
+ dirs.forEach(function (d) {
+ switch (d) {
+ case 'example': case 'examples': return res.example = d
+ case 'test': case 'tests': return res.test = d
+ case 'doc': case 'docs': return res.doc = d
+ case 'man': return res.man = d
+ }
+ })
+ if (Object.keys(res).length === 0) res = undefined
+ return cb(null, res)
+ })
+ },
+ "dependencies" : typeof dependencies !== 'undefined' ? dependencies
+ : function (cb) {
+ fs.readdir('node_modules', function (er, dir) {
+ if (er) return cb()
+ var deps = {}
+ var n = dir.length
+ dir.forEach(function (d) {
+ if (d.match(/^\./)) return next()
+ if (d.match(/^(expresso|mocha|tap|coffee-script|coco|streamline)$/))
+ return next()
+ fs.readFile('node_modules/' + d + '/package.json', function (er, p) {
+ if (er) return next()
+ try { p = JSON.parse(p) } catch (e) { return next() }
+ if (!p.version) return next()
+ deps[d] = '~' + p.version
+ return next()
+ })
+ })
+ function next () {
+ if (--n === 0) return cb(null, deps)
+ }
+ })
+ },
+ "devDependencies" : typeof devDependencies !== 'undefined' ? devDependencies
+ : function (cb) {
+ // same as dependencies but for dev deps
+ fs.readdir('node_modules', function (er, dir) {
+ if (er) return cb()
+ var deps = {}
+ var n = dir.length
+ dir.forEach(function (d) {
+ if (d.match(/^\./)) return next()
+ if (!d.match(/^(expresso|mocha|tap|coffee-script|coco|streamline)$/))
+ return next()
+ fs.readFile('node_modules/' + d + '/package.json', function (er, p) {
+ if (er) return next()
+ try { p = JSON.parse(p) } catch (e) { return next() }
+ if (!p.version) return next()
+ deps[d] = '~' + p.version
+ return next()
+ })
+ })
+ function next () {
+ if (--n === 0) return cb(null, deps)
+ }
+ })
+ },
+ "scripts" : (function () {
+ // check to see what framework is in use, if any
+ try { var d = fs.readdirSync('node_modules') }
+ catch (e) { d = [] }
+ var s = typeof scripts === 'undefined' ? {} : scripts
+
+ if (d.indexOf('coffee-script') !== -1)
+ s.prepublish = prompt('build command',
+ s.prepublish || 'coffee src/*.coffee -o lib')
+
+ var notest = 'echo "Error: no test specified" && exit 1'
+ function tx (test) {
+ return test || notest
+ }
+
+ if (!s.test || s.test === notest) {
+ if (d.indexOf('tap') !== -1)
+ s.test = prompt('test command', 'tap test/*.js', tx)
+ else if (d.indexOf('expresso') !== -1)
+ s.test = prompt('test command', 'expresso test', tx)
+ else if (d.indexOf('mocha') !== -1)
+ s.test = prompt('test command', 'mocha', tx)
+ else
+ s.test = prompt('test command', tx)
+ }
+
+ return s
+
+ })(),
+
+ "repository" : (function () {
+ try { var gconf = fs.readFileSync('.git/config') }
+ catch (e) { gconf = null }
+ if (gconf) {
+ gconf = gconf.split(/\r?\n/)
+ var i = gconf.indexOf('[remote "origin"]')
+ if (i !== -1) {
+ var u = gconf[i + 1]
+ if (!u.match(/^\s*url =/)) u = gconf[i + 2]
+ if (!u.match(/^\s*url =/)) u = null
+ else u = u.replace(/^\s*url = /, '')
+ }
+ if (u && u.match(/^git@github.com:/))
+ u = u.replace(/^git@github.com:/, 'git://github.com/')
+ }
+
+ return prompt('git repository', u)
+ })(),
+
+ "keywords" : prompt(function (s) {
+ if (!s) return undefined
+ if (Array.isArray(s)) s = s.join(' ')
+ if (typeof s !== 'string') return s
+ return s.split(/[\s,]+/)
+ }),
+ "author" : config['init.author.name']
+ ? {
+ "name" : config['init.author.name'],
+ "email" : config['init.author.email'],
+ "url" : config['init.author.url']
+ }
+ : undefined,
+ "license" : prompt('license', 'BSD')
+}
--- /dev/null
+var PZ = require('../../promzard').PromZard
+var path = require('path')
+var input = path.resolve(__dirname, 'init-input.js')
+
+var fs = require('fs')
+var package = path.resolve(__dirname, 'package.json')
+var pkg
+
+fs.readFile(package, 'utf8', function (er, d) {
+ if (er) ctx = {}
+ try { ctx = JSON.parse(d); pkg = JSON.parse(d) }
+ catch (e) { ctx = {} }
+
+ ctx.dirname = path.dirname(package)
+ ctx.basename = path.basename(ctx.dirname)
+ if (!ctx.version) ctx.version = undefined
+
+ // this should be replaced with the npm conf object
+ ctx.config = {}
+
+ console.error('ctx=', ctx)
+
+ var pz = new PZ(input, ctx)
+
+ pz.on('data', function (data) {
+ console.error('pz data', data)
+ if (!pkg) pkg = {}
+ Object.keys(data).forEach(function (k) {
+ if (data[k] !== undefined && data[k] !== null) pkg[k] = data[k]
+ })
+ console.error('package data %s', JSON.stringify(data, null, 2))
+ fs.writeFile(package, JSON.stringify(pkg, null, 2), function (er) {
+ if (er) throw er
+ console.log('ok')
+ })
+ })
+})
--- /dev/null
+{
+ "name": "npm-init",
+ "version": "0.0.0",
+ "description": "an initter you init wit, innit?",
+ "main": "index.js",
+ "scripts": {
+ "test": "asdf"
+ },
+ "license": "BSD"
+}
\ No newline at end of file
--- /dev/null
+module.exports = {
+ "name" : basename.replace(/^node-/, ''),
+ "version" : "0.0.0",
+ "description" : (function (cb) {
+ var fs = require('fs');
+ var value;
+ try {
+ var src = fs.readFileSync('README.markdown', 'utf8');
+ value = src.split('\n').filter(function (line) {
+ return /\s+/.test(line)
+ && line.trim() !== basename.replace(/^node-/, '')
+ ;
+ })[0]
+ .trim()
+ .replace(/^./, function (c) { return c.toLowerCase() })
+ .replace(/\.$/, '')
+ ;
+ }
+ catch (e) {}
+
+ return prompt('description', value);
+ })(),
+ "main" : prompt('entry point', 'index.js'),
+ "bin" : function (cb) {
+ var path = require('path');
+ var fs = require('fs');
+ var exists = fs.exists || path.exists;
+ exists('bin/cmd.js', function (ex) {
+ var bin
+ if (ex) {
+ var bin = {}
+ bin[basename.replace(/^node-/, '')] = 'bin/cmd.js'
+ }
+ cb(null, bin);
+ });
+ },
+ "directories" : {
+ "example" : "example",
+ "test" : "test"
+ },
+ "dependencies" : {},
+ "devDependencies" : {
+ "tap" : "~0.2.5"
+ },
+ "scripts" : {
+ "test" : "tap test/*.js"
+ },
+ "repository" : {
+ "type" : "git",
+ "url" : "git://github.com/substack/" + basename + ".git"
+ },
+ "homepage" : "https://github.com/substack/" + basename,
+ "keywords" : prompt(function (s) { return s.split(/\s+/) }),
+ "author" : {
+ "name" : "James Halliday",
+ "email" : "mail@substack.net",
+ "url" : "http://substack.net"
+ },
+ "license" : "MIT",
+ "engine" : { "node" : ">=0.6" }
+}
--- /dev/null
+{
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "name": "promzard",
+ "description": "A reimplementation of @SubStack's [prompter](https://github.com/substack/node-prompter), which does not use AST traversal.",
+ "version": "0.1.5",
+ "repository": {
+ "url": "git://github.com/isaacs/promzard"
+ },
+ "dependencies": {
+ "read": "0"
+ },
+ "devDependencies": {
+ "tap": "~0.2.5"
+ },
+ "main": "promzard.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "readme": "# promzard\n\nA reimplementation of @SubStack's\n[prompter](https://github.com/substack/node-prompter), which does not\nuse AST traversal.\n\nFrom another point of view, it's a reimplementation of\n[@Marak](https://github.com/marak)'s\n[wizard](https://github.com/Marak/wizard) which doesn't use schemas.\n\nThe goal is a nice drop-in enhancement for `npm init`.\n\n## Usage\n\n```javascript\nvar promzard = require('promzard')\npromzard(inputFile, optionalContextAdditions, function (er, data) {\n // .. you know what you doing ..\n})\n```\n\nIn the `inputFile` you can have something like this:\n\n```javascript\nvar fs = require('fs')\nmodule.exports = {\n \"greeting\": prompt(\"Who shall you greet?\", \"world\", function (who) {\n return \"Hello, \" + who\n }),\n \"filename\": __filename,\n \"directory\": function (cb) {\n fs.readdir(__dirname, cb)\n }\n}\n```\n\nWhen run, promzard will display the prompts and resolve the async\nfunctions in order, and then either give you an error, or the resolved\ndata, ready to be dropped into a JSON file or some other place.\n\n\n### promzard(inputFile, ctx, callback)\n\nThe inputFile is just a node module. You can require() things, set\nmodule.exports, etc. Whatever that module exports is the result, and it\nis walked over to call any functions as described below.\n\nThe only caveat is that you must give PromZard the full absolute path\nto the module (you can get this via Node's `require.resolve`.) Also,\nthe `prompt` function is injected into the context object, so watch out.\n\nWhatever you put in that `ctx` will of course also be available in the\nmodule. You can get quite fancy with this, passing in existing configs\nand so on.\n\n### Class: promzard.PromZard(file, ctx)\n\nJust like the `promzard` function, but the EventEmitter that makes it\nall happen. Emits either a `data` event with the data, or a `error`\nevent if it blows up.\n\nIf `error` is emitted, then `data` never will be.\n\n### prompt(...)\n\nIn the promzard input module, you can call the `prompt` function.\nThis prompts the user to input some data. The arguments are interpreted\nbased on type:\n\n1. `string` The first string encountered is the prompt. The second is\n the default value.\n2. `function` A transformer function which receives the data and returns\n something else. More than meets the eye.\n3. `object` The `prompt` member is the prompt, the `default` member is\n the default value, and the `transform` is the transformer.\n\nWhatever the final value is, that's what will be put on the resulting\nobject.\n\n### Functions\n\nIf there are any functions on the promzard input module's exports, then\npromzard will call each of them with a callback. This way, your module\ncan do asynchronous actions if necessary to validate or ascertain\nwhatever needs verification.\n\nThe functions are called in the context of the ctx object, and are given\na single argument, which is a callback that should be called with either\nan error, or the result to assign to that spot.\n\nIn the async function, you can also call prompt() and return the result\nof the prompt in the callback.\n\nFor example, this works fine in a promzard module:\n\n```\nexports.asyncPrompt = function (cb) {\n fs.stat(someFile, function (er, st) {\n // if there's an error, no prompt, just error\n // otherwise prompt and use the actual file size as the default\n cb(er, prompt('file size', st.size))\n })\n}\n```\n\nYou can also return other async functions in the async function\ncallback. Though that's a bit silly, it could be a handy way to reuse\nfunctionality in some cases.\n\n### Sync vs Async\n\nThe `prompt()` function is not synchronous, though it appears that way.\nIt just returns a token that is swapped out when the data object is\nwalked over asynchronously later, and returns a token.\n\nFor that reason, prompt() calls whose results don't end up on the data\nobject are never shown to the user. For example, this will only prompt\nonce:\n\n```\nexports.promptThreeTimes = prompt('prompt me once', 'shame on you')\nexports.promptThreeTimes = prompt('prompt me twice', 'um....')\nexports.promptThreeTimes = prompt('you cant prompt me again')\n```\n\n### Isn't this exactly the sort of 'looks sync' that you said was bad about other libraries?\n\nYeah, sorta. I wouldn't use promzard for anything more complicated than\na wizard that spits out prompts to set up a config file or something.\nMaybe there are other use cases I haven't considered.\n",
+ "_id": "promzard@0.1.5",
+ "_from": "promzard@~0.1.5"
+}
--- /dev/null
+module.exports = promzard
+promzard.PromZard = PromZard
+
+var fs = require('fs')
+var vm = require('vm')
+var util = require('util')
+var files = {}
+var crypto = require('crypto')
+var EventEmitter = require('events').EventEmitter
+var read = require('read')
+
+var Module = require('module').Module
+var path = require('path')
+
+function promzard (file, ctx, cb) {
+ if (typeof ctx === 'function') cb = ctx, ctx = null;
+ if (!ctx) ctx = {};
+ var pz = new PromZard(file, ctx)
+ pz.on('error', cb)
+ pz.on('data', function (data) {
+ cb(null, data)
+ })
+}
+
+function PromZard (file, ctx) {
+ if (!(this instanceof PromZard))
+ return new PromZard(file, ctx)
+ EventEmitter.call(this)
+ this.file = file
+ this.ctx = ctx
+ this.unique = crypto.randomBytes(8).toString('hex')
+ this.load()
+}
+
+PromZard.prototype = Object.create(
+ EventEmitter.prototype,
+ { constructor: {
+ value: PromZard,
+ readable: true,
+ configurable: true,
+ writable: true,
+ enumerable: false } } )
+
+PromZard.prototype.load = function () {
+ if (files[this.file])
+ return this.loaded()
+
+ fs.readFile(this.file, 'utf8', function (er, d) {
+ if (er && this.backupFile) {
+ this.file = this.backupFile
+ delete this.backupFile
+ return this.load()
+ }
+ if (er)
+ return this.emit('error', this.error = er)
+ files[this.file] = d
+ this.loaded()
+ }.bind(this))
+}
+
+PromZard.prototype.loaded = function () {
+ this.ctx.prompt = this.makePrompt()
+ this.ctx.__filename = this.file
+ this.ctx.__dirname = path.dirname(this.file)
+ this.ctx.__basename = path.basename(this.file)
+ var mod = this.ctx.module = this.makeModule()
+ this.ctx.require = function (path) {
+ return mod.require(path)
+ }
+ this.ctx.require.resolve = function(path) {
+ return Module._resolveFilename(path, mod);
+ }
+ this.ctx.exports = mod.exports
+
+ this.script = this.wrap(files[this.file])
+ var fn = vm.runInThisContext(this.script, this.file)
+ var args = Object.keys(this.ctx).map(function (k) {
+ return this.ctx[k]
+ }.bind(this))
+ try { var res = fn.apply(this.ctx, args) }
+ catch (er) { this.emit('error', er) }
+ if (res &&
+ typeof res === 'object' &&
+ exports === mod.exports &&
+ Object.keys(exports).length === 1) {
+ this.result = res
+ } else {
+ this.result = mod.exports
+ }
+ this.walk()
+}
+
+PromZard.prototype.makeModule = function () {
+ var mod = new Module(this.file, module)
+ mod.loaded = true
+ mod.filename = this.file
+ mod.id = this.file
+ mod.paths = Module._nodeModulePaths(path.dirname(this.file))
+ return mod
+}
+
+PromZard.prototype.wrap = function (body) {
+ var s = '(function( %s ) { %s\n })'
+ var args = Object.keys(this.ctx).join(', ')
+ return util.format(s, args, body)
+}
+
+PromZard.prototype.makePrompt = function () {
+ this.prompts = []
+ return prompt.bind(this)
+ function prompt () {
+ var p, d, t
+ for (var i = 0; i < arguments.length; i++) {
+ var a = arguments[i]
+ if (typeof a === 'string' && p)
+ d = a
+ else if (typeof a === 'string')
+ p = a
+ else if (typeof a === 'function')
+ t = a
+ else if (a && typeof a === 'object') {
+ p = a.prompt || p
+ d = a.default || d
+ t = a.tranform || t
+ }
+ }
+
+ try { return this.unique + '-' + this.prompts.length }
+ finally { this.prompts.push([p, d, t]) }
+ }
+}
+
+PromZard.prototype.walk = function (o, cb) {
+ o = o || this.result
+ cb = cb || function (er, res) {
+ if (er)
+ return this.emit('error', this.error = er)
+ this.result = res
+ return this.emit('data', res)
+ }
+ cb = cb.bind(this)
+ var keys = Object.keys(o)
+ var i = 0
+ var len = keys.length
+
+ L.call(this)
+ function L () {
+ if (this.error)
+ return
+ while (i < len) {
+ var k = keys[i]
+ var v = o[k]
+ i++
+
+ if (v && typeof v === 'object') {
+ return this.walk(v, function (er, res) {
+ if (er) return cb(er)
+ o[k] = res
+ L.call(this)
+ }.bind(this))
+ } else if (v &&
+ typeof v === 'string' &&
+ v.indexOf(this.unique) === 0) {
+ var n = +v.substr(this.unique.length + 1)
+ var prompt = this.prompts[n]
+ if (isNaN(n) || !prompt)
+ continue
+
+ // default to the key
+ if (undefined === prompt[0])
+ prompt[0] = k
+
+ // default to the ctx value, if there is one
+ if (undefined === prompt[1])
+ prompt[1] = this.ctx[k]
+
+ return this.prompt(prompt, function (er, res) {
+ if (er)
+ return this.emit('error', this.error = er);
+ o[k] = res
+ L.call(this)
+ }.bind(this))
+ } else if (typeof v === 'function') {
+ try { return v.call(this.ctx, function (er, res) {
+ if (er)
+ return this.emit('error', this.error = er)
+ o[k] = res
+ // back up so that we process this one again.
+ // this is because it might return a prompt() call in the cb.
+ i --
+ L.call(this)
+ }.bind(this)) }
+ catch (er) { this.emit('error', er) }
+ }
+ }
+ // made it to the end of the loop, maybe
+ if (i >= len)
+ return cb(null, o)
+ }
+}
+
+PromZard.prototype.prompt = function (pdt, cb) {
+ var prompt = pdt[0]
+ var def = pdt[1]
+ var tx = pdt[2]
+
+ if (tx) {
+ cb = function (cb) { return function (er, data) {
+ try { return cb(er, tx(data)) }
+ catch (er) { this.emit('error', er) }
+ }}(cb).bind(this)
+ }
+
+ read({ prompt: prompt + ': ' , default: def }, cb)
+}
+
--- /dev/null
+var tap = require('tap')
+var pz = require('../promzard.js')
+var spawn = require('child_process').spawn
+
+tap.test('run the example', function (t) {
+
+ var example = require.resolve('../example/index.js')
+ var node = process.execPath
+
+ var expect = {
+ "name": "example",
+ "version": "0.0.0",
+ "description": "testing description",
+ "main": "test-entry.js",
+ "directories": {
+ "example": "example",
+ "test": "test"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tap": "~0.2.5"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/substack/example.git"
+ },
+ "homepage": "https://github.com/substack/example",
+ "keywords": [
+ "fugazi",
+ "function",
+ "waiting",
+ "room"
+ ],
+ "author": {
+ "name": "James Halliday",
+ "email": "mail@substack.net",
+ "url": "http://substack.net"
+ },
+ "license": "MIT",
+ "engine": {
+ "node": ">=0.6"
+ }
+ }
+
+ console.error('%s %s', node, example)
+ var c = spawn(node, [example], { customFds: [-1,-1,-1] })
+ var output = ''
+ c.stdout.on('data', function (d) {
+ output += d
+ respond()
+ })
+
+ var actual = ''
+ c.stderr.on('data', function (d) {
+ actual += d
+ })
+
+ function respond () {
+ console.error('respond', output)
+ if (output.match(/description: $/)) {
+ c.stdin.write('testing description\n')
+ return
+ }
+ if (output.match(/entry point: \(index\.js\) $/)) {
+ c.stdin.write('test-entry.js\n')
+ return
+ }
+ if (output.match(/keywords: $/)) {
+ c.stdin.write('fugazi function waiting room\n')
+ return
+ }
+ }
+
+ c.on('close', function () {
+ console.error('actual', actual)
+ actual = JSON.parse(actual)
+ t.deepEqual(actual, expect)
+ t.end()
+ })
+})
--- /dev/null
+exports.a = 1 + 2
+exports.b = prompt('To be or not to be?', '!2b')
+exports.c = {}
+exports.c.x = prompt()
+exports.c.y = tmpdir + "/y/file.txt"
--- /dev/null
+var test = require('tap').test;
+var promzard = require('../');
+
+test('exports', function (t) {
+ t.plan(1);
+
+ var ctx = { tmpdir : '/tmp' }
+ var file = __dirname + '/exports.input';
+ promzard(file, ctx, function (err, output) {
+ t.same(
+ {
+ a : 3,
+ b : '!2b',
+ c : {
+ x : 55,
+ y : '/tmp/y/file.txt',
+ }
+ },
+ output
+ );
+ });
+
+ setTimeout(function () {
+ process.stdin.emit('data', '\n');
+ }, 100);
+
+ setTimeout(function () {
+ process.stdin.emit('data', '55\n');
+ }, 200);
+});
--- /dev/null
+var fs = require('fs')
+
+module.exports = {
+ "a": 1 + 2,
+ "b": prompt('To be or not to be?', '!2b', function (s) {
+ return s.toUpperCase() + '...'
+ }),
+ "c": {
+ "x": prompt(function (x) { return x * 100 }),
+ "y": tmpdir + "/y/file.txt"
+ },
+ a_function: function (cb) {
+ fs.readFile(__filename, 'utf8', cb)
+ },
+ asyncPrompt: function (cb) {
+ return cb(null, prompt('a prompt at any other time would smell as sweet'))
+ }
+}
--- /dev/null
+var test = require('tap').test;
+var promzard = require('../');
+var fs = require('fs')
+
+test('prompt callback param', function (t) {
+ t.plan(1);
+
+ var ctx = { tmpdir : '/tmp' }
+ var file = __dirname + '/fn.input';
+ promzard(file, ctx, function (err, output) {
+ var expect =
+ {
+ a : 3,
+ b : '!2B...',
+ c : {
+ x : 5500,
+ y : '/tmp/y/file.txt',
+ }
+ }
+ expect.a_function = fs.readFileSync(file, 'utf8')
+ expect.asyncPrompt = 'async prompt'
+ t.same(
+ output,
+ expect
+ );
+ });
+
+ setTimeout(function () {
+ process.stdin.emit('data', '\n');
+ }, 100);
+
+ setTimeout(function () {
+ process.stdin.emit('data', '55\n');
+ }, 200);
+
+ setTimeout(function () {
+ process.stdin.emit('data', 'async prompt')
+ }, 300)
+});
--- /dev/null
+module.exports = {
+ "a": 1 + 2,
+ "b": prompt('To be or not to be?', '!2b'),
+ "c": {
+ "x": prompt(),
+ "y": tmpdir + "/y/file.txt"
+ }
+}
--- /dev/null
+var test = require('tap').test;
+var promzard = require('../');
+
+test('simple', function (t) {
+ t.plan(1);
+
+ var ctx = { tmpdir : '/tmp' }
+ var file = __dirname + '/simple.input';
+ promzard(file, ctx, function (err, output) {
+ t.same(
+ {
+ a : 3,
+ b : '!2b',
+ c : {
+ x : 55,
+ y : '/tmp/y/file.txt',
+ }
+ },
+ output
+ );
+ });
+
+ setTimeout(function () {
+ process.stdin.emit('data', '\n');
+ }, 100);
+
+ setTimeout(function () {
+ process.stdin.emit('data', '55\n');
+ }, 200);
+});
--- /dev/null
+{
+ "name": "init-package-json",
+ "version": "0.0.4",
+ "main": "init-package-json.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/init-package-json"
+ },
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "license": "BSD",
+ "description": "A node module to get your node module started.",
+ "dependencies": {
+ "promzard": "~0.1.5",
+ "read": "~0.1.0",
+ "read-package-json": "0",
+ "semver": "~1.0.14"
+ },
+ "devDependencies": {
+ "tap": "~0.2.5",
+ "rimraf": "~2.0.2"
+ },
+ "keywords": [
+ "init",
+ "package.json",
+ "package",
+ "helper",
+ "wizard",
+ "wizerd",
+ "prompt",
+ "start"
+ ],
+ "readme": "# init-package-json\n\nA node module to get your node module started.\n\n## Usage\n\n```javascript\nvar init = require('init-package-json')\nvar path = require('path')\n\n// a path to a promzard module. In the event that this file is\n// not found, one will be provided for you.\nvar initFile = path.resolve(process.env.HOME, '.npm-init')\n\n// the dir where we're doin stuff.\nvar dir = process.cwd()\n\n// extra stuff that gets put into the PromZard module's context.\n// In npm, this is the resolved config object. Exposed as 'config'\n// Optional.\nvar configData = { some: 'extra stuff' }\n\n// Any existing stuff from the package.json file is also exposed in the\n// PromZard module as the `package` object. There will also be free\n// vars for:\n// * `filename` path to the package.json file\n// * `basename` the tip of the package dir\n// * `dirname` the parent of the package dir\n\ninit(dir, initFile, configData, function (er, data) {\n // the data's already been written to {dir}/package.json\n // now you can do stuff with it\n})\n```\n\nOr from the command line:\n\n```\n$ npm-init\n```\n\nSee [PromZard](https://github.com/isaacs/promzard) for details about\nwhat can go in the config file.\n",
+ "_id": "init-package-json@0.0.4",
+ "_from": "init-package-json@0"
+}
--- /dev/null
+Copyright (c) Isaac Z. Schlueter
+All rights reserved.
+
+The BSD License
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+# lockfile
+
+A very polite lock file utility, which endeavors to not litter, and to
+wait patiently for others.
+
+## Usage
+
+```javascript
+var lockFile = require('lockfile')
+
+// opts is optional, and defaults to {}
+lockFile.lock('some-file.lock', opts, function (er, fd) {
+ // if the er happens, then it failed to acquire a lock.
+ // if there was not an error, then the fd is opened in
+ // wx mode. If you want to write something to it, go ahead.
+
+ // do my stuff, free of interruptions
+ // then, some time later, do:
+ lockFile.unlock('some-file.lock', function (er) {
+ // er means that an error happened, and is probably bad.
+ })
+})
+```
+
+## Methods
+
+Sync methods return the value/throw the error, others don't. Standard
+node fs stuff.
+
+All known locks are removed when the process exits. Of course, it's
+possible for certain types of failures to cause this to fail, but a best
+effort is made to not be a litterbug.
+
+### lockFile.lock(path, [opts], cb)
+
+Acquire a file lock on the specified path. Returns the FD.
+
+### lockFile.lockSync(path, [opts])
+
+Acquire a file lock on the specified path
+
+### lockFile.unlock(path, cb)
+
+Close and unlink the lockfile.
+
+### lockFile.unlockSync(path)
+
+Close and unlink the lockfile.
+
+### lockFile.check(path, [opts], cb)
+
+Check if the lockfile is locked and not stale.
+
+Returns boolean.
+
+### lockFile.checkSync(path, [opts], cb)
+
+Check if the lockfile is locked and not stale.
+
+Callback is called with `cb(error, isLocked)`.
+
+## Options
+
+### opts.wait
+
+A number of milliseconds to wait for locks to expire before giving up.
+Only used by lockFile.lock. Relies on fs.watch. If the lock is not
+cleared by the time the wait expires, then it returns with the original
+error.
+
+### opts.stale
+
+A number of milliseconds before locks are considered to have expired.
+
+### opts.retries
+
+Used by lock and lockSync. Retry `n` number of times before giving up.
+
+### opts.retryWait
+
+Used by lock. Wait `n` milliseconds before retrying.
--- /dev/null
+var fs = require('fs')
+
+var wx = 'wx'
+if (process.version.match(/^v0.[456]/)) {
+ var c = require('constants')
+ wx = c.O_TRUNC | c.O_CREAT | c.O_WRONLY | c.O_EXCL
+}
+
+var locks = {}
+
+process.on('exit', function () {
+ // cleanup
+ Object.keys(locks).forEach(exports.unlockSync)
+})
+
+// XXX https://github.com/joyent/node/issues/3555
+// Remove when node 0.8 is deprecated.
+process.on('uncaughtException', function H (er) {
+ var l = process.listeners('uncaughtException').filter(function (h) {
+ return h !== H
+ })
+ if (!l.length) {
+ // cleanup
+ Object.keys(locks).forEach(exports.unlockSync)
+ process.removeListener('uncaughtException', H)
+ throw er
+ }
+})
+
+exports.unlock = function (path, cb) {
+ // best-effort. unlocking an already-unlocked lock is a noop
+ fs.unlink(path, function (unlinkEr) {
+ if (!locks.hasOwnProperty(path)) return cb()
+ fs.close(locks[path], function (closeEr) {
+ delete locks[path]
+ cb()
+ })
+ })
+}
+
+exports.unlockSync = function (path) {
+ try { fs.unlinkSync(path) } catch (er) {}
+ if (!locks.hasOwnProperty(path)) return
+ // best-effort. unlocking an already-unlocked lock is a noop
+ try { fs.close(locks[path]) } catch (er) {}
+ delete locks[path]
+}
+
+
+// if the file can be opened in readonly mode, then it's there.
+// if the error is something other than ENOENT, then it's not.
+exports.check = function (path, opts, cb) {
+ if (typeof opts === 'function') cb = opts, opts = {}
+ fs.open(path, 'r', function (er, fd) {
+ if (er) {
+ if (er.code !== 'ENOENT') return cb(er)
+ return cb(null, false)
+ }
+
+ if (!opts.stale) {
+ return fs.close(fd, function (er) {
+ return cb(er, true)
+ })
+ }
+
+ fs.fstat(fd, function (er, st) {
+ if (er) return fs.close(fd, function (er2) {
+ return cb(er)
+ })
+
+ fs.close(fd, function (er) {
+ var age = Date.now() - st.ctime.getTime()
+ return cb(er, age <= opts.stale)
+ })
+ })
+ })
+}
+
+exports.checkSync = function (path, opts) {
+ opts = opts || {}
+ if (opts.wait) {
+ throw new Error('opts.wait not supported sync for obvious reasons')
+ }
+
+ try {
+ var fd = fs.openSync(path, 'r')
+ } catch (er) {
+ if (er.code !== 'ENOENT') throw er
+ return false
+ }
+
+ if (!opts.stale) {
+ fs.closeSync(fd)
+ return true
+ }
+
+ // file exists. however, might be stale
+ if (opts.stale) {
+ try {
+ var st = fs.fstatSync(fd)
+ } finally {
+ fs.closeSync(fd)
+ }
+ var age = Date.now() - st.ctime.getTime()
+ return (age <= opts.stale)
+ }
+}
+
+
+
+exports.lock = function (path, opts, cb) {
+ if (typeof opts === 'function') cb = opts, opts = {}
+
+ if (typeof opts.retries === 'number' && opts.retries > 0) {
+ cb = (function (orig) { return function (er, fd) {
+ if (!er) return orig(er, fd)
+ var newRT = opts.retries - 1
+ opts_ = Object.create(opts, { retries: { value: newRT }})
+ if (opts.retryWait) setTimeout(function() {
+ exports.lock(path, opts_, orig)
+ }, opts.retryWait)
+ else exports.lock(path, opts_, orig)
+ }})(cb)
+ }
+
+ // try to engage the lock.
+ // if this succeeds, then we're in business.
+ fs.open(path, wx, function (er, fd) {
+ if (!er) {
+ locks[path] = fd
+ return cb(null, fd)
+ }
+
+ // something other than "currently locked"
+ // maybe eperm or something.
+ if (er.code !== 'EEXIST') return cb(er)
+
+ // someone's got this one. see if it's valid.
+ if (opts.stale) fs.stat(path, function (er, st) {
+ if (er) {
+ if (er.code === 'ENOENT') {
+ // expired already!
+ var opts_ = Object.create(opts, { stale: { value: false }})
+ exports.lock(path, opts_, cb)
+ return
+ }
+ return cb(er)
+ }
+
+ var age = Date.now() - st.ctime.getTime()
+ if (age > opts.stale) {
+ exports.unlock(path, function (er) {
+ if (er) return cb(er)
+ var opts_ = Object.create(opts, { stale: { value: false }})
+ exports.lock(path, opts_, cb)
+ })
+ } else notStale(er, path, opts, cb)
+ })
+ else notStale(er, path, opts, cb)
+ })
+}
+
+function notStale (er, path, opts, cb) {
+ if (typeof opts.wait === 'number' && opts.wait > 0) {
+ // wait for some ms for the lock to clear
+ var start = Date.now()
+
+ var retried = false
+ function retry () {
+ if (retried) return
+ retried = true
+ // maybe already closed.
+ try { watcher.close() } catch (e) {}
+ clearTimeout(timer)
+ var newWait = Date.now() - start
+ var opts_ = Object.create(opts, { wait: { value: newWait }})
+ exports.lock(path, opts_, cb)
+ }
+
+ try {
+ var watcher = fs.watch(path, function (change) {
+ if (change === 'rename') {
+ // ok, try and get it now.
+ // if this fails, then continue waiting, maybe.
+ retry()
+ }
+ })
+ watcher.on('error', function (er) {
+ // usually means it expired before the watcher spotted it
+ retry()
+ })
+ } catch (er) {
+ retry()
+ }
+
+ var timer = setTimeout(function () {
+ try { watcher.close() } catch (e) {}
+ cb(er)
+ }, opts.wait)
+ } else {
+ // failed to lock!
+ return cb(er)
+ }
+}
+
+exports.lockSync = function (path, opts) {
+ opts = opts || {}
+ if (opts.wait || opts.retryWait) {
+ throw new Error('opts.wait not supported sync for obvious reasons')
+ }
+
+ try {
+ var fd = fs.openSync(path, wx)
+ locks[path] = fd
+ return fd
+ } catch (er) {
+ if (er.code !== 'EEXIST') return retryThrow(path, opts, er)
+
+ if (opts.stale) {
+ var st = fs.statSync(path)
+ var age = Date.now() - st.ctime.getTime()
+ if (age > opts.stale) {
+ exports.unlockSync(path)
+ return exports.lockSync(path, opts)
+ }
+ }
+
+ // failed to lock!
+ return retryThrow(path, opts, er)
+ }
+}
+
+function retryThrow (path, opts, er) {
+ if (typeof opts.retries === 'number' && opts.retries > 0) {
+ var newRT = opts.retries - 1
+ var opts_ = Object.create(opts, { retries: { value: newRT }})
+ return exports.lockSync(path, opts_)
+ }
+ throw er
+}
+
--- /dev/null
+{
+ "name": "lockfile",
+ "version": "0.2.1",
+ "main": "lockfile.js",
+ "directories": {
+ "test": "test"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tap": "~0.2.5"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/lockfile"
+ },
+ "keywords": [
+ "lockfile",
+ "lock",
+ "file",
+ "fs",
+ "O_EXCL"
+ ],
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "license": "BSD",
+ "description": "A very polite lock file utility, which endeavors to not litter, and to wait patiently for others.",
+ "readme": "# lockfile\n\nA very polite lock file utility, which endeavors to not litter, and to\nwait patiently for others.\n\n## Usage\n\n```javascript\nvar lockFile = require('lockfile')\n\n// opts is optional, and defaults to {}\nlockFile.lock('some-file.lock', opts, function (er, fd) {\n // if the er happens, then it failed to acquire a lock.\n // if there was not an error, then the fd is opened in\n // wx mode. If you want to write something to it, go ahead.\n\n // do my stuff, free of interruptions\n // then, some time later, do:\n lockFile.unlock('some-file.lock', function (er) {\n // er means that an error happened, and is probably bad.\n })\n})\n```\n\n## Methods\n\nSync methods return the value/throw the error, others don't. Standard\nnode fs stuff.\n\nAll known locks are removed when the process exits. Of course, it's\npossible for certain types of failures to cause this to fail, but a best\neffort is made to not be a litterbug.\n\n### lockFile.lock(path, [opts], cb)\n\nAcquire a file lock on the specified path. Returns the FD.\n\n### lockFile.lockSync(path, [opts])\n\nAcquire a file lock on the specified path\n\n### lockFile.unlock(path, cb)\n\nClose and unlink the lockfile.\n\n### lockFile.unlockSync(path)\n\nClose and unlink the lockfile.\n\n### lockFile.check(path, [opts], cb)\n\nCheck if the lockfile is locked and not stale.\n\nReturns boolean.\n\n### lockFile.checkSync(path, [opts], cb)\n\nCheck if the lockfile is locked and not stale.\n\nCallback is called with `cb(error, isLocked)`.\n\n## Options\n\n### opts.wait\n\nA number of milliseconds to wait for locks to expire before giving up.\nOnly used by lockFile.lock. Relies on fs.watch. If the lock is not\ncleared by the time the wait expires, then it returns with the original\nerror.\n\n### opts.stale\n\nA number of milliseconds before locks are considered to have expired.\n\n### opts.retries\n\nUsed by lock and lockSync. Retry `n` number of times before giving up.\n\n### opts.retryWait\n\nUsed by lock. Wait `n` milliseconds before retrying.\n",
+ "_id": "lockfile@0.2.1",
+ "_from": "lockfile@>=0.2"
+}
--- /dev/null
+# Authors, sorted by whether or not they are me
+Isaac Z. Schlueter <i@izs.me>
+Carlos Brito Lage <carlos@carloslage.net>
+Marko Mikulicic <marko.mikulicic@isti.cnr.it>
+Trent Mick <trentm@gmail.com>
Usage:
var LRU = require("lru-cache")
- , cache = LRU(10) // max 10 items. default = Infinity
+ , cache = LRU(10, // max length. default = Infinity
+ // calculate how "big" each item is
+ //
+ // defaults to function(){return 1}, ie, just limit
+ // the item count, without any knowledge as to their
+ // relative size.
+ function (item) { return item.length })
+
cache.set("key", "value")
cache.get("key") // "value"
+ cache.reset() // empty the cache
+
+If you put more stuff in it, then items will fall out.
+
+If you try to put an oversized thing in it, then it'll fall out right
+away.
+
RTFS for more info.
return Object.prototype.hasOwnProperty.call(obj, key)
}
-function LRUCache (maxLength) {
+function naiveLength () { return 1 }
+
+function LRUCache (maxLength, lengthCalculator) {
if (!(this instanceof LRUCache)) {
- return new LRUCache(maxLength)
+ return new LRUCache(maxLength, lengthCalculator)
+ }
+
+ if (typeof lengthCalculator !== "function") {
+ lengthCalculator = naiveLength
}
+ if (!maxLength || !(typeof maxLength === "number") || maxLength <= 0 ) {
+ maxLength = Infinity
+ }
+
+
var cache = {} // hash of items by key
, lruList = {} // list of items in order of use recency
, lru = 0 // least recently used
, mru = 0 // most recently used
, length = 0 // number of items in the list
+ , itemCount = 0
+
// resize the cache when the maxLength changes.
Object.defineProperty(this, "maxLength",
, enumerable : true
})
- this.maxLength = maxLength
+ // resize the cache when the lengthCalculator changes.
+ Object.defineProperty(this, "lengthCalculator",
+ { set : function (lC) {
+ if (typeof lC !== "function") {
+ lengthCalculator = naiveLength
+ length = itemCount
+ Object.keys(cache).forEach(function (key) {
+ cache[key].length = 1
+ })
+ } else {
+ lengthCalculator = lC
+ length = 0
+ Object.keys(cache).forEach(function (key) {
+ cache[key].length = lengthCalculator(cache[key].value)
+ length += cache[key].length
+ })
+ }
+
+ if (length > maxLength) trim()
+ }
+ , get : function () { return lengthCalculator }
+ , enumerable : true
+ })
Object.defineProperty(this, "length",
{ get : function () { return length }
, enumerable : true
})
+
+ Object.defineProperty(this, "itemCount",
+ { get : function () { return itemCount }
+ , enumerable : true
+ })
+
this.reset = function () {
cache = {}
lruList = {}
lru = 0
mru = 0
length = 0
+ itemCount = 0
+ }
+
+ // Provided for debugging/dev purposes only. No promises whatsoever that
+ // this API stays stable.
+ this.dump = function () {
+ return cache
}
this.set = function (key, value) {
if (hOP(cache, key)) {
this.get(key)
cache[key].value = value
- return undefined
+ return
}
- var hit = {key:key, value:value, lu:mru++}
+
+ var hit = {key:key, value:value, lu:mru++, length:lengthCalculator(value)}
+
+ // oversized objects fall out of cache automatically.
+ if (hit.length > maxLength) return
+
+ length += hit.length
lruList[hit.lu] = cache[key] = hit
- length ++
+ itemCount ++
+
if (length > maxLength) trim()
}
this.get = function (key) {
- if (!hOP(cache, key)) return undefined
+ if (!hOP(cache, key)) return
var hit = cache[key]
delete lruList[hit.lu]
if (hit.lu === lru) lruWalk()
}
this.del = function (key) {
- if (!hOP(cache, key)) return undefined
+ if (!hOP(cache, key)) return
var hit = cache[key]
delete cache[key]
delete lruList[hit.lu]
if (hit.lu === lru) lruWalk()
- length --
+ length -= hit.length
+ itemCount --
}
function lruWalk () {
// lru has been deleted, hop up to the next hit.
- lru = Object.keys(lruList).shift()
+ lru = Object.keys(lruList)[0]
}
function trim () {
- if (length <= maxLength) return undefined
- var prune = Object.keys(lruList).slice(0, length - maxLength)
- for (var i = 0, l = (length - maxLength); i < l; i ++) {
+ if (length <= maxLength) return
+ var prune = Object.keys(lruList)
+ for (var i = 0; i < prune.length && length > maxLength; i ++) {
+ length -= lruList[prune[i]].length
delete cache[ lruList[prune[i]].key ]
delete lruList[prune[i]]
}
- length = maxLength
lruWalk()
}
}
-{ "name": "lru-cache"
-, "description": "A cache object that deletes the least-recently-used items."
-, "version": "1.0.5"
-, "author": "Isaac Z. Schlueter <i@izs.me>"
-, "scripts": { "test": "tap test" }
-, "main": "lib/lru-cache.js"
-, "repository": "git://github.com/isaacs/node-lru-cache.git"
-, "devDependencies": { "tap": "0.1" }
-, "license":
- { "type": "MIT"
- , "url": "http://github.com/isaacs/node-lru-cache/raw/master/LICENSE"
- }
+{
+ "name": "lru-cache",
+ "description": "A cache object that deletes the least-recently-used items.",
+ "version": "1.1.0",
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me"
+ },
+ "scripts": {
+ "test": "tap test"
+ },
+ "main": "lib/lru-cache.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/node-lru-cache.git"
+ },
+ "devDependencies": {
+ "tap": ""
+ },
+ "license": {
+ "type": "MIT",
+ "url": "http://github.com/isaacs/node-lru-cache/raw/master/LICENSE"
+ },
+ "_npmUser": {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ },
+ "_id": "lru-cache@1.1.0",
+ "contributors": [
+ {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me"
+ },
+ {
+ "name": "Carlos Brito Lage",
+ "email": "carlos@carloslage.net"
+ },
+ {
+ "name": "Marko Mikulicic",
+ "email": "marko.mikulicic@isti.cnr.it"
+ },
+ {
+ "name": "Trent Mick",
+ "email": "trentm@gmail.com"
+ }
+ ],
+ "dependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "_engineSupported": true,
+ "_npmVersion": "1.1.25",
+ "_nodeVersion": "v0.7.10-pre",
+ "_defaultsLoaded": true,
+ "_from": "lru-cache@1"
}
-module.exports = minimatch
+;(function (require, exports, module, platform) {
+
+if (module) module.exports = minimatch
+else exports.minimatch = minimatch
+
+if (!require) {
+ require = function (id) {
+ switch (id) {
+ case "path": return { basename: function (f) {
+ f = f.split(/[\/\\]/)
+ var e = f.pop()
+ if (!e) e = f.pop()
+ return e
+ }}
+ case "lru-cache": return function LRUCache () {
+ // not quite an LRU, but still space-limited.
+ var cache = {}
+ var cnt = 0
+ this.set = function (k, v) {
+ cnt ++
+ if (cnt >= 100) cache = {}
+ cache[k] = v
+ }
+ this.get = function (k) { return cache[k] }
+ }
+ }
+ }
+}
+
minimatch.Minimatch = Minimatch
var LRU = require("lru-cache")
}
}
+function ext (a, b) {
+ a = a || {}
+ b = b || {}
+ var t = {}
+ Object.keys(b).forEach(function (k) {
+ t[k] = b[k]
+ })
+ Object.keys(a).forEach(function (k) {
+ t[k] = a[k]
+ })
+ return t
+}
+
+minimatch.defaults = function (def) {
+ if (!def || !Object.keys(def).length) return minimatch
+
+ var orig = minimatch
+
+ var m = function minimatch (p, pattern, options) {
+ return orig.minimatch(p, pattern, ext(def, options))
+ }
+
+ m.Minimatch = function Minimatch (pattern, options) {
+ return new orig.Minimatch(pattern, ext(def, options))
+ }
+
+ return m
+}
+
+Minimatch.defaults = function (def) {
+ if (!def || !Object.keys(def).length) return Minimatch
+ return minimatch.defaults(def).Minimatch
+}
+
+
function minimatch (p, pattern, options) {
if (typeof pattern !== "string") {
throw new TypeError("glob pattern string required")
var options = this.options
- // first, normalize any slash-separated path parts.
- // f = path.normalize(f)
-
// windows: need to use /, not \
// On other platforms, \ is a valid (albeit bad) filename char.
- if (process.platform === "win32") {
+ if (platform === "win32") {
f = f.split("\\").join("/")
}
function regExpEscape (s) {
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
}
+
+})( typeof require === "function" ? require : null,
+ this,
+ typeof module === "object" ? module : null,
+ typeof process === "object" ? process.platform : "win32"
+ )
},
"name": "minimatch",
"description": "a glob matcher in javascript",
- "version": "0.2.2",
+ "version": "0.2.5",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/minimatch.git"
"node": "*"
},
"dependencies": {
- "lru-cache": "~1.0.5"
+ "lru-cache": "~1"
},
"devDependencies": {
"tap": ""
"name": "isaacs",
"email": "i@izs.me"
},
- "_id": "minimatch@0.2.2",
+ "_id": "minimatch@0.2.5",
"optionalDependencies": {},
"_engineSupported": true,
- "_npmVersion": "1.1.12",
- "_nodeVersion": "v0.7.7-pre",
+ "_npmVersion": "1.1.25",
+ "_nodeVersion": "v0.7.10-pre",
"_defaultsLoaded": true,
- "_from": "minimatch@0"
+ "_from": "minimatch@~0.2"
}
--- /dev/null
+node_modules/
+npm-debug.log
\ No newline at end of file
--- /dev/null
+--- /dev/null
++++ .gitignore
+@@ -0,0 +1,2 @@
++node_modules/
++npm-debug.log
\ No newline at end of file
--- /dev/null
+node_modules/
+npm-debug.log
\ No newline at end of file
--- /dev/null
+language: node_js
+node_js:
+ - 0.4
+ - 0.6
{
"name": "mkdirp",
"description": "Recursively mkdir, like `mkdir -p`",
- "version": "0.3.2",
+ "version": "0.3.3",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"name": "isaacs",
"email": "i@izs.me"
},
- "_id": "mkdirp@0.3.2",
+ "_id": "mkdirp@0.3.3",
"dependencies": {},
"optionalDependencies": {},
"_engineSupported": true,
- "_npmVersion": "1.1.23",
+ "_npmVersion": "1.1.24",
"_nodeVersion": "v0.7.10-pre",
"_defaultsLoaded": true,
- "dist": {
- "shasum": "bebd1a16759571a6bd30f89a7edffc38ad442881"
- },
- "_from": "../mkdirp"
+ "_from": "mkdirp@0.3"
}
`-d`) switch when running the either `configure` or `build` command.
-The "gyp" file
---------------
+The "binding.gyp" file
+----------------------
Previously when node had `node-waf` you had to write a `wscript` file. The
replacement for that is the `binding.gyp` file, which describes the configuration
-to build your module in a JSON-like format. A barebones `gyp` file appropriate for
-building a node addon looks like:
+to build your module in a JSON-like format. This file gets placed in the root of
+your package, alongside the `package.json` file.
+
+A barebones `gyp` file appropriate for building a node addon looks like:
``` json
{
}
```
-Some additional resources:
+Some additional resources for writing `gyp` files:
* ["Hello World" node addon example](https://github.com/joyent/node/tree/master/test/addons/hello-world)
* [gyp user documentation](http://code.google.com/p/gyp/wiki/GypUserDocumentation)
* [gyp input format reference](http://code.google.com/p/gyp/wiki/InputFormatReference)
+ * ['"binding.gyp" files out in the wild' wiki page](https://github.com/TooTallNate/node-gyp/wiki/%22binding.gyp%22-files-out-in-the-wild)
Commands
*/
var gyp = require('../')
- , inspect = require('util').inspect
- , ansi = require('ansi')
- , cursor = ansi(process.stderr)
+var log = require('npmlog')
/**
* Process and execute the selected commands.
return prog.usageAndExit()
}
+log.info('it worked if it ends with', 'ok')
+log.info('using', 'node-gyp@%s', prog.version)
+log.info('using', 'node@%s', process.versions.node)
-/**
- * Set up logging handlers.
- */
-
-prog.on('info', function () {
- cursor.fg.green().write('info ')
- .fg.grey().write(arguments[0] + ' ')
- .reset()
- for (var i=1, l=arguments.length; i<l; i++) {
- cursor.write(arguments[i] + ' ')
- }
- cursor.write('\n')
-})
-
-prog.on('warn', function () {
- cursor.fg.yellow().bg.red().write('warn')
- .fg.reset().bg.reset().write(' ')
- .fg.grey().write(arguments[0])
- .write(' ')
- .reset()
- for (var i=1, l=arguments.length; i<l; i++) {
- cursor.write(arguments[i] + ' ')
- }
- cursor.write('\n')
-})
-
-if (prog.opts.verbose) {
- prog.on('verbose', function () {
- cursor.fg.cyan().write('verb ')
- .fg.grey().write(arguments[0] + ' ')
- .reset()
- for (var i=1, l=arguments.length; i<l; i++) {
- cursor.write(inspect(arguments[i]) + ' ')
- }
- cursor.write('\n')
- })
-}
-
-prog.on('spawn', function (command, args, proc) {
- cursor.fg.magenta().write('spawn ')
- .fg.grey().write(command + ' ')
- .reset().write(inspect(args) + '\n')
-})
-
-prog.info('it worked if it ends with', 'ok')
/**
* Change dir if -C/--directory was passed.
try {
var stat = fs.statSync(dir)
if (stat.isDirectory()) {
- prog.info('chdir:', dir)
+ log.info('chdir', dir)
process.chdir(dir)
} else {
- prog.warn(dir + ' is not a directory')
+ log.warn('chdir', dir + ' is not a directory')
}
} catch (e) {
- prog.warn('directory:', e.message)
+ if (e.code === 'ENOENT') {
+ log.warn('chdir', dir + ' is not a directory')
+ } else {
+ log.warn('chdir', 'error during chdir() "%s"', e.message)
+ }
}
}
if (prog.todo.length === 0) {
// done!
completed = true
- prog.info('done', 'ok')
+ log.info('done', 'ok')
return
}
var command = prog.todo.shift()
+
+ // is this an alias?
+ if (command in prog.aliases) {
+ command = prog.aliases[command]
+ }
+
prog.commands[command](prog.argv.slice(), function (err) {
if (err) {
- cursor.fg.red().write('ERR! ')
- .fg.reset().write(err.stack + '\n')
- cursor.fg.red().write('ERR! ')
- .fg.reset().write('not ok\n')
+ log.error(command + ' error', err.stack)
+ log.error('not ok')
return process.exit(1)
}
if (command == 'list') {
console.log(version)
})
} else {
- prog.info('No node development files installed. Use `node-gyp install` to install a version.')
+ console.log('No node development files installed. Use `node-gyp install` to install a version.')
}
} else if (arguments.length >= 2) {
console.log.apply(console, [].slice.call(arguments, 1))
}
+
+ // now run the next command in the queue
process.nextTick(run)
})
}
process.on('exit', function (code) {
if (!completed && !code) {
- cursor.fg.red().write('ERR! ')
- .fg.reset().write('Completion callback never invoked!\n')
- cursor.fg.red().write('ERR! ')
- .fg.reset().write('This is a bug in `node-gyp`. Please open an Issue:\n')
- cursor.fg.red().write('ERR! ')
- .fg.reset().write(' https://github.com/TooTallNate/node-gyp/issues\n')
- cursor.fg.red().write('ERR! ')
- .fg.reset().write('not ok\n')
+ log.error('Completion callback never invoked!')
+ log.error('This is a bug in `node-gyp`, please file an Issue:')
+ log.error('', ' https://github.com/TooTallNate/node-gyp/issues')
+ log.error('not ok')
process.exit(6)
}
})
process.on('uncaughtException', function (err) {
- cursor.fg.red().write('ERR! ')
- .fg.reset().write('UNCAUGHT EXCEPTION:\n')
- cursor.fg.red().write('ERR! ')
- .fg.reset().write(err.stack + '\n')
- cursor.fg.red().write('ERR! ')
- .fg.reset().write('This is a bug in `node-gyp`. Please open an Issue:\n')
- cursor.fg.red().write('ERR! ')
- .fg.reset().write(' https://github.com/TooTallNate/node-gyp/issues\n')
- cursor.fg.red().write('ERR! ')
- .fg.reset().write('not ok\n')
- process.exit(1)
+ log.error('UNCAUGHT EXCEPTION', err.stack)
+ log.error('This is a bug in `node-gyp`, please file an Issue:')
+ log.error('', ' https://github.com/TooTallNate/node-gyp/issues')
+ log.error('not ok')
+ process.exit(7)
})
'cflags': [ '-m32' ],
'ldflags': [ '-m32' ],
}],
+ [ 'target_arch=="x64"', {
+ 'cflags': [ '-m64' ],
+ 'ldflags': [ '-m64' ],
+ }],
[ 'OS=="linux"', {
'ldflags': [ '-rdynamic' ],
}],
var fs = require('graceful-fs')
, path = require('path')
, glob = require('glob')
+ , log = require('npmlog')
, which = require('which')
- , mkdirp = require('./util/mkdirp')
+ , mkdirp = require('mkdirp')
, win = process.platform == 'win32'
, openbsd = process.platform == 'openbsd'
function build (gyp, argv, callback) {
- gyp.verbose('build args', argv)
var makeCommand = openbsd ? 'gmake' : 'make'
var command = win ? 'msbuild' : makeCommand
, buildDir = path.resolve('build')
buildType = 'Release'
}
- gyp.verbose('build type:', buildType)
- gyp.verbose('architecture:', arch)
- gyp.verbose('node dev dir:', nodeDir)
+ log.verbose('build type:', buildType)
+ log.verbose('architecture:', arch)
+ log.verbose('node dev dir:', nodeDir)
if (win) {
findSolutionFile()
return callback(new Error('Could not find *.sln file. Did you run "configure"?'))
}
guessedSolution = files[0]
- gyp.verbose('found first Solution file', guessedSolution)
+ log.verbose('found first Solution file', guessedSolution)
doWhich()
})
}
}
return
}
- gyp.verbose('`which` succeeded for `' + command + '`', execPath)
+ log.verbose('`which` succeeded for `' + command + '`', execPath)
copyNodeLib()
})
}
*/
function guessMsbuild () {
- gyp.verbose('could not find "msbuild.exe". guessing location')
+ log.verbose('could not find "msbuild.exe". guessing location')
// This is basically just hard-coded. If this causes problems
// then we'll think of something more clever.
var windir = process.env.WINDIR || process.env.SYSTEMROOT || 'C:\\WINDOWS'
mkdirp(buildDir, function (err, isNew) {
if (err) return callback(err)
- gyp.verbose('"' + buildType + '" dir needed to be created?', isNew)
+ log.verbose('"' + buildType + '" dir needed to be created?', isNew)
var rs = fs.createReadStream(archNodeLibPath)
, ws = fs.createWriteStream(buildNodeLibPath)
+ log.verbose('copying "node.lib" for ' + arch, buildNodeLibPath)
rs.pipe(ws)
rs.on('error', callback)
ws.on('error', callback)
function doBuild () {
// Enable Verbose build
- if (!win && gyp.opts.verbose) {
+ var verbose = log.levels[log.level] <= log.levels.verbose
+ if (!win && verbose) {
argv.push('V=1')
}
- if (win && !gyp.opts.verbose) {
+ if (win && !verbose) {
argv.push('/clp:Verbosity=minimal')
}
*/
var rm = require('rimraf')
+var log = require('npmlog')
function clean (gyp, argv, callback) {
// Remove the 'build' dir
var buildDir = 'build'
- gyp.verbose('removing "build" directory')
+ log.verbose('clean', 'removing "%s" directory', buildDir)
rm(buildDir, callback)
}
var fs = require('graceful-fs')
, path = require('path')
, glob = require('glob')
+ , log = require('npmlog')
, which = require('which')
, semver = require('semver')
- , mkdirp = require('./util/mkdirp')
+ , mkdirp = require('mkdirp')
+ , exec = require('child_process').exec
, win = process.platform == 'win32'
exports.usage = 'Generates ' + (win ? 'MSVC project files' : 'a Makefile') + ' for the current module'
// Make sure that Python is in the $PATH
function checkPython () {
- gyp.verbose('checking for Python executable "' + python + '" in the PATH')
+ log.verbose('check python', 'checking for Python executable "%s" in the PATH', python)
which(python, function (err, execPath) {
if (err) {
if (win) {
}
return
}
- gyp.verbose('`which` succeeded for `' + python + '`', execPath)
- // TODO: ensure compatible Python version
- getNodeDir()
+ log.verbose('`which` succeeded for `' + python + '`', execPath)
+ checkPythonVersion()
})
}
// Called on Windows when "python" isn't available in the current $PATH.
// We're gonna glob C:\python2*
function guessPython () {
- gyp.verbose('could not find "' + python + '". guessing location')
+ log.verbose('could not find "' + python + '". guessing location')
var rootDir = process.env.HOMEDRIVE || process.env.SystemDrive || 'C:\\'
if (rootDir[rootDir.length - 1] !== '\\') {
rootDir += '\\'
}
var pythonPath = path.resolve(rootDir, 'Python27', 'python.exe')
- gyp.verbose('ensuring that file exists:', pythonPath)
+ log.verbose('ensuring that file exists:', pythonPath)
fs.stat(pythonPath, function (err, stat) {
if (err) {
if (err.code == 'ENOENT') {
failNoPython()
} else {
- callbackk(err)
+ callback(err)
}
return
}
python = pythonPath
- getNodeDir()
+ checkPythonVersion()
+ })
+ }
+
+ function checkPythonVersion () {
+ exec(python + ' --version', function (err, stdout, stderr) {
+ if (err) {
+ return callback(err)
+ }
+ var version = stderr.trim().replace(/[^\d\.]+/g, '')
+ if (semver.lt(version, '3.0.0')) {
+ getNodeDir()
+ } else {
+ failPython3()
+ }
})
}
+ '", you can set the PYTHON env variable.'))
}
+ function failPython3 () {
+ callback(new Error('Python executable "' + python
+ + '" is Python 3, which is not supported.\n'
+ + 'You can set the PYTHON env variable to point to a Python 2 interpreter.'))
+ }
+
function getNodeDir () {
// 'python' should be set by now
nodeDir = nodeDir.replace(/^~/, process.env.HOME)
}
- gyp.verbose('compiling against specified --nodedir dev files', nodeDir)
+ log.verbose('get node dir', 'compiling against specified --nodedir dev files: %s', nodeDir)
createBuildDir()
} else {
if (gyp.opts.target) {
// if --target was given, then determine a target version to compile for
versionStr = gyp.opts.target
- gyp.verbose('compiling against --target node version', versionStr)
+ log.verbose('get node dir', 'compiling against --target node version: %s', versionStr)
} else {
// if no --target was specified then use the current host node version
versionStr = process.version
- gyp.verbose('no --target version specified, falling back to host node version', versionStr)
+ log.verbose('get node dir', 'no --target version specified, falling back to host node version: %s', versionStr)
}
// make sure we have a valid version
gyp.opts.ensure = true
gyp.commands.install([ versionStr ], function (err, version) {
if (err) return callback(err)
- gyp.verbose('target node version installed:', version)
+ log.verbose('get node dir', 'target node version installed:', version)
nodeDir = path.resolve(gyp.devDir, version)
createBuildDir()
})
}
function createBuildDir () {
- gyp.verbose('attempting to create "build" dir', buildDir)
+ log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir)
mkdirp(buildDir, function (err, isNew) {
if (err) return callback(err)
- gyp.verbose('"build" dir needed to be created?', isNew)
+ log.verbose('build dir', '"build" dir needed to be created?', isNew)
createConfigFile()
})
}
function createConfigFile (err) {
if (err) return callback(err)
- gyp.verbose('creating build/config.gypi file')
- configPath = path.resolve(buildDir, 'config.gypi')
+ var configFilename = 'config.gypi'
+ configPath = path.resolve(buildDir, configFilename)
+
+ log.verbose(configFilename, 'creating config file')
var config = process.config || {}
, defaults = config.target_defaults
return v
}
+ log.silly(configFilename, config)
+
// now write out the config.gypi file to the build/ dir
var prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step'
, json = JSON.stringify(config, boolsToString, 2)
- gyp.verbose('writing out config file', configPath)
+ log.verbose(configFilename, 'writing out config file: %s', configPath)
fs.writeFile(configPath, [prefix, json, ''].join('\n'), runGypAddon)
}
if (!~argv.indexOf('-f') && !~argv.indexOf('--format')) {
if (win) {
- gyp.verbose('gyp format was not specified; forcing "msvs"')
+ log.verbose('gyp_addon', 'gyp format was not specified; forcing "msvs"')
// force the 'make' target for non-Windows
argv.push('-f', 'msvs')
} else {
- gyp.verbose('gyp format was not specified; forcing "make"')
+ log.verbose('gyp_addon', 'gyp format was not specified; forcing "make"')
// force the 'make' target for non-Windows
argv.push('-f', 'make')
}
, rm = require('rimraf')
, path = require('path')
, zlib = require('zlib')
+ , log = require('npmlog')
, semver = require('semver')
, fstream = require('fstream')
, request = require('request')
, minimatch = require('minimatch')
- , mkdir = require('./util/mkdirp')
+ , mkdir = require('mkdirp')
, distUrl = 'http://nodejs.org/dist'
, win = process.platform == 'win32'
if (cb.done) return
cb.done = true
if (err) {
- gyp.verbose('got an error, rolling back install')
+ log.warn('install', 'got an error, rolling back install')
// roll-back the install if anything went wrong
gyp.commands.remove([ version ], function (err2) {
callback(err)
// Determine which node dev files version we are installing
var versionStr = argv[0] || gyp.opts.target || process.version
- gyp.verbose('input version string', versionStr)
+ log.verbose('install', 'input version string', versionStr)
// parse the version to normalize and ensure it's valid
var version = semver.parse(versionStr)
// "legacy" versions are 0.7 and 0.6
var isLegacy = semver.lt(versionStr, '0.8.0')
- gyp.verbose('installing legacy version?', isLegacy)
+ log.verbose('installing legacy version?', isLegacy)
if (semver.lt(versionStr, '0.6.0')) {
return callback(new Error('Minimum target version is `0.6.0` or greater. Got: ' + versionStr))
if (version[5] === '-pre') {
version[3] = +version[3] - 1
version[5] = null
- gyp.verbose('-pre version detected, adjusting patch version')
+ log.verbose('-pre version detected, adjusting patch version')
}
// flatten version into String
version = version.slice(1, 4).join('.')
- gyp.verbose('installing version', version)
+ log.verbose('install', 'installing version: %s', version)
// the directory where the dev files will be installed
var devDir = path.resolve(gyp.devDir, version)
// If '--ensure' was passed, then don't *always* install the version;
// check if it is already installed, and only install when needed
if (gyp.opts.ensure) {
- gyp.verbose('--ensure was passed, so won\'t reinstall if already installed')
+ log.verbose('install', '--ensure was passed, so won\'t reinstall if already installed')
fs.stat(devDir, function (err, stat) {
if (err) {
if (err.code == 'ENOENT') {
- gyp.verbose('version not already installed, continuing with install', version)
+ log.verbose('install', 'version not already installed, continuing with install', version)
go()
} else {
cb(err)
}
return
}
- gyp.verbose('version is already installed, need to check "installVersion"')
+ log.verbose('install', 'version is already installed, need to check "installVersion"')
var installVersionFile = path.resolve(devDir, 'installVersion')
fs.readFile(installVersionFile, 'ascii', function (err, ver) {
if (err && err.code != 'ENOENT') {
return cb(err)
}
var installVersion = parseInt(ver, 10) || 0
- gyp.verbose('got "installVersion":', installVersion)
- gyp.verbose('needs "installVersion":', gyp.package.installVersion)
+ log.verbose('got "installVersion"', installVersion)
+ log.verbose('needs "installVersion"', gyp.package.installVersion)
if (installVersion < gyp.package.installVersion) {
- gyp.verbose('version is no good; reinstalling')
+ log.verbose('install', 'version is no good; reinstalling')
go()
} else {
- gyp.verbose('version is good')
+ log.verbose('install', 'version is good')
cb()
}
})
}
function download (url, onError) {
- gyp.info('downloading:', url)
+ log.http('GET', url)
+
var requestOpts = {
uri: url
, onResponse: true
|| process.env.HTTP_PROXY
|| process.env.npm_config_proxy
if (proxyUrl) {
- gyp.verbose('using proxy:', proxyUrl)
+ log.verbose('proxy', proxyUrl)
requestOpts.proxy = proxyUrl
}
- return request(requestOpts, onError)
+ var req = request(requestOpts, onError)
+ req.on('response', function (res) {
+ log.http(res.statusCode, url)
+ })
+ return req
}
function go () {
+ log.verbose('ensuring nodedir is created', devDir)
+
// first create the dir for the node dev files
mkdir(devDir, function (err, created) {
if (err) return cb(err)
if (created) {
- gyp.verbose('created:', devDir)
- } else {
- gyp.verbose('directory already existed:', devDir)
+ log.verbose('created nodedir', created)
}
// now download the node tarball
// only .h header files and the gyp files get extracted
function isValid () {
var name = this.path.substring(devDir.length + 1)
- , _valid = valid(name)
+ var isValid = valid(name)
if (name === '' && this.type === 'Directory') {
// the first directory entry is ok
return true
}
- if (_valid) {
- gyp.verbose('extracted file from tarball', name)
+ if (isValid) {
+ log.verbose('extracted file from tarball', name)
extractCount++
} else {
// invalid
+ log.silly('ignoring from tarball', name)
}
- return _valid
+ return isValid
}
gunzip.on('error', cb)
if (extractCount === 0) {
return cb(new Error('There was a fatal problem while downloading/extracting the tarball'))
}
- gyp.verbose('done parsing tarball')
+ log.verbose('tarball', 'done parsing tarball')
var async = 0
if (isLegacy) {
function copyLegacy (done) {
// legacy versions of node (< 0.8) require the legacy files to be copied
// over since they contain many bugfixes from the current node build system
- gyp.verbose('copying "legacy" gyp configuration files for version', version)
+ log.verbose('legacy', 'copying "legacy" gyp configuration files for version', version)
var legacyDir = path.resolve(__dirname, '..', 'legacy')
- gyp.verbose('using "legacy" dir', legacyDir)
- gyp.verbose('copying to "dev" dir', devDir)
+ log.verbose('legacy', 'using "legacy" dir', legacyDir)
+ log.verbose('legacy', 'copying to "dev" dir', devDir)
var reader = fstream.Reader({ path: legacyDir, type: 'Directory' })
- , writer = fstream.Writer({ path: devDir, type: 'Directory' })
+ var writer = fstream.Writer({ path: devDir, type: 'Directory' })
reader.on('entry', function onEntry (entry) {
- gyp.verbose('reading entry', entry.path)
+ log.verbose('legacy', 'reading entry:', entry.path)
entry.on('entry', onEntry)
})
}
function downloadNodeLib (done) {
- gyp.verbose('on Windows; need to download `node.lib`...')
+ log.verbose('on Windows; need to download `node.lib`...')
var dir32 = path.resolve(devDir, 'ia32')
, dir64 = path.resolve(devDir, 'x64')
, nodeLibPath32 = path.resolve(dir32, 'node.lib')
, nodeLibUrl32 = distUrl + '/v' + version + '/node.lib'
, nodeLibUrl64 = distUrl + '/v' + version + '/x64/node.lib'
- gyp.verbose('32-bit node.lib dir', dir32)
- gyp.verbose('64-bit node.lib dir', dir64)
- gyp.verbose('`node.lib` 32-bit url', nodeLibUrl32)
- gyp.verbose('`node.lib` 64-bit url', nodeLibUrl64)
+ log.verbose('32-bit node.lib dir', dir32)
+ log.verbose('64-bit node.lib dir', dir64)
+ log.verbose('`node.lib` 32-bit url', nodeLibUrl32)
+ log.verbose('`node.lib` 64-bit url', nodeLibUrl64)
var async = 2
mkdir(dir32, function (err) {
if (err) return done(err)
- gyp.verbose('streaming 32-bit node.lib to:', nodeLibPath32)
+ log.verbose('streaming 32-bit node.lib to:', nodeLibPath32)
var req = download(nodeLibUrl32)
req.on('error', done)
})
mkdir(dir64, function (err) {
if (err) return done(err)
- gyp.verbose('streaming 64-bit node.lib to:', nodeLibPath64)
+ log.verbose('streaming 64-bit node.lib to:', nodeLibPath64)
var req = download(nodeLibUrl64)
req.on('error', done)
var fs = require('graceful-fs')
, path = require('path')
+ , log = require('npmlog')
function list (gyp, args, callback) {
- gyp.verbose('using node-gyp dir', gyp.devDir)
+ var devDir = gyp.devDir
+ log.verbose('list', 'using node-gyp dir:', devDir)
- // readdir the node-gyp dir
- fs.readdir(gyp.devDir, onreaddir)
+ // readdir() the node-gyp dir
+ fs.readdir(devDir, onreaddir)
function onreaddir (err, versions) {
if (err && err.code != 'ENOENT') {
return callback(err)
}
- if (versions) {
+ if (Array.isArray(versions)) {
versions = versions.filter(function (v) { return v != 'current' })
} else {
versions = []
var fs = require('graceful-fs')
, path = require('path')
, nopt = require('nopt')
+ , log = require('npmlog')
, child_process = require('child_process')
, EE = require('events').EventEmitter
, inherits = require('util').inherits
, 'rm': 'remove'
}
+// differentiate node-gyp's logs from npm's
+log.heading = 'gyp'
+
/**
* The `gyp` function.
*/
}
function Gyp () {
- var me = this
+ var self = this
// set the dir where node-gyp dev files get installed
- // TODO: make this configurable?
+ // TODO: make this *more* configurable?
// see: https://github.com/TooTallNate/node-gyp/issues/21
var homeDir = process.env.HOME || process.env.USERPROFILE
this.devDir = path.resolve(homeDir, '.node-gyp')
this.commands = {}
commands.forEach(function (command) {
- me.commands[command] = function (argv, callback) {
- me.verbose('command', command, argv)
- return require('./' + command)(me, argv, callback)
+ self.commands[command] = function (argv, callback) {
+ log.verbose('command', command, argv)
+ return require('./' + command)(self, argv, callback)
}
})
-
- Object.keys(aliases).forEach(function (alias) {
- me.commands[alias] = me.commands[aliases[alias]]
- })
}
inherits(Gyp, EE)
exports.Gyp = Gyp
, directory: String // bin
, msvs_version: String // 'configure'
, ensure: Boolean // 'install'
- , verbose: Boolean // everywhere
, solution: String // 'build' (windows only)
, proxy: String // 'install'
, nodedir: String // 'configure'
+ , loglevel: String // everywhere
}
/**
proto.shorthands = {
release: '--no-debug'
, C: '--directory'
- , d: '--debug'
+ , debug: '--debug'
+ , silly: '--loglevel=silly'
+ , verbose: '--loglevel=verbose'
}
/**
+ * expose the command aliases for the bin file to use.
+ */
+
+proto.aliases = aliases
+
+/**
* Parses the given argv array and sets the 'opts',
* 'argv' and 'command' properties.
*/
var commands = []
this.argv.slice().forEach(function (arg) {
- if (arg in this.commands) {
+ if (arg in this.commands || arg in this.aliases) {
this.argv.splice(this.argv.indexOf(arg), 1)
commands.push(arg)
}
if (name.indexOf(npm_config_prefix) !== 0) return
var val = process.env[name]
if (name === npm_config_prefix + 'loglevel') {
- // "loglevel" is a special case; check for "verbose"
- if (val === 'verbose') {
- this.opts.verbose = true
- }
+ log.level = val
} else {
// take the config name and check if it's one that node-gyp cares about
name = name.substring(npm_config_prefix.length)
}
}
}, this)
+
+ if (this.opts.loglevel) {
+ log.level = this.opts.loglevel
+ }
+ log.resume()
}
/**
opts.customFds = [ 0, 1, 2 ]
}
var cp = child_process.spawn(command, args, opts)
- this.emit('spawn', command, args, cp)
+ log.info('spawn', command)
+ log.info('spawn args', args)
return cp
}
/**
- * Logging mechanisms.
+ * Prints the usage instructions and then exits.
*/
-proto.info = function info () {
- var args = Array.prototype.slice.call(arguments)
- args.unshift('info')
- this.emit.apply(this, args)
-}
-proto.warn = function warn () {
- var args = Array.prototype.slice.call(arguments)
- args.unshift('warn')
- this.emit.apply(this, args)
-}
-
-proto.verbose = function verbose () {
- var args = Array.prototype.slice.call(arguments)
- args.unshift('verbose')
- this.emit.apply(this, args)
-}
-
proto.usageAndExit = function usageAndExit () {
var usage = [
''
, ' $ node-gyp <command> --help'
, ''
, 'node-gyp@' + this.version + ' ' + path.resolve(__dirname, '..')
+ , 'node@' + process.versions.node
].join('\n')
console.log(usage)
exports.usage = 'Runs "clean", "configure" and "build" all at once'
+var log = require('npmlog')
+
function rebuild (gyp, argv, callback) {
// first "clean"
gyp.commands.clean([], function (err) {
if (err) {
// don't bail
- gyp.info(err.stack);
+ log.info('rebuild clean failed', err.stack);
}
gyp.commands.configure([], function (err) {
var fs = require('fs')
, rm = require('rimraf')
, path = require('path')
+ , log = require('npmlog')
, semver = require('semver')
function remove (gyp, argv, callback) {
- gyp.verbose('using node-gyp dir', gyp.devDir)
+ var devDir = gyp.devDir
+ log.verbose('remove', 'using node-gyp dir:', devDir)
// get the user-specified version to remove
var v = argv[0] || gyp.opts.target
+ log.verbose('remove', 'removing target version:', v)
if (!v) {
return callback(new Error('You must specify a version number to remove. Ex: "' + process.version + '"'))
version = version.slice(1, 4).join('.')
var versionPath = path.resolve(gyp.devDir, version)
- gyp.verbose('removing development files for version', version)
+ log.verbose('remove', 'removing development files for version:', version)
// first check if its even installed
fs.stat(versionPath, function (err, stat) {
if (err) {
if (err.code == 'ENOENT') {
- gyp.info('version was already not installed', version)
- callback()
+ callback(null, 'version was already uninstalled: ' + version)
} else {
callback(err)
}
+++ /dev/null
-
-/**
- * Tiny wrapper around substack's mkdirp module, to add a return value
- * to it. `true` if any directories were created in the process, `false`
- * if the target directory already existed. The `err` is still the first
- * argument in case anything actually wrong happens.
- */
-
-var fs = require('fs')
- , mkdirp_ = require('mkdirp')
-
-module.exports = function mkdirp (path, cb) {
- // first stat() to see if the target exists
- fs.stat(path, function (err) {
- if (err) {
- if (err.code == 'ENOENT') {
- // doesn't exist, mkdirp it
- mkdirp_(path, function (err) {
- if (err) return cb(err)
- cb(err, true)
- })
- } else {
- cb(err)
- }
- return
- }
- cb(err, false)
- })
-}
+++ /dev/null
-
-/**
- * Reference: http://en.wikipedia.org/wiki/ANSI_escape_code
- */
-
-/**
- * Module dependencies.
- */
-
-var prefix = '\033[' // For all escape codes
- , suffix = 'm'; // Only for color codes
-
-/**
- * The ANSI escape sequences.
- */
-
-var codes = {
- up: 'A'
- , down: 'B'
- , forward: 'C'
- , back: 'D'
- , nextLine: 'E'
- , previousLine: 'F'
- , horizontalAbsolute: 'G'
- , eraseData: 'J'
- , eraseLine: 'K'
- , scrollUp: 'S'
- , scrollDown: 'T'
- , savePosition: 's'
- , restorePosition: 'u'
- , hide: '?25l'
- , show: '?25h'
-};
-
-/**
- * Rendering ANSI codes.
- */
-
-var styles = {
- bold: 1
- , italic: 3
- , underline: 4
- , inverse: 7
-};
-
-/**
- * The negating ANSI code for the rendering modes.
- */
-
-var reset = {
- bold: 22
- , italic: 23
- , underline: 24
- , inverse: 27
- , foreground: 39
- , background: 49
-};
-
-/**
- * The standard, styleable ANSI colors.
- */
-
-var colors = {
- white: 37
- , grey: 90
- , black: 30
- , blue: 34
- , cyan: 36
- , green: 32
- , magenta: 35
- , red: 31
- , yellow: 33
-};
-
-
-/**
- * Creates a Cursor instance based off the given `writable stream` instance.
- */
-
-function ansi (stream, options) {
- return new Cursor(stream, options);
-}
-module.exports = exports = ansi;
-
-/**
- * The `Cursor` class.
- */
-
-function Cursor (stream, options) {
- this.stream = stream;
- this.fg = this.foreground = new Foreground(this);
- this.bg = this.background = new Background(this);
-}
-exports.Cursor = Cursor;
-
-/**
- * The `Foreground` class.
- */
-
-function Foreground (cursor) {
- this.cursor = cursor;
-}
-exports.Foreground = Foreground;
-
-/**
- * The `Background` class.
- */
-
-function Background (cursor) {
- this.cursor = cursor;
-}
-exports.Background = Background;
-
-/**
- * Helper function that calls `write()` on the underlying Stream.
- */
-
-Cursor.prototype.write = function () {
- this.stream.write.apply(this.stream, arguments);
- return this;
-}
-
-/**
- * Set up the positional ANSI codes.
- */
-
-Object.keys(codes).forEach(function (name) {
- var code = String(codes[name]);
- Cursor.prototype[name] = function () {
- var c = code;
- if (arguments.length > 0) {
- c = Math.round(arguments[0]) + code;
- }
- this.write(prefix + c);
- return this;
- }
-});
-
-/**
- * Set up the functions for the rendering ANSI codes.
- */
-
-Object.keys(styles).forEach(function (style) {
- var name = style[0].toUpperCase() + style.substring(1);
-
- Cursor.prototype[style] = function () {
- this.write(prefix + styles[style] + suffix);
- return this;
- }
-
- Cursor.prototype['reset'+name] = function () {
- this.write(prefix + reset[style] + suffix);
- return this;
- }
-});
-
-/**
- * Setup the functions for the standard colors.
- */
-
-Object.keys(colors).forEach(function (color) {
- Foreground.prototype[color] = function () {
- this.cursor.write(prefix + colors[color] + suffix);
- return this.cursor;
- }
-
- var bgCode = colors[color] + 10;
- Background.prototype[color] = function () {
- this.cursor.write(prefix + bgCode + suffix);
- return this.cursor;
- }
-
- Cursor.prototype[color] = function () {
- return this.foreground[color]();
- }
-});
-
-/**
- * Makes a beep sound!
- */
-
-Cursor.prototype.beep = function () {
- this.write('\007');
- return this;
-}
-
-/**
- * Moves cursor to specific position
- */
-
-Cursor.prototype.goto = function (x, y) {
- x = ~~x;
- y = ~~y;
- this.write(prefix + y + ';' + x + 'H');
- return this;
-}
-
-/**
- * Reset the foreground color.
- */
-
-Foreground.prototype.reset = function () {
- this.cursor.write(prefix + reset.foreground + suffix);
- return this.cursor;
-}
-
-/**
- * Reset the background color.
- */
-
-Background.prototype.reset = function () {
- this.cursor.write(prefix + reset.background + suffix);
- return this.cursor;
-}
-
-/**
- * Resets all ANSI formatting on the stream.
- */
-
-Cursor.prototype.reset = function () {
- this.write(prefix + '0' + suffix);
- return this;
-}
-
-/**
- * Sets the foreground color with the given RGB values.
- * The closest match out of the 216 colors is picked.
- */
-
-Foreground.prototype.rgb = function (r, g, b) {
- this.cursor.write(prefix + '38;5;' + rgb(r, g, b) + suffix);
- return this.cursor;
-}
-
-/**
- * Sets the background color with the given RGB values.
- * The closest match out of the 216 colors is picked.
- */
-
-Background.prototype.rgb = function (r, g, b) {
- this.cursor.write(prefix + '48;5;' + rgb(r, g, b) + suffix);
- return this.cursor;
-}
-
-/**
- * Same as `cursor.fg.rgb()`.
- */
-
-Cursor.prototype.rgb = function (r, g, b) {
- return this.foreground.rgb(r, g, b);
-}
-
-/**
- * Accepts CSS color codes for use with ANSI escape codes.
- * For example: `#FF000` would be bright red.
- */
-
-Foreground.prototype.hex = Background.prototype.hex = function (color) {
- var rgb = hex(color);
- return this.rgb(rgb[0], rgb[1], rgb[2]);
-}
-
-/**
- * Same as `cursor.fg.hex()`.
- */
-
-Cursor.prototype.hex = function (color) {
- return this.foreground.hex(color);
-}
-
-function rgb (r, g, b) {
- var red = r / 255 * 5
- , green = g / 255 * 5
- , blue = b / 255 * 5;
- return rgb5(red, green, blue);
-}
-
-function rgb5 (r, g, b) {
- var red = Math.round(r)
- , green = Math.round(g)
- , blue = Math.round(b);
- return 16 + (red*36) + (green*6) + blue;
-}
-
-function hex (color) {
- var c = color[0] === '#' ? color.substring(1) : color
- , r = c.substring(0, 2)
- , g = c.substring(2, 4)
- , b = c.substring(4, 6);
- return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)];
-}
+++ /dev/null
-var http = require('http')
- , ansi = require('./')
- , Canvas = require('canvas')
- , Image = Canvas.Image
- , imageFile = process.argv[2] || __dirname + '/examples/yoshi.png'
- , image = require('fs').readFileSync(imageFile)
-
-var img = new Image()
-img.src = image
-
-var server = http.createServer(function (req, res) {
- draw(res);
-})
-
-server.listen(8080, function () {
- console.error('HTTP server listening on:', this.address())
-})
-
-function draw (stream) {
- var cursor = ansi(stream)
- , pixel = ' '
- , width = img.width
- , scaleW = img.width > width ? width / img.width : 1
- , w = Math.floor(img.width * scaleW)
- , h = Math.floor(img.height * scaleW);
-
- var canvas = new Canvas(w, h)
- , ctx = canvas.getContext('2d');
-
- ctx.drawImage(img, 0, 0, w, h);
-
- var data = ctx.getImageData(0, 0, w, h).data;
-
- for (var i=0, l=data.length; i<l; i+=4) {
- var r = data[i]
- , g = data[i+1]
- , b = data[i+2]
- , alpha = data[i+3];
- if (alpha > 0) {
- cursor.bg.rgb(r, g, b);
- } else {
- cursor.bg.reset();
- }
- stream.write(pixel);
- if ((i/4|0) % w === (w-1)) {
- cursor.bg.reset();
- stream.write('\n');
- }
- }
- stream.end();
-}
{
"name": "node-gyp",
- "description": "Node.js native addon build tool",
+ "description": "`node-gyp` is a cross-platform command-line tool written in Node.js for compiling",
"keywords": [
"native",
"addon",
"bindings",
"gyp"
],
- "version": "0.4.5",
- "installVersion": 8,
+ "version": "0.5.2",
+ "installVersion": 9,
"author": {
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",
},
"main": "./lib/node-gyp.js",
"dependencies": {
- "ansi": "0.0.x",
"glob": "3",
"graceful-fs": "1",
"fstream": "~0.1.13",
- "minimatch": "0.2.x",
+ "minimatch": "0.2",
"mkdirp": "0.3",
"nopt": "1",
- "request": "2.9.x",
+ "npmlog": "0",
+ "request": "2.9",
"rimraf": "2",
"semver": "1",
"tar": "~0.1.12",
"engines": {
"node": ">= 0.6.0"
},
- "_npmUser": {
- "name": "isaacs",
- "email": "i@izs.me"
- },
- "_id": "node-gyp@0.4.5",
- "devDependencies": {},
- "optionalDependencies": {},
- "_engineSupported": true,
- "_npmVersion": "1.1.23",
- "_nodeVersion": "v0.7.10-pre",
- "_defaultsLoaded": true,
+ "readme": "node-gyp\n=========\n### Node.js native addon build tool\n\n`node-gyp` is a cross-platform command-line tool written in Node.js for compiling\nnative addon modules for Node.js, which takes away the pain of dealing with the\nvarious differences in build platforms. It is the replacement to the `node-waf`\nprogram which is removed for node `v0.8`. If you have a native addon for node that\nstill has a `wscript` file, then you should definitely add a `binding.gyp` file\nto support the latest versions of node.\n\nMultiple target versions of node are supported (i.e. `0.6`, `0.7`,..., `1.0`,\netc.), regardless of what version of node is actually installed on your system\n(`node-gyp` downloads the necessary development files for the target version).\n\n#### Features:\n\n * Easy to use, consistent interface\n * Same commands to build your module on every platform\n * Supports multiple target versions of Node\n\n\nInstallation\n------------\n\nYou can install with `npm`:\n\n``` bash\n$ npm install -g node-gyp\n```\n\nYou will also need to install:\n\n * On Unix:\n * `python`\n * `make`\n * A proper C/C++ compiler toolchain, like GCC\n * On Windows:\n * [Python][windows-python] ([`v2.7.2`][windows-python-v2.7.2] recommended, `v3.x.x` not yet supported)\n * Microsoft Visual C++ ([Express][msvc] version works well)\n * For 64-bit builds of node and native modules you will _also_ need the [Windows 7 64-bit SDK][win7sdk]\n\nHow to Use\n----------\n\nTo compile your native addon, first go to its root directory:\n\n``` bash\n$ cd my_node_addon\n```\n\nThe next step is to generate the appropriate project build files for the current\nplatform. Use `configure` for that:\n\n``` bash\n$ node-gyp configure\n```\n\n__Note__: The `configure` step looks for the `binding.gyp` file in the current\ndirectory to processs. See below for instructions on creating the `binding.gyp` file.\n\nNow you will have either a `Makefile` (on Unix platforms) or a `vcxproj` file\n(on Windows) in the `build/` directory. Next invoke the `build` command:\n\n``` bash\n$ node-gyp build\n```\n\nNow you have your compiled `.node` bindings file! The compiled bindings end up\nin `build/Debug/` or `build/Release/`, depending on the build mode. At this point\nyou can require the `.node` file with Node and run your tests!\n\n__Note:__ To create a _Debug_ build of the bindings file, pass the `--debug` (or\n`-d`) switch when running the either `configure` or `build` command.\n\n\nThe \"binding.gyp\" file\n----------------------\n\nPreviously when node had `node-waf` you had to write a `wscript` file. The\nreplacement for that is the `binding.gyp` file, which describes the configuration\nto build your module in a JSON-like format. This file gets placed in the root of\nyour package, alongside the `package.json` file.\n\nA barebones `gyp` file appropriate for building a node addon looks like:\n\n``` json\n{\n \"targets\": [\n {\n \"target_name\": \"binding\",\n \"sources\": [ \"src/binding.cc\" ]\n }\n ]\n}\n```\n\nSome additional resources for writing `gyp` files:\n\n * [\"Hello World\" node addon example](https://github.com/joyent/node/tree/master/test/addons/hello-world)\n * [gyp user documentation](http://code.google.com/p/gyp/wiki/GypUserDocumentation)\n * [gyp input format reference](http://code.google.com/p/gyp/wiki/InputFormatReference)\n * ['\"binding.gyp\" files out in the wild' wiki page](https://github.com/TooTallNate/node-gyp/wiki/%22binding.gyp%22-files-out-in-the-wild)\n\n\nCommands\n--------\n\n`node-gyp` responds to the following commands:\n\n * `build` - Invokes `make`/`msbuild.exe` and builds the native addon\n * `clean` - Removes any generated project files and the `out` dir\n * `configure` - Generates project build files for the current platform\n * `rebuild` - Runs \"clean\", \"configure\" and \"build\" all at once\n * `install` - Installs node development files for the given version.\n * `list` - Lists the currently installed node development file versions\n * `remove` - Removes a node development files for a given version\n\n\nLicense\n-------\n\n(The MIT License)\n\nCopyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n[windows-python]: http://www.python.org/getit/windows\n[windows-python-v2.7.2]: http://www.python.org/download/releases/2.7.2#download\n[msvc]: http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-express\n[win7sdk]: http://www.microsoft.com/download/en/details.aspx?displayLang=en&id=8279\n",
+ "_id": "node-gyp@0.5.2",
"dist": {
- "shasum": "9ddb9c4218322fd3d6b9771b27c236c4e5965e3b"
+ "shasum": "7410e3dd9d950592ee80d09c7e5ef22286f79c0f"
},
- "_from": "node-gyp@~0.4.4"
+ "_from": "node-gyp@0.5.2"
}
--- /dev/null
+test/fixtures/cache
--- /dev/null
+Copyright (c) Isaac Z. Schlueter
+All rights reserved.
+
+The BSD License
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+# npm-registry-client
+
+The code that npm uses to talk to the registry.
+
+It handles all the caching and HTTP calls.
+
+## Usage
+
+```javascript
+var RegClient = require('npm-registry-client')
+var client = new RegClient(options)
+
+client.get("npm", "latest", 1000, function (er, data, raw, res) {
+ // error is an error if there was a problem.
+ // data is the parsed data object
+ // raw is the json string
+ // res is the response from couch
+})
+```
+
+# Options
+
+* `registry` **Required** {String} URL to the registry
+* `cache` **Required** {String} Path to the cache folder
+* `alwaysAuth` {Boolean} Auth even for GET requests.
+* `auth` {String} A base64-encoded `username:password`
+* `email` {String} User's email address
+* `tag` {String} The default tag to use when publishing new packages.
+ Default = `"latest"`
+* `ca` {String} Cerficate signing authority certificates to trust.
+* `strictSSL` {Boolean} Whether or not to be strict with SSL
+ certificates. Default = `true`
+* `userAgent` {String} User agent header to send. Default =
+ `"node/{process.version}"`
+* `log` {Object} The logger to use. Defaults to `require("npmlog")` if
+ that works, otherwise logs are disabled.
+* `retries` {Number} Number of times to retry on GET failures.
+ Default=2
+* `retryFactor` {Number} `factor` setting for `node-retry`. Default=10
+* `retryMinTimeout` {Number} `minTimeout` setting for `node-retry`.
+ Default=10000 (10 seconds)
+* `retryMaxTimeout` {Number} `maxTimeout` setting for `node-retry`.
+ Default=60000 (60 seconds)
+
+# client.request(method, where, [what], [etag], [nofollow], cb)
+
+* `method` {String} HTTP method
+* `where` {String} Path to request on the server
+* `what` {Stream | Buffer | String | Object} The request body. Objects
+ that are not Buffers or Streams are encoded as JSON.
+* `etag` {String} The cached ETag
+* `nofollow` {Boolean} Prevent following 302/301 responses
+* `cb` {Function}
+ * `error` {Error | null}
+ * `data` {Object} the parsed data object
+ * `raw` {String} the json
+ * `res` {Response Object} response from couch
+
+Make a request to the registry. All the other methods are wrappers
+around this. one.
+
+# client.adduser(username, password, email, cb)
+
+* `username` {String}
+* `password` {String}
+* `email` {String}
+* `cb` {Function}
+
+Add a user account to the registry, or verify the credentials.
+
+# client.get(url, [timeout], [nofollow], [staleOk], cb)
+
+* `url` {String} The url path to fetch
+* `timeout` {Number} Number of seconds old that a cached copy must be
+ before a new request will be made.
+* `nofollow` {Boolean} Do not follow 301/302 responses
+* `staleOk` {Boolean} If there's cached data available, then return that
+ to the callback quickly, and update the cache the background.
+
+Fetches data from the registry via a GET request, saving it in
+the cache folder with the ETag.
+
+# client.publish(data, tarball, [readme], cb)
+
+* `data` {Object} Package data
+* `tarball` {String | Stream} Filename or stream of the package tarball
+* `readme` {String} Contents of the README markdown file
+* `cb` {Function}
+
+Publish a package to the registry.
+
+Note that this does not create the tarball from a folder. However, it
+can accept a gzipped tar stream or a filename to a tarball.
+
+# client.star(package, starred, cb)
+
+* `package` {String} Name of the package to star
+* `starred` {Boolean} True to star the package, false to unstar it.
+* `cb` {Function}
+
+Star or unstar a package.
+
+Note that the user does not have to be the package owner to star or
+unstar a package, though other writes do require that the user be the
+package owner.
+
+# client.tag(project, version, tag, cb)
+
+* `project` {String} Project name
+* `version` {String} Version to tag
+* `tag` {String} Tag name to apply
+* `cb` {Function}
+
+Mark a version in the `dist-tags` hash, so that `pkg@tag`
+will fetch the specified version.
+
+# client.unpublish(name, [ver], cb)
+
+* `name` {String} package name
+* `ver` {String} version to unpublish. Leave blank to unpublish all
+ versions.
+* `cb` {Function}
+
+Remove a version of a package (or all versions) from the registry. When
+the last version us unpublished, the entire document is removed from the
+database.
+
+# client.upload(where, file, [etag], [nofollow], cb)
+
+* `where` {String} URL path to upload to
+* `file` {String | Stream} Either the filename or a readable stream
+* `etag` {String} Cache ETag
+* `nofollow` {Boolean} Do not follow 301/302 responses
+* `cb` {Function}
+
+Upload an attachment. Mostly used by `client.publish()`.
--- /dev/null
+
+// utilities for working with the js-registry site.
+
+module.exports = RegClient
+
+var fs = require('fs')
+, url = require('url')
+, path = require('path')
+, CouchLogin = require('couch-login')
+, npmlog
+
+try {
+ npmlog = require("npmlog")
+} catch (er) {
+ npmlog = { error: noop, warn: noop, info: noop,
+ verbose: noop, silly: noop, http: noop,
+ pause: noop, resume: noop }
+}
+
+function noop () {}
+
+function RegClient (options) {
+ // a registry url must be provided.
+ var registry = url.parse(options.registry)
+ if (!registry.protocol) throw new Error(
+ 'Invalid registry: ' + registry.url)
+ this.registry = registry.href
+ if (this.registry.slice(-1) !== '/') {
+ this.registry += '/'
+ }
+
+ this.retries = options.retries || 2
+ this.retryFactor = options.retryFactor || 10
+ this.retryMinTimeout = options.retryMinTimeout || 10000
+ this.retryMaxTimeout = options.retryMaxTimeout || 60000
+
+ this.cache = options.cache
+ if (!this.cache) throw new Error("Cache dir is required")
+
+ this.alwaysAuth = options.alwaysAuth || false
+
+ this.auth = options.auth || null
+ if (this.auth) {
+ var a = new Buffer(this.auth, "base64").toString()
+ a = a.split(":")
+ this.username = a.shift()
+ this.password = a.join(":")
+ } else {
+ this.username = options.username
+ this.password = options.password
+
+ // if username and password are set, but auth isn't, use them.
+ if (this.username && this.password) {
+ var a = this.username + ":" + this.password
+ this.auth = new Buffer(a, "utf8").toString("base64")
+ }
+ }
+
+ if (this.auth && !this.alwaysAuth) {
+ // if we're always authing, then we just send the
+ // user/pass on every thing. otherwise, create a
+ // session, and use that.
+ this.token = options.token
+ this.couchLogin = new CouchLogin(this.registry, this.token)
+ }
+
+ this.email = options.email || null
+ this.defaultTag = options.tag || "latest"
+
+ this.ca = options.ca || null
+
+ this.strictSSL = options.strictSSL
+ if (this.strictSSL === undefined) this.strictSSL = true
+
+ this.userAgent = options.userAgent
+ if (this.userAgent === undefined) {
+ this.userAgent = 'node/' + process.version
+ }
+
+ this.cacheMin = options.cacheMin || 0
+ this.cacheMax = options.cacheMax || Infinity
+
+ this.proxy = options.proxy
+ this.httpsProxy = options.httpsProxy || options.proxy
+
+ this.log = options.log || npmlog
+}
+
+require('fs').readdirSync(__dirname + "/lib").forEach(function (f) {
+ if (!f.match(/\.js$/)) return
+ RegClient.prototype[f.replace(/\.js$/, '')] = require('./lib/' + f)
+})
--- /dev/null
+
+module.exports = adduser
+
+var uuid = require("node-uuid")
+ , crypto
+
+try {
+} catch (ex) {}
+
+function sha (s) {
+ return crypto.createHash("sha1").update(s).digest("hex")
+}
+
+function adduser (username, password, email, cb) {
+ if (!crypto) crypto = require("crypto")
+
+ password = ("" + (password || "")).trim()
+ if (!password) return cb(new Error("No password supplied."))
+
+ email = ("" + (email || "")).trim()
+ if (!email) return cb(new Error("No email address supplied."))
+ if (!email.match(/^[^@]+@[^\.]+\.[^\.]+/)) {
+ return cb(new Error("Please use a real email address."))
+ }
+
+ if (password.indexOf(":") !== -1) return cb(new Error(
+ "Sorry, ':' chars are not allowed in passwords.\n"+
+ "See <https://issues.apache.org/jira/browse/COUCHDB-969> for why."))
+
+ var salt = uuid()
+ , userobj =
+ { name : username
+ , salt : salt
+ , password_sha : sha(password + salt)
+ , email : email
+ , _id : 'org.couchdb.user:'+username
+ , type : "user"
+ , roles : []
+ , date: new Date().toISOString()
+ }
+
+ cb = done.call(this, cb)
+
+ this.log.verbose("adduser", "before first PUT", userobj)
+ this.request('PUT'
+ , '/-/user/org.couchdb.user:'+encodeURIComponent(username)
+ , userobj
+ , function (error, data, json, response) {
+ // if it worked, then we just created a new user, and all is well.
+ // but if we're updating a current record, then it'll 409 first
+ if (error && !this.auth) {
+ // must be trying to re-auth on a new machine.
+ // use this info as auth
+ var b = new Buffer(username + ":" + password)
+ this.auth = b.toString("base64")
+ }
+
+ if (!error || !response || response.statusCode !== 409) {
+ return cb(error, data, json, response)
+ }
+
+ this.log.verbose("adduser", "update existing user")
+ return this.request('GET'
+ , '/-/user/org.couchdb.user:'+encodeURIComponent(username)
+ , function (er, data, json, response) {
+ if (er || data.error) {
+ return cb(er, data, json, response)
+ }
+ Object.keys(data).forEach(function (k) {
+ if (!userobj[k]) {
+ userobj[k] = data[k]
+ }
+ })
+ this.log.verbose("adduser", "userobj", userobj)
+ this.request('PUT'
+ , '/-/user/org.couchdb.user:'+encodeURIComponent(username)
+ + "/-rev/" + userobj._rev
+ , userobj
+ , cb )
+ }.bind(this))
+ }.bind(this))
+}
+
+function done (cb) {
+ return function (error, data, json, response) {
+ if (!error && (!response || response.statusCode === 201)) {
+ return cb(error, data, json, response)
+ }
+ this.log.verbose("adduser", "back", [error, data, json])
+ if (!error) {
+ error = new Error( (response && response.statusCode || "") + " "+
+ "Could not create user\n"+JSON.stringify(data))
+ }
+ if (response
+ && (response.statusCode === 401 || response.statusCode === 403)) {
+ this.log.warn("adduser", "Incorrect username or password\n"
+ +"You can reset your account by visiting:\n"
+ +"\n"
+ +" http://admin.npmjs.org/reset\n")
+ }
+
+ return cb(error)
+ }.bind(this)
+}
module.exports = get
-var GET = require("./request.js").GET
- , fs = require("graceful-fs")
- , npm = require("../../npm.js")
+var fs = require("graceful-fs")
, path = require("path")
- , log = require("../log.js")
, mkdir = require("mkdirp")
- , cacheStat = null
, chownr = require("chownr")
-function get (project, version, timeout, nofollow, staleOk, cb) {
+function get (uri, timeout, nofollow, staleOk, cb) {
if (typeof cb !== "function") cb = staleOk, staleOk = false
if (typeof cb !== "function") cb = nofollow, nofollow = false
if (typeof cb !== "function") cb = timeout, timeout = -1
if (typeof cb !== "function") cb = version, version = null
- if (typeof cb !== "function") cb = project, project = null
- if (typeof cb !== "function") {
- throw new Error("No callback provided to registry.get")
- }
- timeout = Math.min(timeout, npm.config.get("cache-max"))
- timeout = Math.max(timeout, npm.config.get("cache-min"))
+ timeout = Math.min(timeout, this.cacheMax)
+ timeout = Math.max(timeout, this.cacheMin)
if ( process.env.COMP_CWORD !== undefined
&& process.env.COMP_LINE !== undefined
&& process.env.COMP_POINT !== undefined
) timeout = Math.max(timeout, 60000)
- var uri = []
- uri.push(project || "")
- if (version) uri.push(version)
- uri = uri.join("/")
-
// /-/all is special.
// It uses timestamp-based caching and partial updates,
// because it is a monster.
if (uri === "/-/all") {
- return requestAll(cb)
+ return requestAll.call(this, cb)
}
- var cache = path.join(npm.cache, uri, ".cache.json")
+ var cache = path.join(this.cache, uri, ".cache.json")
fs.stat(cache, function (er, stat) {
if (!er) fs.readFile(cache, function (er, data) {
try { data = JSON.parse(data) }
catch (ex) { data = null }
- get_(uri, timeout, cache, stat, data, nofollow, staleOk, cb)
- })
- else get_(uri, timeout, cache, null, null, nofollow, staleOk, cb)
- })
+ get_.call(this, uri, timeout, cache, stat, data, nofollow, staleOk, cb)
+ }.bind(this))
+ else get_.call(this, uri, timeout, cache, null, null, nofollow, staleOk, cb)
+ }.bind(this))
}
function requestAll (cb) {
- var cache = path.join(npm.cache, "/-/all", ".cache.json")
+ var cache = path.join(this.cache, "/-/all", ".cache.json")
- mkdir(path.join(npm.cache, "-", "all"), function (er) {
+ mkdir(path.join(this.cache, "-", "all"), function (er) {
fs.readFile(cache, function (er, data) {
- if (er) return requestAll_(0, {}, cb)
+ if (er) return requestAll_.call(this, 0, {}, cb)
try {
data = JSON.parse(data)
} catch (ex) {
fs.writeFile(cache, "{}", function (er) {
- if (er) return cb(new Error("Broken cache. "
- +"Please run 'npm cache clean' "
- +"and try again."))
- return requestAll_(0, {}, cb)
+ if (er) return cb(new Error("Broken cache."))
+ return requestAll_.call(this, 0, {}, cb)
})
}
var t = +data._updated || 0
- requestAll_(t, data, cb)
- })
- })
+ requestAll_.call(this, t, data, cb)
+ }.bind(this))
+ }.bind(this))
}
function requestAll_ (c, data, cb) {
var uri = "/-/all/since?stale=update_after&startkey=" + c
if (c === 0) {
- log.warn("Building the local index for the first time, please be patient")
+ this.log.warn("", "Building the local index for the first time, please be patient")
uri = "/-/all"
}
- var cache = path.join(npm.cache, "-/all", ".cache.json")
- GET(uri, function (er, updates, _, res) {
+ var cache = path.join(this.cache, "-/all", ".cache.json")
+ this.request('GET', uri, function (er, updates, _, res) {
if (er) return cb(er, data)
var headers = res.headers
- , updated = Date.parse(headers.date)
+ , updated = data._updated || Date.parse(headers.date)
Object.keys(updates).forEach(function (p) {
data[p] = updates[p]
})
if (data && data._etag) etag = data._etag
if (timeout && timeout > 0 && stat && data) {
if ((Date.now() - stat.mtime.getTime())/1000 < timeout) {
- log.verbose("not expired, no request", "registry.get " +uri)
+ this.log.verbose("registry.get", uri, "not expired, no request")
delete data._etag
return cb(null, data, JSON.stringify(data), {statusCode:304})
}
if (staleOk) {
- log.verbose("staleOk, background update", "registry.get " +uri)
+ this.log.verbose("registry.get", uri, "staleOk, background update")
delete data._etag
process.nextTick(cb.bind( null, null, data, JSON.stringify(data)
, {statusCode: 304} ))
}
}
- GET(uri, etag, nofollow, function (er, remoteData, raw, response) {
+ this.request('GET', uri, null, etag, nofollow, function (er, remoteData, raw, response) {
// if we get an error talking to the registry, but we have it
// from the cache, then just pretend we got it.
if (er && cache && data && !data.error) {
}
if (response) {
- log.silly([response.statusCode, response.headers], "get cb")
+ this.log.silly("registry.get", "cb", [response.statusCode, response.headers])
if (response.statusCode === 304 && etag) {
remoteData = data
- log.verbose(uri+" from cache", "etag")
+ this.log.verbose("etag", uri+" from cache")
}
}
cb(er, data, raw, response)
}
- saveToCache(cache, data, saved)
- })
+ saveToCache.call(this, cache, data, saved)
+ }.bind(this))
}
function saveToCache (cache, data, saved) {
- if (cacheStat) {
- return saveToCache_(cache, data, cacheStat.uid, cacheStat.gid, saved)
+ if (this.cacheStat) {
+ var cs = this.cacheStat
+ return saveToCache_.call(this, cache, data, cs.uid, cs.gid, saved)
}
- fs.stat(npm.cache, function (er, st) {
+ fs.stat(this.cache, function (er, st) {
if (er) {
return fs.stat(process.env.HOME || "", function (er, st) {
// if this fails, oh well.
if (er) return saved()
- cacheStat = st
- return saveToCache(cache, data, saved)
- })
+ this.cacheStat = st
+ return saveToCache.call(this, cache, data, saved)
+ }.bind(this))
}
- cacheStat = st || { uid: null, gid: null }
- return saveToCache(cache, data, saved)
- })
+ this.cacheStat = st || { uid: null, gid: null }
+ return saveToCache.call(this, cache, data, saved)
+ }.bind(this))
}
function saveToCache_ (cache, data, uid, gid, saved) {
--- /dev/null
+
+module.exports = publish
+
+var path = require("path")
+ , url = require("url")
+
+function publish (data, tarball, readme, cb) {
+ if (typeof cb !== "function") cb = readme, readme = ""
+
+ if (!this.email || !this.auth || !this.username) {
+ return cb(new Error("auth and email required for publishing"))
+ }
+
+ // add the dist-url to the data, pointing at the tarball.
+ // if the {name} isn't there, then create it.
+ // if the {version} is already there, then fail.
+ // then:
+ // PUT the data to {config.registry}/{data.name}/{data.version}
+ var registry = this.registry
+
+ readme = readme ? "" + readme : ""
+
+ var fullData =
+ { _id : data.name
+ , name : data.name
+ , description : data.description
+ , "dist-tags" : {}
+ , versions : {}
+ , readme: readme
+ , maintainers :
+ [ { name : this.username
+ , email : this.email
+ }
+ ]
+ }
+
+ var tbName = data.name + "-" + data.version + ".tgz"
+ , tbURI = data.name + "/-/" + tbName
+
+ data._id = data.name+"@"+data.version
+ data.dist = data.dist || {}
+ data.dist.tarball = url.resolve(registry, tbURI)
+ .replace(/^https:\/\//, "http://")
+
+
+ // first try to just PUT the whole fullData, and this will fail if it's
+ // already there, because it'll be lacking a _rev, so couch'll bounce it.
+ this.request("PUT", encodeURIComponent(data.name), fullData,
+ function (er, parsed, json, response) {
+ // get the rev and then upload the attachment
+ // a 409 is expected here, if this is a new version of an existing package.
+ if (er
+ && !(response && response.statusCode === 409)
+ && !( parsed
+ && parsed.reason ===
+ "must supply latest _rev to update existing package" )) {
+ this.log.error("publish", "Failed PUT response "
+ +(response && response.statusCode))
+ return cb(er)
+ }
+ var dataURI = encodeURIComponent(data.name)
+ + "/" + encodeURIComponent(data.version)
+
+ var tag = data.tag || this.defaultTag || "latest"
+ dataURI += "/-tag/" + tag
+
+ // let's see what versions are already published.
+ // could be that we just need to update the bin dist values.
+ this.request("GET", data.name, function (er, fullData) {
+ if (er) return cb(er)
+
+ var exists = fullData.versions && fullData.versions[data.version]
+ if (exists) return cb(conflictError.call(this, data._id))
+
+ // this way, it'll also get attached to packages that were previously
+ // published with a version of npm that lacked this feature.
+ if (!fullData.readme) {
+ data.readme = readme
+ }
+
+ this.request("PUT", dataURI, data, function (er) {
+ if (er) {
+ if (er.message.indexOf("conflict Document update conflict.") === 0) {
+ return cb(conflictError.call(this, data._id))
+ }
+ this.log.error("publish", "Error sending version data")
+ return cb(er)
+ }
+
+ this.log.verbose("publish", "attach 2", [data.name, tarball, tbName])
+ attach.call(this, data.name, tarball, tbName, function (er) {
+ this.log.verbose("publish", "attach 3"
+ ,[er, data.name])
+ return cb(er)
+ }.bind(this))
+ }.bind(this))
+ }.bind(this))
+ }.bind(this)) // pining for fat arrows.
+}
+
+function conflictError (pkgid) {
+ var e = new Error("publish fail")
+ e.code = "EPUBLISHCONFLICT"
+ e.pkgid = pkgid
+ return e
+}
+
+function attach (doc, file, filename, cb) {
+ doc = encodeURIComponent(doc)
+ this.request("GET", doc, function (er, d) {
+ if (er) return cb(er)
+ if (!d) return cb(new Error(
+ "Attempting to upload to invalid doc "+doc))
+ var rev = "-rev/"+d._rev
+ , attURI = doc + "/-/" + encodeURIComponent(filename) + "/" + rev
+ this.log.verbose("uploading", [attURI, file])
+ this.upload(attURI, file, cb)
+ }.bind(this))
+}
--- /dev/null
+module.exports = regRequest
+
+var url = require("url")
+ , fs = require("graceful-fs")
+ , rm = require("rimraf")
+ , asyncMap = require("slide").asyncMap
+ , Stream = require("stream").Stream
+ , request = require("request")
+ , retry = require("retry")
+
+function regRequest (method, where, what, etag, nofollow, cb_) {
+ if (typeof cb_ !== "function") cb_ = nofollow, nofollow = false
+ if (typeof cb_ !== "function") cb_ = etag, etag = null
+ if (typeof cb_ !== "function") cb_ = what, what = null
+
+ // Since there are multiple places where an error could occur,
+ // don't let the cb be called more than once.
+ var errState = null
+ function cb (er) {
+ if (errState) return
+ if (er) errState = er
+ cb_.apply(null, arguments)
+ }
+
+ if (where.match(/^\/?favicon.ico/)) {
+ return cb(new Error("favicon.ico isn't a package, it's a picture."))
+ }
+
+ var registry = this.registry
+
+ var adduserChange = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)\/-rev/
+ , adduserNew = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)/
+ , nu = where.match(adduserNew)
+ , uc = where.match(adduserChange)
+ , isUpload = what || this.alwaysAuth
+ , isDel = method === "DELETE"
+ , authRequired = isUpload && !nu || uc || isDel
+
+ // resolve to a full url on the registry
+ if (!where.match(/^https?:\/\//)) {
+ this.log.verbose("url raw", where)
+
+ var q = where.split("?")
+ where = q.shift()
+ q = q.join("?")
+
+ if (where.charAt(0) !== "/") where = "/" + where
+ where = "." + where.split("/").map(function (p) {
+ p = p.trim()
+ if (p.match(/^org.couchdb.user/)) {
+ return p.replace(/\//g, encodeURIComponent("/"))
+ }
+ return encodeURIComponent(p)
+ }).join("/")
+ if (q) where += "?" + q
+ this.log.verbose("url resolving", [registry, where])
+ where = url.resolve(registry, where)
+ this.log.verbose("url resolved", where)
+ }
+
+ var remote = url.parse(where)
+ , auth = this.auth
+
+ if (authRequired && !this.alwaysAuth) {
+ var couch = this.couchLogin
+ , token = couch && (this.token || couch.token)
+ , validToken = token && couch.valid(token)
+
+ if (!validToken) token = null
+ else this.token = token
+
+ if (couch && !token) {
+ // login to get a valid token
+ var a = { name: this.username, password: this.password }
+ var args = arguments
+ return this.couchLogin.login(a, function (er, cr, data) {
+ if (er || !couch.valid(couch.token)) {
+ er = er || new Error('login error')
+ return cb(er, cr, data)
+ }
+ this.token = this.couchLogin.token
+ return regRequest.call(this, method, where, what, etag, nofollow, cb_)
+ }.bind(this))
+ }
+ }
+
+ // now we either have a valid token, or an auth.
+
+ if (authRequired && !auth && !token) {
+ return cb(new Error(
+ "Cannot insert data into the registry without auth"))
+ }
+
+ if (auth && !token) {
+ remote.auth = new Buffer(auth, "base64").toString("utf8")
+ }
+
+ // Tuned to spread 3 attempts over about a minute.
+ // See formula at <https://github.com/tim-kos/node-retry>.
+ var operation = retry.operation({
+ retries: this.retries,
+ factor: this.retryFactor,
+ minTimeout: this.retryMinTimeout,
+ maxTimeout: this.retryMaxTimeout
+ })
+ var self = this
+ operation.attempt(function (currentAttempt) {
+ self.log.info("retry", "registry request attempt " + currentAttempt
+ + " at " + (new Date()).toLocaleTimeString())
+ makeRequest.call(self, method, remote, where, what, etag, nofollow, token
+ , function (er, parsed, raw, response) {
+ // Only retry on 408, 5xx or no `response`.
+ var statusCode = response && response.statusCode
+ var statusRetry = !statusCode || (statusCode === 408 || statusCode >= 500)
+ if (er && statusRetry && operation.retry(er)) {
+ self.log.info("retry", "will retry, error on last attempt: " + er)
+ return
+ }
+ cb.apply(null, arguments)
+ })
+ })
+}
+
+function makeRequest (method, remote, where, what, etag, nofollow, tok, cb_) {
+ var cbCalled = false
+ function cb () {
+ if (cbCalled) return
+ cbCalled = true
+ cb_.apply(null, arguments)
+ }
+
+ var opts = { url: remote
+ , method: method
+ , ca: this.ca
+ , strictSSL: this.strictSSL }
+ , headers = opts.headers = {}
+ if (etag) {
+ this.log.verbose("etag", etag)
+ headers[method === "GET" ? "if-none-match" : "if-match"] = etag
+ }
+
+ if (tok) {
+ headers.cookie = 'AuthSession=' + tok.AuthSession
+ }
+
+ headers.accept = "application/json"
+
+ headers["user-agent"] = this.userAgent
+
+ opts.proxy = remote.protocol === "https:"
+ ? this.httpsProxy : this.proxy
+
+ // figure out wth 'what' is
+ if (what) {
+ if (Buffer.isBuffer(what) || typeof what === "string") {
+ opts.body = what
+ headers["content-type"] = "application/json"
+ headers["content-length"] = Buffer.byteLength(what)
+ } else if (what instanceof Stream) {
+ headers["content-type"] = "application/octet-stream"
+ if (what.size) headers["content-length"] = what.size
+ } else {
+ delete what._etag
+ opts.json = what
+ }
+ }
+
+ if (nofollow) {
+ opts.followRedirect = false
+ }
+
+ this.log.http(method, remote.href || "/")
+
+ var done = requestDone.call(this, method, where, cb)
+ var req = request(opts, done)
+
+ req.on("error", cb)
+ req.on("socket", function (s) {
+ s.on("error", cb)
+ })
+
+ if (what && (what instanceof Stream)) {
+ what.pipe(req)
+ }
+}
+
+// cb(er, parsed, raw, response)
+function requestDone (method, where, cb) {
+ return function (er, response, data) {
+ if (er) return cb(er)
+
+ this.log.http(response.statusCode, url.parse(where).href)
+
+ var parsed
+
+ if (Buffer.isBuffer(data)) {
+ data = data.toString()
+ }
+
+ if (data && typeof data === "string" && response.statusCode !== 304) {
+ try {
+ parsed = JSON.parse(data)
+ } catch (ex) {
+ ex.message += "\n" + data
+ this.log.verbose("bad json", data)
+ this.log.error("registry", "error parsing json")
+ return cb(ex, null, data, response)
+ }
+ } else if (data) {
+ parsed = data
+ data = JSON.stringify(parsed)
+ }
+
+ // expect data with any error codes
+ if (!data && response.statusCode >= 400) {
+ return cb( response.statusCode + " "
+ + require("http").STATUS_CODES[response.statusCode]
+ , null, data, response )
+ }
+
+ var er = null
+ if (parsed && response.headers.etag) {
+ parsed._etag = response.headers.etag
+ }
+
+ if (parsed && parsed.error && response.statusCode >= 400) {
+ var w = url.parse(where).pathname.substr(1)
+ if (!w.match(/^-/) && parsed.error === "not_found") {
+ w = w.split("/")
+ name = w[w.indexOf("_rewrite") + 1]
+ er = new Error("404 Not Found: "+name)
+ er.code = "E404"
+ er.pkgid = name
+ } else {
+ er = new Error(
+ parsed.error + " " + (parsed.reason || "") + ": " + w)
+ }
+ } else if (method !== "HEAD" && method !== "GET") {
+ // invalidate cache
+ // This is irrelevant for commands that do etag caching, but
+ // ls and view also have a timed cache, so this keeps the user
+ // from thinking that it didn't work when it did.
+ // Note that failure is an acceptable option here, since the
+ // only result will be a stale cache for some helper commands.
+ var path = require("path")
+ , p = url.parse(where).pathname.split("/")
+ , _ = "/"
+ , caches = p.map(function (part) {
+ return _ = path.join(_, part)
+ }).map(function (cache) {
+ return path.join(this.cache, cache, ".cache.json")
+ }, this)
+
+ // if the method is DELETE, then also remove the thing itself.
+ // Note that the search index is probably invalid. Whatever.
+ // That's what you get for deleting stuff. Don't do that.
+ if (method === "DELETE") {
+ p = p.slice(0, p.indexOf("-rev"))
+ caches.push(path.join(this.cache, p.join("/")))
+ }
+
+ asyncMap(caches, rm, function () {})
+ }
+ return cb(er, parsed, data, response)
+ }.bind(this)
+}
--- /dev/null
+
+module.exports = star
+
+function star (package, starred, cb) {
+ if (!this.username) return cb(new Error(
+ "Must be logged in to star/unstar packages"))
+
+ var users = {}
+
+ this.request("GET", package, function (er, fullData) {
+ if (er) return cb(er)
+
+ fullData = { _id: fullData._id
+ , _rev: fullData._rev
+ , users: fullData.users || {} }
+
+ if (starred) {
+ this.log.info("starring", fullData._id)
+ fullData.users[this.username] = true
+ this.log.verbose("starring", fullData)
+ } else {
+ delete fullData.users[this.username]
+ this.log.info("unstarring", fullData._id)
+ this.log.verbose("unstarring", fullData)
+ }
+
+ return this.request("PUT", package, fullData, cb)
+ }.bind(this))
+}
--- /dev/null
+
+module.exports = tag
+
+function tag (project, version, tag, cb) {
+ this.request("PUT", project+"/"+tag, JSON.stringify(version), cb)
+}
--- /dev/null
+
+// fetch the data
+// modify to remove the version in question
+// If no versions remaining, then DELETE
+// else, PUT the modified data
+// delete the tarball
+
+module.exports = unpublish
+
+var semver = require("semver")
+ , url = require("url")
+ , chain = require("slide").chain
+
+function unpublish (name, ver, cb) {
+ if (typeof cb !== "function") cb = ver, ver = null
+
+ this.get(name, null, -1, true, function (er, data) {
+ if (er) {
+ this.log.info("unpublish", name+" not published")
+ return cb()
+ }
+ // remove all if no version specified
+ if (!ver) {
+ this.log.info("unpublish", "No version specified, removing all")
+ return this.request("DELETE", name+'/-rev/'+data._rev, cb)
+ }
+
+ var versions = data.versions || {}
+ , versionPublic = versions.hasOwnProperty(ver)
+
+ if (!versionPublic) {
+ this.log.info("unpublish", name+"@"+ver+" not published")
+ } else {
+ var dist = versions[ver].dist
+ this.log.verbose("unpublish", "removing attachments", dist)
+ }
+
+ delete versions[ver]
+ // if it was the only version, then delete the whole package.
+ if (!Object.keys(versions).length) {
+ this.log.info("unpublish", "No versions remain, removing entire package")
+ return this.request("DELETE", name+"/-rev/"+data._rev, cb)
+ }
+
+ if (!versionPublic) return cb()
+
+ var latestVer = data["dist-tags"].latest
+ for (var tag in data["dist-tags"]) {
+ if (data["dist-tags"][tag] === ver) delete data["dist-tags"][tag]
+ }
+
+ if (latestVer === ver) {
+ data["dist-tags"].latest =
+ Object.getOwnPropertyNames(versions).sort(semver.compare).pop()
+ }
+
+ var rev = data._rev
+ delete data._revisions
+ delete data._attachments
+ var cb_ = detacher.call(this, data, dist, cb)
+ this.request("PUT", name+"/-rev/"+rev, data, function (er) {
+ if (er) {
+ this.log.error("unpublish", "Failed to update data")
+ }
+ cb_(er)
+ }.bind(this))
+ }.bind(this))
+}
+
+function detacher (data, dist, cb) {
+ return function (er) {
+ if (er) return cb(er)
+ this.get(data.name, function (er, data) {
+ if (er) return cb(er)
+
+ var tb = url.parse(dist.tarball)
+
+ detach.call(this, data, tb.pathname, data._rev, function (er) {
+ if (er || !dist.bin) return cb(er)
+ chain(Object.keys(dist.bin).map(function (bt) {
+ return function (cb) {
+ var d = dist.bin[bt]
+ detach.call(this, data, url.parse(d.tarball).pathname, null, cb)
+ }.bind(this)
+ }, this), cb)
+ }.bind(this))
+ }.bind(this))
+ }.bind(this)
+}
+
+function detach (data, path, rev, cb) {
+ if (rev) {
+ path += "/-rev/" + rev
+ this.log.info("detach", path)
+ return this.request("DELETE", path, cb)
+ }
+ this.get(data.name, function (er, data) {
+ rev = data._rev
+ if (!rev) return cb(new Error(
+ "No _rev found in "+data._id))
+ detach.call(this, data, path, rev, cb)
+ }.bind(this))
+}
--- /dev/null
+module.exports = upload
+
+var fs = require('fs')
+, Stream = require("stream").Stream
+
+function upload (where, file, etag, nofollow, cb) {
+ if (typeof nofollow === "function") cb = nofollow, nofollow = false
+ if (typeof etag === "function") cb = etag, etag = null
+
+ if (file instanceof Stream) {
+ return this.request("PUT", where, file, etag, nofollow, cb)
+ }
+
+ fs.stat(file, function (er, stat) {
+ if (er) return cb(er)
+ var s = fs.createReadStream(file)
+ s.size = stat.size
+ s.on("error", cb)
+
+ this.request("PUT", where, s, etag, nofollow, cb)
+ }.bind(this))
+}
--- /dev/null
+{
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "name": "npm-registry-client",
+ "description": "Client for the npm registry",
+ "version": "0.0.9",
+ "repository": {
+ "url": "git://github.com/isaacs/npm-registry-client"
+ },
+ "main": "index.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {
+ "node-uuid": "~1.3.3",
+ "request": "~2.9.202",
+ "graceful-fs": "~1.1.8",
+ "semver": "~1.0.14",
+ "slide": "~1.1.3",
+ "chownr": "0",
+ "mkdirp": "~0.3.3",
+ "rimraf": "~2.0.1",
+ "retry": "0.6.0",
+ "couch-login": "~0.1.6",
+ "npmlog": ""
+ },
+ "devDependencies": {
+ "tap": ""
+ },
+ "optionalDependencies": {
+ "npmlog": ""
+ },
+ "license": "BSD",
+ "readme": "# npm-registry-client\n\nThe code that npm uses to talk to the registry.\n\nIt handles all the caching and HTTP calls.\n\n## Usage\n\n```javascript\nvar RegClient = require('npm-registry-client')\nvar client = new RegClient(options)\n\nclient.get(\"npm\", \"latest\", 1000, function (er, data, raw, res) {\n // error is an error if there was a problem.\n // data is the parsed data object\n // raw is the json string\n // res is the response from couch\n})\n```\n\n# Options\n\n* `registry` **Required** {String} URL to the registry\n* `cache` **Required** {String} Path to the cache folder\n* `alwaysAuth` {Boolean} Auth even for GET requests.\n* `auth` {String} A base64-encoded `username:password`\n* `email` {String} User's email address\n* `tag` {String} The default tag to use when publishing new packages.\n Default = `\"latest\"`\n* `ca` {String} Cerficate signing authority certificates to trust.\n* `strictSSL` {Boolean} Whether or not to be strict with SSL\n certificates. Default = `true`\n* `userAgent` {String} User agent header to send. Default =\n `\"node/{process.version}\"`\n* `log` {Object} The logger to use. Defaults to `require(\"npmlog\")` if\n that works, otherwise logs are disabled.\n* `retries` {Number} Number of times to retry on GET failures.\n Default=2\n* `retryFactor` {Number} `factor` setting for `node-retry`. Default=10\n* `retryMinTimeout` {Number} `minTimeout` setting for `node-retry`.\n Default=10000 (10 seconds)\n* `retryMaxTimeout` {Number} `maxTimeout` setting for `node-retry`.\n Default=60000 (60 seconds)\n\n# client.request(method, where, [what], [etag], [nofollow], cb)\n\n* `method` {String} HTTP method\n* `where` {String} Path to request on the server\n* `what` {Stream | Buffer | String | Object} The request body. Objects\n that are not Buffers or Streams are encoded as JSON.\n* `etag` {String} The cached ETag\n* `nofollow` {Boolean} Prevent following 302/301 responses\n* `cb` {Function}\n * `error` {Error | null}\n * `data` {Object} the parsed data object\n * `raw` {String} the json\n * `res` {Response Object} response from couch\n\nMake a request to the registry. All the other methods are wrappers\naround this. one.\n\n# client.adduser(username, password, email, cb)\n\n* `username` {String}\n* `password` {String}\n* `email` {String}\n* `cb` {Function}\n\nAdd a user account to the registry, or verify the credentials.\n\n# client.get(url, [timeout], [nofollow], [staleOk], cb)\n\n* `url` {String} The url path to fetch\n* `timeout` {Number} Number of seconds old that a cached copy must be\n before a new request will be made.\n* `nofollow` {Boolean} Do not follow 301/302 responses\n* `staleOk` {Boolean} If there's cached data available, then return that\n to the callback quickly, and update the cache the background.\n\nFetches data from the registry via a GET request, saving it in\nthe cache folder with the ETag.\n\n# client.publish(data, tarball, [readme], cb)\n\n* `data` {Object} Package data\n* `tarball` {String | Stream} Filename or stream of the package tarball\n* `readme` {String} Contents of the README markdown file\n* `cb` {Function}\n\nPublish a package to the registry.\n\nNote that this does not create the tarball from a folder. However, it\ncan accept a gzipped tar stream or a filename to a tarball.\n\n# client.star(package, starred, cb)\n\n* `package` {String} Name of the package to star\n* `starred` {Boolean} True to star the package, false to unstar it.\n* `cb` {Function}\n\nStar or unstar a package.\n\nNote that the user does not have to be the package owner to star or\nunstar a package, though other writes do require that the user be the\npackage owner.\n\n# client.tag(project, version, tag, cb)\n\n* `project` {String} Project name\n* `version` {String} Version to tag\n* `tag` {String} Tag name to apply\n* `cb` {Function}\n\nMark a version in the `dist-tags` hash, so that `pkg@tag`\nwill fetch the specified version.\n\n# client.unpublish(name, [ver], cb)\n\n* `name` {String} package name\n* `ver` {String} version to unpublish. Leave blank to unpublish all\n versions.\n* `cb` {Function}\n\nRemove a version of a package (or all versions) from the registry. When\nthe last version us unpublished, the entire document is removed from the\ndatabase.\n\n# client.upload(where, file, [etag], [nofollow], cb)\n\n* `where` {String} URL path to upload to\n* `file` {String | Stream} Either the filename or a readable stream\n* `etag` {String} Cache ETag\n* `nofollow` {Boolean} Do not follow 301/302 responses\n* `cb` {Function}\n\nUpload an attachment. Mostly used by `client.publish()`.\n",
+ "_id": "npm-registry-client@0.0.9",
+ "_from": "npm-registry-client@0"
+}
--- /dev/null
+Copyright (c) Isaac Z. Schlueter
+All rights reserved.
+
+The BSD License
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+# npmlog
+
+The logger util that npm uses.
+
+This logger is very basic. It does the logging for npm. It supports
+custom levels and colored output.
+
+By default, logs are written to stderr. If you want to send log messages
+to outputs other than streams, then you can change the `log.stream`
+member, or you can just listen to the events that it emits, and do
+whatever you want with them.
+
+# Basic Usage
+
+```
+var log = require('npmlog')
+
+// additional stuff ---------------------------+
+// message ----------+ |
+// prefix ----+ | |
+// level -+ | | |
+// v v v v
+ log.info('fyi', 'I have a kitty cat: %j', myKittyCat)
+```
+
+## log.level
+
+* {String}
+
+The level to display logs at. Any logs at or above this level will be
+displayed. The special level `silent` will prevent anything from being
+displayed ever.
+
+## log.record
+
+* {Array}
+
+An array of all the log messages that have been entered.
+
+## log.maxRecordSize
+
+* {Number}
+
+The maximum number of records to keep. If log.record gets bigger than
+10% over this value, then it is sliced down to 90% of this value.
+
+The reason for the 10% window is so that it doesn't have to resize a
+large array on every log entry.
+
+## log.prefixStyle
+
+* {Object}
+
+A style object that specifies how prefixes are styled. (See below)
+
+## log.headingStyle
+
+* {Object}
+
+A style object that specifies how the heading is styled. (See below)
+
+## log.heading
+
+* {String} Default: ""
+
+If set, a heading that is printed at the start of every line.
+
+## log.stream
+
+* {Stream} Default: `process.stderr`
+
+The stream where output is written.
+
+## log.enableColor()
+
+Force colors to be used on all messages, regardless of the output
+stream.
+
+## log.disableColor()
+
+Disable colors on all messages.
+
+## log.pause()
+
+Stop emitting messages to the stream, but do not drop them.
+
+## log.resume()
+
+Emit all buffered messages that were written while paused.
+
+## log.log(level, prefix, message, ...)
+
+* `level` {String} The level to emit the message at
+* `prefix` {String} A string prefix. Set to "" to skip.
+* `message...` Arguments to `util.format`
+
+Emit a log message at the specified level.
+
+## log\[level](prefix, message, ...)
+
+For example,
+
+* log.silly(prefix, message, ...)
+* log.verbose(prefix, message, ...)
+* log.info(prefix, message, ...)
+* log.http(prefix, message, ...)
+* log.warn(prefix, message, ...)
+* log.error(prefix, message, ...)
+
+Like `log.log(level, prefix, message, ...)`. In this way, each level is
+given a shorthand, so you can do `log.info(prefix, message)`.
+
+## log.addLevel(level, n, style, disp)
+
+* `level` {String} Level indicator
+* `n` {Number} The numeric level
+* `style` {Object} Object with fg, bg, inverse, etc.
+* `disp` {String} Optional replacement for `level` in the output.
+
+Sets up a new level with a shorthand function and so forth.
+
+Note that if the number is `Infinity`, then setting the level to that
+will cause all log messages to be suppressed. If the number is
+`-Infinity`, then the only way to show it is to enable all log messages.
+
+# Events
+
+Events are all emitted with the message object.
+
+* `log` Emitted for all messages
+* `log.<level>` Emitted for all messages with the `<level>` level.
+* `<prefix>` Messages with prefixes also emit their prefix as an event.
+
+# Style Objects
+
+Style objects can have the following fields:
+
+* `fg` {String} Color for the foreground text
+* `bg` {String} Color for the background
+* `bold`, `inverse`, `underline` {Boolean} Set the associated property
+* `bell` {Boolean} Make a noise (This is pretty annoying, probably.)
+
+# Message Objects
+
+Every log event is emitted with a message object, and the `log.record`
+list contains all of them that have been created. They have the
+following fields:
+
+* `id` {Number}
+* `level` {String}
+* `prefix` {String}
+* `message` {String} Result of `util.format()`
+* `messageRaw` {Array} Arguments to `util.format()`
--- /dev/null
+var EE = require('events').EventEmitter
+var log = exports = module.exports = new EE
+var util = require('util')
+
+var ansi = require('ansi')
+log.cursor = ansi(process.stderr)
+log.stream = process.stderr
+
+// by default, let ansi decide based on tty-ness.
+var colorEnabled = undefined
+log.enableColor = function () {
+ colorEnabled = true
+ this.cursor.enabled = true
+}
+log.disableColor = function () {
+ colorEnabled = false
+ this.cursor.enabled = false
+}
+
+// default level
+log.level = 'info'
+
+// temporarily stop emitting, but don't drop
+log.pause = function () {
+ this._paused = true
+}
+
+log.resume = function () {
+ if (!this._paused) return
+ this._paused = false
+
+ var b = this._buffer
+ this._buffer = []
+ b.forEach(function (m) {
+ this.emitLog(m)
+ }, this)
+}
+
+log._buffer = []
+
+var id = 0
+log.record = []
+log.maxRecordSize = 10000
+log.log = function (lvl, prefix, message) {
+ var l = this.levels[lvl]
+ if (l === undefined) {
+ return this.emit('error', new Error(util.format(
+ 'Undefined log level: %j', lvl)))
+ }
+
+ var a = new Array(arguments.length - 2)
+ var stack = null
+ for (var i = 2; i < arguments.length; i ++) {
+ var arg = a[i-2] = arguments[i]
+
+ // resolve stack traces to a plain string.
+ if (typeof arg === 'object' && arg &&
+ (arg instanceof Error) && arg.stack) {
+ arg.stack = stack = arg.stack + ''
+ }
+ }
+ if (stack) a.unshift(stack + '\n')
+ message = util.format.apply(util, a)
+
+ var m = { id: id++,
+ level: lvl,
+ prefix: String(prefix || ''),
+ message: message,
+ messageRaw: a }
+
+ this.emit('log', m)
+ this.emit('log.' + lvl, m)
+ if (m.prefix) this.emit(m.prefix, m)
+
+ this.record.push(m)
+ var mrs = this.maxRecordSize
+ var n = this.record.length - mrs
+ if (n > mrs / 10) {
+ var newSize = Math.floor(mrs * 0.9)
+ this.record = this.record.slice(-1 * newSize)
+ }
+
+ this.emitLog(m)
+}
+
+log.emitLog = function (m) {
+ if (this._paused) {
+ this._buffer.push(m)
+ return
+ }
+ var l = this.levels[m.level]
+ if (l === undefined) return
+ if (l < this.levels[this.level]) return
+ if (l > 0 && !isFinite(l)) return
+
+ var style = log.style[m.level]
+ var disp = log.disp[m.level] || m.level
+ m.message.split(/\r?\n/).forEach(function (line) {
+ if (this.heading) {
+ this.write(this.heading, this.headingStyle)
+ this.write(' ')
+ }
+ this.write(disp, log.style[m.level])
+ var p = m.prefix || ''
+ if (p) this.write(' ')
+ this.write(p, this.prefixStyle)
+ this.write(' ' + line + '\n')
+ }, this)
+}
+
+log.write = function (msg, style) {
+ if (!this.cursor) return
+ if (this.stream !== this.cursor.stream) {
+ this.cursor = ansi(this.stream, { enabled: colorEnabled })
+ }
+
+ style = style || {}
+ if (style.fg) this.cursor.fg[style.fg]()
+ if (style.bg) this.cursor.bg[style.bg]()
+ if (style.bold) this.cursor.bold()
+ if (style.underline) this.cursor.underline()
+ if (style.inverse) this.cursor.inverse()
+ if (style.beep) this.cursor.beep()
+ this.cursor.write(msg).reset()
+}
+
+log.addLevel = function (lvl, n, style, disp) {
+ if (!disp) disp = lvl
+ this.levels[lvl] = n
+ this.style[lvl] = style
+ if (!this[lvl]) this[lvl] = function () {
+ var a = new Array(arguments.length + 1)
+ a[0] = lvl
+ for (var i = 0; i < arguments.length; i ++) {
+ a[i + 1] = arguments[i]
+ }
+ return this.log.apply(this, a)
+ }
+ this.disp[lvl] = disp
+}
+
+log.prefixStyle = { fg: 'magenta' }
+log.headingStyle = { fg: 'white', bg: 'black' }
+
+log.style = {}
+log.levels = {}
+log.disp = {}
+log.addLevel('silly', -Infinity, { inverse: true }, 'sill')
+log.addLevel('verbose', 1000, { fg: 'blue', bg: 'black' }, 'verb')
+log.addLevel('info', 2000, { fg: 'green' })
+log.addLevel('http', 3000, { fg: 'green', bg: 'black' })
+log.addLevel('warn', 4000, { fg: 'black', bg: 'red' }, 'WARN')
+log.addLevel('error', 5000, { fg: 'red', bg: 'black' }, 'ERR!')
+log.addLevel('silent', Infinity)
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
- "name": "glob",
- "description": "a little globber",
- "version": "3.1.9",
+ "name": "npmlog",
+ "description": "logger for npm",
+ "version": "0.0.2",
"repository": {
"type": "git",
- "url": "git://github.com/isaacs/node-glob.git"
+ "url": "git://github.com/isaacs/npmlog.git"
},
- "main": "glob.js",
- "engines": {
- "node": "*"
+ "main": "log.js",
+ "scripts": {
+ "test": "tap test/*.js"
},
"dependencies": {
- "minimatch": "0.2",
- "graceful-fs": "~1.1.2",
- "inherits": "1"
+ "ansi": "~0.1.2"
},
"devDependencies": {
- "tap": "~0.2.3",
- "mkdirp": "0",
- "rimraf": "1"
- },
- "scripts": {
- "test": "tap test/*.js"
+ "tap": ""
},
"license": "BSD",
"_npmUser": {
"name": "isaacs",
"email": "i@izs.me"
},
- "_id": "glob@3.1.9",
+ "_id": "npmlog@0.0.2",
"optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
"_engineSupported": true,
- "_npmVersion": "1.1.23",
+ "_npmVersion": "1.1.24",
"_nodeVersion": "v0.7.10-pre",
"_defaultsLoaded": true,
- "_from": "glob@3"
+ "dist": {
+ "shasum": "f0cf4b2c519950c00e91ba8e2868b62bf86254f6"
+ },
+ "_from": "npmlog@0"
}
--- /dev/null
+Copyright (c) Isaac Z. Schlueter
+All rights reserved.
+
+The BSD License
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+# osenv
+
+Look up environment settings specific to different operating systems.
+
+## Usage
+
+```javascript
+var osenv = require('osenv')
+var path = osenv.path()
+var user = osenv.user()
+// etc.
+
+// Some things are not reliably in the env, and have a fallback command:
+var h = osenv.hostname(function (er, hostname) {
+ h = hostname
+})
+// This will still cause it to be memoized, so calling osenv.hostname()
+// is now an immediate operation.
+
+// You can always send a cb, which will get called in the nextTick
+// if it's been memoized, or wait for the fallback data if it wasn't
+// found in the environment.
+osenv.hostname(function (er, hostname) {
+ if (er) console.error('error looking up hostname')
+ else console.log('this machine calls itself %s', hostname)
+})
+```
+
+## osenv.hostname()
+
+The machine name. Calls `hostname` if not found.
+
+## osenv.user()
+
+The currently logged-in user. Calls `whoami` if not found.
+
+## osenv.prompt()
+
+Either PS1 on unix, or PROMPT on Windows.
+
+## osenv.tmpdir()
+
+The place where temporary files should be created.
+
+## osenv.home()
+
+No place like it.
+
+## osenv.path()
+
+An array of the places that the operating system will search for
+executables.
+
+## osenv.editor()
+
+Return the executable name of the editor program. This uses the EDITOR
+and VISUAL environment variables, and falls back to `vi` on Unix, or
+`notepad.exe` on Windows.
+
+## osenv.shell()
+
+The SHELL on Unix, which Windows calls the ComSpec. Defaults to 'bash'
+or 'cmd'.
--- /dev/null
+var isWindows = process.platform === 'win32'
+var windir = isWindows ? process.env.windir || 'C:\\Windows' : null
+var path = require('path')
+var exec = require('child_process').exec
+
+// looking up envs is a bit costly.
+// Also, sometimes we want to have a fallback
+// Pass in a callback to wait for the fallback on failures
+// After the first lookup, always returns the same thing.
+function memo (key, lookup, fallback) {
+ var fell = false
+ var falling = false
+ exports[key] = function (cb) {
+ var val = lookup()
+ if (!val && !fell && !falling && fallback) {
+ fell = true
+ falling = true
+ exec(fallback, function (er, output, stderr) {
+ falling = false
+ if (er) return // oh well, we tried
+ val = output.trim()
+ })
+ }
+ exports[key] = function (cb) {
+ if (cb) process.nextTick(cb.bind(null, null, val))
+ return val
+ }
+ if (cb && !falling) process.nextTick(cb.bind(null, null, val))
+ return val
+ }
+}
+
+memo('user', function () {
+ return ( isWindows
+ ? process.env.USERDOMAIN + '\\' + process.env.USERNAME
+ : process.env.USER
+ )
+}, 'whoami')
+
+memo('prompt', function () {
+ return isWindows ? process.env.PROMPT : process.env.PS1
+})
+
+memo('hostname', function () {
+ return isWindows ? process.env.COMPUTERNAME : process.env.HOSTNAME
+}, 'hostname')
+
+memo('tmpdir', function () {
+ var t = isWindows ? 'temp' : 'tmp'
+ return process.env.TMPDIR ||
+ process.env.TMP ||
+ process.env.TEMP ||
+ ( exports.home() ? path.resolve(exports.home(), t)
+ : isWindows ? path.resolve(windir, t)
+ : '/tmp'
+ )
+})
+
+memo('home', function () {
+ return ( isWindows ? process.env.USERPROFILE
+ : process.env.HOME
+ )
+})
+
+memo('path', function () {
+ return (process.env.PATH ||
+ process.env.Path ||
+ process.env.path).split(isWindows ? ';' : ':')
+})
+
+memo('editor', function () {
+ return process.env.EDITOR ||
+ process.env.VISUAL ||
+ (isWindows ? 'notepad.exe' : 'vi')
+})
+
+memo('shell', function () {
+ return isWindows ? process.env.ComSpec || 'cmd'
+ : process.env.SHELL || 'bash'
+})
--- /dev/null
+{
+ "name": "osenv",
+ "version": "0.0.3",
+ "main": "osenv.js",
+ "directories": {
+ "test": "test"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tap": "~0.2.5"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/osenv"
+ },
+ "keywords": [
+ "environment",
+ "variable",
+ "home",
+ "tmpdir",
+ "path",
+ "prompt",
+ "ps1"
+ ],
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "license": "BSD",
+ "description": "Look up environment settings specific to different operating systems",
+ "readme": "# osenv\n\nLook up environment settings specific to different operating systems.\n\n## Usage\n\n```javascript\nvar osenv = require('osenv')\nvar path = osenv.path()\nvar user = osenv.user()\n// etc.\n\n// Some things are not reliably in the env, and have a fallback command:\nvar h = osenv.hostname(function (er, hostname) {\n h = hostname\n})\n// This will still cause it to be memoized, so calling osenv.hostname()\n// is now an immediate operation.\n\n// You can always send a cb, which will get called in the nextTick\n// if it's been memoized, or wait for the fallback data if it wasn't\n// found in the environment.\nosenv.hostname(function (er, hostname) {\n if (er) console.error('error looking up hostname')\n else console.log('this machine calls itself %s', hostname)\n})\n```\n\n## osenv.hostname()\n\nThe machine name. Calls `hostname` if not found.\n\n## osenv.user()\n\nThe currently logged-in user. Calls `whoami` if not found.\n\n## osenv.prompt()\n\nEither PS1 on unix, or PROMPT on Windows.\n\n## osenv.tmpdir()\n\nThe place where temporary files should be created.\n\n## osenv.home()\n\nNo place like it.\n\n## osenv.path()\n\nAn array of the places that the operating system will search for\nexecutables.\n\n## osenv.editor() \n\nReturn the executable name of the editor program. This uses the EDITOR\nand VISUAL environment variables, and falls back to `vi` on Unix, or\n`notepad.exe` on Windows.\n\n## osenv.shell()\n\nThe SHELL on Unix, which Windows calls the ComSpec. Defaults to 'bash'\nor 'cmd'.\n",
+ "_id": "osenv@0.0.3",
+ "_from": "osenv@latest"
+}
--- /dev/null
+# read-installed
+
+Read all the installed packages in a folder, and return a tree
+structure with all the data.
+
+npm uses this.
+
+## Usage
+
+```javascript
+var readInstalled = require("read-installed")
+// depth is optional, defaults to Infinity
+readInstalled(folder, depth, function (er, data) {
+ ...
+})
+```
--- /dev/null
+{
+ "name": "read-installed",
+ "description": "Read all the installed packages in a folder, and return a tree structure with all the data.",
+ "version": "0.0.1",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/read-installed"
+ },
+ "main": "read-installed.js",
+ "scripts": {
+ "test": "node test/basic.js"
+ },
+ "dependencies": {
+ "semver": "~1.0.14",
+ "slide": "~1.1.3",
+ "read-package-json": "0",
+ "graceful-fs": "~1.1.8",
+ "npmlog": "0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "~1.1.8",
+ "npmlog": "0"
+ },
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "readme": "# read-installed\n\nRead all the installed packages in a folder, and return a tree\nstructure with all the data.\n\nnpm uses this.\n\n## Usage\n\n```javascript\nvar readInstalled = require(\"read-installed\")\n// depth is optional, defaults to Infinity\nreadInstalled(folder, depth, function (er, data) {\n ...\n})\n```\n",
+ "_id": "read-installed@0.0.1",
+ "_from": "read-installed"
+}
*/
+try {
+ var fs = require("graceful-fs")
+} catch (er) {
+ var fs = require("fs")
+}
+
+try {
+ var log = require("npmlog")
+} catch (_) {
+ var log = { verbose: noop, info: noop, warn: noop, error: noop }
+ function noop () {}
+}
-var npm = require("../npm.js")
- , fs = require("graceful-fs")
- , path = require("path")
- , asyncMap = require("slide").asyncMap
- , semver = require("semver")
- , readJson = require("./read-json.js")
- , log = require("./log.js")
- , url = require("url")
+var path = require("path")
+var asyncMap = require("slide").asyncMap
+var semver = require("semver")
+var readJson = require("read-package-json")
+var url = require("url")
module.exports = readInstalled
-function readInstalled (folder, cb) {
- var d = npm.config.get("depth")
- readInstalled_(folder, null, null, null, 0, d, function (er, obj) {
+function readInstalled (folder, depth, cb) {
+ if (typeof cb !== "function") cb = depth, depth = Infinity
+ readInstalled_(folder, null, null, null, 0, depth, function (er, obj) {
if (er) return cb(er)
// now obj has all the installed things, where they're installed
// figure out the inheritance links, now that the object is built.
if (parent && !obj.link) obj.parent = parent
rpSeen[real] = obj
obj.depth = depth
- if (depth >= maxDepth) return cb(null, obj)
+ //if (depth >= maxDepth) return cb(null, obj)
asyncMap(installed, function (pkg, cb) {
var rv = obj.dependencies[pkg]
if (!rv && obj.devDependencies) rv = obj.devDependencies[pkg]
+ if (depth >= maxDepth) {
+ // just try to get the version number
+ var pkgfolder = path.resolve(folder, "node_modules", pkg)
+ , jsonFile = path.resolve(pkgfolder, "package.json")
+ return readJson(jsonFile, function (er, depData) {
+ // already out of our depth, ignore errors
+ if (er || !depData || !depData.version) return cb(null, obj)
+ obj.dependencies[pkg] = depData.version
+ cb(null, obj)
+ })
+ }
+
readInstalled_( path.resolve(folder, "node_modules/"+pkg)
, obj, pkg, obj.dependencies[pkg], depth + 1, maxDepth
, cb )
+
}, function (er, installedData) {
if (er) return cb(er)
installedData.forEach(function (dep) {
&& !url.parse(deps[d]).protocol
&& !semver.satisfies(found.version, deps[d])) {
// the bad thing will happen
- log.warn(obj.path + " requires "+d+"@'"+deps[d]
+ log.warn("unmet dependency", obj.path + " requires "+d+"@'"+deps[d]
+"' but will load\n"
+found.path+",\nwhich is version "+found.version
- ,"unmet dependency")
+ )
found.invalid = true
}
deps[d] = found
}
})
- log.verbose([obj._id], "returning")
+ log.verbose("readInstalled", "returning", obj._id)
return obj
}
for (var i in obj) o[i] = copy(obj[i])
return o
}
-
-if (module === require.main) {
- var util = require("util")
- console.error("testing")
-
- var called = 0
- readInstalled(process.cwd(), function (er, map) {
- console.error(called ++)
- if (er) return console.error(er.stack || er.message)
- cleanup(map)
- console.error(util.inspect(map, true, 10, true))
- })
-
- var seen = []
- function cleanup (map) {
- if (seen.indexOf(map) !== -1) return
- seen.push(map)
- for (var i in map) switch (i) {
- case "_id":
- case "path":
- case "extraneous": case "invalid":
- case "dependencies": case "name":
- continue
- default: delete map[i]
- }
- var dep = map.dependencies
-// delete map.dependencies
- if (dep) {
-// map.dependencies = dep
- for (var i in dep) if (typeof dep[i] === "object") {
- cleanup(dep[i])
- }
- }
- return map
- }
-}
--- /dev/null
+# read-package-json
+
+This is the thing that npm uses to read package.json files. It
+validates some stuff, and loads some default things.
+
+It keeps a cache of the files you've read, so that you don't end
+up reading the same package.json file multiple times.
+
+Note that if you just want to see what's literally in the package.json
+file, you can usually do `var data = require('some-module/package.json')`.
+
+This module is basically only needed by npm, but it's handy to see what
+npm will see when it looks at your package.
+
+## Usage
+
+```javascript
+var readJson = require('read-package-json')
+
+readJson('/path/to/package.json', function (er, data) {
+ if (er) {
+ console.error("There was an error reading the file")
+ return
+ }
+
+ console.error('the package data is', data)
+}
+```
+
+## readJson(file, cb)
+
+* `file` {String} The path to the package.json file
+* `cb` {Function}
+
+Reads the JSON file and does the things.
+
+## `package.json` Fields
+
+See `man 5 package.json` or `npm help json`.
+
+## readJson.log
+
+By default this is a reference to the `npmlog` module. But if that
+module can't be found, then it'll be set to just a dummy thing that does
+nothing.
+
+Replace with your own `{log,warn,error}` object for fun loggy time.
+
+## readJson.extras(file, data, cb)
+
+Run all the extra stuff relative to the file, with the parsed data.
+
+Modifies the data as it does stuff. Calls the cb when it's done.
+
+## readJson.extraSet = [fn, fn, ...]
+
+Array of functions that are called by `extras`. Each one receives the
+arguments `fn(file, data, cb)` and is expected to call `cb(er, data)`
+when done or when an error occurs.
+
+Order is indeterminate, so each function should be completely
+independent.
+
+Mix and match!
+
+## readJson.cache
+
+The `lru-cache` object that readJson uses to not read the same file over
+and over again. See
+[lru-cache](https://github.com/isaacs/node-lru-cache) for details.
+
+## Other Relevant Files Besides `package.json`
+
+Some other files have an effect on the resulting data object, in the
+following ways:
+
+### `README?(.*)`
+
+If there is a `README` or `README.*` file present, then npm will attach
+a `readme` field to the data with the contents of this file.
+
+Owing to the fact that roughly 100% of existing node modules have
+Markdown README files, it will generally be assumed to be Markdown,
+regardless of the extension. Please plan accordingly.
+
+### `server.js`
+
+If there is a `server.js` file, and there is not already a
+`scripts.start` field, then `scripts.start` will be set to `node
+server.js`.
+
+### `AUTHORS`
+
+If there is not already a `contributors` field, then the `contributors`
+field will be set to the contents of the `AUTHORS` file, split by lines,
+and parsed.
+
+### `bindings.gyp`
+
+If a bindings.gyp file exists, and there is not already a
+`scripts.install` field, then the `scripts.install` field will be set to
+`node-gyp rebuild`.
+
+### `wscript`
+
+If a wscript file exists, and there is not already a `scripts.install`
+field, then the `scripts.install` field will be set to `node-waf clean ;
+node-waf configure build`.
+
+Note that the `bindings.gyp` file supercedes this, since node-waf has
+been deprecated in favor of node-gyp.
+
+### `index.js`
+
+If the json file does not exist, but there is a `index.js` file
+present instead, and that file has a package comment, then it will try
+to parse the package comment, and use that as the data instead.
+
+A package comment looks like this:
+
+```javascript
+/**package
+ * { "name": "my-bare-module"
+ * , "version": "1.2.3"
+ * , "description": "etc...." }
+ **/
+
+// or...
+
+/**package
+{ "name": "my-bare-module"
+, "version": "1.2.3"
+, "description": "etc...." }
+**/
+```
+
+The important thing is that it starts with `/**package`, and ends with
+`**/`. If the package.json file exists, then the index.js is not
+parsed.
+
+### `{directories.man}/*.[0-9]`
+
+If there is not already a `man` field defined as an array of files or a
+single file, and
+there is a `directories.man` field defined, then that directory will
+be searched for manpages.
+
+Any valid manpages found in that directory will be assigned to the `man`
+array, and installed in the appropriate man directory at package install
+time, when installed globally on a Unix system.
+
+### `{directories.bin}/*`
+
+If there is not already a `bin` field defined as a string filename or a
+hash of `<name> : <filename>` pairs, then the `directories.bin`
+directory will be searched and all the files within it will be linked as
+executables at install time.
+
+When installing locally, npm links bins into `node_modules/.bin`, which
+is in the `PATH` environ when npm runs scripts. When
+installing globally, they are linked into `{prefix}/bin`, which is
+presumably in the `PATH` environment variable.
--- /dev/null
+{
+ "name": "read-package-json",
+ "version": "0.1.1",
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "description": "The thing npm uses to read package.json files with semantics and defaults and validation",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/read-package-json.git"
+ },
+ "main": "read-json.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {
+ "glob": "~3.1.9",
+ "lru-cache": "~1.1.0",
+ "semver": "~1.0.14",
+ "slide": "~1.1.3",
+ "npmlog": "0",
+ "graceful-fs": "~1.1.8"
+ },
+ "devDependencies": {
+ "tap": "~0.2.5"
+ },
+ "optionalDependencies": {
+ "npmlog": "0",
+ "graceful-fs": "~1.1.8"
+ },
+ "readme": "# read-package-json\n\nThis is the thing that npm uses to read package.json files. It\nvalidates some stuff, and loads some default things.\n\nIt keeps a cache of the files you've read, so that you don't end\nup reading the same package.json file multiple times.\n\nNote that if you just want to see what's literally in the package.json\nfile, you can usually do `var data = require('some-module/package.json')`.\n\nThis module is basically only needed by npm, but it's handy to see what\nnpm will see when it looks at your package.\n\n## Usage\n\n```javascript\nvar readJson = require('read-package-json')\n\nreadJson('/path/to/package.json', function (er, data) {\n if (er) {\n console.error(\"There was an error reading the file\")\n return\n }\n\n console.error('the package data is', data)\n}\n```\n\n## readJson(file, cb)\n\n* `file` {String} The path to the package.json file\n* `cb` {Function}\n\nReads the JSON file and does the things.\n\n## `package.json` Fields\n\nSee `man 5 package.json` or `npm help json`.\n\n## readJson.log\n\nBy default this is a reference to the `npmlog` module. But if that\nmodule can't be found, then it'll be set to just a dummy thing that does\nnothing.\n\nReplace with your own `{log,warn,error}` object for fun loggy time.\n\n## readJson.extras(file, data, cb)\n\nRun all the extra stuff relative to the file, with the parsed data.\n\nModifies the data as it does stuff. Calls the cb when it's done.\n\n## readJson.extraSet = [fn, fn, ...]\n\nArray of functions that are called by `extras`. Each one receives the\narguments `fn(file, data, cb)` and is expected to call `cb(er, data)`\nwhen done or when an error occurs.\n\nOrder is indeterminate, so each function should be completely\nindependent.\n\nMix and match!\n\n## readJson.cache\n\nThe `lru-cache` object that readJson uses to not read the same file over\nand over again. See\n[lru-cache](https://github.com/isaacs/node-lru-cache) for details.\n\n## Other Relevant Files Besides `package.json`\n\nSome other files have an effect on the resulting data object, in the\nfollowing ways:\n\n### `README?(.*)`\n\nIf there is a `README` or `README.*` file present, then npm will attach\na `readme` field to the data with the contents of this file.\n\nOwing to the fact that roughly 100% of existing node modules have\nMarkdown README files, it will generally be assumed to be Markdown,\nregardless of the extension. Please plan accordingly.\n\n### `server.js`\n\nIf there is a `server.js` file, and there is not already a\n`scripts.start` field, then `scripts.start` will be set to `node\nserver.js`.\n\n### `AUTHORS`\n\nIf there is not already a `contributors` field, then the `contributors`\nfield will be set to the contents of the `AUTHORS` file, split by lines,\nand parsed.\n\n### `bindings.gyp`\n\nIf a bindings.gyp file exists, and there is not already a\n`scripts.install` field, then the `scripts.install` field will be set to\n`node-gyp rebuild`.\n\n### `wscript`\n\nIf a wscript file exists, and there is not already a `scripts.install`\nfield, then the `scripts.install` field will be set to `node-waf clean ;\nnode-waf configure build`.\n\nNote that the `bindings.gyp` file supercedes this, since node-waf has\nbeen deprecated in favor of node-gyp.\n\n### `index.js`\n\nIf the json file does not exist, but there is a `index.js` file\npresent instead, and that file has a package comment, then it will try\nto parse the package comment, and use that as the data instead.\n\nA package comment looks like this:\n\n```javascript\n/**package\n * { \"name\": \"my-bare-module\"\n * , \"version\": \"1.2.3\"\n * , \"description\": \"etc....\" }\n **/\n\n// or...\n\n/**package\n{ \"name\": \"my-bare-module\"\n, \"version\": \"1.2.3\"\n, \"description\": \"etc....\" }\n**/\n```\n\nThe important thing is that it starts with `/**package`, and ends with\n`**/`. If the package.json file exists, then the index.js is not\nparsed.\n\n### `{directories.man}/*.[0-9]`\n\nIf there is not already a `man` field defined as an array of files or a\nsingle file, and\nthere is a `directories.man` field defined, then that directory will\nbe searched for manpages.\n\nAny valid manpages found in that directory will be assigned to the `man`\narray, and installed in the appropriate man directory at package install\ntime, when installed globally on a Unix system.\n\n### `{directories.bin}/*`\n\nIf there is not already a `bin` field defined as a string filename or a\nhash of `<name> : <filename>` pairs, then the `directories.bin`\ndirectory will be searched and all the files within it will be linked as\nexecutables at install time.\n\nWhen installing locally, npm links bins into `node_modules/.bin`, which\nis in the `PATH` environ when npm runs scripts. When\ninstalling globally, they are linked into `{prefix}/bin`, which is\npresumably in the `PATH` environment variable.\n",
+ "_id": "read-package-json@0.1.1",
+ "_from": "read-package-json@~0.1.0"
+}
--- /dev/null
+// vim: set softtabstop=16 shiftwidth=16:
+
+try {
+ readJson.log = require("npmlog")
+} catch (er) {
+ readJson.log = {
+ info: function () {},
+ verbose: function () {},
+ warn: function () {}
+ }
+}
+
+
+try {
+ var fs = require("graceful-fs")
+} catch (er) {
+ var fs = require("fs")
+}
+
+
+module.exports = readJson
+
+var LRU = require("lru-cache")
+readJson.cache = new LRU(1000)
+var path = require("path")
+var glob = require("glob")
+var slide = require("slide")
+var asyncMap = slide.asyncMap
+var semver = require("semver")
+
+// put more stuff on here to customize.
+readJson.extraSet = [gypfile, wscript, serverjs, authors, readme, mans, bins]
+
+var typoWarned = {}
+// http://registry.npmjs.org/-/fields
+var typos = { "dependancies": "dependencies"
+ , "dependecies": "dependencies"
+ , "depdenencies": "dependencies"
+ , "devEependencies": "devDependencies"
+ , "depends": "dependencies"
+ , "dev-dependencies": "devDependencies"
+ , "devDependences": "devDependencies"
+ , "devDepenencies": "devDependencies"
+ , "devdependencies": "devDependencies"
+ , "repostitory": "repository"
+ , "prefereGlobal": "preferGlobal"
+ , "hompage": "homepage"
+ , "hampage": "homepage"
+ , "autohr": "author"
+ , "autor": "author"
+ , "contributers": "contributors"
+ , "publicationConfig": "publishConfig"
+ }
+var bugsTypos = { "web": "url", "name": "url" }
+var scriptTypos = { "server": "start", "tests": "test" }
+var depTypes = [ "dependencies"
+ , "devDependencies"
+ , "optionalDependencies" ]
+
+
+function readJson (file, cb) {
+ var c = readJson.cache.get(file)
+ if (c) {
+ readJson.log.verbose("from cache", file)
+ cb = cb.bind(null, null, c)
+ return process.nextTick(cb);
+ }
+ readJson.log.verbose("read json", file)
+ cb = (function (orig) { return function (er, data) {
+ if (data) readJson.cache.set(file, data);
+ return orig(er, data)
+ } })(cb)
+ readJson_(file, cb)
+}
+
+
+function readJson_ (file, cb) {
+ fs.readFile(file, "utf8", function (er, d) {
+ if (er && er.code === "ENOENT") {
+ indexjs(file, er, cb)
+ return
+ }
+ if (er) return cb(er);
+ try {
+ d = JSON.parse(d)
+ } catch (er) {
+ er = parseError(er, file);
+ return cb(er);
+ }
+ extras(file, d, cb)
+ })
+}
+
+
+function indexjs (file, er, cb) {
+ if (path.basename(file) === "index.js") {
+ return cb(er);
+ }
+ var index = path.resolve(path.dirname(file), "index.js")
+ fs.readFile(index, "utf8", function (er2, d) {
+ if (er2) return cb(er);
+ d = parseIndex(d)
+ if (!d) return cb(er);
+ extras(file, d, cb)
+ })
+}
+
+
+readJson.extras = extras
+function extras (file, data, cb) {
+ asyncMap(readJson.extraSet, function (fn, cb) {
+ return fn(file, data, cb)
+ }, function (er) {
+ if (er) return cb(er);
+ final(file, data, cb)
+ })
+}
+
+function gypfile (file, data, cb) {
+ var dir = path.dirname(file)
+ var s = data.scripts || {}
+ if (s.install || s.preinstall) {
+ return cb(null, data);
+ }
+ glob("*.gyp", { cwd: dir }, function (er, files) {
+ if (er) return cb(er);
+ gypfile_(file, data, files, cb)
+ })
+}
+
+function gypfile_ (file, data, files, cb) {
+ if (!files.length) return cb(null, data);
+ var s = data.scripts || {}
+ s.install = "node-gyp rebuild"
+ data.scripts = s
+ data.gypfile = true
+ return cb(null, data);
+}
+
+function wscript (file, data, cb) {
+ var dir = path.dirname(file)
+ var s = data.scripts || {}
+ if (s.install || s.preinstall) {
+ return cb(null, data);
+ }
+ glob("wscript", { cwd: dir }, function (er, files) {
+ if (er) return cb(er);
+ wscript_(file, data, files, cb)
+ })
+}
+function wscript_ (file, data, files, cb) {
+ if (!files.length || data.gypfile) return cb(null, data);
+ var s = data.scripts || {}
+ s.install = "node-waf clean ; node-waf configure build"
+ data.scripts = s
+ return cb(null, data);
+}
+
+function serverjs (file, data, cb) {
+ var dir = path.dirname(file)
+ var s = data.scripts || {}
+ if (s.start) return cb(null, data)
+ glob("server.js", { cwd: dir }, function (er, files) {
+ if (er) return cb(er);
+ serverjs_(file, data, files, cb)
+ })
+}
+function serverjs_ (file, data, files, cb) {
+ if (!files.length) return cb(null, data);
+ var s = data.scripts || {}
+ s.start = "node server.js"
+ data.scripts = s
+ return cb(null, data)
+}
+
+function authors (file, data, cb) {
+ if (data.contributors) return cb(null, data);
+ var af = path.resolve(path.dirname(file), "AUTHORS")
+ fs.readFile(af, "utf8", function (er, ad) {
+ // ignore error. just checking it.
+ if (er) return cb(null, data);
+ authors_(file, data, ad, cb)
+ })
+}
+function authors_ (file, data, ad, cb) {
+ ad = ad.split(/\r?\n/g).map(function (line) {
+ return line.replace(/^\s*#.*$/, '').trim()
+ }).filter(function (line) {
+ return line
+ })
+ data.contributors = ad
+ return cb(null, data)
+}
+
+var defDesc = "Unnamed repository; edit this file " +
+ "'description' to name the repository."
+function gitDescription (file, data, cb) {
+ if (data.description) return cb(null, data);
+ var dir = path.dirname(file)
+ // just cuz it'd be nice if this file mattered...
+ var gitDesc = path.resolve(dir, '.git/description')
+ fs.readFile(gitDesc, 'utf8', function (er, desc) {
+ if (desc) desc = desc.trim()
+ if (!er && desc !== defDesc)
+ data.description = desc
+ return cb(null, data)
+ })
+}
+
+function readmeDescription (file, data) {
+ if (data.description) return cb(null, data);
+ var d = data.readme
+ if (!d) return;
+ // the first block of text before the first heading
+ // that isn't the first line heading
+ d = d.trim().split('\n')
+ for (var s = 0; d[s] && d[s].trim().match(/^(#|$)/); s ++);
+ var l = d.length
+ for (var e = s + 1; e < l && d[e].trim(); e ++);
+ data.description = d.slice(s, e).join(' ').trim()
+}
+
+function readme (file, data, cb) {
+ if (data.readme) return cb(null, data);
+ var dir = path.dirname(file)
+ var globOpts = { cwd: dir, nocase: true }
+ glob("README?(.*)", globOpts, function (er, files) {
+ if (er) return cb(er);
+ if (!files.length) return cb()
+ var rm = path.resolve(dir, files[0])
+ readme_(file, data, rm, cb)
+ })
+}
+function readme_(file, data, rm, cb) {
+ fs.readFile(rm, "utf8", function (er, rm) {
+ data.readme = rm
+ return cb(er, data)
+ })
+}
+
+function mans (file, data, cb) {
+ var m = data.directories && data.directories.man
+ if (data.man || !m) return cb(null, data);
+ m = path.resolve(path.dirname(file), m)
+ glob("**/*.[0-9]", { cwd: m }, function (er, mans) {
+ if (er) return cb(er);
+ mans_(file, data, mans, cb)
+ })
+}
+function mans_ (file, data, mans, cb) {
+ var m = data.directories && data.directories.man
+ data.man = mans.map(function (mf) {
+ return path.resolve(m, mf)
+ })
+ return cb(null, data)
+}
+
+function bins (file, data, cb) {
+ if (Array.isArray(data.bin)) {
+ return bins_(file, data, data.bin, cb)
+ }
+ var m = data.directories && data.directories.bin
+ if (data.bin || !m) return cb(null, data);
+ m = path.resolve(path.dirname(file), m)
+ glob("**", { cwd: m }, function (er, bins) {
+ if (er) return cb(er);
+ bins_(file, data, bins, cb)
+ })
+}
+function bins_ (file, data, bins, cb) {
+ var m = data.directories && data.directories.bin
+ data.bin = bins.reduce(function (acc, mf) {
+ if (mf && mf.charAt(0) !== '.') {
+ var f = path.basename(mf)
+ acc[f] = path.join(m, mf)
+ }
+ return acc
+ }, {})
+ return cb(null, data)
+}
+
+function final (file, data, cb) {
+ var ret = validName(file, data)
+ if (ret !== true) return cb(ret);
+ ret = validVersion(file, data)
+ if (ret !== true) return cb(ret);
+
+ data._id = data.name + "@" + data.version
+ typoWarn(file, data)
+ validRepo(file, data)
+ validFiles(file, data)
+ validBin(file, data)
+ validMan(file, data)
+ validBundled(file, data)
+ objectifyDeps(file, data)
+ unParsePeople(file, data)
+ parsePeople(file, data)
+
+ if (data.readme && !data.description)
+ readmeDescription(file, data)
+
+ readJson.cache.set(file, data)
+ cb(null, data)
+}
+
+
+// /**package { "name": "foo", "version": "1.2.3", ... } **/
+function parseIndex (data) {
+ data = data.split(/^\/\*\*package(?:\s|$)/m)
+ if (data.length < 2) return null
+ data = data[1]
+ data = data.split(/\*\*\/$/m)
+ if (data.length < 2) return null
+ data = data[0]
+ data = data.replace(/^\s*\*/mg, "")
+ try {
+ return JSON.parse(data)
+ } catch (er) {
+ return null
+ }
+}
+
+function parseError (ex, file) {
+ var e = new Error("Failed to parse json\n"+ex.message)
+ e.code = "EJSONPARSE"
+ e.file = file
+ return e
+}
+
+// a warning for deprecated or likely-incorrect fields
+function typoWarn (file, data) {
+ if (typoWarned[data._id]) return;
+ typoWarned[data._id] = true
+ if (data.modules) {
+ warn(file, data,
+ "'modules' is deprecated")
+ delete data.modules
+ }
+ Object.keys(typos).forEach(function (d) {
+ checkTypo(file, data, d)
+ })
+ bugsTypoWarn(file, data)
+ scriptTypoWarn(file, data)
+ noreadmeWarn(file, data)
+}
+
+function noreadmeWarn (file, data) {
+ if (data.readme) return;
+ warn(file, data, "No README.md file found!")
+ data.readme = "ERROR: No README.md file found!"
+}
+
+function checkTypo (file, data, d) {
+ if (!data.hasOwnProperty(d)) return;
+ warn(file, data,
+ "'" + d + "' should probably be '" + typos[d] + "'" )
+}
+
+function bugsTypoWarn (file, data) {
+ var b = data.bugs
+ if (!b || typeof b !== "object") return
+ Object.keys(b).forEach(function (k) {
+ if (bugsTypos[k]) {
+ b[bugsTypos[k]] = b[k]
+ delete b[k]
+ }
+ })
+}
+
+function scriptTypoWarn (file, data) {
+ var s = data.scripts
+ if (!s || typeof s !== "object") return
+ Object.keys(s).forEach(function (k) {
+ if (scriptTypos[k]) {
+ scriptWarn_(file, data, k)
+ }
+ })
+}
+function scriptWarn_ (file, data, k) {
+ warn(file, data, "scripts['" + k + "'] should probably " +
+ "be scripts['" + scriptTypos[k] + "']")
+}
+
+function validRepo (file, data) {
+ if (data.repostories) {
+ warnRepositories(file, data)
+ }
+ if (!data.repository) return;
+ if (typeof data.repository === "string") {
+ data.repository = {
+ type: "git",
+ url: data.repository
+ }
+ }
+ var r = data.repository.url || ""
+ // use the non-private urls
+ r = r.replace(/^(https?|git):\/\/[^\@]+\@github.com/,
+ '$1://github.com')
+ r = r.replace(/^https?:\/\/github.com/,
+ 'git://github.com')
+ if (r.match(/github.com\/[^\/]+\/[^\/]+\.git\.git$/)) {
+ warn(file, data, "Probably broken git " +
+ "url: " + r)
+ }
+}
+function warnRepostories (file, data) {
+ warn(file, data,
+ "'repositories' (plural) Not supported.\n" +
+ "Please pick one as the 'repository' field");
+ data.repository = data.repositories[0]
+}
+
+function validFiles (file, data) {
+ var files = data.files
+ if (files && !Array.isArray(files)) {
+ warn(file, data, "Invalid 'files' member")
+ delete data.files
+ }
+}
+
+function validBin (file, data) {
+ if (!data.bin) return;
+ if (typeof data.bin === "string") {
+ var b = {}
+ b[data.name] = data.bin
+ data.bin = b
+ }
+}
+
+function validMan (file, data) {
+ if (!data.man) return;
+ if (typeof data.man === "string") {
+ data.man = [ data.man ]
+ }
+}
+
+function validBundled (file, data) {
+ var bdd = "bundledDependencies"
+ var bd = "bundleDependencies"
+ if (data[bdd] && !data[bd]) {
+ data[bd] = data[bdd]
+ delete data[bdd]
+ }
+
+ if (data[bd] && !Array.isArray(data[bd])) {
+ warn(file, data, "bundleDependencies " +
+ "must be an array")
+ }
+}
+
+function objectifyDeps (file, data) {
+ depTypes.forEach(function (d) {
+ objectifyDep_(file, data, d)
+ })
+
+ var o = data.optionalDependencies
+ if (!o) return;
+ var d = data.dependencies || {}
+ Object.keys(o).forEach(function (k) {
+ d[k] = o[k]
+ })
+ data.dependencies = d
+}
+function objectifyDep_ (file, data, type) {
+ if (!data[type]) return;
+ data[type] = depObjectify(file, data, data[type])
+}
+function depObjectify (file, data, deps) {
+ if (!deps) return {}
+ if (typeof deps === "string") {
+ deps = deps.trim().split(/[\n\r\s\t ,]+/)
+ }
+ if (!Array.isArray(deps)) return deps
+ var o = {}
+ deps.forEach(function (d) {
+ d = d.trim().split(/(:?[@\s><=])/)
+ var dn = d.shift()
+ var dv = d.join("")
+ dv = dv.trim()
+ dv = dv.replace(/^@/, "")
+ o[dn] = dv
+ })
+ return o
+}
+
+
+function warn (f, d, m) {
+ readJson.log.warn("package.json", d._id, m)
+}
+
+
+function validName (file, data) {
+ if (!data.name) return new Error("No 'name' field")
+ data.name = data.name.trim()
+ if (data.name.charAt(0) === "." ||
+ data.name.match(/[\/@\s\+%:]/) ||
+ data.name.toLowerCase() === "node_modules" ||
+ data.name.toLowerCase() === "favicon.ico") {
+ return new Error("Invalid name: " +
+ JSON.stringify(data.name))
+ }
+ return true
+}
+
+
+function parseKeywords (file, data) {
+ var kw = data.keywords
+ if (typeof kw === "string") {
+ kw = kw.split(/,\s+/)
+ data.keywords = kw
+ }
+}
+
+function validVersion (file, data) {
+ var v = data.version
+ if (!v) return new Error("no version");
+ if (!semver.valid(v)) {
+ return new Error("invalid version: "+v)
+ }
+ data.version = semver.clean(data.version)
+ return true
+}
+function unParsePeople (file, data) {
+ return parsePeople(file, data, true)
+}
+
+function parsePeople (file, data, un) {
+ var fn = un ? unParsePerson : parsePerson
+ if (data.author) data.author = fn(data.author)
+ ;["maintainers", "contributors"].forEach(function (set) {
+ if (!Array.isArray(data[set])) return;
+ data[set] = data[set].map(fn)
+ })
+ return data
+}
+
+function unParsePerson (person) {
+ if (typeof person === "string") return person
+ var name = person.name || ""
+ var u = person.url || person.web
+ var url = u ? (" ("+u+")") : ""
+ var e = person.email || person.mail
+ var email = e ? (" <"+e+">") : ""
+ return name+email+url
+}
+
+function parsePerson (person) {
+ if (typeof person !== "string") return person
+ var name = person.match(/^([^\(<]+)/)
+ var url = person.match(/\(([^\)]+)\)/)
+ var email = person.match(/<([^>]+)>/)
+ var obj = {}
+ if (name && name[0].trim()) obj.name = name[0].trim()
+ if (email) obj.email = email[1];
+ if (url) obj.url = url[1];
+ return obj
+}
* `prompt` What to write to stdout before reading input.
* `silent` Don't echo the output as the user types it.
* `num` Max number of chars to read from terminal.
-* `delim` The char that means we're done. Default: `"\n"`
* `timeout` Number of ms to wait for user input before giving up.
* `default` The default value if the user enters nothing.
-If silent is true, or num is set, or delim is something other than
-`"\n"`, then read will set raw mode, and read character by character.
+If silent is true, or num is set, and the input is a TTY,
+then read will set raw mode, and read character by character.
-At this time, backspace and arrow keys are not supported in raw mode.
+At this time, backspace and arrow keys are not supported very well.
It's probably not too hard to add support for this, perhaps using node's
built-in readline module.
, StringDecoder = require("string_decoder").StringDecoder
function raw (mode) {
+ if (process.stdin.setRawMode) {
+ if (process.stdin.isTTY) {
+ process.stdin.setRawMode(mode)
+ }
+ return
+ }
+ // old style
try {
- process.stdin.setRawMode(mode)
- } catch (e) {
tty.setRawMode(mode)
- }
+ } catch (e) {}
}
+
function read (opts, cb) {
if (!cb) cb = opts, opts = {}
, silent = opts.silent
, timeout = opts.timeout
, num = opts.num || null
- , delim = opts.delim || "\n"
if (p && def) p += "("+(silent ? "<default hidden>" : def)+") "
// switching into raw mode is a little bit painful.
// avoid if possible.
- var r = silent || num || delim !== "\n" ? rawRead : normalRead
+ var r = silent || num ? rawRead : normalRead
+ if (r === rawRead && !process.stdin.isTTY) r = normalRead
if (timeout) {
cb = (function (cb) {
if (p && !process.stdout.write(p)) {
process.stdout.on("drain", function D () {
process.stdout.removeListener("drain", D)
- r(def, timeout, delim, silent, num, cb)
+ r(def, timeout, silent, num, cb)
})
} else {
process.nextTick(function () {
- r(def, timeout, delim, silent, num, cb)
+ r(def, timeout, silent, num, cb)
})
}
}
-function normalRead (def, timeout, delim, silent, num, cb) {
+function normalRead (def, timeout, silent, num, cb) {
var stdin = process.openStdin()
, val = ""
, decoder = new StringDecoder("utf8")
buffer = ""
// \r has no place here.
- // XXX But what if \r is the delim or something dumb like that?
- // Meh. If anyone complains about this, deal with it.
val = val.replace(/\r/g, "")
- // TODO Make delim configurable
- if (val.indexOf(delim) !== -1) {
+ if (val.indexOf("\n") !== -1) {
// pluck off any delims at the beginning.
- if (val !== delim) {
+ if (val !== "\n") {
var i, l
for (i = 0, l = val.length; i < l; i ++) {
- if (val.charAt(i) !== delim) break
+ if (val.charAt(i) !== "\n") break
}
if (i !== 0) val = val.substr(i)
}
+ // hack. if we get the number of chars, just pretend there was a delim
+ if (num > 0 && val.length >= num) {
+ val = val.substr(0, num) + "\n" + val.substr(num)
+ }
+
// buffer whatever might have come *after* the delimter
- var delimIndex = val.indexOf(delim)
+ var delimIndex = val.indexOf("\n")
if (delimIndex !== -1) {
buffer = val.substr(delimIndex)
val = val.substr(0, delimIndex)
})
}
-function rawRead (def, timeout, delim, silent, num, cb) {
+function rawRead (def, timeout, silent, num, cb) {
var stdin = process.openStdin()
, val = ""
, decoder = new StringDecoder
stdin.on("error", cb)
stdin.on("data", function D (c) {
// \r is my enemy.
- c = decoder.write(c).replace(/\r/g, "\n")
+ var s = decoder.write(c).replace(/\r/g, "\n")
+ var i = 0
- switch (c) {
- case "": // probably just a \r that was ignored.
+ LOOP: while (c = s.charAt(i++)) switch (c) {
+ case "\u007f": // backspace
+ val = val.substr(0, val.length - 1)
+ if (!silent) process.stdout.write('\b \b')
break
case "\u0004": // EOF
- case delim:
+ case "\n":
raw(false)
stdin.removeListener("data", D)
stdin.removeListener("error", cb)
stdin.removeListener("error", cb)
stdin.pause()
return cb(new Error("cancelled"))
- break
default: // just a normal char
val += buffer + c
buffer = ""
if (!silent) process.stdout.write(c)
- // explicitly process a delim if we have enough chars.
- if (num && val.length >= num) D(delim)
- break
+ // explicitly process a delim if we have enough chars
+ // and stop the processing.
+ if (num && val.length >= num) D("\n")
+ break LOOP
}
})
}
{
"name": "read",
- "version": "0.0.2",
+ "version": "0.1.0",
"main": "lib/read.js",
"dependencies": {},
- "devDependencies": {},
+ "devDependencies": {
+ "tap": "*"
+ },
"engines": {
"node": ">=0.6"
},
"url": "git://github.com/isaacs/read.git"
},
"license": "BSD",
- "_id": "read@0.0.2",
- "optionalDependencies": {},
- "_engineSupported": true,
- "_npmVersion": "1.1.15",
- "_nodeVersion": "v0.7.7",
- "_defaultsLoaded": true,
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "readme": "For reading user input from stdin.\n\n## USAGE\n\n```javascript\nvar read = require(\"read\")\nread(options, callback)\n```\n\nThe callback gets called with either the user input, or the default\nspecified, or an error, in the traditional `callback(error, result)`\nnode style.\n\n## OPTIONS\n\nEvery option is optional.\n\n* `prompt` What to write to stdout before reading input.\n* `silent` Don't echo the output as the user types it.\n* `num` Max number of chars to read from terminal.\n* `timeout` Number of ms to wait for user input before giving up.\n* `default` The default value if the user enters nothing.\n\nIf silent is true, or num is set, and the input is a TTY,\nthen read will set raw mode, and read character by character.\n\nAt this time, backspace and arrow keys are not supported very well.\nIt's probably not too hard to add support for this, perhaps using node's\nbuilt-in readline module.\n\n## CONTRIBUTING\n\nPatches welcome.\n\n## BUGS\n\nIn node 0.6.0 through 0.6.5, you must explicitly call\n`process.stdin.destroy()` or `process.exit()` when you know that your\nprogram is done reading, or else it will keep the event loop running\nforever.\n\nSee: <https://github.com/joyent/node/issues/2257>\n",
+ "_id": "read@0.1.0",
"_from": "read@0"
}
## Super simple to use
-Request is designed to be the simplest way possible to make http calls. It support HTTPS and follows redirects by default.
+Request is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default.
```javascript
var request = require('request');
* `followRedirect` - follow HTTP 3xx responses as redirects. defaults to true.
* `followAllRedirects` - follow non-GET HTTP 3xx responses as redirects. defaults to false.
* `maxRedirects` - the maximum number of redirects to follow, defaults to 10.
-* `onResponse` - If true the callback will be fired on the "response" event instead of "end". If a function it will be called on "response" and not effect the regular semantics of the main callback on "end".
* `encoding` - Encoding to be used on `setEncoding` of response data. If set to `null`, the body is returned as a Buffer.
* `pool` - A hash object containing the agents for these requests. If omitted this request will use the global pool which is set to node's default maxSockets.
* `pool.maxSockets` - Integer containing the maximum amount of sockets in the pool.
--- /dev/null
+
+/*!
+ * knox - auth
+ * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var crypto = require('crypto')
+ , parse = require('url').parse;
+
+/**
+ * Valid keys.
+ */
+
+var keys = [
+ 'acl'
+ , 'location'
+ , 'logging'
+ , 'notification'
+ , 'partNumber'
+ , 'policy'
+ , 'requestPayment'
+ , 'torrent'
+ , 'uploadId'
+ , 'uploads'
+ , 'versionId'
+ , 'versioning'
+ , 'versions'
+ , 'website'
+];
+
+/**
+ * Return an "Authorization" header value with the given `options`
+ * in the form of "AWS <key>:<signature>"
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+exports.authorization = function(options){
+ return 'AWS ' + options.key + ':' + exports.sign(options);
+};
+
+/**
+ * Simple HMAC-SHA1 Wrapper
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+exports.hmacSha1 = function(options){
+ return crypto.createHmac('sha1', options.secret).update(options.message).digest('base64');
+};
+
+/**
+ * Create a base64 sha1 HMAC for `options`.
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+exports.sign = function(options){
+ options.message = exports.stringToSign(options);
+ return exports.hmacSha1(options);
+};
+
+/**
+ * Create a base64 sha1 HMAC for `options`.
+ *
+ * Specifically to be used with S3 presigned URLs
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+exports.signQuery = function(options){
+ options.message = exports.queryStringToSign(options);
+ return exports.hmacSha1(options);
+};
+
+/**
+ * Return a string for sign() with the given `options`.
+ *
+ * Spec:
+ *
+ * <verb>\n
+ * <md5>\n
+ * <content-type>\n
+ * <date>\n
+ * [headers\n]
+ * <resource>
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+exports.stringToSign = function(options){
+ var headers = options.amazonHeaders || '';
+ if (headers) headers += '\n';
+ return [
+ options.verb
+ , options.md5
+ , options.contentType
+ , options.date.toUTCString()
+ , headers + options.resource
+ ].join('\n');
+};
+
+/**
+ * Return a string for sign() with the given `options`, but is meant exclusively
+ * for S3 presigned URLs
+ *
+ * Spec:
+ *
+ * <date>\n
+ * <resource>
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+exports.queryStringToSign = function(options){
+ return 'GET\n\n\n' +
+ options.date + '\n' +
+ options.resource;
+};
+
+/**
+ * Perform the following:
+ *
+ * - ignore non-amazon headers
+ * - lowercase fields
+ * - sort lexicographically
+ * - trim whitespace between ":"
+ * - join with newline
+ *
+ * @param {Object} headers
+ * @return {String}
+ * @api private
+ */
+
+exports.canonicalizeHeaders = function(headers){
+ var buf = []
+ , fields = Object.keys(headers);
+ for (var i = 0, len = fields.length; i < len; ++i) {
+ var field = fields[i]
+ , val = headers[field]
+ , field = field.toLowerCase();
+ if (0 !== field.indexOf('x-amz')) continue;
+ buf.push(field + ':' + val);
+ }
+ return buf.sort().join('\n');
+};
+
+/**
+ * Perform the following:
+ *
+ * - ignore non sub-resources
+ * - sort lexicographically
+ *
+ * @param {String} resource
+ * @return {String}
+ * @api private
+ */
+
+exports.canonicalizeResource = function(resource){
+ var url = parse(resource, true)
+ , path = url.pathname
+ , buf = [];
+
+ Object.keys(url.query).forEach(function(key){
+ if (!~keys.indexOf(key)) return;
+ var val = '' == url.query[key] ? '' : '=' + encodeURIComponent(url.query[key]);
+ buf.push(key + val);
+ });
+
+ return path + (buf.length
+ ? '?' + buf.sort().join('&')
+ : '');
+};
--- /dev/null
+var crypto = require('crypto')
+
+// The Authentication Header
+//
+// The Amazon S3 REST API uses the standard HTTPAuthorization header to pass authentication information. (The name of the standard header is unfortunate because it carries authentication information, not authorization).Under the Amazon S3 authentication scheme, the Authorization header has the following form.
+//
+// Authorization: AWS AWSAccessKeyId:Signature
+//
+// Developers are issued an AWS Access Key ID and AWS SecretAccess Key when they register. For request authentication, theAWSAccessKeyId element identifies the secret key that was used to compute the signature, and (indirectly) the developer making the request.
+//
+// The Signature element is the RFC 2104HMAC-SHA1 of selected elements from the request, and so theSignature part of the Authorization header will vary from request to request. If the request signature calculated by the system matches theSignature included with the request, then the requester will have demonstrated possession to the AWSSecret Access Key. The request will then be processed under the identity, and with the authority, of the developer to whom the key was issued.
+//
+// Following is pseudo-grammar that illustrates the construction of the Authorization request header (\nmeans the Unicode code point U+000A commonly called newline).
+
+function authorizationHeader (accessKey) {
+ // Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
+
+ var authorization = 'AWS' + " " + accessKey + ":" + signature()
+
+ return authorization
+}
+
+//
+
+function signature (secret, verb, md5, contenttype, date, amzheaders, bucket, path) {
+ // Signature = Base64( HMAC-SHA1( UTF-8-Encoding-Of( YourSecretAccessKeyID, StringToSign ) ) );
+
+ function encodeSignature (stringToSign) {
+ return crypto.createHash('sha1').update(stringToSign).digest('base64')
+ }
+
+ //
+ // StringToSign = HTTP-Verb + "\n" +
+ // Content-MD5 + "\n" +
+ // Content-Type + "\n" +
+ // Date + "\n" +
+ // CanonicalizedAmzHeaders +
+ // CanonicalizedResource;
+
+ function compileStringToSign () {
+ var s =
+ verb + '\n'
+ (md5 || '') + '\n'
+ (contenttype || '') + '\n'
+ date.toUTCString() + '\n'
+ canonicalizeAmzHeaders(amzheaders) +
+ canonicalizeResource()
+ return s
+ }
+
+ //
+ // CanonicalizedResource = [ "/" + Bucket ] +
+ // <HTTP-Request-URI, from the protocol name up to the query string> +
+ // [ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
+
+ function canonicalizeResource () {
+ return '/' + bucket + path
+ }
+
+ //
+ // CanonicalizedAmzHeaders = <described below>
+ //
+ // HMAC-SHA1 is an algorithm defined by RFC 2104 (go to RFC 2104 - Keyed-Hashing for Message Authentication ). The algorithm takes as input two byte-strings: a key and a message. For Amazon S3 Request authentication, use your AWS Secret Access Key (YourSecretAccessKeyID) as the key, and the UTF-8 encoding of the StringToSign as the message. The output of HMAC-SHA1 is also a byte string, called the digest. The Signature request parameter is constructed by Base64 encoding this digest.
+ // Request Canonicalization for Signing
+ //
+ // Recall that when the system receives an authenticated request, it compares the computed request signature with the signature provided in the request in StringToSign. For that reason, you must compute the signature using the same method used by Amazon S3. We call the process of putting a request in an agreed-upon form for signing "canonicalization".
+
+}
+
+
+
+// Constructing the CanonicalizedResource Element
+//
+// CanonicalizedResource represents the Amazon S3 resource targeted by the request. Construct it for a REST request as follows:
+//
+// Launch Process
+//
+// 1
+//
+//
+// Start with the empty string ("").
+//
+// 2
+//
+//
+// If the request specifies a bucket using the HTTP Host header (virtual hosted-style), append the bucket name preceded by a "/" (e.g., "/bucketname"). For path-style requests and requests that don't address a bucket, do nothing. For more information on virtual hosted-style requests, see Virtual Hosting of Buckets.
+//
+// 3
+//
+//
+// Append the path part of the un-decoded HTTP Request-URI, up-to but not including the query string.
+//
+// 4
+//
+//
+// If the request addresses a sub-resource, like ?versioning, ?location, ?acl, ?torrent, ?lifecycle, or ?versionid append the sub-resource, its value if it has one, and the question mark. Note that in case of multiple sub-resources, sub-resources must be lexicographically sorted by sub-resource name and separated by '&'. e.g. ?acl&versionId=value.
+//
+// The list of sub-resources that must be included when constructing the CanonicalizedResource Element are: acl, lifecycle, location, logging, notification, partNumber, policy, requestPayment, torrent, uploadId, uploads, versionId, versioning, versions and website.
+//
+// If the request specifies query string parameters overriding the response header values (see Get Object), append the query string parameters, and its values. When signing you do not encode these values. However, when making the request, you must encode these parameter values. The query string parameters in a GET request include response-content-type, response-content-language, response-expires, response-cache-control, response-content-disposition, response-content-encoding.
+//
+// The delete query string parameter must be including when creating the CanonicalizedResource for a Multi-Object Delete request.
+//
+// Elements of the CanonicalizedResource that come from the HTTP Request-URI should be signed literally as they appear in the HTTP request, including URL-Encoding meta characters.
+//
+// The CanonicalizedResource might be different than the HTTP Request-URI. In particular, if your request uses the HTTP Host header to specify a bucket, the bucket does appear in the HTTP Request-URI. However, the CanonicalizedResource continues to include the bucket. Query string parameters might also appear in the Request-URI but are not included in CanonicalizedResource. For more information, see Virtual Hosting of Buckets.
+// Constructing the CanonicalizedAmzHeaders Element
+//
+// To construct the CanonicalizedAmzHeaders part of StringToSign, select all HTTP request headers that start with 'x-amz-' (using a case-insensitive comparison) and use the following process.
+//
+// CanonicalizedAmzHeaders Process
+// 1 Convert each HTTP header name to lower-case. For example, 'X-Amz-Date' becomes 'x-amz-date'.
+// 2 Sort the collection of headers lexicographically by header name.
+// 3 Combine header fields with the same name into one "header-name:comma-separated-value-list" pair as prescribed by RFC 2616, section 4.2, without any white-space between values. For example, the two metadata headers 'x-amz-meta-username: fred' and 'x-amz-meta-username: barney' would be combined into the single header 'x-amz-meta-username: fred,barney'.
+// 4 "Unfold" long headers that span multiple lines (as allowed by RFC 2616, section 4.2) by replacing the folding white-space (including new-line) by a single space.
+// 5 Trim any white-space around the colon in the header. For example, the header 'x-amz-meta-username: fred,barney' would become 'x-amz-meta-username:fred,barney'
+// 6 Finally, append a new-line (U+000A) to each canonicalized header in the resulting list. Construct the CanonicalizedResource element by concatenating all headers in this list into a single string.
+//
+// Positional versus Named HTTP Header StringToSign Elements
+//
+// The first few header elements of StringToSign (Content-Type, Date, and Content-MD5) are positional in nature. StringToSign does not include the names of these headers, only their values from the request. In contrast, the 'x-amz-' elements are named; Both the header names and the header values appear in StringToSign.
+//
+// If a positional header called for in the definition of StringToSign is not present in your request, (Content-Type or Content-MD5, for example, are optional for PUT requests, and meaningless for GET requests), substitute the empty string ("") in for that position.
+// Time Stamp Requirement
+//
+// A valid time stamp (using either the HTTP Date header or an x-amz-date alternative) is mandatory for authenticated requests. Furthermore, the client time-stamp included with an authenticated request must be within 15 minutes of the Amazon S3 system time when the request is received. If not, the request will fail with the RequestTimeTooSkewed error status code. The intention of these restrictions is to limit the possibility that intercepted requests could be replayed by an adversary. For stronger protection against eavesdropping, use the HTTPS transport for authenticated requests.
+//
+// Some HTTP client libraries do not expose the ability to set the Date header for a request. If you have trouble including the value of the 'Date' header in the canonicalized headers, you can set the time-stamp for the request using an 'x-amz-date' header instead. The value of the x-amz-date header must be in one of the RFC 2616 formats (http://www.ietf.org/rfc/rfc2616.txt). When an x-amz-date header is present in a request, the system will ignore any Date header when computing the request signature. Therefore, if you include the x-amz-date header, use the empty string for the Date when constructing the StringToSign. See the next section for an example.
, CookieJar = require('./vendor/cookie/jar')
, cookieJar = new CookieJar
, tunnel = require('./tunnel')
+ , aws = require('./aws')
;
if (process.logging) {
http.Agent.call(this, options)
}
util.inherits(https.Agent, http.Agent)
- https.Agent.prototype._getConnection = function(host, port, cb) {
- var s = tls.connect(port, host, this.options, function() {
+ https.Agent.prototype._getConnection = function (host, port, cb) {
+ var s = tls.connect(port, host, this.options, function () {
// do other checks here?
if (cb) cb()
})
if (!options) options = {}
- if (!self.pool) self.pool = globalPool
+ if (!self.pool && self.pool !== false) self.pool = globalPool
self.dests = []
self.__isRequestRequest = true
self._callback.apply(self, arguments)
self._callbackCalled = true
}
+ self.on('error', self.callback.bind())
+ self.on('complete', self.callback.bind(self, null))
}
if (self.url) {
// do the HTTP CONNECT dance using koichik/node-tunnel
if (http.globalAgent && self.uri.protocol === "https:") {
+ self.tunnel = true
var tunnelFn = self.proxy.protocol === "http:"
? tunnel.httpsOverHttp : tunnel.httpsOverHttps
var tunnelOptions = { proxy: { host: self.proxy.hostname
- , port: +self.proxy.port
+ , port: +self.proxy.port
, proxyAuth: self.proxy.auth }
, ca: this.ca }
}
}
+ if (!self.uri.host || !self.uri.pathname) {
+ // Invalid URI: it may generate lot of bad errors, like "TypeError: Cannot call method 'indexOf' of undefined" in CookieJar
+ // Detect and reject it as soon as possible
+ var faultyUri = url.format(self.uri)
+ var message = 'Invalid URI "' + faultyUri + '"'
+ if (Object.keys(options).length === 0) {
+ // No option ? This can be the sign of a redirect
+ // As this is a case where the user cannot do anything (he didn't call request directly with this URL)
+ // he should be warned that it can be caused by a redirection (can save some hair)
+ message += '. This can be caused by a crappy redirection.'
+ }
+ self.emit('error', new Error(message))
+ return // This error was fatal
+ }
+
self._redirectsFollowed = self._redirectsFollowed || 0
self.maxRedirects = (self.maxRedirects !== undefined) ? self.maxRedirects : 10
self.followRedirect = (self.followRedirect !== undefined) ? self.followRedirect : true
self.host = self.uri.hostname
}
- if (self.onResponse === true) {
- self.onResponse = self.callback
- delete self.callback
- }
-
self.clientErrorHandler = function (error) {
if (self._aborted) return
return
}
if (self.timeout && self.timeoutTimer) {
- clearTimeout(self.timeoutTimer);
- self.timeoutTimer = null;
+ clearTimeout(self.timeoutTimer)
+ self.timeoutTimer = null
}
self.emit('error', error)
}
- if (self.onResponse) self.on('error', function (e) {self.onResponse(e)})
- if (self.callback) self.on('error', function (e) {self.callback(e)})
if (options.form) {
self.form(options.form)
if (options.oauth) {
self.oauth(options.oauth)
}
+
+ if (options.aws) {
+ self.aws(options.aws)
+ }
if (self.uri.auth && !self.headers.authorization) {
self.headers.authorization = "Basic " + toBase64(self.uri.auth.split(':').map(function(item){ return qs.unescape(item)}).join(':'))
if (options.json) {
self.json(options.json)
} else if (options.multipart) {
+ self.boundary = uuid()
self.multipart(options.multipart)
}
if (self.body) {
if (Array.isArray(self.body)) {
- self.body.forEach(function(part) {
+ self.body.forEach(function (part) {
self.write(part)
})
} else {
console.warn("options.requestBodyStream is deprecated, please pass the request object to stream.pipe.")
self.requestBodyStream.pipe(self)
} else if (!self.src) {
- self.headers['content-length'] = 0
- self.end()
+ if (self.method !== 'GET' && typeof self.method !== 'undefined') {
+ self.headers['content-length'] = 0;
+ }
+ self.end();
}
self.ntick = true
})
}
-// Must call this when following a redirect from https to http or vice versa
-// Attempts to keep everything as identical as possible, but update the
-// httpModule, Tunneling agent, and/or Forever Agent in use.
-Request.prototype._updateProtocol = function () {
- var self = this
- var protocol = self.uri.protocol
-
- if (protocol === 'https:') {
- // previously was doing http, now doing https
- // if it's https, then we might need to tunnel now.
- if (self.proxy) {
- self.tunnel = true
- var tunnelFn = self.proxy.protocol === 'http:'
- ? tunnel.httpsOverHttp : tunnel.httpsOverHttps
- var tunnelOptions = { proxy: { host: self.proxy.hostname
- , post: +self.proxy.port
- , proxyAuth: self.proxy.auth }
- , ca: self.ca }
- self.agent = tunnelFn(tunnelOptions)
- return
- }
-
- self.httpModule = https
- switch (self.agentClass) {
- case ForeverAgent:
- self.agentClass = ForeverAgent.SSL
- break
- case http.Agent:
- self.agentClass = https.Agent
- break
- default:
- // nothing we can do. Just hope for the best.
- return
- }
-
- // if there's an agent, we need to get a new one.
- if (self.agent) self.agent = self.getAgent()
-
- } else {
- if (log) log('previously https, now http')
- // previously was doing https, now doing http
- // stop any tunneling.
- if (self.tunnel) self.tunnel = false
- self.httpModule = http
- switch (self.agentClass) {
- case ForeverAgent.SSL:
- self.agentClass = ForeverAgent
- break
- case https.Agent:
- self.agentClass = http.Agent
- break
- default:
- // nothing we can do. just hope for the best
- return
- }
-
- // if there's an agent, then get a new one.
- if (self.agent) {
- self.agent = null
- self.agent = self.getAgent()
- }
- }
-}
-
Request.prototype.getAgent = function () {
var Agent = this.agentClass
var options = {}
poolKey += this.host + ':' + this.port
}
- // ca option is only relevant if proxy or destination are https
- var proxy = this.proxy
- if (typeof proxy === 'string') proxy = url.parse(proxy)
- var caRelevant = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'
- if (options.ca && caRelevant) {
+ if (options.ca) {
if (poolKey) poolKey += ':'
poolKey += options.ca
}
return this.httpModule.globalAgent
}
- // we're using a stored agent. Make sure it's protocol-specific
- poolKey = this.uri.protocol + poolKey
-
// already generated an agent for this setting
if (this.pool[poolKey]) return this.pool[poolKey]
self.method = self.method || 'GET'
self.href = self.uri.href
if (log) log('%method %href', self)
+
+ if (self.src && self.src.stat && self.src.stat.size) {
+ self.headers['content-length'] = self.src.stat.size
+ }
+ if (self._aws) {
+ self.aws(self._aws, true)
+ }
+
self.req = self.httpModule.request(self, function (response) {
if (self._aborted) return
if (self._paused) response.pause()
self.response = response
response.request = self
+ response.toJSON = toJSON
if (self.httpModule === https &&
self.strictSSL &&
if (self.setHost) delete self.headers.host
if (self.timeout && self.timeoutTimer) {
- clearTimeout(self.timeoutTimer);
- self.timeoutTimer = null;
+ clearTimeout(self.timeoutTimer)
+ self.timeoutTimer = null
}
+ var addCookie = function (cookie) {
+ if (self._jar) self._jar.add(new Cookie(cookie))
+ else cookieJar.add(new Cookie(cookie))
+ }
+
if (response.headers['set-cookie'] && (!self._disableCookies)) {
- response.headers['set-cookie'].forEach(function(cookie) {
- if (self._jar) self._jar.add(new Cookie(cookie))
- else cookieJar.add(new Cookie(cookie))
- })
+ if (Array.isArray(response.headers['set-cookie'])) response.headers['set-cookie'].forEach(addCookie)
+ else addCookie(response.headers['set-cookie'])
}
if (response.statusCode >= 300 && response.statusCode < 400 &&
if (!isUrl.test(response.headers.location)) {
response.headers.location = url.resolve(self.uri.href, response.headers.location)
}
-
- var uriPrev = self.uri
- self.uri = url.parse(response.headers.location)
-
- // handle the case where we change protocol from https to http or vice versa
- if (self.uri.protocol !== uriPrev.protocol) {
- self._updateProtocol()
- }
-
+ self.uri = response.headers.location
self.redirects.push(
{ statusCode : response.statusCode
, redirectUri: response.headers.location
}
)
- self.method = 'GET'; // Force all redirects to use GET
+ if (self.followAllRedirects) self.method = 'GET'
+ // self.method = 'GET'; // Force all redirects to use GET || commented out fixes #215
delete self.req
delete self.agent
delete self._started
self.emit('response', response)
- if (self.onResponse) {
- self.onResponse(null, response)
- }
if (self.callback) {
var buffer = []
var bodyLen = 0
response.body = JSON.parse(response.body)
} catch (e) {}
}
-
- self.callback(null, response, response.body)
+
+ self.emit('complete', response, response.body)
})
}
}
})
if (self.timeout && !self.timeoutTimer) {
- self.timeoutTimer = setTimeout(function() {
+ self.timeoutTimer = setTimeout(function () {
self.req.abort()
var e = new Error("ETIMEDOUT")
e.code = "ETIMEDOUT"
// Set additional timeout on socket - in case if remote
// server freeze after sending headers
if (self.req.setTimeout) { // only works on node 0.6+
- self.req.setTimeout(self.timeout, function(){
- if (self.req) {
- self.req.abort()
- var e = new Error("ESOCKETTIMEDOUT")
- e.code = "ESOCKETTIMEDOUT"
- self.emit("error", e)
- }
+ self.req.setTimeout(self.timeout, function () {
+ if (self.req) {
+ self.req.abort()
+ var e = new Error("ESOCKETTIMEDOUT")
+ e.code = "ESOCKETTIMEDOUT"
+ self.emit("error", e)
+ }
})
}
}
self.req.on('error', self.clientErrorHandler)
+ self.req.on('drain', function() {
+ self.emit('drain')
+ })
self.emit('request', self.req)
}
-Request.prototype.abort = function() {
+Request.prototype.abort = function () {
this._aborted = true;
if (this.req) {
return this
}
Request.prototype.setHeaders = function (headers) {
- for (i in headers) {this.setHeader(i, headers[i])}
+ for (var i in headers) {this.setHeader(i, headers[i])}
return this
}
Request.prototype.qs = function (q, clobber) {
self.body = []
if (!self.headers['content-type']) {
- self.headers['content-type'] = 'multipart/related;boundary="frontier"';
+ self.headers['content-type'] = 'multipart/related; boundary=' + self.boundary;
} else {
- self.headers['content-type'] = self.headers['content-type'].split(';')[0] + ';boundary="frontier"';
+ self.headers['content-type'] = self.headers['content-type'].split(';')[0] + '; boundary=' + self.boundary;
}
+ console.log('boundary >> ' + self.boundary)
+
if (!multipart.forEach) throw new Error('Argument error, options.multipart.')
multipart.forEach(function (part) {
var body = part.body
- if(!body) throw Error('Body attribute missing in multipart.')
+ if(body == null) throw Error('Body attribute missing in multipart.')
delete part.body
- var preamble = '--frontier\r\n'
- Object.keys(part).forEach(function(key){
+ var preamble = '--' + self.boundary + '\r\n'
+ Object.keys(part).forEach(function (key) {
preamble += key + ': ' + part[key] + '\r\n'
})
preamble += '\r\n'
self.body.push(new Buffer(body))
self.body.push(new Buffer('\r\n'))
})
- self.body.push(new Buffer('--frontier--'))
+ self.body.push(new Buffer('--' + self.boundary + '--'))
return self
}
Request.prototype.json = function (val) {
}
return this
}
+Request.prototype.aws = function (opts, now) {
+ if (!now) {
+ this._aws = opts
+ return this
+ }
+ var date = new Date()
+ this.setHeader('date', date.toUTCString())
+ this.setHeader('authorization', aws.authorization(
+ { key: opts.key
+ , secret: opts.secret
+ , verb: this.method
+ , date: date
+ , resource: aws.canonicalizeResource('/' + opts.bucket + this.path)
+ , contentType: this.headers['content-type'] || ''
+ , md5: this.headers['content-md5'] || ''
+ , amazonHeaders: aws.canonicalizeHeaders(this.headers)
+ }
+ ))
+
+ return this
+}
+
Request.prototype.oauth = function (_oauth) {
var form
if (this.headers['content-type'] &&
delete oa['oauth_'+i]
}
}
- this.headers.authorization =
+ this.headers.Authorization =
'OAuth '+Object.keys(oa).sort().map(function (i) {return i+'="'+oauth.rfc3986(oa[i])+'"'}).join(',')
- this.headers.authorization += ',oauth_signature="'+oauth.rfc3986(signature)+'"'
+ this.headers.Authorization += ',oauth_signature="'+oauth.rfc3986(signature)+'"'
return this
}
Request.prototype.jar = function (jar) {
}
Request.prototype.write = function () {
if (!this._started) this.start()
- this.req.write.apply(this.req, arguments)
+ return this.req.write.apply(this.req, arguments)
}
Request.prototype.end = function (chunk) {
if (chunk) this.write(chunk)
// organize params for post, put, head, del
function initParams(uri, options, callback) {
if ((typeof options === 'function') && !callback) callback = options;
- if (typeof options === 'object') {
+ if (options && typeof options === 'object') {
options.uri = uri;
} else if (typeof uri === 'string') {
options = {uri:uri};
function request (uri, options, callback) {
if (typeof uri === 'undefined') throw new Error('undefined is not a valid uri or options object.')
if ((typeof options === 'function') && !callback) callback = options;
- if (typeof options === 'object') {
+ if (options && typeof options === 'object') {
options.uri = uri;
} else if (typeof uri === 'string') {
options = {uri:uri};
for (var i in options) {
if (params.options[i] === undefined) params.options[i] = options[i]
}
- return method(params.uri, params.options, params.callback)
+ return method(params.options, params.callback)
}
return d
}
if (typeof str !== 'string') throw new Error("The cookie function only accepts STRING as param")
return new Cookie(str)
}
+
+// Safe toJSON
+
+function getSafe (self, uuid) {
+ if (typeof self === 'object' || typeof self === 'function') var safe = {}
+ if (Array.isArray(self)) var safe = []
+
+ var recurse = []
+
+ Object.defineProperty(self, uuid, {})
+
+ var attrs = Object.keys(self).filter(function (i) {
+ if (i === uuid) return false
+ if ( (typeof self[i] !== 'object' && typeof self[i] !== 'function') || self[i] === null) return true
+ return !(Object.getOwnPropertyDescriptor(self[i], uuid))
+ })
+
+
+ for (var i=0;i<attrs.length;i++) {
+ if ( (typeof self[attrs[i]] !== 'object' && typeof self[attrs[i]] !== 'function') ||
+ self[attrs[i]] === null
+ ) {
+ safe[attrs[i]] = self[attrs[i]]
+ } else {
+ recurse.push(attrs[i])
+ Object.defineProperty(self[attrs[i]], uuid, {})
+ }
+ }
+
+ for (var i=0;i<recurse.length;i++) {
+ safe[recurse[i]] = getSafe(self[recurse[i]], uuid)
+ }
+
+ return safe
+}
+
+function toJSON () {
+ return getSafe(this, (((1+Math.random())*0x10000)|0).toString(16))
+}
+
+Request.prototype.toJSON = toJSON
+
function rfc3986 (str) {
return encodeURIComponent(str)
- .replace('!','%21')
- .replace('*','%2A')
- .replace('(','%28')
- .replace(')','%29')
- .replace("'",'%27')
+ .replace(/!/g,'%21')
+ .replace(/\*/g,'%2A')
+ .replace(/\(/g,'%28')
+ .replace(/\)/g,'%29')
+ .replace(/'/g,'%27')
;
}
// big WTF here with the escape + encoding but it's what twitter wants
return escape(rfc3986(i)) + "%3D" + escape(rfc3986(params[i]))
}).join("%26")
- var key = consumer_secret + '&'
- if (token_secret) key += token_secret
+ var key = encodeURIComponent(consumer_secret) + '&'
+ if (token_secret) key += encodeURIComponent(token_secret)
return sha1(key, base)
}
"util",
"utility"
],
- "version": "2.9.153",
+ "version": "2.9.203",
"author": {
"name": "Mikeal Rogers",
"email": "mikeal.rogers@gmail.com"
},
"repository": {
"type": "git",
- "url": "git://github.com/mikeal/request.git"
+ "url": "http://github.com/mikeal/request.git"
},
"bugs": {
"url": "http://github.com/mikeal/request/issues"
"scripts": {
"test": "node tests/run.js"
},
- "_npmUser": {
- "name": "isaacs",
- "email": "i@izs.me"
- },
- "_id": "request@2.9.153",
- "dependencies": {},
- "devDependencies": {},
- "optionalDependencies": {},
- "_engineSupported": true,
- "_npmVersion": "1.1.8",
- "_nodeVersion": "v0.6.12",
- "_defaultsLoaded": true,
- "dist": {
- "shasum": "6f07c2ba3acfbe0cfe941f43647102740f05ff73"
- },
- "_from": "../request"
+ "readme": "# Request -- Simplified HTTP request method\n\n## Install\n\n<pre>\n npm install request\n</pre>\n\nOr from source:\n\n<pre>\n git clone git://github.com/mikeal/request.git \n cd request\n npm link\n</pre>\n\n## Super simple to use\n\nRequest is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default.\n\n```javascript\nvar request = require('request');\nrequest('http://www.google.com', function (error, response, body) {\n if (!error && response.statusCode == 200) {\n console.log(body) // Print the google web page.\n }\n})\n```\n\n## Streaming\n\nYou can stream any response to a file stream.\n\n```javascript\nrequest('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))\n```\n\nYou can also stream a file to a PUT or POST request. This method will also check the file extension against a mapping of file extensions to content-types, in this case `application/json`, and use the proper content-type in the PUT request if one is not already provided in the headers.\n\n```javascript\nfs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json'))\n```\n\nRequest can also pipe to itself. When doing so the content-type and content-length will be preserved in the PUT headers.\n\n```javascript\nrequest.get('http://google.com/img.png').pipe(request.put('http://mysite.com/img.png'))\n```\n\nNow let's get fancy.\n\n```javascript\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n if (req.method === 'PUT') {\n req.pipe(request.put('http://mysite.com/doodle.png'))\n } else if (req.method === 'GET' || req.method === 'HEAD') {\n request.get('http://mysite.com/doodle.png').pipe(resp)\n } \n }\n})\n```\n\nYou can also pipe() from a http.ServerRequest instance and to a http.ServerResponse instance. The HTTP method and headers will be sent as well as the entity-body data. Which means that, if you don't really care about security, you can do:\n\n```javascript\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n var x = request('http://mysite.com/doodle.png')\n req.pipe(x)\n x.pipe(resp)\n }\n})\n```\n\nAnd since pipe() returns the destination stream in node 0.5.x you can do one line proxying :)\n\n```javascript\nreq.pipe(request('http://mysite.com/doodle.png')).pipe(resp)\n```\n\nAlso, none of this new functionality conflicts with requests previous features, it just expands them.\n\n```javascript\nvar r = request.defaults({'proxy':'http://localproxy.com'})\n\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n r.get('http://google.com/doodle.png').pipe(resp)\n }\n})\n```\n\nYou can still use intermediate proxies, the requests will still follow HTTP forwards, etc.\n\n## OAuth Signing\n\n```javascript\n// Twitter OAuth\nvar qs = require('querystring')\n , oauth =\n { callback: 'http://mysite.com/callback/'\n , consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n }\n , url = 'https://api.twitter.com/oauth/request_token'\n ;\nrequest.post({url:url, oauth:oauth}, function (e, r, body) {\n // Assume by some stretch of magic you aquired the verifier\n var access_token = qs.parse(body)\n , oauth = \n { consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n , token: access_token.oauth_token\n , verifier: VERIFIER\n , token_secret: access_token.oauth_token_secret\n }\n , url = 'https://api.twitter.com/oauth/access_token'\n ;\n request.post({url:url, oauth:oauth}, function (e, r, body) {\n var perm_token = qs.parse(body)\n , oauth = \n { consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n , token: perm_token.oauth_token\n , token_secret: perm_token.oauth_token_secret\n }\n , url = 'https://api.twitter.com/1/users/show.json?'\n , params = \n { screen_name: perm_token.screen_name\n , user_id: perm_token.user_id\n }\n ;\n url += qs.stringify(params)\n request.get({url:url, oauth:oauth, json:true}, function (e, r, user) {\n console.log(user)\n })\n })\n})\n```\n\n\n\n### request(options, callback)\n\nThe first argument can be either a url or an options object. The only required option is uri, all others are optional.\n\n* `uri` || `url` - fully qualified uri or a parsed url object from url.parse()\n* `qs` - object containing querystring values to be appended to the uri\n* `method` - http method, defaults to GET\n* `headers` - http headers, defaults to {}\n* `body` - entity body for POST and PUT requests. Must be buffer or string.\n* `form` - sets `body` but to querystring representation of value and adds `Content-type: application/x-www-form-urlencoded; charset=utf-8` header.\n* `json` - sets `body` but to JSON representation of value and adds `Content-type: application/json` header.\n* `multipart` - (experimental) array of objects which contains their own headers and `body` attribute. Sends `multipart/related` request. See example below.\n* `followRedirect` - follow HTTP 3xx responses as redirects. defaults to true.\n* `followAllRedirects` - follow non-GET HTTP 3xx responses as redirects. defaults to false.\n* `maxRedirects` - the maximum number of redirects to follow, defaults to 10.\n* `encoding` - Encoding to be used on `setEncoding` of response data. If set to `null`, the body is returned as a Buffer.\n* `pool` - A hash object containing the agents for these requests. If omitted this request will use the global pool which is set to node's default maxSockets.\n* `pool.maxSockets` - Integer containing the maximum amount of sockets in the pool.\n* `timeout` - Integer containing the number of milliseconds to wait for a request to respond before aborting the request\t\n* `proxy` - An HTTP proxy to be used. Support proxy Auth with Basic Auth the same way it's supported with the `url` parameter by embedding the auth info in the uri.\n* `oauth` - Options for OAuth HMAC-SHA1 signing, see documentation above.\n* `strictSSL` - Set to `true` to require that SSL certificates be valid. Note: to use your own certificate authority, you need to specify an agent that was created with that ca as an option.\n* `jar` - Set to `false` if you don't want cookies to be remembered for future use or define your custom cookie jar (see examples section)\n\n\nThe callback argument gets 3 arguments. The first is an error when applicable (usually from the http.Client option not the http.ClientRequest object). The second in an http.ClientResponse object. The third is the response body String or Buffer.\n\n## Convenience methods\n\nThere are also shorthand methods for different HTTP METHODs and some other conveniences.\n\n### request.defaults(options) \n \nThis method returns a wrapper around the normal request API that defaults to whatever options you pass in to it.\n\n### request.put\n\nSame as request() but defaults to `method: \"PUT\"`.\n\n```javascript\nrequest.put(url)\n```\n\n### request.post\n\nSame as request() but defaults to `method: \"POST\"`.\n\n```javascript\nrequest.post(url)\n```\n\n### request.head\n\nSame as request() but defaults to `method: \"HEAD\"`.\n\n```javascript\nrequest.head(url)\n```\n\n### request.del\n\nSame as request() but defaults to `method: \"DELETE\"`.\n\n```javascript\nrequest.del(url)\n```\n\n### request.get\n\nAlias to normal request method for uniformity.\n\n```javascript\nrequest.get(url)\n```\n### request.cookie\n\nFunction that creates a new cookie.\n\n```javascript\nrequest.cookie('cookie_string_here')\n```\n### request.jar\n\nFunction that creates a new cookie jar.\n\n```javascript\nrequest.jar()\n```\n\n\n## Examples:\n\n```javascript\n var request = require('request')\n , rand = Math.floor(Math.random()*100000000).toString()\n ;\n request(\n { method: 'PUT'\n , uri: 'http://mikeal.iriscouch.com/testjs/' + rand\n , multipart: \n [ { 'content-type': 'application/json'\n , body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}})\n }\n , { body: 'I am an attachment' }\n ] \n }\n , function (error, response, body) {\n if(response.statusCode == 201){\n console.log('document saved as: http://mikeal.iriscouch.com/testjs/'+ rand)\n } else {\n console.log('error: '+ response.statusCode)\n console.log(body)\n }\n }\n )\n```\nCookies are enabled by default (so they can be used in subsequent requests). To disable cookies set jar to false (either in defaults or in the options sent).\n\n```javascript\nvar request = request.defaults({jar: false})\nrequest('http://www.google.com', function () {\n request('http://images.google.com')\n})\n```\n\nIf you to use a custom cookie jar (instead of letting request use its own global cookie jar) you do so by setting the jar default or by specifying it as an option:\n\n```javascript\nvar j = request.jar()\nvar request = request.defaults({jar:j})\nrequest('http://www.google.com', function () {\n request('http://images.google.com')\n})\n```\nOR\n\n```javascript\nvar j = request.jar()\nvar cookie = request.cookie('your_cookie_here')\nj.add(cookie)\nrequest({url: 'http://www.google.com', jar: j}, function () {\n request('http://images.google.com')\n})\n```\n",
+ "_id": "request@2.9.203",
+ "_from": "request@~2.9"
}
--- /dev/null
+/node_modules/*
--- /dev/null
+Copyright (c) 2011:
+Tim Koschützki (tim@debuggable.com)
+Felix Geisendörfer (felix@debuggable.com)
+
+ 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.
--- /dev/null
+SHELL := /bin/bash
+
+test:
+ @node test/runner.js
+
+.PHONY: test
+
--- /dev/null
+# retry
+
+Abstraction for exponential and custom retry strategies for failed operations.
+
+## Installation
+
+ npm install retry
+
+## Current Status
+
+This module has been tested and is ready to be used.
+
+## Tutorial
+
+The example below will retry a potentially failing `dns.resolve` operation
+`10` times using an exponential backoff strategy. With the default settings, this
+means the last attempt is made after `34 minutes and 7 seconds`.
+
+``` javascript
+var dns = require('dns');
+var retry = require('retry');
+
+function faultTolerantResolve(address, cb) {
+ var operation = retry.operation();
+
+ operation.attempt(function(currentAttempt) {
+ dns.resolve(address, function(err, addresses) {
+ if (operation.retry(err)) {
+ return;
+ }
+
+ cb(operation.mainError(), addresses);
+ });
+ });
+}
+
+faultTolerantResolve('nodejs.org', function(err, addresses) {
+ console.log(err, addresses);
+});
+```
+
+Of course you can also configure the factors that go into the exponential
+backoff. See the API documentation below for all available settings.
+currentAttempt is an int representing the number of attempts so far.
+
+``` javascript
+var operation = retry.operation({
+ retries: 5,
+ factor: 3,
+ minTimeout: 1 * 1000,
+ maxTimeout: 60 * 1000,
+ randomize: true,
+});
+```
+
+## API
+
+### retry.operation([options])
+
+Creates a new `RetryOperation` object. See the `retry.timeouts()` function
+below for available `options`.
+
+### retry.timeouts([options])
+
+Returns an array of timeouts. All time `options` and return values are in
+milliseconds. If `options` is an array, a copy of that array is returned.
+
+`options` is a JS object that can contain any of the following keys:
+
+* `retries`: The maximum amount of times to retry the operation. Default is `10`.
+* `factor`: The exponential factor to use. Default is `2`.
+* `minTimeout`: The amount of time before starting the first retry. Default is `1000`.
+* `maxTimeout`: The maximum amount of time between two retries. Default is `Infinity`.
+* `randomize`: Randomizes the timeouts by multiplying with a factor between `1` to `2`. Default is `false`.
+
+The formula used to calculate the individual timeouts is:
+
+```
+var Math.min(random * minTimeout * Math.pow(factor, attempt), maxTimeout);
+```
+
+Have a look at [this article][article] for a better explanation of approach.
+
+If you want to tune your `factor` / `times` settings to attempt the last retry
+after a certain amount of time, you can use wolfram alpha. For example in order
+to tune for `10` attempts in `5 minutes`, you can use this equation:
+
+![screenshot](https://github.com/tim-kos/node-retry/raw/master/equation.gif)
+
+Explaining the various values from left to right:
+
+* `k = 0 ... 9`: The `retries` value (10)
+* `1000`: The `minTimeout` value in ms (1000)
+* `x^k`: No need to change this, `x` will be your resulting factor
+* `5 * 60 * 1000`: The desired total amount of time for retrying in ms (5 minutes)
+
+To make this a little easier for you, use wolfram alpha to do the calculations:
+
+[http://www.wolframalpha.com/input/?i=Sum%5B1000*x^k%2C+{k%2C+0%2C+9}%5D+%3D+5+*+60+*+1000]()
+
+[article]: http://dthain.blogspot.com/2009/02/exponential-backoff-in-distributed.html
+
+### new RetryOperation(timeouts)
+
+Creates a new `RetryOperation` where `timeouts` is an array where each value is
+a timeout given in milliseconds.
+
+#### retryOperation.errors()
+
+Returns an array of all errors that have been passed to
+`retryOperation.retry()` so far.
+
+#### retryOperation.mainError()
+
+A reference to the error object that occured most frequently. Errors are
+compared using the `error.message` property.
+
+If multiple error messages occured the same amount of time, the last error
+object with that message is returned.
+
+If no errors occured so far, the value is `null`.
+
+#### retryOperation.attempt(fn, timeoutOps)
+
+Defines the function `fn` that is to be retried and executes it for the first
+time right away. The `fn` function can receive an optional `currentAttempt` callback that represents the number of attempts to execute `fn` so far.
+
+Optionally defines `timeoutOps` which is an object having a property `timeout` in miliseconds and a property `cb` callback function.
+Whenever your retry operation takes longer than `timeout` to execute, the timeout callback function `cb` is called.
+
+
+#### retryOperation.try(fn)
+
+This is an alias for `retryOperation.attempt(fn)`. This is deprecated.
+
+#### retryOperation.start(fn)
+
+This is an alias for `retryOperation.attempt(fn)`. This is deprecated.
+
+#### retryOperation.retry(error)
+
+Returns `false` when no `error` value is given, or the maximum amount of retries
+has been reached.
+
+Otherwise it returns `true`, and retries the operation after the timeout for
+the current attempt number.
+
+#### retryOperation.attempts()
+
+Returns an int representing the number of attempts it took to call `fn` before it was successful.
+
+## License
+
+retry is licensed under the MIT license.
+
+
+#Changelog
+
+0.6.0 Introduced optional timeOps parameter for the attempt() function which is an object having a property timeout in miliseconds and a property cb callback function. Whenever your retry operation takes longer than timeout to execute, the timeout callback function cb is called.
+
+0.5.0 Some minor refactorings.
+
+0.4.0 Changed retryOperation.try() to retryOperation.attempt(). Deprecated the aliases start() and try() for it.
+
+0.3.0 Added retryOperation.start() which is an alias for retryOperation.try().
+
+0.2.0 Added attempts() function and parameter to retryOperation.try() representing the number of attempts it took to call fn().
--- /dev/null
+module.exports = require('./lib/retry');
\ No newline at end of file
--- /dev/null
+var RetryOperation = require('./retry_operation');
+
+exports.operation = function(options) {
+ var timeouts = exports.timeouts(options);
+ return new RetryOperation(timeouts);
+};
+
+exports.timeouts = function(options) {
+ if (options instanceof Array) {
+ return [].concat(options);
+ }
+
+ var opts = {
+ retries: 10,
+ factor: 2,
+ minTimeout: 1 * 1000,
+ maxTimeout: Infinity,
+ randomize: false
+ };
+ for (var key in options) {
+ opts[key] = options[key];
+ }
+
+ if (opts.minTimeout > opts.maxTimeout) {
+ throw new Error('minTimeout is greater than maxTimeout');
+ }
+
+ var timeouts = [];
+ for (var i = 0; i < opts.retries; i++) {
+ timeouts.push(this._createTimeout(i, opts));
+ }
+
+ // sort the array numerically ascending
+ timeouts.sort(function(a,b) {
+ return a - b;
+ });
+
+ return timeouts;
+};
+
+exports._createTimeout = function(attempt, opts) {
+ var random = (opts.randomize)
+ ? (Math.random() + 1)
+ : 1;
+
+ var timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempt));
+ timeout = Math.min(timeout, opts.maxTimeout);
+
+ return timeout;
+};
\ No newline at end of file
--- /dev/null
+function RetryOperation(timeouts) {
+ this._timeouts = timeouts;
+ this._fn = null;
+ this._errors = [];
+ this._attempts = 1;
+ this._operationTimeout = null;
+ this._operationTimeoutCb = null;
+ this._timeout = null;
+}
+module.exports = RetryOperation;
+
+RetryOperation.prototype.retry = function(err) {
+ if (this._timeout) {
+ clearTimeout(this._timeout);
+ }
+
+ if (!err) {
+ return false;
+ }
+
+ this._errors.push(err);
+
+ var timeout = this._timeouts.shift();
+ if (timeout === undefined) {
+ return false;
+ }
+
+ this._attempts++;
+
+ var self = this;
+ setTimeout(function() {
+ self._fn(self._attempts);
+
+ if (self._operationTimeoutCb) {
+ self._timeout = setTimeout(function() {
+ self._operationTimeoutCb(self._attempts);
+ }, self._operationTimeout);
+ }
+ }, timeout);
+
+ return true;
+};
+
+RetryOperation.prototype.attempt = function(fn, timeoutOps) {
+ this._fn = fn;
+
+ if (timeoutOps) {
+ if (timeoutOps.timeout) {
+ this._operationTimeout = timeoutOps.timeout;
+ }
+ if (timeoutOps.cb) {
+ this._operationTimeoutCb = timeoutOps.cb;
+ }
+ }
+
+ this._fn(this._attempts);
+
+ var self = this;
+ if (this._operationTimeoutCb) {
+ this._timeout = setTimeout(function() {
+ self._operationTimeoutCb();
+ }, self._operationTimeout);
+ }
+};
+
+RetryOperation.prototype.try = function(fn) {
+ console.log('Using RetryOperation.try() is deprecated');
+ this.attempt(fn);
+};
+
+RetryOperation.prototype.start = function(fn) {
+ console.log('Using RetryOperation.start() is deprecated');
+ this.attempt(fn);
+};
+
+RetryOperation.prototype.start = RetryOperation.prototype.try;
+
+RetryOperation.prototype.errors = function() {
+ return this._errors;
+};
+
+RetryOperation.prototype.attempts = function() {
+ return this._attempts;
+};
+
+RetryOperation.prototype.mainError = function() {
+ if (this._errors.length === 0) {
+ return null;
+ }
+
+ var counts = {};
+ var mainError = null;
+ var mainErrorCount = 0;
+
+ for (var i = 0; i < this._errors.length; i++) {
+ var error = this._errors[i];
+ var message = error.message;
+ var count = (counts[message] || 0) + 1;
+
+ counts[message] = count;
+
+ if (count >= mainErrorCount) {
+ mainError = error;
+ mainErrorCount = count;
+ }
+ }
+
+ return mainError;
+};
\ No newline at end of file
--- /dev/null
+{
+ "author": {
+ "name": "Tim Koschützki",
+ "email": "tim@debuggable.com",
+ "url": "http://debuggable.com/"
+ },
+ "name": "retry",
+ "description": "Abstraction for exponential and custom retry strategies for failed operations.",
+ "version": "0.6.0",
+ "homepage": "https://github.com/tim-kos/node-retry",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/felixge/node-retry.git"
+ },
+ "directories": {
+ "lib": "./lib"
+ },
+ "main": "index",
+ "engines": {
+ "node": "*"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "fake": "0.2.0",
+ "far": "0.0.1"
+ },
+ "_id": "retry@0.6.0",
+ "_from": "retry"
+}
-{"name":"rimraf"
-,"version":"2.0.1"
-,"main":"rimraf.js"
-,"description":"A deep deletion module for node (like `rm -rf`)"
-,"author":"Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)"
-,"license":
- {"type":"MIT", "url": "https://github.com/isaacs/rimraf/raw/master/LICENSE"}
-,"optionalDependencies":{"graceful-fs":"~1.1"}
-,"repository":"git://github.com/isaacs/rimraf.git"
-,"scripts":{"test":"cd test && bash run.sh"}}
+{
+ "name": "rimraf",
+ "version": "2.0.2",
+ "main": "rimraf.js",
+ "description": "A deep deletion module for node (like `rm -rf`)",
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "license": {
+ "type": "MIT",
+ "url": "https://github.com/isaacs/rimraf/raw/master/LICENSE"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "~1.1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/rimraf.git"
+ },
+ "scripts": {
+ "test": "cd test && bash run.sh"
+ },
+ "_npmUser": {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ },
+ "_id": "rimraf@2.0.2",
+ "contributors": [
+ {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me"
+ },
+ {
+ "name": "Wayne Larsen",
+ "email": "wayne@larsen.st",
+ "url": "http://github.com/wvl"
+ },
+ {
+ "name": "ritch",
+ "email": "skawful@gmail.com"
+ },
+ {
+ "name": "Marcel Laverdet"
+ },
+ {
+ "name": "Yosef Dinerstein",
+ "email": "yosefd@microsoft.com"
+ }
+ ],
+ "dependencies": {
+ "graceful-fs": "~1.1"
+ },
+ "devDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "_engineSupported": true,
+ "_npmVersion": "1.1.24",
+ "_nodeVersion": "v0.7.10-pre",
+ "_defaultsLoaded": true,
+ "_from": "rimraf@2"
+}
fs = require("fs")
}
-var lstat = process.platform === "win32" ? "stat" : "lstat"
- , lstatSync = lstat + "Sync"
+var lstat = "lstat"
+if (process.platform === "win32") {
+ // not reliable on windows prior to 0.7.9
+ var v = process.version.replace(/^v/, '').split(/\.|-/).map(Number)
+ if (v[0] === 0 && (v[1] < 7 || v[1] == 7 && v[2] < 9)) {
+ lstat = "stat"
+ }
+}
+if (!fs[lstat]) lstat = "stat"
+var lstatSync = lstat + "Sync"
// for EMFILE handling
var timeout = 0
rimraf_(p, function CB (er) {
if (er) {
if (er.code === "EBUSY" && busyTries < exports.BUSYTRIES_MAX) {
- var time = (exports.BUSYTRIES_MAX - busyTries) * 100
busyTries ++
+ var time = busyTries * 100
// try again, with the same exact callback as this one.
return setTimeout(function () {
rimraf_(p, CB)
- })
+ }, time)
}
// this one won't happen if graceful-fs is used.
--- /dev/null
+Copyright (c) Isaac Z. Schlueter
+All rights reserved.
+
+The BSD License
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
{
+ "version": "1.1.37",
"name": "npm",
"publishConfig": {
"proprietary-attribs": false
"install",
"package.json"
],
- "version": "1.1.24",
"preferGlobal": true,
"config": {
"publishtest": false
"fstream": "~0.1.17",
"block-stream": "*",
"inherits": "1",
- "mkdirp": "0.3",
+ "mkdirp": "~0.3.3",
"read": "0",
- "lru-cache": "1",
- "node-gyp": "~0.4.5",
+ "lru-cache": "~1.1.0",
+ "node-gyp": "~0.5",
"fstream-npm": "0.1",
"uid-number": "0",
"archy": "0",
- "chownr": "0"
+ "chownr": "0",
+ "npmlog": "0",
+ "ansi": "~0.1.2",
+ "npm-registry-client": "0",
+ "read-package-json": "~0.1.1",
+ "read-installed": "0",
+ "glob": "~3.1.9",
+ "init-package-json": "0",
+ "osenv": "0",
+ "lockfile": ">=0.2",
+ "retry": "~0.6.0",
+ "couch-login": "~0.1.6"
},
"bundleDependencies": [
- "slide",
- "ini",
"semver",
+ "ini",
+ "slide",
"abbrev",
"graceful-fs",
"minimatch",
"nopt",
"node-uuid",
+ "proto-list",
"rimraf",
"request",
- "proto-list",
"which",
"tar",
"fstream",
"fstream-npm",
"uid-number",
"archy",
- "chownr"
+ "chownr",
+ "npmlog",
+ "ansi",
+ "npm-registry-client",
+ "read-package-json",
+ "read-installed",
+ "glob",
+ "init-package-json",
+ "osenv",
+ "lockfile",
+ "retry",
+ "couch-login"
],
"devDependencies": {
- "ronn": "https://github.com/isaacs/ronnjs/tarball/master"
+ "ronn": "https://github.com/isaacs/ronnjs/tarball/master",
+ "tap": "~0.2.5"
},
"engines": {
"node": "0.6 || 0.7 || 0.8",
"npm": "1"
},
"scripts": {
- "test": "node ./test/run.js",
- "prepublish": "npm prune; rm -rf node_modules/*/{test,example,bench}*; make -j4 doc",
+ "test": "node ./test/run.js && tap test/tap/*.js",
+ "prepublish": "npm prune ; make -j4 doc",
"dumpconf": "env | grep npm | sort | uniq"
},
"licenses": [
--- /dev/null
+#!/usr/bin/env node
+console.log('ok')
--- /dev/null
+{ "name":"npm-test-array-bin"
+, "version":"1.2.5"
+, "bin": [ "bin/array-bin" ]
+, "scripts": { "test": "node test.js" } }
--- /dev/null
+var c = require('child_process').spawn('array-bin', [], {
+ env: process.env }).on('close', function (code) {
+ if (code) throw new Error('exited badly with code = ' + code)
+})
+c.stdout.pipe(process.stdout)
+c.stderr.pipe(process.stderr)
--- /dev/null
+#!/usr/bin/env node
+console.log('ok')
--- /dev/null
+{ "name":"npm-test-dir-bin"
+, "version":"1.2.5"
+, "directories": { "bin": "./bin" }
+, "scripts": { "test": "node test.js" } }
--- /dev/null
+require('child_process').spawn('dir-bin', [], {
+ env: process.env }).on('exit', function (code) {
+ if (code) throw new Error('exited badly with code = ' + code)
+})
+
--- /dev/null
+I WILL NOT BE IGNORED!
--- /dev/null
+var test = require("tap").test
+ , fs = require("fs")
+ , path = require("path")
+ , existsSync = fs.existsSync || path.existsSync
+ , spawn = require("child_process").spawn
+ , npm = require("../../")
+
+test("not every pkg.name can be required", function (t) {
+ t.plan(1)
+
+ setup(function () {
+ npm.install(".", function (err) {
+ if (err) return t.fail(err)
+ t.ok(existsSync(__dirname +
+ "/false_name/node_modules/tap/node_modules/buffer-equal"))
+ })
+ })
+})
+
+function setup (cb) {
+ process.chdir(__dirname + "/false_name")
+ npm.load(function () {
+ spawn("rm", [ "-rf", __dirname + "/false_name/node_modules" ])
+ .on("exit", function () {
+ fs.mkdirSync(__dirname + "/false_name/node_modules")
+ cb()
+ })
+ })
+}
--- /dev/null
+module.exports = true
--- /dev/null
+{
+ "name": "buffer-equal",
+ "version": "0.0.0",
+ "main": "index.js",
+ "dependencies": {
+ "tap": "0.2.5"
+ }
+}