1- const { stripVTControlCharacters } = require ( 'node:util' )
1+ /* eslint-disable max-len */
2+ const { stripVTControlCharacters : strip } = require ( 'node:util' )
23const { Minipass } = require ( 'minipass' )
3- const columnify = require ( 'columnify' )
44
55// This module consumes package data in the following format:
66//
@@ -16,14 +16,48 @@ const columnify = require('columnify')
1616// The returned stream will format this package data
1717// into a byte stream of formatted, displayable output.
1818
19- module . exports = async ( opts ) => {
20- return opts . json ? new JSONOutputStream ( ) : new TextOutputStream ( opts )
19+ function filter ( data , exclude ) {
20+ const words = [ data . name ]
21+ . concat ( data . maintainers . map ( m => m . username ) )
22+ . concat ( data . keywords || [ ] )
23+ . map ( f => f ?. trim ?. ( ) )
24+ . filter ( Boolean )
25+ . join ( ' ' )
26+ . toLowerCase ( )
27+
28+ if ( exclude . find ( pattern => {
29+ // Treats both /foo and /foo/ as regex searches
30+ if ( pattern . startsWith ( '/' ) ) {
31+ if ( pattern . endsWith ( '/' ) ) {
32+ pattern = pattern . slice ( 0 , - 1 )
33+ }
34+ return words . match ( new RegExp ( pattern . slice ( 1 ) ) )
35+ }
36+ return words . includes ( pattern )
37+ } ) ) {
38+ return false
39+ }
40+
41+ return true
42+ }
43+
44+ module . exports = ( opts ) => {
45+ return opts . json ? new JSONOutputStream ( opts ) : new TextOutputStream ( opts )
2146}
2247
2348class JSONOutputStream extends Minipass {
2449 #didFirst = false
50+ #exclude
51+
52+ constructor ( opts ) {
53+ super ( )
54+ this . #exclude = opts . exclude
55+ }
2556
2657 write ( obj ) {
58+ if ( ! filter ( obj , this . #exclude) ) {
59+ return
60+ }
2761 if ( ! this . #didFirst) {
2862 super . write ( '[\n' )
2963 this . #didFirst = true
@@ -41,94 +75,96 @@ class JSONOutputStream extends Minipass {
4175}
4276
4377class TextOutputStream extends Minipass {
44- #opts
45- #line = 0
78+ #args
79+ #chalk
80+ #exclude
81+ #parseable
4682
4783 constructor ( opts ) {
4884 super ( )
49- this . #opts = opts
85+ this . #args = opts . args . map ( s => s . toLowerCase ( ) ) . filter ( Boolean )
86+ this . #chalk = opts . npm . chalk
87+ this . #exclude = opts . exclude
88+ this . #parseable = opts . parseable
5089 }
5190
52- write ( pkg ) {
53- return super . write ( this . #prettify ( pkg ) )
54- }
55-
56- #prettify ( data ) {
91+ write ( data ) {
92+ if ( ! filter ( data , this . #exclude ) ) {
93+ return
94+ }
95+ // Normalize
5796 const pkg = {
58- author : data . maintainers . map ( ( m ) => `=${ stripVTControlCharacters ( m . username ) } ` ) . join ( ' ' ) ,
59- date : 'prehistoric' ,
60- description : stripVTControlCharacters ( data . description ?? '' ) ,
61- keywords : '' ,
62- name : stripVTControlCharacters ( data . name ) ,
97+ authors : data . maintainers . map ( ( m ) => `${ strip ( m . username ) } ` ) . join ( ' ' ) ,
98+ publisher : strip ( data . publisher . username ) ,
99+ date : data . date ? data . date . toISOString ( ) . slice ( 0 , 10 ) : 'prehistoric' ,
100+ description : strip ( data . description ?? '' ) ,
101+ keywords : [ ] ,
102+ name : strip ( data . name ) ,
63103 version : data . version ,
64104 }
65105 if ( Array . isArray ( data . keywords ) ) {
66- pkg . keywords = data . keywords . map ( ( k ) => stripVTControlCharacters ( k ) ) . join ( ' ' )
106+ pkg . keywords = data . keywords . map ( strip )
67107 } else if ( typeof data . keywords === 'string' ) {
68- pkg . keywords = stripVTControlCharacters ( data . keywords . replace ( / [ , \s ] + / , ' ' ) )
69- }
70- if ( data . date ) {
71- pkg . date = data . date . toISOString ( ) . split ( 'T' ) [ 0 ] // remove time
108+ pkg . keywords = strip ( data . keywords . replace ( / [ , \s ] + / , ' ' ) ) . split ( ' ' )
72109 }
73110
74- const columns = [ 'name' , 'description' , 'author' , 'date' , 'version' , 'keywords' ]
75- if ( this . #opts. parseable ) {
76- return columns . map ( ( col ) => pkg [ col ] && ( '' + pkg [ col ] ) . replace ( / \t / g, ' ' ) ) . join ( '\t' )
111+ let output
112+ if ( this . #parseable) {
113+ output = [ pkg . name , pkg . description , pkg . author , pkg . date , pkg . version , pkg . keywords ]
114+ . filter ( Boolean )
115+ . map ( col => ( '' + col ) . replace ( / \t / g, ' ' ) ) . join ( '\t' )
116+ return super . write ( output )
77117 }
78118
79- // stdout in tap is never a tty
80- /* istanbul ignore next */
81- const maxWidth = process . stdout . isTTY ? process . stdout . getWindowSize ( ) [ 0 ] : Infinity
82- let output = columnify (
83- [ pkg ] ,
84- {
85- include : columns ,
86- showHeaders : ++ this . #line <= 1 ,
87- columnSplitter : ' | ' ,
88- truncate : ! this . #opts. long ,
89- config : {
90- name : { minWidth : 25 , maxWidth : 25 , truncate : false , truncateMarker : '' } ,
91- description : { minWidth : 20 , maxWidth : 20 } ,
92- author : { minWidth : 15 , maxWidth : 15 } ,
93- date : { maxWidth : 11 } ,
94- version : { minWidth : 8 , maxWidth : 8 } ,
95- keywords : { maxWidth : Infinity } ,
96- } ,
119+ const keywords = pkg . keywords . map ( k => {
120+ if ( this . #args. includes ( k ) ) {
121+ return this . #chalk. cyan ( k )
122+ } else {
123+ return k
124+ }
125+ } ) . join ( ' ' )
126+
127+ let description = [ ]
128+ for ( const arg of this . #args) {
129+ const finder = pkg . description . toLowerCase ( ) . split ( arg . toLowerCase ( ) )
130+ let p = 0
131+ for ( const f of finder ) {
132+ description . push ( pkg . description . slice ( p , p + f . length ) )
133+ const word = pkg . description . slice ( p + f . length , p + f . length + arg . length )
134+ description . push ( this . #chalk. cyan ( word ) )
135+ p += f . length + arg . length
97136 }
98- ) . split ( '\n' ) . map ( line => line . slice ( 0 , maxWidth ) ) . join ( '\n' )
99-
100- if ( ! this . #opts. color ) {
101- return output
102137 }
103-
104- const colors = [ '31m' , '33m' , '32m' , '36m' , '34m' , '35m' ]
105-
106- this . #opts. args . forEach ( ( arg , i ) => {
107- const markStart = String . fromCharCode ( i % colors . length + 1 )
108- const markEnd = String . fromCharCode ( 0 )
109-
110- if ( arg . charAt ( 0 ) === '/' ) {
111- output = output . replace (
112- new RegExp ( arg . slice ( 1 , - 1 ) , 'gi' ) ,
113- bit => `${ markStart } ${ bit } ${ markEnd } `
114- )
115- } else {
116- // just a normal string, do the split/map thing
138+ description = description . filter ( Boolean )
139+ let name = pkg . name
140+ if ( this . #args. includes ( pkg . name ) ) {
141+ name = this . #chalk. cyan ( pkg . name )
142+ } else {
143+ name = [ ]
144+ for ( const arg of this . #args) {
145+ const finder = pkg . name . toLowerCase ( ) . split ( arg . toLowerCase ( ) )
117146 let p = 0
118-
119- output = output . toLowerCase ( ) . split ( arg . toLowerCase ( ) ) . map ( piece => {
120- piece = output . slice ( p , p + piece . length )
121- p += piece . length
122- const mark = `${ markStart } ${ output . slice ( p , p + arg . length ) } ${ markEnd } `
123- p += arg . length
124- return `${ piece } ${ mark } `
125- } ) . join ( '' )
147+ for ( const f of finder ) {
148+ name . push ( pkg . name . slice ( p , p + f . length ) )
149+ const word = pkg . name . slice ( p + f . length , p + f . length + arg . length )
150+ name . push ( this . #chalk. cyan ( word ) )
151+ p += f . length + arg . length
152+ }
126153 }
127- } )
154+ name = this . #chalk. blue ( name . join ( '' ) )
155+ }
128156
129- for ( let i = 1 ; i <= colors . length ; i ++ ) {
130- output = output . split ( String . fromCharCode ( i ) ) . join ( `\u001B[${ colors [ i - 1 ] } ` )
157+ if ( description . length ) {
158+ output = `${ name } \n${ description . join ( '' ) } \n`
159+ } else {
160+ output = `${ name } \n`
161+ }
162+ output += `Version ${ this . #chalk. blue ( pkg . version ) } published ${ this . #chalk. blue ( pkg . date ) } by ${ this . #chalk. blue ( pkg . publisher ) } \n`
163+ output += `Maintainers: ${ pkg . authors } \n`
164+ if ( keywords ) {
165+ output += `Keywords: ${ keywords } \n`
131166 }
132- return output . split ( '\u0000' ) . join ( '\u001B[0m' ) . trim ( )
167+ output += `${ this . #chalk. blue ( `https://npm.im/${ pkg . name } ` ) } \n`
168+ return super . write ( output )
133169 }
134170}
0 commit comments