1 | 'use strict'
|
2 |
|
3 | module.exports = exports = search
|
4 |
|
5 | var npm = require('./npm.js')
|
6 | var allPackageSearch = require('./search/all-package-search')
|
7 | var esearch = require('./search/esearch.js')
|
8 | var formatPackageStream = require('./search/format-package-stream.js')
|
9 | var usage = require('./utils/usage')
|
10 | var output = require('./utils/output.js')
|
11 | var log = require('npmlog')
|
12 | var ms = require('mississippi')
|
13 |
|
14 | search.usage = usage(
|
15 | 'search',
|
16 | 'npm search [--long] [search terms ...]'
|
17 | )
|
18 |
|
19 | search.completion = function (opts, cb) {
|
20 | cb(null, [])
|
21 | }
|
22 |
|
23 | function 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 |
|
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 |
|
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 |
|
65 |
|
66 | var outputStream = formatPackageStream({
|
67 | args: args,
|
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 |
|
91 | function 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 |
|
98 | function 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 | }
|