package/package.json000644 0000002544 010513 0ustar00000000 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.md000644 0000002306 010032 0ustar00000000 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.js000644 0000011467 007676 0ustar00000000 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.md000644 0000001363 007627 0ustar00000000 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.md000644 0000010723 007502 0ustar00000000 000000 # npm-logical-tree [![npm version](https://img.shields.io/npm/v/npm-logical-tree.svg)](https://npm.im/npm-logical-tree) [![license](https://img.shields.io/npm/l/npm-logical-tree.svg)](https://npm.im/npm-logical-tree) [![Travis](https://img.shields.io/travis/npm/logical-tree.svg)](https://travis-ci.org/npm/logical-tree) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/npm/logical-tree?svg=true)](https://ci.appveyor.com/project/npm/logical-tree) [![Coverage Status](https://coveralls.io/repos/github/npm/logical-tree/badge.svg?branch=latest)](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}) ```