├── .gitignore ├── .nyc_output ├── 07f100cc-4bb5-4823-bb85-9dc5654e3df4.json ├── 367ef5a1-d536-4f8a-908a-ff8af420d45b.json └── processinfo │ ├── 07f100cc-4bb5-4823-bb85-9dc5654e3df4.json │ ├── 367ef5a1-d536-4f8a-908a-ff8af420d45b.json │ └── index.json ├── .travis.yml ├── LICENSE ├── README.md ├── demo └── index.js ├── index.js ├── lib └── guessVersion.js ├── package-lock.json ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /.nyc_output/07f100cc-4bb5-4823-bb85-9dc5654e3df4.json: -------------------------------------------------------------------------------- 1 | {"/Users/anvaka/projects/npmgraphbuilder/index.js":{"path":"/Users/anvaka/projects/npmgraphbuilder/index.js","statementMap":{"0":{"start":{"line":1,"column":10},"end":{"line":1,"column":36}},"1":{"start":{"line":2,"column":19},"end":{"line":2,"column":51}},"2":{"start":{"line":3,"column":14},"end":{"line":3,"column":33}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":28}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":35}},"5":{"start":{"line":9,"column":2},"end":{"line":9,"column":44}},"6":{"start":{"line":10,"column":2},"end":{"line":12,"column":3}},"7":{"start":{"line":11,"column":4},"end":{"line":11,"column":62}},"8":{"start":{"line":14,"column":14},"end":{"line":14,"column":33}},"9":{"start":{"line":16,"column":2},"end":{"line":21,"column":4}},"10":{"start":{"line":19,"column":6},"end":{"line":19,"column":20}},"11":{"start":{"line":24,"column":4},"end":{"line":24,"column":74}},"12":{"start":{"line":24,"column":22},"end":{"line":24,"column":74}},"13":{"start":{"line":25,"column":4},"end":{"line":25,"column":68}},"14":{"start":{"line":25,"column":16},"end":{"line":25,"column":68}},"15":{"start":{"line":26,"column":4},"end":{"line":26,"column":37}},"16":{"start":{"line":26,"column":18},"end":{"line":26,"column":37}},"17":{"start":{"line":28,"column":16},"end":{"line":28,"column":18}},"18":{"start":{"line":29,"column":20},"end":{"line":29,"column":39}},"19":{"start":{"line":31,"column":4},"end":{"line":35,"column":7}},"20":{"start":{"line":37,"column":4},"end":{"line":37,"column":31}},"21":{"start":{"line":40,"column":6},"end":{"line":42,"column":7}},"22":{"start":{"line":41,"column":8},"end":{"line":41,"column":31}},"23":{"start":{"line":44,"column":17},"end":{"line":44,"column":28}},"24":{"start":{"line":46,"column":19},"end":{"line":46,"column":43}},"25":{"start":{"line":47,"column":6},"end":{"line":51,"column":7}},"26":{"start":{"line":48,"column":8},"end":{"line":50,"column":11}},"27":{"start":{"line":49,"column":10},"end":{"line":49,"column":51}},"28":{"start":{"line":53,"column":6},"end":{"line":58,"column":7}},"29":{"start":{"line":55,"column":8},"end":{"line":57,"column":11}},"30":{"start":{"line":56,"column":10},"end":{"line":56,"column":55}},"31":{"start":{"line":60,"column":24},"end":{"line":60,"column":50}},"32":{"start":{"line":61,"column":6},"end":{"line":76,"column":7}},"33":{"start":{"line":62,"column":8},"end":{"line":75,"column":11}},"34":{"start":{"line":63,"column":10},"end":{"line":73,"column":11}},"35":{"start":{"line":65,"column":26},"end":{"line":65,"column":34}},"36":{"start":{"line":66,"column":12},"end":{"line":66,"column":63}},"37":{"start":{"line":67,"column":27},"end":{"line":67,"column":29}},"38":{"start":{"line":68,"column":12},"end":{"line":68,"column":48}},"39":{"start":{"line":70,"column":12},"end":{"line":72,"column":15}},"40":{"start":{"line":74,"column":10},"end":{"line":74,"column":49}},"41":{"start":{"line":77,"column":6},"end":{"line":79,"column":7}},"42":{"start":{"line":78,"column":8},"end":{"line":78,"column":74}},"43":{"start":{"line":81,"column":6},"end":{"line":81,"column":67}},"44":{"start":{"line":84,"column":8},"end":{"line":84,"column":39}},"45":{"start":{"line":85,"column":8},"end":{"line":85,"column":45}},"46":{"start":{"line":87,"column":8},"end":{"line":90,"column":9}},"47":{"start":{"line":89,"column":10},"end":{"line":89,"column":37}},"48":{"start":{"line":92,"column":8},"end":{"line":92,"column":21}},"49":{"start":{"line":97,"column":28},"end":{"line":97,"column":50}},"50":{"start":{"line":98,"column":21},"end":{"line":98,"column":30}},"51":{"start":{"line":99,"column":6},"end":{"line":99,"column":65}},"52":{"start":{"line":104,"column":6},"end":{"line":112,"column":7}},"53":{"start":{"line":105,"column":8},"end":{"line":105,"column":21}},"54":{"start":{"line":106,"column":8},"end":{"line":106,"column":26}},"55":{"start":{"line":107,"column":8},"end":{"line":107,"column":26}},"56":{"start":{"line":109,"column":8},"end":{"line":109,"column":58}},"57":{"start":{"line":110,"column":8},"end":{"line":110,"column":44}},"58":{"start":{"line":111,"column":8},"end":{"line":111,"column":21}},"59":{"start":{"line":115,"column":25},"end":{"line":115,"column":41}},"60":{"start":{"line":117,"column":6},"end":{"line":117,"column":26}},"61":{"start":{"line":119,"column":6},"end":{"line":119,"column":29}},"62":{"start":{"line":121,"column":6},"end":{"line":123,"column":7}},"63":{"start":{"line":122,"column":8},"end":{"line":122,"column":39}},"64":{"start":{"line":125,"column":6},"end":{"line":125,"column":24}},"65":{"start":{"line":127,"column":6},"end":{"line":130,"column":7}},"66":{"start":{"line":129,"column":8},"end":{"line":129,"column":15}},"67":{"start":{"line":131,"column":6},"end":{"line":131,"column":27}},"68":{"start":{"line":133,"column":6},"end":{"line":135,"column":7}},"69":{"start":{"line":134,"column":8},"end":{"line":134,"column":54}},"70":{"start":{"line":138,"column":10},"end":{"line":142,"column":12}},"71":{"start":{"line":149,"column":2},"end":{"line":149,"column":70}},"72":{"start":{"line":153,"column":2},"end":{"line":157,"column":4}}},"fnMap":{"0":{"name":"buildGraph","decl":{"start":{"line":8,"column":9},"end":{"line":8,"column":19}},"loc":{"start":{"line":8,"column":31},"end":{"line":146,"column":1}},"line":8},"1":{"name":"(anonymous_1)","decl":{"start":{"line":18,"column":20},"end":{"line":18,"column":21}},"loc":{"start":{"line":18,"column":34},"end":{"line":20,"column":5}},"line":18},"2":{"name":"createNpmDependenciesGraph","decl":{"start":{"line":23,"column":11},"end":{"line":23,"column":37}},"loc":{"start":{"line":23,"column":67},"end":{"line":145,"column":3}},"line":23},"3":{"name":"processQueue","decl":{"start":{"line":39,"column":13},"end":{"line":39,"column":25}},"loc":{"start":{"line":39,"column":33},"end":{"line":94,"column":5}},"line":39},"4":{"name":"(anonymous_4)","decl":{"start":{"line":48,"column":27},"end":{"line":48,"column":28}},"loc":{"start":{"line":48,"column":45},"end":{"line":50,"column":9}},"line":48},"5":{"name":"(anonymous_5)","decl":{"start":{"line":55,"column":27},"end":{"line":55,"column":28}},"loc":{"start":{"line":55,"column":45},"end":{"line":57,"column":9}},"line":55},"6":{"name":"(anonymous_6)","decl":{"start":{"line":62,"column":36},"end":{"line":62,"column":37}},"loc":{"start":{"line":62,"column":43},"end":{"line":75,"column":9}},"line":62},"7":{"name":"processRegistryResponse","decl":{"start":{"line":83,"column":15},"end":{"line":83,"column":38}},"loc":{"start":{"line":83,"column":44},"end":{"line":93,"column":7}},"line":83},"8":{"name":"getCacheKey","decl":{"start":{"line":96,"column":13},"end":{"line":96,"column":24}},"loc":{"start":{"line":96,"column":31},"end":{"line":100,"column":5}},"line":96},"9":{"name":"traverseDependencies","decl":{"start":{"line":102,"column":13},"end":{"line":102,"column":33}},"loc":{"start":{"line":102,"column":53},"end":{"line":144,"column":5}},"line":102},"10":{"name":"addToQueue","decl":{"start":{"line":137,"column":15},"end":{"line":137,"column":25}},"loc":{"start":{"line":137,"column":32},"end":{"line":143,"column":9}},"line":137},"11":{"name":"isHttp","decl":{"start":{"line":148,"column":9},"end":{"line":148,"column":15}},"loc":{"start":{"line":148,"column":25},"end":{"line":150,"column":1}},"line":148},"12":{"name":"isRemote","decl":{"start":{"line":152,"column":9},"end":{"line":152,"column":17}},"loc":{"start":{"line":152,"column":27},"end":{"line":158,"column":1}},"line":152}},"branchMap":{"0":{"loc":{"start":{"line":9,"column":8},"end":{"line":9,"column":43}},"type":"binary-expr","locations":[{"start":{"line":9,"column":8},"end":{"line":9,"column":11}},{"start":{"line":9,"column":15},"end":{"line":9,"column":43}}],"line":9},"1":{"loc":{"start":{"line":10,"column":2},"end":{"line":12,"column":3}},"type":"if","locations":[{"start":{"line":10,"column":2},"end":{"line":12,"column":3}},{"start":{"line":10,"column":2},"end":{"line":12,"column":3}}],"line":10},"2":{"loc":{"start":{"line":24,"column":4},"end":{"line":24,"column":74}},"type":"if","locations":[{"start":{"line":24,"column":4},"end":{"line":24,"column":74}},{"start":{"line":24,"column":4},"end":{"line":24,"column":74}}],"line":24},"3":{"loc":{"start":{"line":25,"column":4},"end":{"line":25,"column":68}},"type":"if","locations":[{"start":{"line":25,"column":4},"end":{"line":25,"column":68}},{"start":{"line":25,"column":4},"end":{"line":25,"column":68}}],"line":25},"4":{"loc":{"start":{"line":26,"column":4},"end":{"line":26,"column":37}},"type":"if","locations":[{"start":{"line":26,"column":4},"end":{"line":26,"column":37}},{"start":{"line":26,"column":4},"end":{"line":26,"column":37}}],"line":26},"5":{"loc":{"start":{"line":40,"column":6},"end":{"line":42,"column":7}},"type":"if","locations":[{"start":{"line":40,"column":6},"end":{"line":42,"column":7}},{"start":{"line":40,"column":6},"end":{"line":42,"column":7}}],"line":40},"6":{"loc":{"start":{"line":47,"column":6},"end":{"line":51,"column":7}},"type":"if","locations":[{"start":{"line":47,"column":6},"end":{"line":51,"column":7}},{"start":{"line":47,"column":6},"end":{"line":51,"column":7}}],"line":47},"7":{"loc":{"start":{"line":53,"column":6},"end":{"line":58,"column":7}},"type":"if","locations":[{"start":{"line":53,"column":6},"end":{"line":58,"column":7}},{"start":{"line":53,"column":6},"end":{"line":58,"column":7}}],"line":53},"8":{"loc":{"start":{"line":61,"column":6},"end":{"line":76,"column":7}},"type":"if","locations":[{"start":{"line":61,"column":6},"end":{"line":76,"column":7}},{"start":{"line":61,"column":6},"end":{"line":76,"column":7}}],"line":61},"9":{"loc":{"start":{"line":61,"column":10},"end":{"line":61,"column":43}},"type":"binary-expr","locations":[{"start":{"line":61,"column":10},"end":{"line":61,"column":22}},{"start":{"line":61,"column":26},"end":{"line":61,"column":43}}],"line":61},"10":{"loc":{"start":{"line":63,"column":10},"end":{"line":73,"column":11}},"type":"if","locations":[{"start":{"line":63,"column":10},"end":{"line":73,"column":11}},{"start":{"line":63,"column":10},"end":{"line":73,"column":11}}],"line":63},"11":{"loc":{"start":{"line":77,"column":6},"end":{"line":79,"column":7}},"type":"if","locations":[{"start":{"line":77,"column":6},"end":{"line":79,"column":7}},{"start":{"line":77,"column":6},"end":{"line":79,"column":7}}],"line":77},"12":{"loc":{"start":{"line":87,"column":8},"end":{"line":90,"column":9}},"type":"if","locations":[{"start":{"line":87,"column":8},"end":{"line":90,"column":9}},{"start":{"line":87,"column":8},"end":{"line":90,"column":9}}],"line":87},"13":{"loc":{"start":{"line":99,"column":13},"end":{"line":99,"column":65}},"type":"cond-expr","locations":[{"start":{"line":99,"column":31},"end":{"line":99,"column":54}},{"start":{"line":99,"column":57},"end":{"line":99,"column":65}}],"line":99},"14":{"loc":{"start":{"line":104,"column":6},"end":{"line":112,"column":7}},"type":"if","locations":[{"start":{"line":104,"column":6},"end":{"line":112,"column":7}},{"start":{"line":104,"column":6},"end":{"line":112,"column":7}}],"line":104},"15":{"loc":{"start":{"line":121,"column":6},"end":{"line":123,"column":7}},"type":"if","locations":[{"start":{"line":121,"column":6},"end":{"line":123,"column":7}},{"start":{"line":121,"column":6},"end":{"line":123,"column":7}}],"line":121},"16":{"loc":{"start":{"line":121,"column":10},"end":{"line":121,"column":56}},"type":"binary-expr","locations":[{"start":{"line":121,"column":10},"end":{"line":121,"column":21}},{"start":{"line":121,"column":25},"end":{"line":121,"column":56}}],"line":121},"17":{"loc":{"start":{"line":127,"column":6},"end":{"line":130,"column":7}},"type":"if","locations":[{"start":{"line":127,"column":6},"end":{"line":130,"column":7}},{"start":{"line":127,"column":6},"end":{"line":130,"column":7}}],"line":127},"18":{"loc":{"start":{"line":133,"column":6},"end":{"line":135,"column":7}},"type":"if","locations":[{"start":{"line":133,"column":6},"end":{"line":135,"column":7}},{"start":{"line":133,"column":6},"end":{"line":135,"column":7}}],"line":133},"19":{"loc":{"start":{"line":149,"column":9},"end":{"line":149,"column":69}},"type":"binary-expr","locations":[{"start":{"line":149,"column":9},"end":{"line":149,"column":36}},{"start":{"line":149,"column":40},"end":{"line":149,"column":69}}],"line":149},"20":{"loc":{"start":{"line":153,"column":9},"end":{"line":157,"column":3}},"type":"binary-expr","locations":[{"start":{"line":153,"column":9},"end":{"line":153,"column":36}},{"start":{"line":154,"column":5},"end":{"line":154,"column":33}},{"start":{"line":155,"column":5},"end":{"line":155,"column":34}},{"start":{"line":156,"column":5},"end":{"line":156,"column":34}}],"line":153}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":0,"8":1,"9":1,"10":0,"11":1,"12":0,"13":1,"14":0,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":0,"23":1,"24":1,"25":1,"26":0,"27":0,"28":1,"29":0,"30":0,"31":1,"32":1,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":1,"42":0,"43":1,"44":1,"45":1,"46":0,"47":0,"48":0,"49":2,"50":2,"51":2,"52":1,"53":0,"54":0,"55":0,"56":1,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":4},"f":{"0":1,"1":0,"2":1,"3":1,"4":0,"5":0,"6":0,"7":1,"8":2,"9":1,"10":0,"11":0,"12":4},"b":{"0":[1,1],"1":[0,1],"2":[0,1],"3":[0,1],"4":[1,0],"5":[0,1],"6":[0,1],"7":[0,1],"8":[0,1],"9":[1,0],"10":[0,0],"11":[0,1],"12":[0,0],"13":[0,2],"14":[0,1],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[4,4,4,4]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"23d714999e5ae73f2aa5595183a235b55575c93c","contentHash":"8c6f0b6f8ebf9ea2d866d7ff7af3ab2f0c3e8d170d4d8a2678ae2e433c70cc75"},"/Users/anvaka/projects/npmgraphbuilder/lib/guessVersion.js":{"path":"/Users/anvaka/projects/npmgraphbuilder/lib/guessVersion.js","statementMap":{"0":{"start":{"line":6,"column":0},"end":{"line":6,"column":30}},"1":{"start":{"line":8,"column":13},"end":{"line":8,"column":30}},"2":{"start":{"line":11,"column":2},"end":{"line":11,"column":53}},"3":{"start":{"line":11,"column":34},"end":{"line":11,"column":53}},"4":{"start":{"line":13,"column":26},"end":{"line":13,"column":59}},"5":{"start":{"line":14,"column":16},"end":{"line":14,"column":76}},"6":{"start":{"line":17,"column":2},"end":{"line":22,"column":3}},"7":{"start":{"line":18,"column":4},"end":{"line":18,"column":56}},"8":{"start":{"line":21,"column":4},"end":{"line":21,"column":73}},"9":{"start":{"line":24,"column":2},"end":{"line":25,"column":21}},"10":{"start":{"line":24,"column":16},"end":{"line":24,"column":96}},"11":{"start":{"line":25,"column":7},"end":{"line":25,"column":21}}},"fnMap":{"0":{"name":"guessVersion","decl":{"start":{"line":10,"column":9},"end":{"line":10,"column":21}},"loc":{"start":{"line":10,"column":50},"end":{"line":26,"column":1}},"line":10},"1":{"name":"(anonymous_1)","decl":{"start":{"line":17,"column":67},"end":{"line":17,"column":68}},"loc":{"start":{"line":17,"column":81},"end":{"line":19,"column":3}},"line":17}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":2},"end":{"line":11,"column":53}},"type":"if","locations":[{"start":{"line":11,"column":2},"end":{"line":11,"column":53}},{"start":{"line":11,"column":2},"end":{"line":11,"column":53}}],"line":11},"1":{"loc":{"start":{"line":17,"column":2},"end":{"line":22,"column":3}},"type":"if","locations":[{"start":{"line":17,"column":2},"end":{"line":22,"column":3}},{"start":{"line":17,"column":2},"end":{"line":22,"column":3}}],"line":17},"2":{"loc":{"start":{"line":17,"column":6},"end":{"line":19,"column":4}},"type":"binary-expr","locations":[{"start":{"line":17,"column":6},"end":{"line":17,"column":14}},{"start":{"line":17,"column":18},"end":{"line":17,"column":39}},{"start":{"line":17,"column":43},"end":{"line":19,"column":4}}],"line":17},"3":{"loc":{"start":{"line":21,"column":14},"end":{"line":21,"column":73}},"type":"binary-expr","locations":[{"start":{"line":21,"column":14},"end":{"line":21,"column":38}},{"start":{"line":21,"column":42},"end":{"line":21,"column":73}}],"line":21},"4":{"loc":{"start":{"line":24,"column":2},"end":{"line":25,"column":21}},"type":"if","locations":[{"start":{"line":24,"column":2},"end":{"line":25,"column":21}},{"start":{"line":24,"column":2},"end":{"line":25,"column":21}}],"line":24}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},"f":{"0":1,"1":0},"b":{"0":[1,0],"1":[0,0],"2":[0,0,0],"3":[0,0],"4":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"8e97b554a2b0478e06c16f541aacb96053fe7d67","contentHash":"91555c5139a5171e83455d52552ab85f5ee98ee5074a38b9ecff191362aff7fe"}} -------------------------------------------------------------------------------- /.nyc_output/367ef5a1-d536-4f8a-908a-ff8af420d45b.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.nyc_output/processinfo/07f100cc-4bb5-4823-bb85-9dc5654e3df4.json: -------------------------------------------------------------------------------- 1 | {"parent":"367ef5a1-d536-4f8a-908a-ff8af420d45b","pid":4534,"argv":["/Users/anvaka/.nvm/versions/node/v16.4.0/bin/node","/Users/anvaka/projects/npmgraphbuilder/test/index.js"],"execArgv":[],"cwd":"/Users/anvaka/projects/npmgraphbuilder","time":1665024224782,"ppid":4533,"coverageFilename":"/Users/anvaka/projects/npmgraphbuilder/.nyc_output/07f100cc-4bb5-4823-bb85-9dc5654e3df4.json","externalId":"test/index.js","uuid":"07f100cc-4bb5-4823-bb85-9dc5654e3df4","files":["/Users/anvaka/projects/npmgraphbuilder/index.js","/Users/anvaka/projects/npmgraphbuilder/lib/guessVersion.js"]} -------------------------------------------------------------------------------- /.nyc_output/processinfo/367ef5a1-d536-4f8a-908a-ff8af420d45b.json: -------------------------------------------------------------------------------- 1 | {"parent":null,"pid":4533,"argv":["/Users/anvaka/.nvm/versions/node/v16.4.0/bin/node","/Users/anvaka/projects/npmgraphbuilder/node_modules/.bin/tap","--branches=30","--lines=30","--statements=30","--functions=30","test/index.js"],"execArgv":[],"cwd":"/Users/anvaka/projects/npmgraphbuilder","time":1665024223802,"ppid":4532,"coverageFilename":"/Users/anvaka/projects/npmgraphbuilder/.nyc_output/367ef5a1-d536-4f8a-908a-ff8af420d45b.json","externalId":"","uuid":"367ef5a1-d536-4f8a-908a-ff8af420d45b","files":[]} -------------------------------------------------------------------------------- /.nyc_output/processinfo/index.json: -------------------------------------------------------------------------------- 1 | {"processes":{"07f100cc-4bb5-4823-bb85-9dc5654e3df4":{"parent":"367ef5a1-d536-4f8a-908a-ff8af420d45b","externalId":"test/index.js","children":[]},"367ef5a1-d536-4f8a-908a-ff8af420d45b":{"parent":null,"children":["07f100cc-4bb5-4823-bb85-9dc5654e3df4"]}},"files":{"/Users/anvaka/projects/npmgraphbuilder/index.js":["07f100cc-4bb5-4823-bb85-9dc5654e3df4"],"/Users/anvaka/projects/npmgraphbuilder/lib/guessVersion.js":["07f100cc-4bb5-4823-bb85-9dc5654e3df4"]},"externalIds":{"test/index.js":{"root":"07f100cc-4bb5-4823-bb85-9dc5654e3df4","children":[]}}} -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 4.4.7 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2024 Andrei Kashcha 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # npmgraphbuilder 2 | 3 | Builds graph of npm dependencies from npm registry. A graph is an instance of 4 | [`ngraph.graph`](https://github.com/anvaka/ngraph.graph). 5 | 6 | [![Build Status](https://travis-ci.org/anvaka/npmgraphbuilder.png)](https://travis-ci.org/anvaka/npmgraphbuilder) 7 | # Demo 8 | 9 | This library is not bound to any particular http client. It requires http client 10 | to be injected into constructor: 11 | 12 | ``` js 13 | var graph = require('ngraph.graph')(); 14 | var graphBuilder = require('npmgraphbuilder')(httpClient); 15 | 16 | graphBuilder.createNpmDependenciesGraph(pkgName, graph) 17 | .then(function (graph) { 18 | console.log('Done.'); 19 | console.log('Nodes count: ', graph.getNodesCount()); 20 | console.log('Edges count: ', graph.getLinksCount()); 21 | }) 22 | .fail(function (err) { 23 | console.error('Failed to build graph: ', err); 24 | }); 25 | ``` 26 | 27 | Here `httpClient` is a `function (url, data) {}`, which returns a promise. 28 | 29 | A demo of `httpClient`, implemented in angular.js: 30 | 31 | ``` js 32 | function httpClient(url, data) { 33 | // since we will be using from a web browser, make sure jsonp is enabled: 34 | data.callback = 'JSON_CALLBACK'; 35 | return $http.jsonp(url, {params: data}); 36 | } 37 | ``` 38 | 39 | A demo of `httpClient` implemented in node.js: 40 | 41 | ``` js 42 | var q = require('q'); // npm install q 43 | var http = require('http'); // stadard http 44 | var querystring = require('querystring'); // standard query string 45 | 46 | function httpClient(url, data) { 47 | var defer = q.defer(); 48 | http.get(url + '?' + querystring.stringify(data), function (res) { 49 | var body = ''; 50 | res.setEncoding('utf8'); 51 | res.on('data', function (chunk) { 52 | body += chunk; 53 | }).on('end', function () { 54 | defer.resolve({ data: JSON.parse(body) }); 55 | }); 56 | }); 57 | 58 | return defer.promise; 59 | } 60 | ``` 61 | 62 | To see working demo please refer to `demo` folder. 63 | 64 | # install 65 | 66 | With [npm](https://npmjs.org) do: 67 | 68 | ``` 69 | npm install npmgraphbuilder 70 | ``` 71 | 72 | # license 73 | 74 | MIT 75 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | var graph = require('ngraph.graph')(); 2 | var graphBuilder = require('../')(httpClient); 3 | 4 | var pkgName = process.argv[2] || 'browserify'; 5 | console.log('building dependencies graph for', pkgName); 6 | 7 | graphBuilder.createNpmDependenciesGraph(pkgName, graph). 8 | then(function (graph) { 9 | console.log('Done.'); 10 | console.log('Nodes count: ', graph.getNodesCount()); 11 | console.log('Edges count: ', graph.getLinksCount()); 12 | console.log('Graph:'); 13 | var serializer = require('ngraph.serialization/json'); 14 | console.log(serializer.save(graph)); 15 | }) 16 | .fail(function (err) { 17 | console.error('Failed to build graph: ', err); 18 | }); 19 | 20 | function httpClient(url, data) { 21 | console.log('Calling: ', url); 22 | var q = require('q'); 23 | var http = require('http'); 24 | var querystring = require('querystring'); 25 | 26 | var defer = q.defer(); 27 | http.get(url + '?' + querystring.stringify(data), function (res) { 28 | var body = ''; 29 | res.setEncoding('utf8'); 30 | res.on('data', function (chunk) { 31 | body += chunk; 32 | }).on('end', function () { 33 | defer.resolve({ data: JSON.parse(body) }); 34 | }); 35 | }); 36 | 37 | return defer.promise; 38 | } 39 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var npa = require('npm-package-arg'); 2 | var guessVersion = require('./lib/guessVersion.js'); 3 | var Promise = require('bluebird'); 4 | 5 | module.exports = buildGraph; 6 | module.exports.isRemote = isRemote; 7 | 8 | function buildGraph(http, url) { 9 | url = url || 'http://registry.npmjs.org/'; 10 | if (url[url.length - 1] !== '/') { 11 | throw new Error('registry url is supposed to end with /'); 12 | } 13 | var progress; 14 | var cache = Object.create(null); 15 | 16 | return { 17 | createNpmDependenciesGraph: createNpmDependenciesGraph, 18 | notifyProgress: function (cb) { 19 | progress = cb; 20 | } 21 | }; 22 | 23 | function createNpmDependenciesGraph(packageName, graph, version) { 24 | if (!packageName) throw new Error('Initial package name is required'); 25 | if (!graph) throw new Error('Graph data structure is required'); 26 | if (!version) version = 'latest'; 27 | 28 | var queue = []; 29 | var processed = Object.create(null); 30 | 31 | queue.push({ 32 | name: packageName, 33 | version: version, 34 | parent: null 35 | }); 36 | 37 | return processQueue(graph); 38 | 39 | function processQueue(graph) { 40 | if (typeof progress === 'function') { 41 | progress(queue.length); 42 | } 43 | 44 | var work = queue.pop(); 45 | 46 | var cached = cache[getCacheKey(work)]; 47 | if (cached) { 48 | return new Promise(function(resolve) { 49 | resolve(processRegistryResponse(cached)); 50 | }); 51 | } 52 | 53 | if (isRemote(work.version)) { 54 | // TODO: This will not download remote dependencies (e.g. git-based) 55 | return new Promise(function(resolve) { 56 | resolve(processRegistryResponse({data: {}})); 57 | }); 58 | } 59 | 60 | var escapedName = npa(work.name).escapedName; 61 | if (!escapedName && isHttp(work.name)) { 62 | return http(work.name).then(res => { 63 | if (res.data) { 64 | // TODO: Validate pkg json 65 | var pkgJSON = res.data; 66 | pkgJSON._id = pkgJSON.name + '@' + pkgJSON.version; 67 | var versions = {}; 68 | versions[pkgJSON.version] = pkgJSON; 69 | 70 | return processRegistryResponse({ 71 | data: Object.assign({}, { versions: versions }) 72 | }); 73 | } 74 | throw new Error('Unexpected response'); 75 | }); 76 | } 77 | if (!escapedName) { 78 | throw new Error('TODO: Escaped name is missing for ' + work.name); 79 | } 80 | 81 | return http(url + escapedName).then(processRegistryResponse); 82 | 83 | function processRegistryResponse(res) { 84 | cache[getCacheKey(work)] = res; 85 | traverseDependencies(work, res.data); 86 | 87 | if (queue.length) { 88 | // continue building the graph 89 | return processQueue(graph); 90 | } 91 | 92 | return graph; 93 | } 94 | } 95 | 96 | function getCacheKey(work) { 97 | var packageIsRemote = isRemote(work.version); 98 | var cacheKey = work.name; 99 | return packageIsRemote ? cacheKey + work.version : cacheKey 100 | } 101 | 102 | function traverseDependencies(work, packageJson) { 103 | var version, pkg, id; 104 | if (isRemote(work.version)) { 105 | version = ''; 106 | pkg = packageJson; 107 | id = work.version; 108 | } else { 109 | version = guessVersion(work.version, packageJson); 110 | pkg = packageJson.versions[version]; 111 | id = pkg._id; 112 | } 113 | 114 | // TODO: here is a good place to address https://github.com/anvaka/npmgraph.an/issues/4 115 | var dependencies = pkg.dependencies; 116 | 117 | graph.beginUpdate(); 118 | 119 | graph.addNode(id, pkg); 120 | 121 | if (work.parent && !graph.hasLink(work.parent, id)) { 122 | graph.addLink(work.parent, id); 123 | } 124 | 125 | graph.endUpdate(); 126 | 127 | if (processed[id]) { 128 | // no need to enqueue this package again - we already downloaded it before 129 | return; 130 | } 131 | processed[id] = true; 132 | 133 | if (dependencies) { 134 | Object.keys(dependencies).forEach(addToQueue); 135 | } 136 | 137 | function addToQueue(name) { 138 | queue.push({ 139 | name: name, 140 | version: dependencies[name], 141 | parent: id 142 | }) 143 | } 144 | } 145 | } 146 | } 147 | 148 | function isHttp(version) { 149 | return typeof version === 'string' && version.match(/^https?:\/\//); 150 | } 151 | 152 | function isRemote(version) { 153 | return typeof version === 'string' && ( 154 | (version.indexOf('git') === 0) || 155 | (version.indexOf('http') === 0) || 156 | (version.indexOf('file') === 0) 157 | ); 158 | } 159 | -------------------------------------------------------------------------------- /lib/guessVersion.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adjusted from https://github.com/npm/npm-remote-ls 3 | * 4 | * Copyright (c) 2014, npm, Inc. and Contributors 5 | */ 6 | module.exports = guessVersion; 7 | 8 | var semver = require('semver'); 9 | 10 | function guessVersion(versionString, packageJson) { 11 | if (versionString === 'latest') versionString = '*' 12 | 13 | var availableVersions = Object.keys(packageJson.versions) 14 | var version = semver.maxSatisfying(availableVersions, versionString, true) 15 | 16 | // check for prerelease-only versions 17 | if (!version && versionString === '*' && availableVersions.every(function (av) { 18 | return new semver.SemVer(av, true).prerelease.length 19 | })) { 20 | // just use latest then 21 | version = packageJson['dist-tags'] && packageJson['dist-tags'].latest 22 | } 23 | 24 | if (!version) throw Error('could not find a satisfactory version for string ' + versionString) 25 | else return version 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npmgraphbuilder", 3 | "version": "2.2.0", 4 | "description": "Builds graph of npm dependencies from npm registry", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tap --branches=30 --lines=30 --statements=30 --functions=30 test/*.js" 8 | }, 9 | "keywords": [ 10 | "npm", 11 | "ngraph", 12 | "graph", 13 | "dependencies" 14 | ], 15 | "author": "Andrei Kashcha", 16 | "license": "MIT", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/anvaka/npmgraphbuilder" 20 | }, 21 | "devDependencies": { 22 | "tap": "~16.3.0", 23 | "q": "~1.5.1", 24 | "ngraph.graph": "0.0.2", 25 | "ngraph.serialization": "0.0.3" 26 | }, 27 | "dependencies": { 28 | "bluebird": "^3.7.2", 29 | "npm-package-arg": "^9.1.2", 30 | "semver": "^7.3.8" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test, 2 | q = require('q'), 3 | createGraph = require('ngraph.graph'), 4 | createGraphBuilder = require('../'); 5 | 6 | test('Passes default data', function (t) { 7 | t.plan(3); 8 | 9 | var builder = createGraphBuilder(function (url) { 10 | t.ok(url, 'Has default registry url'); 11 | t.ok(url.indexOf('browserify') >= 0, 'Has package'); 12 | return q.all([]); 13 | }); 14 | 15 | var graph = createGraph(); 16 | builder.createNpmDependenciesGraph('browserify', graph).fail(function (err) { 17 | t.ok(err, 'Fired error, since we did not provide valid response'); 18 | }); 19 | }); 20 | 21 | test('Checks dependencies', function (t) { 22 | // TODO: implement me. 23 | t.end(); 24 | return; 25 | t.plan(2); 26 | var builder = createGraphBuilder(function (url, data) { 27 | return q.fcall(createFakeResponse); 28 | }); 29 | 30 | var graph = createGraph(); 31 | builder.createNpmDependenciesGraph('browserify', graph) 32 | .then(function () { 33 | t.equals(graph.getNodesCount(), 2, 'Graph has two links'); 34 | t.ok(graph.hasLink('browserify', 'util'), 'Graph has two links'); 35 | }); 36 | }); 37 | 38 | function createFakeResponse() { 39 | return { 40 | data: { 41 | rows: [{ 42 | id: 'browserify', 43 | value: { 44 | _id: 'browserify@3.30.2', 45 | dependencies: { 46 | "util": "~0.10.1" 47 | } 48 | } 49 | } 50 | ] 51 | } 52 | }; 53 | } 54 | --------------------------------------------------------------------------------