Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(search): properly display multiple search terms #7980

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 24 additions & 27 deletions lib/utils/format-search-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ class TextOutputStream extends Minipass {

constructor (opts) {
super()
this.#args = opts.args.map(s => s.toLowerCase()).filter(Boolean)
// Consider a search for "cowboys" and "boy". If we highlight "boys" first the "cowboys" string will no longer string match because of the ansi highlighting added to "boys". If we highlight "boy" second then the ansi reset at the end will make the highlighting only on "cowboy" with a normal "s". Neither is perfect but at least the first option doesn't do partial highlighting. So, we sort strings smaller to larger
this.#args = opts.args.map(s => s.toLowerCase()).filter(Boolean).sort((a, b) => a.length - b.length)
this.#chalk = opts.npm.chalk
this.#exclude = opts.exclude
this.#parseable = opts.parseable
Expand Down Expand Up @@ -124,38 +125,17 @@ class TextOutputStream extends Minipass {
}
}).join(' ')

let description = []
for (const arg of this.#args) {
const finder = pkg.description.toLowerCase().split(arg.toLowerCase())
let p = 0
for (const f of finder) {
description.push(pkg.description.slice(p, p + f.length))
const word = pkg.description.slice(p + f.length, p + f.length + arg.length)
description.push(this.#chalk.cyan(word))
p += f.length + arg.length
}
}
description = description.filter(Boolean)
let name = pkg.name
const description = this.#highlight(pkg.description)
let name
if (this.#args.includes(pkg.name)) {
name = this.#chalk.cyan(pkg.name)
} else {
name = []
for (const arg of this.#args) {
const finder = pkg.name.toLowerCase().split(arg.toLowerCase())
let p = 0
for (const f of finder) {
name.push(pkg.name.slice(p, p + f.length))
const word = pkg.name.slice(p + f.length, p + f.length + arg.length)
name.push(this.#chalk.cyan(word))
p += f.length + arg.length
}
}
name = this.#chalk.blue(name.join(''))
name = this.#highlight(pkg.name)
name = this.#chalk.blue(name)
}

if (description.length) {
output = `${name}\n${description.join('')}\n`
output = `${name}\n${description}\n`
} else {
output = `${name}\n`
}
Expand All @@ -171,4 +151,21 @@ class TextOutputStream extends Minipass {
output += `${this.#chalk.blue(`https://npm.im/${pkg.name}`)}\n`
return super.write(output)
}

#highlight (input) {
let output = input
for (const arg of this.#args) {
let i = output.toLowerCase().indexOf(arg)
while (i > -1) {
const highlit = this.#chalk.cyan(output.slice(i, i + arg.length))
output = [
output.slice(0, i),
highlit,
output.slice(i + arg.length),
].join('')
i = output.toLowerCase().indexOf(arg, i + highlit.length)
}
}
return output
}
}
146 changes: 146 additions & 0 deletions tap-snapshots/test/lib/commands/search.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,152 @@ Maintainers: lukekarrys
https://npm.im/pkg-no-desc
`

exports[`test/lib/commands/search.js TAP search multiple terms --color > should have expected search results with color 1`] = `
libnpm
Collection of programmatic APIs for the npm CLI
Version 3.0.1 published 2019-07-16 by isaacs
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: npm api package manager lib
https://npm.im/libnpm
libnpmaccess
programmatic library for \`npm access\` commands
Version 4.0.1 published 2020-11-03 by nlf
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: libnpmaccess
https://npm.im/libnpmaccess
@evocateur/libnpmaccess
programmatic library for \`npm access\` commands
Version 3.1.2 published 2019-07-16 by evocateur
Maintainers: evocateur
https://npm.im/@evocateur/libnpmaccess
@evocateur/libnpmpublish
Programmatic API for the bits behind npm publish and unpublish
Version 1.2.2 published 2019-07-16 by evocateur
Maintainers: evocateur
https://npm.im/@evocateur/libnpmpublish
libnpmorg
Programmatic api for \`npm org\` commands
Version 2.0.1 published 2020-11-03 by nlf
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: libnpm npm package manager api orgs teams
https://npm.im/libnpmorg
libnpmsearch
Programmatic API for searching in npm and compatible registries.
Version 3.1.0 published 2020-12-08 by isaacs
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: npm search api libnpm
https://npm.im/libnpmsearch
libnpmteam
npm Team management APIs
Version 2.0.2 published 2020-11-03 by nlf
Maintainers: nlf ruyadorno darcyclarke isaacs
https://npm.im/libnpmteam
libnpmpublish
Programmatic API for the bits behind npm publish and unpublish
Version 4.0.0 published 2020-11-03 by nlf
Maintainers: nlf ruyadorno darcyclarke isaacs
https://npm.im/libnpmpublish
libnpmfund
Programmatic API for npm fund
Version 1.0.2 published 2020-12-08 by isaacs
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: npm npmcli libnpm cli git fund gitfund
https://npm.im/libnpmfund
@npmcli/map-workspaces
Retrieves a name:pathname Map for a given workspaces config
Version 1.0.1 published 2020-09-30 by ruyadorno
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: npm bad map npmcli libnpm cli workspaces map-workspaces
https://npm.im/@npmcli/map-workspaces
libnpmversion
library to do the things that 'npm version' does
Version 1.0.7 published 2020-11-04 by isaacs
Maintainers: nlf ruyadorno darcyclarke isaacs
https://npm.im/libnpmversion
@types/libnpmsearch
TypeScript definitions for libnpmsearch
Version 2.0.1 published 2019-09-26 by types
Maintainers: types
https://npm.im/@types/libnpmsearch
pkg-no-desc
Version 1.0.0 published 2019-09-26 by lukekarrys
Maintainers: lukekarrys
https://npm.im/pkg-no-desc
`

