├── test ├── fixtures │ └── apps │ │ ├── app3 │ │ ├── node_modules │ │ │ └── a │ │ │ │ ├── node_modules │ │ │ │ └── b │ │ │ │ └── package.json │ │ ├── package.json │ │ └── target.json │ │ ├── app2 │ │ └── package.json │ │ └── app1 │ │ ├── package.json │ │ ├── target.npm.json │ │ └── target.cnpm.json ├── app3.test.js ├── utils.js ├── app1.test.js └── app2.test.js ├── index.js ├── shameimaru.jpg ├── .travis.yml ├── .eslintrc ├── lib ├── utils.js ├── shameimaru.js └── traverse.js ├── History.md ├── LICENSE ├── .gitignore ├── package.json └── README.md /test/fixtures/apps/app3/node_modules/a/node_modules/b: -------------------------------------------------------------------------------- 1 | abc -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = require("./lib/shameimaru"); 4 | -------------------------------------------------------------------------------- /shameimaru.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/shameimaru/HEAD/shameimaru.jpg -------------------------------------------------------------------------------- /test/fixtures/apps/app3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "a": "*" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/app3/node_modules/a/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "a", 3 | "dependencies": { 4 | "b": "*" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "10" 5 | 6 | env: 7 | - CI=true 8 | 9 | after_script: npm run coverage 10 | 11 | sudo: false 12 | -------------------------------------------------------------------------------- /test/fixtures/apps/app2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "npm": "http://registry.npm.taobao.org/npm/download/npm-3.10.10.tgz", 4 | "cnpm": "6", 5 | "abbrev": "< 1.0.8", 6 | "cmd-shim": "1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/apps/app1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@crand/mt19937": "*", 4 | "algorithmjs": "^1.0.0", 5 | "mz": "^2.7.0", 6 | "npm-package-arg": "^6.1.0", 7 | "any-promise": "0.2.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-xadillax-style", 3 | "parserOptions": { 4 | "ecmaVersion": 2017 5 | }, 6 | "globals": { 7 | "it": true, 8 | "describe": true, 9 | "before": true, 10 | "after": true, 11 | "beforeEach": true, 12 | "afterEach": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("mz/fs"); 4 | 5 | exports.readJSON = async function(filename) { 6 | const content = await fs.readFile(filename, "utf8"); 7 | return JSON.parse(content); 8 | }; 9 | 10 | exports.extraDependenciesFromPackage = function(pkg) { 11 | return Object.assign({}, pkg.dependencies || {}, pkg.optionalDependencies || {}); 12 | }; 13 | -------------------------------------------------------------------------------- /test/fixtures/apps/app3/target.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": { 3 | "ref": "5db625ba-5b24-4f0c-8fa3-97673356f44a", 4 | "name": "a", 5 | "exists": true, 6 | "rawSpec": "*", 7 | "dependencies": { 8 | "b": { 9 | "ref": "224d38ad-3119-4c97-8a38-140b48dfe982", 10 | "name": "b", 11 | "exists": false, 12 | "rawSpec": "*", 13 | "missing": true 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /lib/shameimaru.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | 6 | const traverse = require("./traverse"); 7 | const utils = require("./utils"); 8 | 9 | class Shameimaru { 10 | constructor(projDir) { 11 | this.projDir = projDir; 12 | this.nodeModuleDir = path.resolve(process.cwd(), projDir, "node_modules"); 13 | this.package = JSON.parse(fs.readFileSync(path.join(projDir, "package.json"), "utf8")); 14 | } 15 | 16 | async traverse() { 17 | const ret = await traverse( 18 | utils.extraDependenciesFromPackage(this.package), 19 | this.nodeModuleDir); 20 | return ret; 21 | } 22 | } 23 | 24 | module.exports = Shameimaru; 25 | -------------------------------------------------------------------------------- /test/app3.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("should"); 4 | 5 | const Shameimaru = require("../"); 6 | const utils = require("./utils"); 7 | 8 | describe("app3.test.js", () => { 9 | describe("should not throw error", () => { 10 | it("#traverse", async function() { 11 | const shameimaru = new Shameimaru("./test/fixtures/apps/app3"); 12 | console.time("app3"); 13 | const tree = await shameimaru.traverse(); 14 | console.timeEnd("app3"); 15 | tree.should.containDeep(utils.getResultMatcher("app3/target.json")); 16 | 17 | // require("fs").writeFileSync("./test/fixtures/apps/app3/target.json", JSON.stringify(tree, 0, 2)); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 1.0.6 / 2019-12-30 3 | ================== 4 | 5 | 1.0.5 / 2019-12-30 6 | ================== 7 | 8 | 1.0.4 / 2019-12-30 9 | ================== 10 | 11 | 1.0.3 / 2019-12-30 12 | ================== 13 | 14 | 1.0.2 / 2019-01-21 15 | ================== 16 | 17 | **fixes** 18 | * [[`bb5624f`](http://github.com/XadillaX/shameimaru/commit/bb5624fa15786c82da3a62ced487b94cfdae4055)] - fix: error while there's an broken symbol link (#4) (Khaidi Chu <>) 19 | 20 | 1.0.1 / 2018-06-19 21 | ================== 22 | 23 | **features** 24 | * [[`b16bc0f`](http://github.com/XadillaX/shameimaru/commit/b16bc0feabbd5fb6e43be5778cb0ee76def0f138)] - feat: transfer to node-modules (#2) (Khaidi Chu <>),fatal: No names found, cannot describe anything. 25 | 26 | **others** 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Khaidi Chu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | d.json 64 | .DS_Store 65 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const cp = require("child_process"); 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | 7 | const rimraf = require("rimraf"); 8 | 9 | const REGISTRY = "--registry=https://registry.npm.taobao.org/"; 10 | 11 | exports.install = function(dir, type) { 12 | dir = path.resolve(__dirname, "fixtures/apps/", dir); 13 | rimraf.sync(path.join(dir, "node_modules")); 14 | rimraf.sync(path.join(dir, "package-lock.json")); 15 | cp.execSync(`${path.resolve(__dirname, "../node_modules/.bin", type)} install ${REGISTRY}`, { 16 | cwd: dir 17 | }); 18 | }; 19 | 20 | function washResult(obj) { 21 | for(const key in obj) { 22 | if(!obj.hasOwnProperty(key)) continue; 23 | 24 | const curr = obj[key]; 25 | delete curr.rawSpec; 26 | delete curr.version; 27 | delete curr.from; 28 | delete curr.resolved; 29 | delete curr.ref; 30 | delete curr.ancestor; 31 | if(curr.dependencies) washResult(curr.dependencies); 32 | } 33 | } 34 | 35 | exports.getResultMatcher = function(name) { 36 | const content = fs.readFileSync(path.resolve(__dirname, "fixtures/apps", name), "utf8"); 37 | const obj = JSON.parse(content); 38 | washResult(obj); 39 | return obj; 40 | }; 41 | -------------------------------------------------------------------------------- /test/app1.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("should"); 4 | 5 | const Shameimaru = require("../"); 6 | const utils = require("./utils"); 7 | 8 | describe("app1.test.js", () => { 9 | describe("npm 6", () => { 10 | before(() => { 11 | utils.install("app1", "npm"); 12 | }); 13 | 14 | it("#traverse", async function() { 15 | const shameimaru = new Shameimaru("./test/fixtures/apps/app1"); 16 | console.time("app1#npm"); 17 | const tree = await shameimaru.traverse(); 18 | console.timeEnd("app1#npm"); 19 | tree.should.containDeep(utils.getResultMatcher("app1/target.npm.json")); 20 | 21 | // require("fs").writeFileSync("./test/fixtures/apps/app1/target.npm.json", JSON.stringify(tree, 0, 2)); 22 | }); 23 | }); 24 | 25 | describe("cnpm 6", () => { 26 | before(() => { 27 | utils.install("app1", "cnpm"); 28 | }); 29 | 30 | it("#traverse", async function() { 31 | const shameimaru = new Shameimaru("./test/fixtures/apps/app1"); 32 | console.time("app1#cnpm"); 33 | const tree = await shameimaru.traverse(); 34 | console.timeEnd("app1#cnpm"); 35 | tree.should.containDeep(utils.getResultMatcher("app1/target.cnpm.json")); 36 | 37 | // require("fs").writeFileSync("./test/fixtures/apps/app1/target.cnpm.json", JSON.stringify(tree, 0, 2)); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/app2.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("should"); 4 | 5 | const Shameimaru = require("../"); 6 | const utils = require("./utils"); 7 | 8 | describe("app2.test.js", () => { 9 | describe("npm 6", () => { 10 | before(() => { 11 | utils.install("app2", "npm"); 12 | }); 13 | 14 | it("#traverse", async function() { 15 | const shameimaru = new Shameimaru("./test/fixtures/apps/app2"); 16 | console.time("app2#npm"); 17 | const tree = await shameimaru.traverse(); 18 | console.timeEnd("app2#npm"); 19 | tree.should.containDeep(utils.getResultMatcher("app2/target.npm.json")); 20 | 21 | // require("fs").writeFileSync("./test/fixtures/apps/app2/target.npm.json", JSON.stringify(tree, 0, 2)); 22 | }); 23 | }); 24 | 25 | describe("cnpm 6", () => { 26 | before(() => { 27 | utils.install("app2", "cnpm"); 28 | }); 29 | 30 | it("#traverse", async function() { 31 | const shameimaru = new Shameimaru("./test/fixtures/apps/app2"); 32 | console.time("app2#cnpm"); 33 | const tree = await shameimaru.traverse(); 34 | console.timeEnd("app2#cnpm"); 35 | tree.should.containDeep(utils.getResultMatcher("app2/target.cnpm.json")); 36 | 37 | // require("fs").writeFileSync("./test/fixtures/apps/app2/target.cnpm.json", JSON.stringify(tree, 0, 2)); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shameimaru", 3 | "version": "1.0.6", 4 | "description": "Shameimaru Aya likes to traverse node_modules and capture the tree", 5 | "dependencies": { 6 | "algorithmjs": "^1.0.0", 7 | "mz": "^2.7.0", 8 | "npm-package-arg": "^6.1.0", 9 | "uuid": "^3.2.1" 10 | }, 11 | "devDependencies": { 12 | "cnpm": "^6.0.0", 13 | "coveralls": "^3.0.1", 14 | "eslint": "^4.19.1", 15 | "eslint-config-xadillax-style": "^1.10.0", 16 | "istanbul": "^1.1.0-alpha.1", 17 | "mocha": "^5.2.0", 18 | "mocha-lcov-reporter": "^1.3.0", 19 | "npm": "^6.1.0", 20 | "rimraf": "^2.6.2", 21 | "should": "^13.2.1" 22 | }, 23 | "author": "XadillaX (https://xcoder.in)", 24 | "license": "MIT", 25 | "main": "index.js", 26 | "files": [ 27 | "lib", 28 | "index.js" 29 | ], 30 | "homepage": "https://github.com/node-modules/shameimaru", 31 | "repository": { 32 | "type": "git", 33 | "url": "git://github.com/node-modules/shameimaru.git", 34 | "web": "https://github.com/node-modules/shameimaru" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/node-modules/shameimaru" 38 | }, 39 | "keywords": [ 40 | "dependency tree", 41 | "shameimaru" 42 | ], 43 | "engines": { 44 | "node": ">= 8.0.0" 45 | }, 46 | "scripts": { 47 | "test": "mocha test/*.test.js --timeout=120000", 48 | "coverage": "istanbul cover _mocha --report lcovonly -- test/*.test.js --timeout=120000 -R spec && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shameimaru.js 2 | 3 | [![Shameimaru](http://img.shields.io/npm/v/shameimaru.svg)](https://www.npmjs.org/package/shameimaru) 4 | [![Shameimaru](http://img.shields.io/npm/dm/shameimaru.svg)](https://www.npmjs.org/package/shameimaru) 5 | [![Build Status](https://travis-ci.org/node-modules/shameimaru.svg?branch=master)](https://travis-ci.org/node-modules/shameimaru) 6 | [![Coverage Status](https://img.shields.io/coveralls/node-modules/shameimaru/master.svg)](https://coveralls.io/r/node-modules/shameimaru?branch=master) 7 | [![License](https://img.shields.io/npm/l/shameimaru.svg?style=flat)](https://www.npmjs.org/package/shameimaru) 8 | [![Dependency Status](https://david-dm.org/node-modules/shameimaru.svg)](https://david-dm.org/node-modules/shameimaru) 9 | 10 | Shameimaru Aya likes to traverse node_modules and capture the tree. 11 | 12 | ![Shameimaru](shameimaru.jpg) 13 | 14 | ## Installation 15 | 16 | ```shell 17 | $ npm install --save shameimaru 18 | ``` 19 | 20 | ## Usage 21 | 22 | ```js 23 | const Shameimaru = require("shameimaru"); 24 | 25 | const shameimaru = new Shameimaru(""); 26 | ``` 27 | 28 | > `` is the root path which contains **node_modules** of your project. 29 | 30 | After create the `Shameimaru` instance, you can do `traverse()` through it. 31 | 32 | ```js 33 | const ret = await shameimaru.traverse(); 34 | ``` 35 | 36 | Then you'll get a may-flatten graph-form tree. e.g. 37 | 38 | ```json 39 | { 40 | "@crand/mt19937": { 41 | "ref": "5c2f5c96-9c29-4f3f-8cc1-ec6ab1f4025b", 42 | "name": "@crand/mt19937", 43 | "version": "2.0.0", 44 | "from": "@crand/mt19937@2.0.0", 45 | "resolved": "http://registry.npm.taobao.org/@crand/mt19937/download/@crand/mt19937-2.0.0.tgz", 46 | "exists": true, 47 | "rawSpec": "*" 48 | }, 49 | "any-promise": { 50 | "ref": "78325895-5945-4180-97dd-a01c705b254e", 51 | "name": "any-promise", 52 | "version": "0.2.0", 53 | "from": "any-promise@0.2.0", 54 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-0.2.0.tgz", 55 | "exists": true, 56 | "rawSpec": "0.2.0" 57 | }, 58 | "mz": { 59 | "ref": "63bb611b-232d-4f7a-ba53-3322670ed170", 60 | "name": "mz", 61 | "version": "2.7.0", 62 | "from": "mz@2.7.0", 63 | "resolved": "http://registry.npm.taobao.org/mz/download/mz-2.7.0.tgz", 64 | "exists": true, 65 | "rawSpec": "^2.7.0", 66 | "dependencies": { 67 | "any-promise": { 68 | "ref": "41f0b04f-0904-432f-aa33-13e5cbb8fcdc", 69 | "name": "any-promise", 70 | "version": "1.3.0", 71 | "from": "any-promise@1.3.0", 72 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz", 73 | "exists": true, 74 | "rawSpec": "^1.0.0" 75 | } 76 | }, 77 | ... 78 | }, 79 | ... 80 | } 81 | ``` 82 | 83 | Each element in the result may contains keys as below: 84 | 85 | + `ref`: a random referrence sign in this tree, it's unique; e.g. `63bb611b-232d-4f7a-ba53-3322670ed170` 86 | + `name`: the name of this package (dependency); e.g. `toshihiko` 87 | + `version`: the name of this package (dependency); e.g. `2.7.0` 88 | + `from`: same as `_from` in installed **package.json**; e.g. `mz@^2.0.0` 89 | + `resolved`: same as `_resolved` in installed **package.json**; `http://registry.npm.taobao.org/mz/download/mz-2.7.0.tgz` 90 | + `exists`: whether it's really exist in current tree folder; e.g. `true` 91 | + `ancestor`: if it matches a exactly the same package at any upper directory, it indicates that element's `ref`; e.g. `63bb611b-232d-4f7a-ba53-3322670ed170` 92 | + `rawSpec`: the raw spec of this package in its parent's **package.json**; e.g. `^2.0.0` 93 | + `adjustHere`: this package is not need by its parent, but some package need it flatten here; e.g. `true` 94 | + `missing`: if we can't find this package at any right path, then it will be `true`; e.g. `true` 95 | 96 | ## Contribute 97 | 98 | You're welcome to fork and make pull requests! 99 | -------------------------------------------------------------------------------- /lib/traverse.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require("path"); 4 | 5 | const fs = require("mz/fs"); 6 | const Linklist = require("algorithmjs").ds.Linklist; 7 | const npa = require("npm-package-arg"); 8 | const uuid = require("uuid/v4"); 9 | 10 | const utils = require("./utils"); 11 | 12 | const TRAVERSED = Symbol("traversed"); 13 | 14 | async function getPackagesInDir(nodeModuleDir) { 15 | if(!fs.existsSync(nodeModuleDir) || 16 | !(await fs.stat(nodeModuleDir)).isDirectory()) { 17 | return []; 18 | } 19 | 20 | const depDirs = await fs.readdir(nodeModuleDir); 21 | const pkgs = []; 22 | for(const name of depDirs) { 23 | if(name.startsWith(".") || name.startsWith("_")) continue; 24 | 25 | const fullPath = path.join(nodeModuleDir, name); 26 | 27 | // `fs.stat` will throw error while processing on a broken symbol link that 28 | // could be listed via `fs.readdir`. 29 | // 30 | // So we still need to make sure it's `fs.existsSync` before `fs.stat`. 31 | if(!fs.existsSync(fullPath)) continue; 32 | if(!(await fs.stat(fullPath)).isDirectory()) continue; 33 | 34 | // if the directory name starts with '@', then we add its all child 35 | // directories to the result array; 36 | // 37 | // otherwise, we only push this directory itself. 38 | 39 | if(name.startsWith("@")) { 40 | const atDirs = await fs.readdir(fullPath); 41 | for(const atName of atDirs) { 42 | if(atName.startsWith(".") || atName.startsWith("_")) continue; 43 | 44 | const pkgPath = path.join(fullPath, atName, "package.json"); 45 | if(!fs.existsSync(pkgPath)) continue; 46 | const pkg = await utils.readJSON(pkgPath); 47 | pkgs.push({ pkg, moduleDir: path.dirname(pkgPath) }); 48 | } 49 | continue; 50 | } 51 | 52 | const pkgPath = path.join(fullPath, "package.json"); 53 | if(!fs.existsSync(pkgPath)) continue; 54 | const pkg = await utils.readJSON(pkgPath); 55 | pkgs.push({ pkg, moduleDir: fullPath }); 56 | } 57 | 58 | return pkgs; 59 | } 60 | 61 | function searchAncestor(ancestors, currentFolder, pkg) { 62 | // replace `@foo/bar` to `@foo!bar` to let `path.dirname` regards it as one 63 | // folder. 64 | const name = (typeof pkg === "string" ? pkg : pkg.name).replace("/", "!"); 65 | pkg = typeof pkg === "string" ? null : pkg; 66 | let onlySlash = false; 67 | 68 | do { 69 | if("/" === currentFolder) onlySlash = true; 70 | 71 | const tryFolder = path.join(currentFolder, name); 72 | const ancestor = ancestors[tryFolder]; 73 | if(ancestor) { 74 | return !pkg || pkg.version === ancestor.version && pkg._resolved === ancestor.resolved ? 75 | ancestor : 76 | null; 77 | } 78 | 79 | currentFolder = path.dirname(currentFolder); 80 | } while(!onlySlash); 81 | 82 | return null; 83 | } 84 | 85 | function genRef(refs) { 86 | do { 87 | const ref = uuid(); 88 | if(!refs[ref]) { 89 | refs[ref] = true; 90 | return ref; 91 | } 92 | } while(1); 93 | } 94 | 95 | async function scanDir(q, node, ancestors, refs) { 96 | const { dir, dependencies, tree, dummyFolder } = node; 97 | const pkgs = await getPackagesInDir(dir); 98 | 99 | for(const info of pkgs) { 100 | const { pkg, moduleDir } = info; 101 | 102 | // npmPackageArg eg. 103 | // 104 | // { type: 'range', 105 | // registry: true, 106 | // where: undefined, 107 | // raw: 'toshihiko@^1.0.0-alpha.7', 108 | // name: 'toshihiko', 109 | // escapedName: 'toshihiko', 110 | // scope: undefined, 111 | // rawSpec: '^1.0.0-alpha.7', 112 | // saveSpec: null, 113 | // fetchSpec: '^1.0.0-alpha.7', 114 | // gitRange: undefined, 115 | // gitCommittish: undefined, 116 | // hosted: undefined } 117 | const versionSpec = dependencies[pkg.name]; 118 | 119 | // add meta information to result tree & ancestors 120 | const meta = tree[pkg.name] = { 121 | ref: genRef(refs), 122 | name: pkg.name, 123 | version: pkg.version, 124 | from: pkg._from, 125 | resolved: pkg._resolved, 126 | exists: true 127 | }; 128 | 129 | // replace `@foo/bar` to `@foo!bar` to let `path.dirname` regards it as 130 | // one folder. 131 | const nextFolder = path.join(dummyFolder, pkg.name.replace("/", "!")); 132 | ancestors[nextFolder] = meta; 133 | 134 | // if dependencies column exists this package, we search for its 135 | // ancestor or add a new task to queue; 136 | // 137 | // otherwise, we consider it as flatten and add a new task to queue. 138 | 139 | if(versionSpec) { 140 | const npmPackageArg = npa(`${pkg.name}@${versionSpec}`); 141 | meta.rawSpec = npmPackageArg.rawSpec; 142 | dependencies[pkg.name] = TRAVERSED; 143 | 144 | if(dummyFolder !== "/") { 145 | const ancestor = searchAncestor(ancestors, path.dirname(dummyFolder), pkg); 146 | if(ancestor) { 147 | meta.ancestor = ancestor.ref; 148 | continue; 149 | } 150 | } 151 | } else { 152 | meta.adjustHere = true; 153 | } 154 | 155 | const nextDependencies = utils.extraDependenciesFromPackage(pkg); 156 | if(Object.keys(nextDependencies).length) { 157 | meta.dependencies = {}; 158 | 159 | // after setting the package itself, we push it as next search 160 | // status to the queue. 161 | q.pushBack({ 162 | dir: path.join(moduleDir, "node_modules"), 163 | dependencies: nextDependencies, 164 | tree: meta.dependencies, 165 | dummyFolder: nextFolder 166 | }); 167 | } 168 | } 169 | 170 | // pick up the left packages in dependencies (but not in current directory) 171 | for(const name in dependencies) { 172 | if(!dependencies.hasOwnProperty(name) || 173 | dependencies[name] === TRAVERSED) { 174 | continue; 175 | } 176 | 177 | const versionSpec = dependencies[name]; 178 | const npmPackageArg = npa(`${name}@${versionSpec}`); 179 | const meta = tree[name] = { 180 | ref: genRef(refs), 181 | name, 182 | exists: false, 183 | rawSpec: npmPackageArg.rawSpec 184 | }; 185 | 186 | const ancestor = searchAncestor(ancestors, dummyFolder, name); 187 | if(ancestor) { 188 | meta.ancestor = ancestor.ref; 189 | } else { 190 | meta.missing = true; 191 | } 192 | } 193 | } 194 | 195 | async function traverse(rootDependencies, rootDir) { 196 | const q = new Linklist(); 197 | const rootTree = {}; 198 | const ancestors = {}; 199 | const refs = {}; 200 | 201 | q.pushBack({ 202 | dir: rootDir, 203 | dependencies: Object.assign({}, rootDependencies), 204 | tree: rootTree, 205 | dummyFolder: "/" 206 | }); 207 | 208 | while(q.length) { 209 | const node = q.popFront(); 210 | await scanDir(q, node, ancestors, refs); 211 | } 212 | 213 | return rootTree; 214 | } 215 | 216 | module.exports = traverse; 217 | -------------------------------------------------------------------------------- /test/fixtures/apps/app1/target.npm.json: -------------------------------------------------------------------------------- 1 | { 2 | "@crand/mt19937": { 3 | "ref": "c313eaea-36ce-4096-8e91-6d90d09293c3", 4 | "name": "@crand/mt19937", 5 | "version": "2.0.0", 6 | "from": "@crand/mt19937@*", 7 | "resolved": "http://registry.npm.taobao.org/@crand/mt19937/download/@crand/mt19937-2.0.0.tgz", 8 | "exists": true, 9 | "rawSpec": "*" 10 | }, 11 | "algorithmjs": { 12 | "ref": "41be841d-dba3-47f3-9388-47c818cbd290", 13 | "name": "algorithmjs", 14 | "version": "1.0.0", 15 | "from": "algorithmjs@^1.0.0", 16 | "resolved": "http://registry.npm.taobao.org/algorithmjs/download/algorithmjs-1.0.0.tgz", 17 | "exists": true, 18 | "rawSpec": "^1.0.0" 19 | }, 20 | "any-promise": { 21 | "ref": "db9441a8-bbde-4479-8ee9-ee8a3cfc9cd7", 22 | "name": "any-promise", 23 | "version": "0.2.0", 24 | "from": "any-promise@0.2.0", 25 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-0.2.0.tgz", 26 | "exists": true, 27 | "rawSpec": "0.2.0" 28 | }, 29 | "builtins": { 30 | "ref": "c0746454-ed71-471e-a76a-28ed87f08a52", 31 | "name": "builtins", 32 | "version": "1.0.3", 33 | "from": "builtins@^1.0.3", 34 | "resolved": "http://registry.npm.taobao.org/builtins/download/builtins-1.0.3.tgz", 35 | "exists": true, 36 | "adjustHere": true 37 | }, 38 | "hosted-git-info": { 39 | "ref": "927ea367-8259-4e17-9531-0352228ab1a6", 40 | "name": "hosted-git-info", 41 | "version": "2.7.1", 42 | "from": "hosted-git-info@^2.6.0", 43 | "resolved": "http://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.7.1.tgz", 44 | "exists": true, 45 | "adjustHere": true 46 | }, 47 | "mz": { 48 | "ref": "8d244ce8-682a-485b-a9cf-9efc2cb31f1e", 49 | "name": "mz", 50 | "version": "2.7.0", 51 | "from": "mz@^2.7.0", 52 | "resolved": "http://registry.npm.taobao.org/mz/download/mz-2.7.0.tgz", 53 | "exists": true, 54 | "rawSpec": "^2.7.0", 55 | "dependencies": { 56 | "any-promise": { 57 | "ref": "82477586-75bd-485a-99bb-64f7bf397183", 58 | "name": "any-promise", 59 | "version": "1.3.0", 60 | "from": "any-promise@^1.0.0", 61 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz", 62 | "exists": true, 63 | "rawSpec": "^1.0.0" 64 | }, 65 | "object-assign": { 66 | "ref": "743bb5a0-2cd0-4573-b39c-0ed1db331fa6", 67 | "name": "object-assign", 68 | "exists": false, 69 | "rawSpec": "^4.0.1", 70 | "ancestor": "6f47bbcb-08b0-4254-ba65-26098dc78bbf" 71 | }, 72 | "thenify-all": { 73 | "ref": "84179258-886a-466e-a61a-14b00814a03d", 74 | "name": "thenify-all", 75 | "exists": false, 76 | "rawSpec": "^1.0.0", 77 | "ancestor": "309a7ff8-bcb1-467c-82ba-f5bd090fe7b4" 78 | } 79 | } 80 | }, 81 | "npm-package-arg": { 82 | "ref": "be90906c-9d01-429e-95bf-351aa3c5f0c1", 83 | "name": "npm-package-arg", 84 | "version": "6.1.0", 85 | "from": "npm-package-arg@^6.1.0", 86 | "resolved": "http://registry.npm.taobao.org/npm-package-arg/download/npm-package-arg-6.1.0.tgz", 87 | "exists": true, 88 | "rawSpec": "^6.1.0", 89 | "dependencies": { 90 | "hosted-git-info": { 91 | "ref": "9b10c892-7445-481d-b5f1-5e38928c30d8", 92 | "name": "hosted-git-info", 93 | "exists": false, 94 | "rawSpec": "^2.6.0", 95 | "ancestor": "927ea367-8259-4e17-9531-0352228ab1a6" 96 | }, 97 | "osenv": { 98 | "ref": "a632af20-28b9-4c3f-b426-a12b5a5d4cca", 99 | "name": "osenv", 100 | "exists": false, 101 | "rawSpec": "^0.1.5", 102 | "ancestor": "2cd4b071-3b63-489f-a205-7bcd3acce05f" 103 | }, 104 | "semver": { 105 | "ref": "0c22e0db-e6d5-4695-85e5-972cb500152c", 106 | "name": "semver", 107 | "exists": false, 108 | "rawSpec": "^5.5.0", 109 | "ancestor": "0e50adf2-1dd4-4dda-8e3c-0b6f2c2d7104" 110 | }, 111 | "validate-npm-package-name": { 112 | "ref": "cf913368-9eef-4f95-881b-0f457191724c", 113 | "name": "validate-npm-package-name", 114 | "exists": false, 115 | "rawSpec": "^3.0.0", 116 | "ancestor": "b8c98e3d-232f-467a-a43a-4d4e479c7832" 117 | } 118 | } 119 | }, 120 | "object-assign": { 121 | "ref": "6f47bbcb-08b0-4254-ba65-26098dc78bbf", 122 | "name": "object-assign", 123 | "version": "4.1.1", 124 | "from": "object-assign@^4.0.1", 125 | "resolved": "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", 126 | "exists": true, 127 | "adjustHere": true 128 | }, 129 | "os-homedir": { 130 | "ref": "7fa1ce4b-9136-424d-af70-cd96eabda612", 131 | "name": "os-homedir", 132 | "version": "1.0.2", 133 | "from": "os-homedir@^1.0.0", 134 | "resolved": "http://registry.npm.taobao.org/os-homedir/download/os-homedir-1.0.2.tgz", 135 | "exists": true, 136 | "adjustHere": true 137 | }, 138 | "os-tmpdir": { 139 | "ref": "7f8c4ec0-04c9-4514-9ef7-602f1abd6bc5", 140 | "name": "os-tmpdir", 141 | "version": "1.0.2", 142 | "from": "os-tmpdir@^1.0.0", 143 | "resolved": "http://registry.npm.taobao.org/os-tmpdir/download/os-tmpdir-1.0.2.tgz", 144 | "exists": true, 145 | "adjustHere": true 146 | }, 147 | "osenv": { 148 | "ref": "2cd4b071-3b63-489f-a205-7bcd3acce05f", 149 | "name": "osenv", 150 | "version": "0.1.5", 151 | "from": "osenv@^0.1.5", 152 | "resolved": "http://registry.npm.taobao.org/osenv/download/osenv-0.1.5.tgz", 153 | "exists": true, 154 | "adjustHere": true, 155 | "dependencies": { 156 | "os-homedir": { 157 | "ref": "d3dc14dd-469b-4a33-8f9e-e3b1388d98e0", 158 | "name": "os-homedir", 159 | "exists": false, 160 | "rawSpec": "^1.0.0", 161 | "ancestor": "7fa1ce4b-9136-424d-af70-cd96eabda612" 162 | }, 163 | "os-tmpdir": { 164 | "ref": "9ece8e7b-7f8a-4a0d-b7b0-a545e53212de", 165 | "name": "os-tmpdir", 166 | "exists": false, 167 | "rawSpec": "^1.0.0", 168 | "ancestor": "7f8c4ec0-04c9-4514-9ef7-602f1abd6bc5" 169 | } 170 | } 171 | }, 172 | "semver": { 173 | "ref": "0e50adf2-1dd4-4dda-8e3c-0b6f2c2d7104", 174 | "name": "semver", 175 | "version": "5.6.0", 176 | "from": "semver@^5.5.0", 177 | "resolved": "http://registry.npm.taobao.org/semver/download/semver-5.6.0.tgz", 178 | "exists": true, 179 | "adjustHere": true 180 | }, 181 | "thenify": { 182 | "ref": "8e2baaef-190f-4a62-a60f-655c3b916cde", 183 | "name": "thenify", 184 | "version": "3.3.0", 185 | "from": "thenify@>= 3.1.0 < 4", 186 | "resolved": "http://registry.npm.taobao.org/thenify/download/thenify-3.3.0.tgz", 187 | "exists": true, 188 | "adjustHere": true, 189 | "dependencies": { 190 | "any-promise": { 191 | "ref": "62a745b7-dd1a-497d-871b-33900c7ff602", 192 | "name": "any-promise", 193 | "version": "1.3.0", 194 | "from": "any-promise@^1.0.0", 195 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz", 196 | "exists": true, 197 | "rawSpec": "^1.0.0" 198 | } 199 | } 200 | }, 201 | "thenify-all": { 202 | "ref": "309a7ff8-bcb1-467c-82ba-f5bd090fe7b4", 203 | "name": "thenify-all", 204 | "version": "1.6.0", 205 | "from": "thenify-all@^1.0.0", 206 | "resolved": "http://registry.npm.taobao.org/thenify-all/download/thenify-all-1.6.0.tgz", 207 | "exists": true, 208 | "adjustHere": true, 209 | "dependencies": { 210 | "thenify": { 211 | "ref": "343493e0-ee00-4aa9-8308-7c9cf386211f", 212 | "name": "thenify", 213 | "exists": false, 214 | "rawSpec": ">= 3.1.0 < 4", 215 | "ancestor": "8e2baaef-190f-4a62-a60f-655c3b916cde" 216 | } 217 | } 218 | }, 219 | "validate-npm-package-name": { 220 | "ref": "b8c98e3d-232f-467a-a43a-4d4e479c7832", 221 | "name": "validate-npm-package-name", 222 | "version": "3.0.0", 223 | "from": "validate-npm-package-name@^3.0.0", 224 | "resolved": "http://registry.npm.taobao.org/validate-npm-package-name/download/validate-npm-package-name-3.0.0.tgz", 225 | "exists": true, 226 | "adjustHere": true, 227 | "dependencies": { 228 | "builtins": { 229 | "ref": "8069f4d6-4190-4362-a418-e58e7c114bb4", 230 | "name": "builtins", 231 | "exists": false, 232 | "rawSpec": "^1.0.3", 233 | "ancestor": "c0746454-ed71-471e-a76a-28ed87f08a52" 234 | } 235 | } 236 | } 237 | } -------------------------------------------------------------------------------- /test/fixtures/apps/app1/target.cnpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "@crand/mt19937": { 3 | "ref": "a3a69f47-a3cd-4a29-8b03-5c72927bb3b3", 4 | "name": "@crand/mt19937", 5 | "version": "2.0.0", 6 | "from": "@crand/mt19937@2.0.0", 7 | "resolved": "http://registry.npm.taobao.org/@crand/mt19937/download/@crand/mt19937-2.0.0.tgz", 8 | "exists": true, 9 | "rawSpec": "*" 10 | }, 11 | "algorithmjs": { 12 | "ref": "3f82e097-32a3-410c-b6cd-8c74d04c4951", 13 | "name": "algorithmjs", 14 | "version": "1.0.0", 15 | "from": "algorithmjs@1.0.0", 16 | "resolved": "http://registry.npm.taobao.org/algorithmjs/download/algorithmjs-1.0.0.tgz", 17 | "exists": true, 18 | "rawSpec": "^1.0.0" 19 | }, 20 | "any-promise": { 21 | "ref": "3ee44269-15b0-4176-911b-0a973c0c72d7", 22 | "name": "any-promise", 23 | "version": "0.2.0", 24 | "from": "any-promise@0.2.0", 25 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-0.2.0.tgz", 26 | "exists": true, 27 | "rawSpec": "0.2.0" 28 | }, 29 | "builtins": { 30 | "ref": "13daba98-f761-4765-887e-adde2cb37dc4", 31 | "name": "builtins", 32 | "version": "1.0.3", 33 | "from": "builtins@1.0.3", 34 | "resolved": "http://registry.npm.taobao.org/builtins/download/builtins-1.0.3.tgz", 35 | "exists": true, 36 | "adjustHere": true 37 | }, 38 | "hosted-git-info": { 39 | "ref": "ab016644-741b-4592-8d43-54224b71365d", 40 | "name": "hosted-git-info", 41 | "version": "2.7.1", 42 | "from": "hosted-git-info@2.7.1", 43 | "resolved": "http://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.7.1.tgz", 44 | "exists": true, 45 | "adjustHere": true 46 | }, 47 | "mz": { 48 | "ref": "684166f0-e81d-469f-9082-0dc05ac86668", 49 | "name": "mz", 50 | "version": "2.7.0", 51 | "from": "mz@2.7.0", 52 | "resolved": "http://registry.npm.taobao.org/mz/download/mz-2.7.0.tgz", 53 | "exists": true, 54 | "rawSpec": "^2.7.0", 55 | "dependencies": { 56 | "any-promise": { 57 | "ref": "3145b47e-e9a6-45e2-bdb1-e47555e68c04", 58 | "name": "any-promise", 59 | "version": "1.3.0", 60 | "from": "any-promise@1.3.0", 61 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz", 62 | "exists": true, 63 | "rawSpec": "^1.0.0" 64 | }, 65 | "object-assign": { 66 | "ref": "0e01fb0c-8d1c-4e2c-884c-edfa710d370f", 67 | "name": "object-assign", 68 | "version": "4.1.1", 69 | "from": "object-assign@4.1.1", 70 | "resolved": "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", 71 | "exists": true, 72 | "rawSpec": "^4.0.1", 73 | "ancestor": "12f2e252-b222-48c6-a54f-1e0c27e6f9c2" 74 | }, 75 | "thenify-all": { 76 | "ref": "5cf6e139-467e-4d58-88ce-8a4f8994a86a", 77 | "name": "thenify-all", 78 | "version": "1.6.0", 79 | "from": "thenify-all@1.6.0", 80 | "resolved": "http://registry.npm.taobao.org/thenify-all/download/thenify-all-1.6.0.tgz", 81 | "exists": true, 82 | "rawSpec": "^1.0.0", 83 | "ancestor": "c35f1a6b-a997-4636-bf61-ec4d8da9ab73" 84 | } 85 | } 86 | }, 87 | "npm-package-arg": { 88 | "ref": "0c2d6489-5303-4ccf-a6de-636932919ba5", 89 | "name": "npm-package-arg", 90 | "version": "6.1.0", 91 | "from": "npm-package-arg@6.1.0", 92 | "resolved": "http://registry.npm.taobao.org/npm-package-arg/download/npm-package-arg-6.1.0.tgz", 93 | "exists": true, 94 | "rawSpec": "^6.1.0", 95 | "dependencies": { 96 | "hosted-git-info": { 97 | "ref": "4cd405b9-b12e-46f9-83ed-7a3cd0b3d92a", 98 | "name": "hosted-git-info", 99 | "version": "2.7.1", 100 | "from": "hosted-git-info@2.7.1", 101 | "resolved": "http://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.7.1.tgz", 102 | "exists": true, 103 | "rawSpec": "^2.6.0", 104 | "ancestor": "ab016644-741b-4592-8d43-54224b71365d" 105 | }, 106 | "osenv": { 107 | "ref": "7cc35d8a-aabe-4633-9b1a-7b97ed3a554b", 108 | "name": "osenv", 109 | "version": "0.1.5", 110 | "from": "osenv@0.1.5", 111 | "resolved": "http://registry.npm.taobao.org/osenv/download/osenv-0.1.5.tgz", 112 | "exists": true, 113 | "rawSpec": "^0.1.5", 114 | "ancestor": "aedc85a6-77f0-4116-8d66-f42abf49b34e" 115 | }, 116 | "semver": { 117 | "ref": "d8e7dce8-6f16-434f-a37a-3ed00876f531", 118 | "name": "semver", 119 | "version": "5.6.0", 120 | "from": "semver@5.6.0", 121 | "resolved": "http://registry.npm.taobao.org/semver/download/semver-5.6.0.tgz", 122 | "exists": true, 123 | "rawSpec": "^5.5.0", 124 | "ancestor": "b527ed8e-7758-43fa-814b-0422c7371e36" 125 | }, 126 | "validate-npm-package-name": { 127 | "ref": "f5331224-6267-4c6d-9573-4ba4532fd3b8", 128 | "name": "validate-npm-package-name", 129 | "version": "3.0.0", 130 | "from": "validate-npm-package-name@3.0.0", 131 | "resolved": "http://registry.npm.taobao.org/validate-npm-package-name/download/validate-npm-package-name-3.0.0.tgz", 132 | "exists": true, 133 | "rawSpec": "^3.0.0", 134 | "ancestor": "282c6df7-8cfe-4880-a645-bd8da2e45529" 135 | } 136 | } 137 | }, 138 | "object-assign": { 139 | "ref": "12f2e252-b222-48c6-a54f-1e0c27e6f9c2", 140 | "name": "object-assign", 141 | "version": "4.1.1", 142 | "from": "object-assign@4.1.1", 143 | "resolved": "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", 144 | "exists": true, 145 | "adjustHere": true 146 | }, 147 | "os-homedir": { 148 | "ref": "cdee8dbb-f7db-40fd-b02e-3652d4348f45", 149 | "name": "os-homedir", 150 | "version": "1.0.2", 151 | "from": "os-homedir@1.0.2", 152 | "resolved": "http://registry.npm.taobao.org/os-homedir/download/os-homedir-1.0.2.tgz", 153 | "exists": true, 154 | "adjustHere": true 155 | }, 156 | "os-tmpdir": { 157 | "ref": "a77e594d-12d0-47a7-8fdf-d3139fe494cc", 158 | "name": "os-tmpdir", 159 | "version": "1.0.2", 160 | "from": "os-tmpdir@1.0.2", 161 | "resolved": "http://registry.npm.taobao.org/os-tmpdir/download/os-tmpdir-1.0.2.tgz", 162 | "exists": true, 163 | "adjustHere": true 164 | }, 165 | "osenv": { 166 | "ref": "aedc85a6-77f0-4116-8d66-f42abf49b34e", 167 | "name": "osenv", 168 | "version": "0.1.5", 169 | "from": "osenv@0.1.5", 170 | "resolved": "http://registry.npm.taobao.org/osenv/download/osenv-0.1.5.tgz", 171 | "exists": true, 172 | "adjustHere": true, 173 | "dependencies": { 174 | "os-homedir": { 175 | "ref": "046442b8-977d-41a5-98a3-47fdd8321d21", 176 | "name": "os-homedir", 177 | "version": "1.0.2", 178 | "from": "os-homedir@1.0.2", 179 | "resolved": "http://registry.npm.taobao.org/os-homedir/download/os-homedir-1.0.2.tgz", 180 | "exists": true, 181 | "rawSpec": "^1.0.0", 182 | "ancestor": "cdee8dbb-f7db-40fd-b02e-3652d4348f45" 183 | }, 184 | "os-tmpdir": { 185 | "ref": "98a682b7-f6ce-4519-9f09-d2eb8e39570f", 186 | "name": "os-tmpdir", 187 | "version": "1.0.2", 188 | "from": "os-tmpdir@1.0.2", 189 | "resolved": "http://registry.npm.taobao.org/os-tmpdir/download/os-tmpdir-1.0.2.tgz", 190 | "exists": true, 191 | "rawSpec": "^1.0.0", 192 | "ancestor": "a77e594d-12d0-47a7-8fdf-d3139fe494cc" 193 | } 194 | } 195 | }, 196 | "semver": { 197 | "ref": "b527ed8e-7758-43fa-814b-0422c7371e36", 198 | "name": "semver", 199 | "version": "5.6.0", 200 | "from": "semver@5.6.0", 201 | "resolved": "http://registry.npm.taobao.org/semver/download/semver-5.6.0.tgz", 202 | "exists": true, 203 | "adjustHere": true 204 | }, 205 | "thenify": { 206 | "ref": "cb0fc368-f322-4fa7-9d7c-d881b7158966", 207 | "name": "thenify", 208 | "version": "3.3.0", 209 | "from": "thenify@3.3.0", 210 | "resolved": "http://registry.npm.taobao.org/thenify/download/thenify-3.3.0.tgz", 211 | "exists": true, 212 | "adjustHere": true, 213 | "dependencies": { 214 | "any-promise": { 215 | "ref": "0e9ec575-7c06-4878-8159-fcdabfe582b6", 216 | "name": "any-promise", 217 | "version": "1.3.0", 218 | "from": "any-promise@1.3.0", 219 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz", 220 | "exists": true, 221 | "rawSpec": "^1.0.0" 222 | } 223 | } 224 | }, 225 | "thenify-all": { 226 | "ref": "c35f1a6b-a997-4636-bf61-ec4d8da9ab73", 227 | "name": "thenify-all", 228 | "version": "1.6.0", 229 | "from": "thenify-all@1.6.0", 230 | "resolved": "http://registry.npm.taobao.org/thenify-all/download/thenify-all-1.6.0.tgz", 231 | "exists": true, 232 | "adjustHere": true, 233 | "dependencies": { 234 | "thenify": { 235 | "ref": "de964326-0fbb-4926-8495-081e1da03289", 236 | "name": "thenify", 237 | "version": "3.3.0", 238 | "from": "thenify@3.3.0", 239 | "resolved": "http://registry.npm.taobao.org/thenify/download/thenify-3.3.0.tgz", 240 | "exists": true, 241 | "rawSpec": ">= 3.1.0 < 4", 242 | "ancestor": "cb0fc368-f322-4fa7-9d7c-d881b7158966" 243 | } 244 | } 245 | }, 246 | "validate-npm-package-name": { 247 | "ref": "282c6df7-8cfe-4880-a645-bd8da2e45529", 248 | "name": "validate-npm-package-name", 249 | "version": "3.0.0", 250 | "from": "validate-npm-package-name@3.0.0", 251 | "resolved": "http://registry.npm.taobao.org/validate-npm-package-name/download/validate-npm-package-name-3.0.0.tgz", 252 | "exists": true, 253 | "adjustHere": true, 254 | "dependencies": { 255 | "builtins": { 256 | "ref": "94f04404-a661-4dea-8760-549aaab0bee8", 257 | "name": "builtins", 258 | "version": "1.0.3", 259 | "from": "builtins@1.0.3", 260 | "resolved": "http://registry.npm.taobao.org/builtins/download/builtins-1.0.3.tgz", 261 | "exists": true, 262 | "rawSpec": "^1.0.3", 263 | "ancestor": "13daba98-f761-4765-887e-adde2cb37dc4" 264 | } 265 | } 266 | } 267 | } --------------------------------------------------------------------------------