Permalink
Browse files

initial

  • Loading branch information...
0 parents commit a99305117609ed9eafaa16c4312632704a3ec9ac @dominictarr committed Jun 26, 2013
Showing with 449 additions and 0 deletions.
  1. +22 −0 LICENSE
  2. +34 −0 README.md
  3. +215 −0 index.js
  4. +23 −0 package.json
  5. +155 −0 resolve.js
@@ -0,0 +1,22 @@
+Copyright (c) 2013 Dominic Tarr
+
+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.
@@ -0,0 +1,34 @@
+# npmd-resolve
+
+npmd dependency tree resolution.
+
+given an [npmd](https://github.com/dominictarr/npmd)
+database full of npm package.jsons,
+resolve the dependency tree for a module & version.
+
+## example
+
+``` js
+var resolve = require('npmd-resolve')
+resolve(db.sublevel('ver'), 'browserify', '2',
+ {greedy: true}, function (err, tree) {
+ console.error(tree)
+ })
+```
+
+## data format:
+
+`npmd-resolve` assumes a leveldb of package.json's with format:
+
+`package.name + '!' + paddedSemver(package.version) : package`
+
+padded semver converts semver into strings that are bytewise sorted.
+
+## greedy mode
+
+If `{greedy: true}` is enabled,
+resolve will build a dependency tree that is deduplicated by default.
+
+## License
+
+MIT
@@ -0,0 +1,215 @@
+var range = require('padded-semver').range
+var peek = require('level-peek')
+var path = require('path')
+var pull = require('pull-stream')
+var inspect = require('util').inspect
+var semver = require('semver')
+var cat = require('pull-cat')
+var urlResolve = require('npmd-git-resolve')
+
+//experimenting with different installation resolve
+//algs. the idea is to traverse the tree locally,
+//figure out what is needed, and then install from
+//cache if possible. Else, get the rest in just one
+//bundle. (the registry can stream them into one
+//multipart or a tarball or something)
+
+//I've implemented the basic algorithm that npm uses,
+//and a greedy algorithm. the greedy algorithm
+//would every module into $PWD/node_modules
+//and only creates new node_modules directories
+//when the version range specified is not available.
+
+//testing with the trees for large projects, (such as npm and browserify)
+//this may require 10-30% fewer installs
+
+//opening the database, and running this the first time is pretty slow
+//like 2 seconds to resolve browserify (50 modules)
+//but when the cache is warm it's only 50 ms!
+
+
+function resolve (db, module, vrange, cb) {
+ if(!cb) cb = vrange, vrange = '*'
+
+ if(/^(git|http)/.test(vrange)) {
+ console.error('GET', vrange)
+ return urlResolve(vrange, function (err, pkg) {
+ if(err)
+ console.error(err.stack)
+ if(pkg)
+ console.error(pkg.name+'@'+pkg.shasum)
+ cb(err, pkg)
+ })
+ }
+ //if vrange is a http or git
+ //download the bundle
+ //note - you can download git via an archive...
+ //then unpack to get package.json
+ //
+
+ if(vrange == 'latest')
+ vrange = '*'
+
+ var r = range(vrange || '*')
+
+ r = {
+ min: module + '!'+(r.start || ''),
+ max: module + '!'+(r.end || '~'),
+ }
+
+ peek.last(db, r, function (err, key, pkg) {
+ if(!semver.satisfies(pkg.version, vrange))
+ return cb(new Error(module+'@'+pkg.version +'><'+ vrange))
+ cb(err, pkg)
+ })
+}
+
+function check(pkg, name, range) {
+ if(!pkg) return false
+ if(pkg.tree[name] && semver.satisfies(pkg.tree[name].version, range))
+ return true
+ return check(pkg.parent, name, range)
+}
+
+function clean (t) {
+ var deps = t.dependencies
+ var _deps = t.tree || {}
+
+ delete t.tree
+ delete t._parent
+ delete t.description
+ delete t.devDependencies
+ delete t.tree
+ delete t.scripts
+ delete t.parent
+ delete t.time
+ delete t.size
+
+ for(var k in _deps) {
+ _deps[k].from = deps[k]
+ clean(_deps[k])
+ }
+
+ t.dependencies = _deps
+
+ return t
+}
+
+function resolveTree (db, module, version, cb) {
+
+ resolve(db, module, version, function (err, pkg) {
+ cat([pull.values([pkg]),
+ pull.depthFirst(pkg, function (pkg) {
+ var deps = pkg.dependencies || {}
+ pkg.tree = {}
+ return pull.values(Object.keys(deps))
+ .pipe(pull.asyncMap(function (name, cb) {
+ //check if there is already a module that resolves this...
+
+ //filter out versions that we already have.
+ if(check(pkg, name, deps[name]))
+ return cb()
+
+ resolve(db, name, deps[name], cb)
+ }, 10))
+
+ .pipe(pull.filter(function (_pkg) {
+ if(!_pkg) return
+ _pkg.parent = pkg
+ pkg.tree[_pkg.name] = _pkg
+ return pkg
+ }))
+ })]
+ )
+ .pipe(pull.drain(null, function () {
+ cb(null, clean(pkg))
+ }))
+ })
+}
+
+function resolveTreeGreedy (db, module, version, cb) {
+
+ resolve(db, module, version, function (err, pkg) {
+ var root = pkg
+
+ cat([pull.values([pkg]),
+ pull.depthFirst(pkg, function (pkg) {
+ var deps = pkg.dependencies || {}
+ pkg.tree = {}
+ return pull.values(Object.keys(deps))
+ .pipe(pull.asyncMap(function (name, cb) {
+ //check if there is already a module that resolves this...
+
+ //filter out versions that we already have.
+ if(check(pkg, name, deps[name]))
+ return cb()
+
+ resolve(db, name, deps[name], function (err, _pkg) {
+ cb(null, _pkg)
+ })
+ }))
+
+ .pipe(pull.filter(function (_pkg) {
+ if(!_pkg) return
+ //install non-conflicting modules as low in the tree as possible.
+ //hmm, is this wrong?
+ //hmm, the only way a module is not on the root is if it's
+ //conflicting with one that is already there.
+ //so, what if this module is a child of a conflicting module
+ //aha! we have already checked the path to the root,
+ //and this item would be filtered if it wasn't clear.
+ if(!root.tree[_pkg.name]) {
+ root.tree[_pkg.name] = _pkg
+ _pkg.parent = root
+ }
+ else {
+ _pkg.parent = pkg
+ pkg.tree[_pkg.name] = _pkg
+ }
+ return pkg
+ }))
+ })])
+ .pipe(pull.drain(null, function () {
+ cb(null, clean(pkg))
+ }))
+ })
+}
+
+exports = module.exports = function (db, module, version, opts, cb) {
+ if(!cb)
+ cb = opts, opts = {}
+ if(opts && opts.greedy)
+ resolveTreeGreedy(db, module, version, cb)
+ else
+ resolveTree(db, module, version, cb)
+}
+
+exports.resolveTree = resolveTree
+exports.resolveTreeGreedy = resolveTreeGreedy
+
+exports.db = function (db, config) {
+ db.methods.resolve = {type: 'async'}
+ db.resolve = function (module, vrange, opts, cb) {
+ if(!cb)
+ cb = opts, opts = {}
+
+ resolve(
+ db.sublevel('ver'), module, vrange,
+ { greedy: opts.greedy || config.greedy },
+ cb
+ )
+ }
+}
+
+exports.commands = function (db) {
+ db.commands.resolve = function (config, cb) {
+ db.resolve(config._[0], config._[1] || '*',
+ {greedy: config.greedy},
+ function (err, tree) {
+ if(err) return cb(err)
+ console.log(JSON.stringify(tree, null, 2))
+ cb()
+ })
+ }
+}
+
@@ -0,0 +1,23 @@
+{
+ "name": "npmd-resolve",
+ "description": "",
+ "version": "0.0.0",
+ "homepage": "https://github.com/dominictarr/npmd-resolve",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/dominictarr/npmd-resolve.git"
+ },
+ "dependencies": {
+ "padded-semver": "~1.1.1",
+ "semver": "~2.0.8",
+ "level-peek": "~1.0.6",
+ "pull-stream": "~2.20.0",
+ "npmd-git-resolve": "~2.0.3"
+ },
+ "devDependencies": {},
+ "scripts": {
+ "test": "set -e; for t in test/*.js; do node $t; done"
+ },
+ "author": "Dominic Tarr <dominic.tarr@gmail.com> (http://dominictarr.com)",
+ "license": "MIT"
+}
Oops, something went wrong.

0 comments on commit a993051

Please sign in to comment.