package/package.json 000644 0000002544 010513 0 ustar 00 000000 000000 {
"name": "npm-logical-tree",
"version": "1.2.1",
"description": "Calculate 'logical' trees from a package.json + package-lock",
"main": "index.js",
"files": [
"*.js"
],
"scripts": {
"prerelease": "npm t",
"postrelease": "npm publish && git push --follow-tags",
"pretest": "standard lib test *.js",
"release": "standard-version -s",
"test": "nyc --all -- tap -J test/*.js",
"update-coc": "weallbehave -o . && git add CODE_OF_CONDUCT.md && git commit -m 'docs(coc): updated CODE_OF_CONDUCT.md'",
"update-contrib": "weallcontribute -o . && git add CONTRIBUTING.md && git commit -m 'docs(contributing): updated CONTRIBUTING.md'"
},
"repository": "https://github.com/npm/logical-tree",
"keywords": [
"npm",
"package manager"
],
"author": {
"name": "Kat Marchán",
"email": "kzm@sykosomatic.org",
"twitter": "maybekatz"
},
"contributors": [
{
"name": "Rebecca Turner",
"email": "me@re-becca.org",
"twitter": "ReBeccaOrg"
}
],
"license": "ISC",
"devDependencies": {
"bluebird": "^3.5.1",
"nyc": "^11.1.0",
"standard": "^10.0.2",
"standard-version": "^4.2.0",
"tap": "^10.7.0",
"weallbehave": "^1.2.0",
"weallcontribute": "^1.0.8"
},
"config": {
"nyc": {
"exclude": [
"node_modules/**",
"test/**"
]
}
}
}
package/CHANGELOG.md 000644 0000002306 010032 0 ustar 00 000000 000000 # Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [1.2.1](https://github.com/npm/logical-tree/compare/v1.2.0...v1.2.1) (2018-01-19)
### Bug Fixes
* **requires:** stop requiring version match -- only what require would pick up ([6388fbd](https://github.com/npm/logical-tree/commit/6388fbd))
# [1.2.0](https://github.com/npm/logical-tree/compare/v1.1.0...v1.2.0) (2017-10-13)
### Bug Fixes
* **json:** fix repository url ([e51448a](https://github.com/npm/logical-tree/commit/e51448a))
### Features
* **api:** additional utility functions for dealing with trees ([23f6e69](https://github.com/npm/logical-tree/commit/23f6e69))
# [1.1.0](https://github.com/npm/npm-logical-tree/compare/v1.0.0...v1.1.0) (2017-10-11)
### Features
* **requiredBy:** add requiredBy field to nodes ([c4056fb](https://github.com/npm/npm-logical-tree/commit/c4056fb))
# 1.0.0 (2017-10-07)
### Features
* **api:** Initial Commitâ„¢ ([1025259](https://github.com/npm/npm-logical-tree/commit/1025259))
package/index.js 000644 0000011467 007676 0 ustar 00 000000 000000 'use strict'
let path
class LogicalTree {
constructor (name, address, opts) {
this.name = name
this.version = opts.version
this.address = address || ''
this.optional = !!opts.optional
this.dev = !!opts.dev
this.bundled = !!opts.bundled
this.resolved = opts.resolved
this.integrity = opts.integrity
this.dependencies = new Map()
this.requiredBy = new Set()
}
get isRoot () { return !this.requiredBy.size }
addDep (dep) {
this.dependencies.set(dep.name, dep)
dep.requiredBy.add(this)
return this
}
delDep (dep) {
this.dependencies.delete(dep.name)
dep.requiredBy.delete(this)
return this
}
getDep (name) {
return this.dependencies.get(name)
}
path (prefix) {
if (this.isRoot) {
// The address of the root is the prefix itself.
return prefix || ''
} else {
if (!path) { path = require('path') }
return path.join(
prefix || '',
'node_modules',
this.address.replace(/:/g, '/node_modules/')
)
}
}
// This finds cycles _from_ a given node: if some deeper dep has
// its own cycle, but that cycle does not refer to this node,
// it will return false.
hasCycle (_seen, _from) {
if (!_seen) { _seen = new Set() }
if (!_from) { _from = this }
for (let dep of this.dependencies.values()) {
if (_seen.has(dep)) { continue }
_seen.add(dep)
if (dep === _from || dep.hasCycle(_seen, _from)) {
return true
}
}
return false
}
forEachAsync (fn, opts, _pending) {
if (!opts) { opts = _pending || {} }
if (!_pending) { _pending = new Map() }
const P = opts.Promise || Promise
if (_pending.has(this)) {
return P.resolve(this.hasCycle() || _pending.get(this))
}
const pending = P.resolve().then(() => {
return fn(this, () => {
return promiseMap(
this.dependencies.values(),
dep => dep.forEachAsync(fn, opts, _pending),
opts
)
})
})
_pending.set(this, pending)
return pending
}
forEach (fn, _seen) {
if (!_seen) { _seen = new Set() }
if (_seen.has(this)) { return }
_seen.add(this)
fn(this, () => {
for (let dep of this.dependencies.values()) {
dep.forEach(fn, _seen)
}
})
}
}
module.exports = lockTree
function lockTree (pkg, pkgLock, opts) {
const tree = makeNode(pkg.name, null, pkg)
const allDeps = new Map()
Array.from(
new Set(Object.keys(pkg.devDependencies || {})
.concat(Object.keys(pkg.optionalDependencies || {}))
.concat(Object.keys(pkg.dependencies || {})))
).forEach(name => {
let dep = allDeps.get(name)
if (!dep) {
const depNode = (pkgLock.dependencies || {})[name]
dep = makeNode(name, name, depNode)
}
addChild(dep, tree, allDeps, pkgLock)
})
return tree
}
module.exports.node = makeNode
function makeNode (name, address, opts) {
return new LogicalTree(name, address, opts || {})
}
function addChild (dep, tree, allDeps, pkgLock) {
tree.addDep(dep)
allDeps.set(dep.address, dep)
const addr = dep.address
const lockNode = atAddr(pkgLock, addr)
Object.keys(lockNode.requires || {}).forEach(name => {
const tdepAddr = reqAddr(pkgLock, name, addr)
let tdep = allDeps.get(tdepAddr)
if (!tdep) {
tdep = makeNode(name, tdepAddr, atAddr(pkgLock, tdepAddr))
addChild(tdep, dep, allDeps, pkgLock)
} else {
dep.addDep(tdep)
}
})
}
module.exports._reqAddr = reqAddr
function reqAddr (pkgLock, name, fromAddr) {
const lockNode = atAddr(pkgLock, fromAddr)
const child = (lockNode.dependencies || {})[name]
if (child) {
return `${fromAddr}:${name}`
} else {
const parts = fromAddr.split(':')
while (parts.length) {
parts.pop()
const joined = parts.join(':')
const parent = atAddr(pkgLock, joined)
if (parent) {
const child = (parent.dependencies || {})[name]
if (child) {
return `${joined}${parts.length ? ':' : ''}${name}`
}
}
}
const err = new Error(`${name} not accessible from ${fromAddr}`)
err.pkgLock = pkgLock
err.target = name
err.from = fromAddr
throw err
}
}
module.exports._atAddr = atAddr
function atAddr (pkgLock, addr) {
if (!addr.length) { return pkgLock }
const parts = addr.split(':')
return parts.reduce((acc, next) => {
return acc && (acc.dependencies || {})[next]
}, pkgLock)
}
function promiseMap (arr, fn, opts, _index) {
_index = _index || 0
const P = (opts && opts.Promise) || Promise
if (P.map) {
return P.map(arr, fn, opts)
} else {
if (!(arr instanceof Array)) {
arr = Array.from(arr)
}
if (_index >= arr.length) {
return P.resolve()
} else {
return P.resolve(fn(arr[_index], _index, arr))
.then(() => promiseMap(arr, fn, opts, _index + 1))
}
}
}
package/LICENSE.md 000644 0000001363 007627 0 ustar 00 000000 000000 ISC License
Copyright (c) npm, Inc.
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE COPYRIGHT HOLDER DISCLAIMS
ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md 000644 0000010723 007502 0 ustar 00 000000 000000 # npm-logical-tree [](https://npm.im/npm-logical-tree) [](https://npm.im/npm-logical-tree) [](https://travis-ci.org/npm/logical-tree) [](https://ci.appveyor.com/project/npm/logical-tree) [](https://coveralls.io/github/npm/logical-tree?branch=latest)
[`npm-logical-tree`](https://github.com/npm/npm-logical-tree) is a Node.js
library that takes the contents of a `package.json` and `package-lock.json` (or
`npm-shrinkwrap.json`) and returns a nested tree data structure representing the
logical relationships between the different dependencies.
## Install
`$ npm install npm-logical-tree`
## Table of Contents
* [Example](#example)
* [Contributing](#contributing)
* [API](#api)
* [`logicalTree`](#logical-tree)
* [`logicalTree.node`](#make-node)
* [`tree.isRoot`](#is-root)
* [`tree.addDep`](#add-dep)
* [`tree.delDep`](#del-dep)
* [`tree.getDep`](#get-dep)
* [`tree.path`](#path)
* [`tree.hasCycle`](#has-cycle)
* [`tree.forEach`](#for-each)
* [`tree.forEachAsync`](#for-each-async)
### Example
```javascript
const fs = require('fs')
const logicalTree = require('npm-logical-tree')
const pkg = require('./package.json')
const pkgLock = require('./package-lock.json')
logicalTree(pkg, pkgLock)
// returns:
LogicalTree {
name: 'npm-logical-tree',
version: '1.0.0',
address: null,
optional: false,
dev: false,
bundled: false,
resolved: undefined,
integrity: undefined,
requiredBy: Set { },
dependencies:
Map {
'foo' => LogicalTree {
name: 'foo',
version: '1.2.3',
address: 'foo',
optional: false,
dev: true,
bundled: false,
resolved: 'https://registry.npmjs.org/foo/-/foo-1.2.3.tgz',
integrity: 'sha1-rYUK/p261/SXByi0suR/7Rw4chw=',
dependencies: Map { ... },
requiredBy: Set { ... },
},
...
}
}
```
### Contributing
The npm team enthusiastically welcomes contributions and project participation!
There's a bunch of things you can do if you want to contribute! The [Contributor
Guide](CONTRIBUTING.md) has all the information you need for everything from
reporting bugs to contributing entire new features. Please don't hesitate to
jump in if you'd like to, or even ask us questions if something isn't clear.
All participants and maintainers in this project are expected to follow [Code of
Conduct](CODE_OF_CONDUCT.md), and just generally be excellent to each other.
Please refer to the [Changelog](CHANGELOG.md) for project history details, too.
Happy hacking!
### API
#### `> logicalTree(pkg, lock) -> LogicalTree`
Calculates a logical tree based on a matching `package.json` and
`package-lock.json` pair. A "logical tree" is a fully-nested dependency graph
for an npm package, as opposed to a physical tree which might be flattened.
`logical-tree` will represent deduplicated/flattened nodes using the same object
throughout the tree, so duplication can be checked by object identity.
##### Example
```javascript
const pkg = require('./package.json')
const pkgLock = require('./package-lock.json')
logicalTree(pkg, pkgLock)
// returns:
LogicalTree {
name: 'npm-logical-tree',
version: '1.0.0',
address: null,
optional: false,
dev: false,
bundled: false,
resolved: undefined,
integrity: undefined,
requiredBy: Set { },
dependencies:
Map {
'foo' => LogicalTree {
name: 'foo',
version: '1.2.3',
address: 'foo',
optional: false,
dev: true,
bundled: false,
resolved: 'https://registry.npmjs.org/foo/-/foo-1.2.3.tgz',
integrity: 'sha1-rYUK/p261/SXByi0suR/7Rw4chw=',
requiredBy: Set { ... },
dependencies: Map { ... }
},
...
}
}
```
#### `> logicalTree.node(name, [address, [opts]]) -> LogicalTree`
Manually creates a new LogicalTree node.
##### Options
* `opts.version` - version of the node.
* `opts.optional` - is this node an optionalDep?
* `opts.dev` - is this node a devDep?
* `opts.bundled` - is this bundled?
* `opts.resolved` - resolved address.
* `opts.integrity` - SRI string.
##### Example
```javascript
logicalTree.node('hello', 'subpath:to:@foo/bar', {dev: true})
```