UNPKG

5.85 kBJavaScriptView Raw
1'use strict';
2
3var debug = require('debug')('npmjs::packages')
4 , normalize = require('../normalize')
5 , licenses = require('licenses')
6 , semver = require('../semver');
7
8/**
9 * Get all package information.
10 *
11 * @constructor
12 * @param {Registry} api Reference to the wrapping registry.
13 * @api private
14 */
15function Packages(api) {
16 this.api = api;
17
18 this.send = api.send.bind(api);
19 this.view = api.view.bind(api);
20}
21
22/**
23 * Get information from the npm package. If the name contains an `@` char we
24 * assume that the user wants to get a specific version instead.
25 * Example:
26 *
27 * - primus@0.1.1 would retrieve primus version 0.1.1
28 *
29 * @param {String} name The name of the node module.
30 * @param {Function} fn The callback.
31 * @returns {Assign}
32 * @api public
33 */
34Packages.prototype.get = function get(name, fn) {
35 return this.send(name.replace('@', '/'), fn).map(normalize.packages);
36};
37
38/**
39 * Get all packages that are depended upon a given package name.
40 *
41 * @param {String} name The name of the node module.
42 * @param {Function} fn The callback
43 * @returns {Assign}
44 * @api public
45 */
46Packages.prototype.depended = function depended(name, fn) {
47 return this.view('dependedUpon', {
48 key: name
49 }, fn)
50 .map(this.api.map.simple)
51 .filter(Boolean);
52};
53
54/**
55 * Find out which users have starred the given package.
56 *
57 * @param {String} name The name of the node module.
58 * @param {Function} fn The callback
59 * @returns {Assign}
60 * @api public
61 */
62Packages.prototype.starred = function starred(name, fn) {
63 return this.view('browseStarPackage', {
64 key: name
65 }, fn).map(function map(data) {
66 return data[2];
67 });
68};
69
70/**
71 * Find all packages that matches the giving keywords.
72 *
73 * @param {String} name The keyword.
74 * @param {Function} fn The callback.
75 * @returns {Assign}
76 * @api public
77 */
78Packages.prototype.keyword = function keyword(name, fn) {
79 return this.view('byKeyword', {
80 key: name
81 }, fn)
82 .map(this.api.map.simple)
83 .filter(Boolean);
84};
85
86/**
87 * Retrieve all release specific information for the given package name.
88 *
89 * @param {String} name The package name.
90 * @param {Function} fn The callback.
91 * @api public
92 */
93Packages.prototype.releases = function releases(name, fn) {
94 var api = this.api;
95
96 return this.details(name, fn).emits(function emit(data, add) {
97 if (!data.versions) return;
98
99 //
100 // Add all versions of the given module.
101 //
102 Object.keys(data.versions).forEach(function addmore(version) {
103 var release = data.versions[version];
104 release.date = data.time[version];
105
106 add(normalize.packages(release, data));
107 });
108
109 //
110 // Also add each tag to the releases.
111 //
112 if ('dist-tags' in data) Object.keys(data['dist-tags']).forEach(function (key) {
113 if (key in data.versions) return; // Prevent duplicates
114
115 var version = data['dist-tags'][key]
116 , release;
117
118 //
119 // It's possible that the tag does not exist in the versions object. This
120 // is some odd npm edge case.
121 //
122 // Lesson learned: Never trust npm data structures.
123 //
124 if (!version || !(version in data.versions)) return;
125
126 release = api.merge({}, data.versions[version]);
127
128 //
129 // The JSON.parse(JSON.stringify)) is needed to create a full clone of the
130 // data structure as we're adding tags. That would be override during the
131 // `reduce` procedure.
132 //
133 release.date = data.time[version];
134 release.tag = key;
135
136 add(normalize.packages(release, data));
137 });
138
139 return false;
140 }).reduce(function reduce(memo, release) {
141 memo[release.tag || release.version] = release;
142 return memo;
143 }, {});
144};
145
146/**
147 * Get a specific release of a package.
148 *
149 * @param {String} name The name of the package
150 * @param {String} version A valid version number or tag from the package.
151 * @param {Function} fn The callback
152 * @returns {Assign} Assignment
153 * @api public
154 */
155Packages.prototype.release = function release(name, version, fn) {
156 return this.details(name +'/'+ version, fn).map(normalize.packages);
157};
158
159/**
160 * Get a version for a specific release.
161 *
162 * @param {String} name The name of the package.
163 * @param {String} range The semver version range we should retrieve.
164 * @param {Function} fn The callback
165 * @returns {Assign} Assignment
166 * @api public
167 */
168Packages.prototype.range = function ranged(name, range, fn) {
169 if (!semver.validRange(range)) return fn(new Error('Invalid semver range'));
170
171 return this.releases(name, function releases(err, versions) {
172 if (err) return fn(err);
173
174 if (range in versions) {
175 debug('found and direct range (%s) match for %s', range, name);
176 return fn(undefined, versions[range]);
177 }
178
179 var version = semver.maxSatisfying(Object.keys(versions), range);
180
181 debug('max satisfying version for %s is %s', name, version);
182 fn(undefined, versions[version]);
183 });
184};
185
186/**
187 * Retrieve additional details for the package information. This a lot slower
188 * than a simple `.get` but much more detailed and accurate as it uses custom
189 * parsers and mapping operations to parse the data as good as possible.
190 *
191 * @TODO Extract missing descriptions from github.
192 * @TODO Merge profile information from github / authors.
193 *
194 * @param {String} name The name of the node module.
195 * @param {Function} fn The callback.
196 * @returns {Assign}
197 * @api public
198 */
199Packages.prototype.details = function details(name, fn) {
200 var packages = this;
201
202 return this.get(name, fn).async.map(function map(data, next) {
203 licenses(data, {
204 githulk: packages.api.githulk,
205 npmjs: packages
206 }, function parsed(err, licenses) {
207 data.licenses = licenses;
208
209 if (err) debug('failed to detect license: %s', err.message);
210 return next(err, data);
211 });
212 });
213};
214
215//
216// Expose the module.
217//
218module.exports = Packages;