UNPKG

5.53 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 retreive 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).map(this.api.map.simple);
50};
51
52/**
53 * Find out which users have starred the given package.
54 *
55 * @param {String} name The name of the node module.
56 * @param {Function} fn The callback
57 * @returns {Assign}
58 * @api public
59 */
60Packages.prototype.starred = function starred(name, fn) {
61 return this.view('browseStarPackage', {
62 key: name
63 }, fn).map(function map(data) {
64 return data[2];
65 });
66};
67
68/**
69 * Find all packages that matches the giving keywords.
70 *
71 * @param {String} name The keyword.
72 * @param {Function} fn The callback.
73 * @returns {Assign}
74 * @api public
75 */
76Packages.prototype.keyword = function keyword(name, fn) {
77 return this.view('byKeyword', {
78 key: name
79 }, fn).map(this.api.map.simple);
80};
81
82/**
83 * Retrieve all release specific information for the given package name.
84 *
85 * @param {String} name The package name.
86 * @param {Function} fn The callback.
87 * @api public
88 */
89Packages.prototype.releases = function releases(name, fn) {
90 var api = this.api;
91
92 return this.details(name, fn).emits(function emit(data, add) {
93 if (!data.versions) return;
94
95 //
96 // Add all versions of the given module.
97 //
98 Object.keys(data.versions).forEach(function addmore(version) {
99 var release = data.versions[version];
100 release.date = data.time[version];
101
102 add(normalize.packages(data, release));
103 });
104
105 //
106 // Also add each tag to the releases.
107 //
108 if ('dist-tags' in data) Object.keys(data['dist-tags']).forEach(function (key) {
109 if (key in data.versions) return; // Prevent duplicates
110
111 var version = data['dist-tags'][key]
112 , release = api.merge({}, data.versions[version]);
113
114 //
115 // The JSON.parse(JSON.stringify)) is needed to create a full clone of the
116 // data structure as we're adding tags. That would be override during the
117 // `reduce` procedure.
118 //
119 release.date = data.time[version];
120 release.tag = key;
121
122 add(normalize.packages(data, release));
123 });
124
125 return false;
126 }).reduce(function reduce(memo, release) {
127 memo[release.tag || release.version] = release;
128 return memo;
129 }, {});
130};
131
132/**
133 * Get a specific release of a package.
134 *
135 * @param {String} name The name of the package
136 * @param {String} version A valid version number or tag from the package.
137 * @param {Function} fn The callback
138 * @returns {Assign} Assignment
139 * @api public
140 */
141Packages.prototype.release = function release(name, version, fn) {
142 return this.details(name +'/'+ version, fn).map(normalize.packages);
143};
144
145/**
146 * Get a version for a specific release.
147 *
148 * @param {String} name The name of the package.
149 * @param {String} range The semver version range we should retrieve.
150 * @param {Function} fn The callback
151 * @returns {Assign} Assignment
152 * @api public
153 */
154Packages.prototype.range = function ranged(name, range, fn) {
155 if (!semver.validRange(range)) return fn(new Error('Invalid semver range'));
156
157 return this.releases(name, function releases(err, versions) {
158 if (err) return fn(err);
159
160 if (range in versions) {
161 debug('found and direct range (%s) match for %s', range, name);
162 return fn(undefined, versions[range]);
163 }
164
165 var version = semver.maxSatisfying(Object.keys(versions), range);
166
167 debug('max satisfying version for %s is %s', name, version);
168 fn(undefined, versions[version]);
169 });
170};
171
172/**
173 * Retrieve additional details for the package information. This a lot slower
174 * than a simple `.get` but much more detailed and accurate as it uses custom
175 * parsers and mapping operations to parse the data as good as possible.
176 *
177 * @TODO Extract missing descriptions from github.
178 * @TODO Merge profile information from github / authors.
179 *
180 * @param {String} name The name of the node module.
181 * @param {Function} fn The callback.
182 * @returns {Assign}
183 * @api public
184 */
185Packages.prototype.details = function details(name, fn) {
186 var packages = this;
187
188 return this.get(name, fn).async.map(function map(data, next) {
189 licenses(data, {
190 githulk: packages.api.githulk,
191 npmjs: packages
192 }, function parsed(err, licenses) {
193 data.licenses = licenses;
194
195 if (err) debug('failed to detect license: %s', err.message);
196 return next(err, data);
197 });
198 });
199};
200
201//
202// Expose the module.
203//
204module.exports = Packages;