exports[`test/lib/commands/search.js TAP search multiple terms text > should have expected search results 1`] = `
libnpm
Collection of programmatic APIs for the npm CLI
Version 3.0.1 published 2019-07-16 by isaacs
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: npm api package manager lib
https://npm.im/libnpm
libnpmaccess
programmatic library for \`npm access\` commands
Version 4.0.1 published 2020-11-03 by nlf
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: libnpmaccess
https://npm.im/libnpmaccess
@evocateur/libnpmaccess
programmatic library for \`npm access\` commands
Version 3.1.2 published 2019-07-16 by evocateur
Maintainers: evocateur
https://npm.im/@evocateur/libnpmaccess
@evocateur/libnpmpublish
Programmatic API for the bits behind npm publish and unpublish
Version 1.2.2 published 2019-07-16 by evocateur
Maintainers: evocateur
https://npm.im/@evocateur/libnpmpublish
libnpmorg
Programmatic api for \`npm org\` commands
Version 2.0.1 published 2020-11-03 by nlf
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: libnpm npm package manager api orgs teams
https://npm.im/libnpmorg
libnpmsearch
Programmatic API for searching in npm and compatible registries.
Version 3.1.0 published 2020-12-08 by isaacs
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: npm search api libnpm
https://npm.im/libnpmsearch
libnpmteam
npm Team management APIs
Version 2.0.2 published 2020-11-03 by nlf
Maintainers: nlf ruyadorno darcyclarke isaacs
https://npm.im/libnpmteam
libnpmpublish
Programmatic API for the bits behind npm publish and unpublish
Version 4.0.0 published 2020-11-03 by nlf
Maintainers: nlf ruyadorno darcyclarke isaacs
https://npm.im/libnpmpublish
libnpmfund
Programmatic API for npm fund
Version 1.0.2 published 2020-12-08 by isaacs
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: npm npmcli libnpm cli git fund gitfund
https://npm.im/libnpmfund
@npmcli/map-workspaces
Retrieves a name:pathname Map for a given workspaces config
Version 1.0.1 published 2020-09-30 by ruyadorno
Maintainers: nlf ruyadorno darcyclarke isaacs
Keywords: npm bad map npmcli libnpm cli workspaces map-workspaces
https://npm.im/@npmcli/map-workspaces
libnpmversion
library to do the things that 'npm version' does
Version 1.0.7 published 2020-11-04 by isaacs
Maintainers: nlf ruyadorno darcyclarke isaacs
https://npm.im/libnpmversion
@types/libnpmsearch
TypeScript definitions for libnpmsearch
Version 2.0.1 published 2019-09-26 by types
Maintainers: types
https://npm.im/@types/libnpmsearch
pkg-no-desc
Version 1.0.0 published 2019-09-26 by lukekarrys
Maintainers: lukekarrys
https://npm.im/pkg-no-desc
`

exports[`test/lib/commands/search.js TAP search no publisher > should have filtered expected search results 1`] = `
custom-registry
Version 1.0.0 published prehistoric by ???
Expand Down
24 changes: 24 additions & 0 deletions test/lib/commands/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ t.test('search', t => {
t.matchSnapshot(joinedOutput(), 'should have expected search results')
})

t.test('multiple terms text', async t => {
const { npm, joinedOutput } = await loadMockNpm(t)
const registry = new MockRegistry({
tap: t,
registry: npm.config.get('registry'),
})

registry.search({ results: libnpmsearchResultFixture })
await npm.exec('search', ['libnpm', 'publish'])
t.matchSnapshot(joinedOutput(), 'should have expected search results')
})

t.test('<name> --json', async t => {
const { npm, joinedOutput } = await loadMockNpm(t, { config: { json: true } })
const registry = new MockRegistry({
Expand Down Expand Up @@ -68,6 +80,18 @@ t.test('search', t => {
t.matchSnapshot(joinedOutput(), 'should have expected search results with color')
})

t.test('multiple terms --color', async t => {
const { npm, joinedOutput } = await loadMockNpm(t, { config: { color: 'always' } })
const registry = new MockRegistry({
tap: t,
registry: npm.config.get('registry'),
})

registry.search({ results: libnpmsearchResultFixture })
await npm.exec('search', ['libnpm', 'publish'])
t.matchSnapshot(joinedOutput(), 'should have expected search results with color')
})

t.test('/<name>/--color', async t => {
const { npm, joinedOutput } = await loadMockNpm(t, { config: { color: 'always' } })
const registry = new MockRegistry({
Expand Down
Loading