11cb0ef41Sopenharmony_ciconst { Minipass } = require('minipass')
21cb0ef41Sopenharmony_ciconst Pipeline = require('minipass-pipeline')
31cb0ef41Sopenharmony_ciconst libSearch = require('libnpmsearch')
41cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim.js')
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ciconst formatSearchStream = require('../utils/format-search-stream.js')
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_cifunction filter (data, include, exclude) {
91cb0ef41Sopenharmony_ci  const words = [data.name]
101cb0ef41Sopenharmony_ci    .concat(data.maintainers.map(m => `=${m.username}`))
111cb0ef41Sopenharmony_ci    .concat(data.keywords || [])
121cb0ef41Sopenharmony_ci    .map(f => f && f.trim && f.trim())
131cb0ef41Sopenharmony_ci    .filter(f => f)
141cb0ef41Sopenharmony_ci    .join(' ')
151cb0ef41Sopenharmony_ci    .toLowerCase()
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci  if (exclude.find(e => match(words, e))) {
181cb0ef41Sopenharmony_ci    return false
191cb0ef41Sopenharmony_ci  }
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci  return true
221cb0ef41Sopenharmony_ci}
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_cifunction match (words, pattern) {
251cb0ef41Sopenharmony_ci  if (pattern.startsWith('/')) {
261cb0ef41Sopenharmony_ci    if (pattern.endsWith('/')) {
271cb0ef41Sopenharmony_ci      pattern = pattern.slice(0, -1)
281cb0ef41Sopenharmony_ci    }
291cb0ef41Sopenharmony_ci    pattern = new RegExp(pattern.slice(1))
301cb0ef41Sopenharmony_ci    return words.match(pattern)
311cb0ef41Sopenharmony_ci  }
321cb0ef41Sopenharmony_ci  return words.indexOf(pattern) !== -1
331cb0ef41Sopenharmony_ci}
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js')
361cb0ef41Sopenharmony_ciclass Search extends BaseCommand {
371cb0ef41Sopenharmony_ci  static description = 'Search for packages'
381cb0ef41Sopenharmony_ci  static name = 'search'
391cb0ef41Sopenharmony_ci  static params = [
401cb0ef41Sopenharmony_ci    'long',
411cb0ef41Sopenharmony_ci    'json',
421cb0ef41Sopenharmony_ci    'color',
431cb0ef41Sopenharmony_ci    'parseable',
441cb0ef41Sopenharmony_ci    'description',
451cb0ef41Sopenharmony_ci    'searchopts',
461cb0ef41Sopenharmony_ci    'searchexclude',
471cb0ef41Sopenharmony_ci    'registry',
481cb0ef41Sopenharmony_ci    'prefer-online',
491cb0ef41Sopenharmony_ci    'prefer-offline',
501cb0ef41Sopenharmony_ci    'offline',
511cb0ef41Sopenharmony_ci  ]
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci  static usage = ['[search terms ...]']
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci  async exec (args) {
561cb0ef41Sopenharmony_ci    const opts = {
571cb0ef41Sopenharmony_ci      ...this.npm.flatOptions,
581cb0ef41Sopenharmony_ci      ...this.npm.flatOptions.search,
591cb0ef41Sopenharmony_ci      include: args.map(s => s.toLowerCase()).filter(s => s),
601cb0ef41Sopenharmony_ci      exclude: this.npm.flatOptions.search.exclude.split(/\s+/),
611cb0ef41Sopenharmony_ci    }
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci    if (opts.include.length === 0) {
641cb0ef41Sopenharmony_ci      throw new Error('search must be called with arguments')
651cb0ef41Sopenharmony_ci    }
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci    // Used later to figure out whether we had any packages go out
681cb0ef41Sopenharmony_ci    let anyOutput = false
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci    class FilterStream extends Minipass {
711cb0ef41Sopenharmony_ci      constructor () {
721cb0ef41Sopenharmony_ci        super({ objectMode: true })
731cb0ef41Sopenharmony_ci      }
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci      write (pkg) {
761cb0ef41Sopenharmony_ci        if (filter(pkg, opts.include, opts.exclude)) {
771cb0ef41Sopenharmony_ci          super.write(pkg)
781cb0ef41Sopenharmony_ci        }
791cb0ef41Sopenharmony_ci      }
801cb0ef41Sopenharmony_ci    }
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci    const filterStream = new FilterStream()
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci    // Grab a configured output stream that will spit out packages in the desired format.
851cb0ef41Sopenharmony_ci    const outputStream = await formatSearchStream({
861cb0ef41Sopenharmony_ci      args, // --searchinclude options are not highlighted
871cb0ef41Sopenharmony_ci      ...opts,
881cb0ef41Sopenharmony_ci    })
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci    log.silly('search', 'searching packages')
911cb0ef41Sopenharmony_ci    const p = new Pipeline(
921cb0ef41Sopenharmony_ci      libSearch.stream(opts.include, opts),
931cb0ef41Sopenharmony_ci      filterStream,
941cb0ef41Sopenharmony_ci      outputStream
951cb0ef41Sopenharmony_ci    )
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci    p.on('data', chunk => {
981cb0ef41Sopenharmony_ci      if (!anyOutput) {
991cb0ef41Sopenharmony_ci        anyOutput = true
1001cb0ef41Sopenharmony_ci      }
1011cb0ef41Sopenharmony_ci      this.npm.output(chunk.toString('utf8'))
1021cb0ef41Sopenharmony_ci    })
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci    await p.promise()
1051cb0ef41Sopenharmony_ci    if (!anyOutput && !this.npm.config.get('json') && !this.npm.config.get('parseable')) {
1061cb0ef41Sopenharmony_ci      this.npm.output('No matches found for ' + (args.map(JSON.stringify).join(' ')))
1071cb0ef41Sopenharmony_ci    }
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ci    log.silly('search', 'search completed')
1101cb0ef41Sopenharmony_ci    log.clearProgress()
1111cb0ef41Sopenharmony_ci  }
1121cb0ef41Sopenharmony_ci}
1131cb0ef41Sopenharmony_cimodule.exports = Search
114