UNPKG

3.23 kBJavaScriptView Raw
1'use strict'
2
3module.exports = exports = search
4
5var npm = require('./npm.js')
6var allPackageSearch = require('./search/all-package-search')
7var esearch = require('./search/esearch.js')
8var formatPackageStream = require('./search/format-package-stream.js')
9var usage = require('./utils/usage')
10var output = require('./utils/output.js')
11var log = require('npmlog')
12var ms = require('mississippi')
13
14search.usage = usage(
15 'search',
16 'npm search [--long] [search terms ...]'
17)
18
19search.completion = function (opts, cb) {
20 cb(null, [])
21}
22
23function search (args, cb) {
24 var searchOpts = {
25 description: npm.config.get('description'),
26 exclude: prepareExcludes(npm.config.get('searchexclude')),
27 include: prepareIncludes(args, npm.config.get('searchopts')),
28 limit: npm.config.get('searchlimit'),
29 log: log,
30 staleness: npm.config.get('searchstaleness'),
31 unicode: npm.config.get('unicode')
32 }
33
34 if (searchOpts.include.length === 0) {
35 return cb(new Error('search must be called with arguments'))
36 }
37
38 // Used later to figure out whether we had any packages go out
39 var anyOutput = false
40
41 var entriesStream = ms.through.obj()
42
43 var esearchWritten = false
44 esearch(searchOpts).on('data', function (pkg) {
45 entriesStream.write(pkg)
46 !esearchWritten && (esearchWritten = true)
47 }).on('error', function (e) {
48 if (esearchWritten) {
49 // If esearch errored after already starting output, we can't fall back.
50 return entriesStream.emit('error', e)
51 }
52 log.warn('search', 'fast search endpoint errored. Using old search.')
53 allPackageSearch(searchOpts).on('data', function (pkg) {
54 entriesStream.write(pkg)
55 }).on('error', function (e) {
56 entriesStream.emit('error', e)
57 }).on('end', function () {
58 entriesStream.end()
59 })
60 }).on('end', function () {
61 entriesStream.end()
62 })
63
64 // Grab a configured output stream that will spit out packages in the
65 // desired format.
66 var outputStream = formatPackageStream({
67 args: args, // --searchinclude options are not highlighted
68 long: npm.config.get('long'),
69 description: npm.config.get('description'),
70 json: npm.config.get('json'),
71 parseable: npm.config.get('parseable'),
72 color: npm.color
73 })
74 outputStream.on('data', function (chunk) {
75 if (!anyOutput) { anyOutput = true }
76 output(chunk.toString('utf8'))
77 })
78
79 log.silly('search', 'searching packages')
80 ms.pipe(entriesStream, outputStream, function (er) {
81 if (er) return cb(er)
82 if (!anyOutput && !npm.config.get('json') && !npm.config.get('parseable')) {
83 output('No matches found for ' + (args.map(JSON.stringify).join(' ')))
84 }
85 log.silly('search', 'search completed')
86 log.clearProgress()
87 cb(null, {})
88 })
89}
90
91function prepareIncludes (args, searchopts) {
92 if (typeof searchopts !== 'string') searchopts = ''
93 return searchopts.split(/\s+/).concat(args).map(function (s) {
94 return s.toLowerCase()
95 }).filter(function (s) { return s })
96}
97
98function prepareExcludes (searchexclude) {
99 var exclude
100 if (typeof searchexclude === 'string') {
101 exclude = searchexclude.split(/\s+/)
102 } else {
103 exclude = []
104 }
105 return exclude.map(function (s) {
106 return s.toLowerCase()
107 })
108}