diff options
Diffstat (limited to 'lib/search.js')
-rw-r--r-- | lib/search.js | 194 |
1 files changed, 102 insertions, 92 deletions
diff --git a/lib/search.js b/lib/search.js index 6dc1c027e..a029d62eb 100644 --- a/lib/search.js +++ b/lib/search.js @@ -1,18 +1,18 @@ module.exports = exports = search -var npm = require("./npm.js") - , columnify = require("columnify") - , updateIndex = require("./cache/update-index.js") +var npm = require('./npm.js') +var columnify = require('columnify') +var updateIndex = require('./cache/update-index.js') -search.usage = "npm search [--long] [search terms ...]" - + "\n\naliases: s, se" +search.usage = 'npm search [--long] [search terms ...]' + + '\n\naliases: s, se' search.completion = function (opts, cb) { var compl = {} - , partial = opts.partialWord - , ipartial = partial.toLowerCase() - , plen = partial.length + var partial = opts.partialWord + var ipartial = partial.toLowerCase() + var plen = partial.length // get the batch of data that matches so far. // this is an example of using npm.commands.search programmatically @@ -20,7 +20,7 @@ search.completion = function (opts, cb) { search(opts.conf.argv.remain.slice(2), true, function (er, data) { if (er) return cb(er) Object.keys(data).forEach(function (name) { - data[name].words.split(" ").forEach(function (w) { + data[name].words.split(' ').forEach(function (w) { if (w.toLowerCase().indexOf(ipartial) === 0) { compl[partial + w.substr(plen)] = true } @@ -31,19 +31,25 @@ search.completion = function (opts, cb) { } function search (args, silent, staleness, cb) { - if (typeof cb !== "function") cb = staleness, staleness = 600 - if (typeof cb !== "function") cb = silent, silent = false + if (typeof cb !== 'function') { + cb = staleness + staleness = 600 + } + if (typeof cb !== 'function') { + cb = silent + silent = false + } - var searchopts = npm.config.get("searchopts") - var searchexclude = npm.config.get("searchexclude") + var searchopts = npm.config.get('searchopts') + var searchexclude = npm.config.get('searchexclude') - if (typeof searchopts !== "string") searchopts = "" + if (typeof searchopts !== 'string') searchopts = '' searchopts = searchopts.split(/\s+/) var opts = searchopts.concat(args).map(function (s) { return s.toLowerCase() }).filter(function (s) { return s }) - if (typeof searchexclude === "string") { + if (typeof searchexclude === 'string') { searchexclude = searchexclude.split(/\s+/) } else { searchexclude = [] @@ -74,7 +80,7 @@ function filter (data, args, notArgs) { return Object.keys(data).map(function (d) { return data[d] }).filter(function (d) { - return typeof d === "object" + return typeof d === 'object' }).map(stripData).map(getWords).filter(function (data) { return filterWords(data, args, notArgs) }).reduce(function (l, r) { @@ -84,51 +90,52 @@ function filter (data, args, notArgs) { } function stripData (data) { - return { name: data.name - , description: npm.config.get("description") ? data.description : "" - , maintainers: (data.maintainers || []).map(function (m) { - return "=" + m.name - }) - , url: !Object.keys(data.versions || {}).length ? data.url : null - , keywords: data.keywords || [] - , version: Object.keys(data.versions || {})[0] || [] - , time: data.time - && data.time.modified - && (new Date(data.time.modified).toISOString() - .split("T").join(" ") - .replace(/:[0-9]{2}\.[0-9]{3}Z$/, "")) - .slice(0, -5) // remove time - || "prehistoric" - } + return { + name: data.name, + description: npm.config.get('description') ? data.description : '', + maintainers: (data.maintainers || []).map(function (m) { + return '=' + m.name + }), + url: !Object.keys(data.versions || {}).length ? data.url : null, + keywords: data.keywords || [], + version: Object.keys(data.versions || {})[0] || [], + time: data.time && + data.time.modified && + (new Date(data.time.modified).toISOString() // remove time + .split('T').join(' ') + .replace(/:[0-9]{2}\.[0-9]{3}Z$/, '')) + .slice(0, -5) || + 'prehistoric' + } } function getWords (data) { data.words = [ data.name ] .concat(data.description) .concat(data.maintainers) - .concat(data.url && ("<" + data.url + ">")) + .concat(data.url && ('<' + data.url + '>')) .concat(data.keywords) .map(function (f) { return f && f.trim && f.trim() }) .filter(function (f) { return f }) - .join(" ") + .join(' ') .toLowerCase() return data } function filterWords (data, args, notArgs) { var words = data.words - for (var i = 0, l = args.length; i < l; i ++) { + for (var i = 0, l = args.length; i < l; i++) { if (!match(words, args[i])) return false } - for (i = 0, l = notArgs.length; i < l; i ++) { + for (i = 0, l = notArgs.length; i < l; i++) { if (match(words, notArgs[i])) return false } return true } function match (words, arg) { - if (arg.charAt(0) === "/") { - arg = arg.replace(/\/$/, "") + if (arg.charAt(0) === '/') { + arg = arg.replace(/\/$/, '') arg = new RegExp(arg.substr(1, arg.length - 1)) return words.match(arg) } @@ -136,68 +143,71 @@ function match (words, arg) { } function prettify (data, args) { - var searchsort = (npm.config.get("searchsort") || "NAME").toLowerCase() - , sortField = searchsort.replace(/^\-+/, "") - , searchRev = searchsort.charAt(0) === "-" - , truncate = !npm.config.get("long") + var searchsort = (npm.config.get('searchsort') || 'NAME').toLowerCase() + var sortField = searchsort.replace(/^\-+/, '') + var searchRev = searchsort.charAt(0) === '-' + var truncate = !npm.config.get('long') if (Object.keys(data).length === 0) { - return "No match found for "+(args.map(JSON.stringify).join(" ")) + return 'No match found for ' + (args.map(JSON.stringify).join(' ')) } var lines = Object.keys(data).map(function (d) { // strip keyname return data[d] - }).map(function(dat) { + }).map(function (dat) { dat.author = dat.maintainers delete dat.maintainers dat.date = dat.time delete dat.time return dat - }).map(function(dat) { + }).map(function (dat) { // split keywords on whitespace or , - if (typeof dat.keywords === "string") { + if (typeof dat.keywords === 'string') { dat.keywords = dat.keywords.split(/[,\s]+/) } if (Array.isArray(dat.keywords)) { - dat.keywords = dat.keywords.join(" ") + dat.keywords = dat.keywords.join(' ') } // split author on whitespace or , - if (typeof dat.author === "string") { + if (typeof dat.author === 'string') { dat.author = dat.author.split(/[,\s]+/) } if (Array.isArray(dat.author)) { - dat.author = dat.author.join(" ") + dat.author = dat.author.join(' ') } return dat }) - lines.sort(function(a, b) { + lines.sort(function (a, b) { var aa = a[sortField].toLowerCase() - , bb = b[sortField].toLowerCase() + var bb = b[sortField].toLowerCase() return aa === bb ? 0 : aa < bb ? -1 : 1 }) if (searchRev) lines.reverse() - var columns = npm.config.get("description") - ? ["name", "description", "author", "date", "version", "keywords"] - : ["name", "author", "date", "version", "keywords"] - - var output = columnify(lines, { - include: columns - , truncate: truncate - , config: { - name: { maxWidth: 40, truncate: false, truncateMarker: "" } - , description: { maxWidth: 60 } - , author: { maxWidth: 20 } - , date: { maxWidth: 11 } - , version: { maxWidth: 11 } - , keywords: { maxWidth: Infinity } - } - }) + var columns = npm.config.get('description') + ? ['name', 'description', 'author', 'date', 'version', 'keywords'] + : ['name', 'author', 'date', 'version', 'keywords'] + + var output = columnify( + lines, + { + include: columns, + truncate: truncate, + config: { + name: { maxWidth: 40, truncate: false, truncateMarker: '' }, + description: { maxWidth: 60 }, + author: { maxWidth: 20 }, + date: { maxWidth: 11 }, + version: { maxWidth: 11 }, + keywords: { maxWidth: Infinity } + } + } + ) output = trimToMaxWidth(output) output = highlightSearchTerms(output, args) @@ -205,63 +215,63 @@ function prettify (data, args) { } var colors = [31, 33, 32, 36, 34, 35 ] - , cl = colors.length +var cl = colors.length function addColorMarker (str, arg, i) { var m = i % cl + 1 - , markStart = String.fromCharCode(m) - , markEnd = String.fromCharCode(0) - - if (arg.charAt(0) === "/") { - //arg = arg.replace(/\/$/, "") - return str.replace( new RegExp(arg.substr(1, arg.length - 2), "gi") - , function (bit) { return markStart + bit + markEnd } ) - + var markStart = String.fromCharCode(m) + var markEnd = String.fromCharCode(0) + + if (arg.charAt(0) === '/') { + return str.replace( + new RegExp(arg.substr(1, arg.length - 2), 'gi'), + function (bit) { return markStart + bit + markEnd } + ) } // just a normal string, do the split/map thing var pieces = str.toLowerCase().split(arg.toLowerCase()) - , p = 0 + var p = 0 return pieces.map(function (piece) { piece = str.substr(p, piece.length) - var mark = markStart - + str.substr(p+piece.length, arg.length) - + markEnd + var mark = markStart + + str.substr(p + piece.length, arg.length) + + markEnd p += piece.length + arg.length return piece + mark - }).join("") + }).join('') } function colorize (line) { - for (var i = 0; i < cl; i ++) { + for (var i = 0; i < cl; i++) { var m = i + 1 - var color = npm.color ? "\033["+colors[i]+"m" : "" + var color = npm.color ? '\u001B[' + colors[i] + 'm' : '' line = line.split(String.fromCharCode(m)).join(color) } - var uncolor = npm.color ? "\033[0m" : "" - return line.split("\u0000").join(uncolor) + var uncolor = npm.color ? '\u001B[0m' : '' + return line.split('\u0000').join(uncolor) } -function getMaxWidth() { +function getMaxWidth () { var cols try { - var tty = require("tty") - , stdout = process.stdout + var tty = require('tty') + var stdout = process.stdout cols = !tty.isatty(stdout.fd) ? Infinity : process.stdout.getWindowSize()[0] cols = (cols === 0) ? Infinity : cols } catch (ex) { cols = Infinity } return cols } -function trimToMaxWidth(str) { +function trimToMaxWidth (str) { var maxWidth = getMaxWidth() - return str.split("\n").map(function(line) { + return str.split('\n').map(function (line) { return line.slice(0, maxWidth) - }).join("\n") + }).join('\n') } -function highlightSearchTerms(str, terms) { +function highlightSearchTerms (str, terms) { terms.forEach(function (arg, i) { str = addColorMarker(str, arg, i) }) |