Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dtsearch

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dtsearch - npm Package Compare versions

Comparing version 0.1.2 to 1.0.0

demo.gif

21

declarations/javascript-lp-solver.d.ts

@@ -1,1 +0,20 @@

declare module 'javascript-lp-solver/src/solver';
declare module 'javascript-lp-solver/src/solver' {
export interface Model {
opType: 'max';
optimize: string;
constraints: Record<string, { max: number }>;
variables: {[variable: string]: Record<string, number>};
ints?: string[];
}
export interface ResultMeta {
feasible: boolean;
result: number;
bounded: boolean;
isIntegral: boolean;
}
export type Result = ResultMeta & {[variable: string]: number};
export function Solve(model: Model): Result;
}

@@ -84,2 +84,4 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
var chalk_1 = __importDefault(require("chalk"));
var commander_1 = require("commander");
var he_1 = require("he");

@@ -90,2 +92,15 @@ var solver_1 = __importDefault(require("javascript-lp-solver/src/solver"));

var node_fetch_1 = __importDefault(require("node-fetch"));
var version = require('../package.json').version;
commander_1.program
.version(version)
.arguments('<query>')
.option('--npm', 'Output npm install commands')
.option('-y, --yarn', 'Output yarn add commands')
.option('-n, --num <number>', 'Maximum number of results to show', Number, 10)
.option('--repo', 'Show repo URL, even if package specifies a homepage')
.option('--debug', 'Enable debug logging')
.option('--bundled', 'Only show packages with bundled types')
.option('--dt', 'Only show packages with types on DefinitelyTyped (@types)')
.option('-u, --untyped', 'Search all packages, even those without type declarations.')
.parse(process.argv);
var SEARCH_ENDPOINT = 'https://ofcncog2cu-dsn.algolia.net/1/indexes/npm-search';

@@ -103,5 +118,10 @@ var ATTRIBUTES = [

];
var filters = {
default: 'types.ts:"definitely-typed" OR types.ts:"included"',
dt: 'types.ts:"definitely-typed"',
bundled: 'types.ts:"included"',
};
var PARAMS = {
hitsPerPage: 20,
filters: 'types.ts:"definitely-typed" OR types.ts:"included"',
filters: filters.default,
attributes: ATTRIBUTES.join(','),

@@ -127,2 +147,3 @@ 'x-algolia-agent': 'Algolia for vanilla JavaScript (lite) 3.27.1',

format: function (h) { return h.objectID; },
highlight: function (v, h) { return highlightValue(v, h._highlightResult.name); },
importance: 100,

@@ -149,4 +170,15 @@ },

{
header: 'npm',
format: function (h) { return makeInstallCommand('npm install', h); },
importance: -1,
},
{
header: 'yarn',
format: function (h) { return makeInstallCommand('yarn add', h); },
importance: -1,
},
{
header: 'description',
format: function (h) { return he_1.decode(h.description || ''); },
highlight: function (v, h) { return highlightValue(v, h._highlightResult.description); },
maxWidth: 40,

@@ -159,2 +191,3 @@ importance: 25,

format: function (h) { return he_1.decode(h.description || ''); },
highlight: function (v, h) { return highlightValue(v, h._highlightResult.description); },
maxWidth: 60,

@@ -167,2 +200,3 @@ importance: 30,

format: function (h) { return he_1.decode(h.description || ''); },
highlight: function (v, h) { return highlightValue(v, h._highlightResult.description); },
importance: 35,

@@ -172,13 +206,12 @@ mutexGroup: 'desc',

{
header: 'date',
format: function (h) { return moment_1.default(h.modified).format('YYYY-MM-DD'); },
importance: 1,
},
{
header: 'updated',
format: function (h) { return moment_1.default(h.modified).fromNow(); },
importance: 5,
align: 'right',
},
{
header: 'date',
format: function (h) { return moment_1.default(h.modified).format('YYYY-MM-DD'); },
importance: 1,
},
{
header: 'homepage',

@@ -188,3 +221,19 @@ format: function (h) { return h.homepage || (h.repository ? h.repository.url : ''); },

},
{
header: 'repo',
format: function (h) { return h.repository ? h.repository.url : ''; },
importance: -1,
}
];
function makeInstallCommand(cmd, _a) {
var types = _a.types, objectID = _a.objectID;
var install = cmd + " " + objectID;
if (types.ts === 'included') {
return install;
}
else if (types.ts === 'definitely-typed') {
return install + " && " + cmd + " -D " + types.definitelyTyped;
}
return '';
}
function pickColumns(widths) {

@@ -209,3 +258,4 @@ var e_1, _a;

columns.forEach(function (c, i) {
constraints[i] = { max: 1 };
// name is included here just for debugging.
constraints[i] = { max: 1, name: c.header + (c.maxWidth ? '/' + c.maxWidth : '') };
});

@@ -222,3 +272,9 @@ var model = {

};
if (commander_1.program.debug) {
console.log('Column LP model:', model);
}
var result = solver_1.default.Solve(model);
if (commander_1.program.debug) {
console.log('LP result:', result);
}
if (result.feasible) {

@@ -241,2 +297,31 @@ return columns.map(function (c, i) { return result[i] ? i : null; }).filter(isNonNullish);

}
function highlightValue(val, highlightResult) {
var e_2, _a;
if (!highlightResult || highlightResult.matchLevel === 'none') {
return val;
}
else if (highlightResult.fullyHighlighted) {
return chalk_1.default.bold(val);
}
try {
for (var _b = __values(highlightResult.matchedWords), _c = _b.next(); !_c.done; _c = _b.next()) {
var word = _c.value;
val = val.replace(new RegExp(word, 'ig'), chalk_1.default.bold(word));
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
return val;
// Alternatively, this could use highlightResult.value.
// The problem there is that string padding has already happened.
// 'Foo <em>bar</em> baz <em>quux</em>' -->
// [ 'Foo ', '<em>bar', ' baz ', '<em>quux', '' ]
// const parts = highlightResult.value.split(/(<em>.*?)<\/em>/);
// return parts.map(p => p.startsWith('<em>') ? chalk.bold(p.slice(4)) : p).join('');
}
function isNonNullish(x) {

@@ -252,3 +337,3 @@ return x !== null && x !== undefined;

}
function printTable(rows) {
function printTable(rows, hits) {
var cols = columns.map(function (c, j) { return __spread([

@@ -260,3 +345,12 @@ c.header.toUpperCase()

var colIndices = pickColumns(widths);
var pickedCols = colIndices.map(function (i) { return formattedCols[i]; });
var pickedCols = colIndices.map(function (i) {
var spec = columns[i];
var highlight = spec.highlight;
if (!highlight) {
return formattedCols[i];
}
else {
return formattedCols[i].map(function (v, j) { return j > 0 ? highlight(v, hits[j - 1]) : v; });
}
});
var _loop_1 = function (i) {

@@ -270,37 +364,115 @@ var cols_1 = pickedCols.map(function (c) { return c[i]; });

}
function adjustImportance(header, newImportance) {
var e_3, _a;
var adjusted = false;
try {
for (var columns_1 = __values(columns), columns_1_1 = columns_1.next(); !columns_1_1.done; columns_1_1 = columns_1.next()) {
var col = columns_1_1.value;
if (col.header === header) {
col.importance = newImportance;
adjusted = true;
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (columns_1_1 && !columns_1_1.done && (_a = columns_1.return)) _a.call(columns_1);
}
finally { if (e_3) throw e_3.error; }
}
if (!adjusted) {
throw new Error("Unable to find column with header " + header);
}
}
function applyFlags() {
// Add special coluns if the user asks for them.
if (commander_1.program.yarn) {
adjustImportance('yarn', 1000);
}
if (commander_1.program.npm) {
adjustImportance('npm', 1000);
}
if (commander_1.program.yarn || commander_1.program.npm) {
adjustImportance('types', 25);
}
if (commander_1.program.repo) {
adjustImportance('repo', 1000);
adjustImportance('homepage', -1);
}
var flags = ['untyped', 'dt', 'bundled'];
if (lodash_1.default.sum(flags.map(function (flag) { return commander_1.program[flag] ? 1 : 0; })) > 1) {
throw new Error("May only specify one of " + flags);
}
if (commander_1.program.untyped) {
delete PARAMS.filters;
}
else if (commander_1.program.dt) {
PARAMS.filters = filters.dt;
}
else if (commander_1.program.bundled) {
PARAMS.filters = filters.bundled;
}
}
(function () { return __awaiter(void 0, void 0, void 0, function () {
var _a, query, params, _b, _c, _d, k, v, qs, response, result, hits, table;
var e_2, _e;
return __generator(this, function (_f) {
switch (_f.label) {
var query, num, params, _a, _b, _c, k, v, qs, url, startMs, response, elapsedMs, result, hits, table;
var e_4, _d;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
_a = __read(process.argv, 3), query = _a[2];
query = commander_1.program.args.join(' ');
applyFlags();
num = commander_1.program.num;
PARAMS.hitsPerPage = Math.floor(num * 1.5);
params = new URLSearchParams();
try {
for (_b = __values(Object.entries(PARAMS)), _c = _b.next(); !_c.done; _c = _b.next()) {
_d = __read(_c.value, 2), k = _d[0], v = _d[1];
for (_a = __values(Object.entries(PARAMS)), _b = _a.next(); !_b.done; _b = _a.next()) {
_c = __read(_b.value, 2), k = _c[0], v = _c[1];
params.set(k, '' + v);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (_c && !_c.done && (_e = _b.return)) _e.call(_b);
if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
}
finally { if (e_2) throw e_2.error; }
finally { if (e_4) throw e_4.error; }
}
params.set('query', query);
qs = params.toString();
return [4 /*yield*/, node_fetch_1.default(SEARCH_ENDPOINT + "?" + qs)];
url = SEARCH_ENDPOINT + "?" + qs;
if (commander_1.program.debug) {
console.log('Algolia query params:', params);
console.log('Fetching', url);
}
startMs = Date.now();
return [4 /*yield*/, node_fetch_1.default(url)];
case 1:
response = _f.sent();
response = _e.sent();
if (!response.ok) {
throw new Error(response.status + " " + response.statusText);
}
elapsedMs = Date.now() - startMs;
if (commander_1.program.debug) {
console.log('Algolia responded in', elapsedMs, 'ms');
}
return [4 /*yield*/, response.json()];
case 2:
result = _f.sent();
result = _e.sent();
hits = result.hits.filter(function (hit) { return !hit.objectID.startsWith('@types/'); });
table = hits.slice(0, 10).map(formatResult);
printTable(table);
if (hits.length === 0) {
console.log('No results. Try dtsearch -u to include packages without types.');
return [2 /*return*/];
}
if (commander_1.program.debug) {
console.log("Got " + result.hits.length + " results, pared down to " + hits.length);
console.log(result);
}
hits = hits.slice(0, num);
table = hits.map(formatResult);
printTable(table, hits);
if (hits.length < num) {
console.log("\nOnly " + hits.length + " result" + (hits.length > 1 ? 's' : '') + ". " +
"Try dtsearch -u to include packages without types.");
}
return [2 /*return*/];

@@ -307,0 +479,0 @@ }

{
"name": "dtsearch",
"version": "0.1.2",
"description": "Search for npm packages with types, either on Definitely Typed or bundled.",
"version": "1.0.0",
"description": "Find packages with TypeScript types, either bundled or on Definitely Typed",
"keywords": [
"definitelytyped",
"typescript",
"dts",
"types",
"search"
],
"main": "dist/index.js",

@@ -12,2 +19,3 @@ "author": "Dan Vanderkam (danvdk@gmail.com)",

"devDependencies": {
"@types/chalk": "^2.2.0",
"@types/he": "^1.1.1",

@@ -19,2 +27,4 @@ "@types/lodash": "^4.14.149",

"dependencies": {
"chalk": "^3.0.0",
"commander": "^5.0.0",
"he": "^1.2.0",

@@ -21,0 +31,0 @@ "javascript-lp-solver": "^0.4.24",

@@ -5,4 +5,82 @@ # dtsearch

Usage with `npx`:
```
$ npx dtsearch sprintf
DLS NAME TYPES DESCRIPTION
533.3k sprintf @types/sprintf sprintf() for node.js
47.4m sprintf-js @types/sprintf-js JavaScript sprintf implementation
82.9m extsprintf @types/extsprintf extended POSIX-style sprintf
2.1m ssf <bundled> Format data using ECMA-376 spreadsheet Format Codes
1.6m printj <bundled> Pure-JS printf
123k voca @types/voca The ultimate JavaScript string library
746.4k printf <bundled> Full implementation of the `printf` family in pure JS.
1.5k sprintfjs <bundled> POSIX sprintf(3)-style String Formatting for JavaScript
169 @jitesoft/sprintf <bundled> sprintf function for javascript.
94 stringd <bundled> A string variable parser for JavaScript
```
Alternatively, you can install `dtsearch` globally using either:
npm install --global dtsearch
yarn global add dtsearch
You can use `--yarn` or `--npm` to produce copy/pastable commands to depend on packages _and_ their types:
![Demonstration of search for a library and installing it using yarn](demo.gif)
## Background
There are two ways to distribute TypeScript types for a package on npm:
1. With the package itself ("bundled" or "included"). This is common if the package is written in TypeScript, or if the owner is committed to maintaining its type declarations. The tell-tale sign of bundled types is a `typings` entry in `package.json`.
2. As a separate `@types` package on [DefinitelyTyped]. This is more common for packages which are written in plain JavaScript or another language. The type declarations are often written by someone other than the package author.
Both approaches are common and there are many tradeoffs between them.
As a TypeScript user, you'll often find yourself wanting to search for a package that does X and has type declarations (of either form). The usual approach is to search for packages and then check if they have type declarations ([yarnpkg] has recently added TypeScript badges which help with this).
Once you've found a package, you need to run different commands depending on whether it bundles its types or gets them from DefinitelyTyped. For example, using `yarn` and [`moment`][moment]:
yarn add moment # bundled types
# Types on DefinitelyTyped
yarn add moment-timezone
yarn add -D @types/moment-timzeone
`dtsearch` aims to solve these problems with a fast, simple CLI. It lets you search only packages with types and shows you the exact commands you need to run to add them to your project.
## How this works
This uses Algolia's [npm search][2], the same search that you find on [yarnpkg].
## Options
- `-n`, `--num <number>` Maximum number of results to show (default: 10)
- `--npm` Output `npm install` commands
- `-y`, `--yarn` Output yarn add commands
- `--bundled` Only show packages with bundled types
- `--dt` Only show packages with types on DefinitelyTyped (@types)
- `-u`, `--untyped` Search all packages, even those without type declarations.
- `--repo` Show repo URLs, even if package specifies a homepage
- `--debug` Enable debug logging
## Related Work
- The old [`typings search`](https://yarnpkg.com/package/typings) command from c. 2016 (before `@types`).
- Microsoft's [TypeSearch](https://microsoft.github.io/TypeSearch/). Unfortunately this only searches DefinitelyTyped and only searches package names. It does not search bundled types or package descriptions.
- [yarnpkg]'s search. This shows small "TS" icons next to packages with type declarations, either bundled or on DT. It does not surface a filter to search only packages with type declarations, however.
- [pikapkg] lets you search packages with a [`has:types`][pikasearch] filter. This only searches bundled typings; it does not consider types on DT.
## Support
If you like this tool, consider buying my book, [_Effective TypeScript_][ets]. [Chapter 6] and particularly Item 46 ("Understand the Three Versions Involved in Type Declarations") are all about the trials and tribulations of getting TypeScript types for your dependencies.
[DefinitelyTyped]: https://github.com/DefinitelyTyped/DefinitelyTyped
[2]: https://discourse.algolia.com/t/2016-algolia-community-gift-yarn-package-search/319
[moment]: https://momentjs.com/
[yarnpkg]: https://yarnpkg.com/
[pikapkg]: https://www.pika.dev/
[pikasearch]: https://www.pika.dev/search?q=has%3Atypes%20moment
[ets]: https://effectivetypescript.com/
[Chapter 6]: https://effectivetypescript.com/#Chapter-6-Types-Declarations-and-types

@@ -0,1 +1,3 @@

import chalk from 'chalk';
import {program} from 'commander';
import {decode} from 'he';

@@ -6,4 +8,19 @@ import solver from 'javascript-lp-solver/src/solver';

import fetch from 'node-fetch';
import { AlgoliaResponse, Hit } from './response';
import { AlgoliaResponse, Hit, Description } from './response';
const version = require('../package.json').version;
program
.version(version)
.arguments('<query>')
.option('--npm', 'Output npm install commands')
.option('-y, --yarn', 'Output yarn add commands')
.option('-n, --num <number>', 'Maximum number of results to show', Number, 10)
.option('--repo', 'Show repo URL, even if package specifies a homepage')
.option('--debug', 'Enable debug logging')
.option('--bundled', 'Only show packages with bundled types')
.option('--dt', 'Only show packages with types on DefinitelyTyped (@types)')
.option('-u, --untyped', 'Search all packages, even those without type declarations.')
.parse(process.argv);
const SEARCH_ENDPOINT = 'https://ofcncog2cu-dsn.algolia.net/1/indexes/npm-search'

@@ -21,5 +38,10 @@ const ATTRIBUTES = [

];
const filters = {
default: 'types.ts:"definitely-typed" OR types.ts:"included"',
dt: 'types.ts:"definitely-typed"',
bundled: 'types.ts:"included"',
};
const PARAMS = {
hitsPerPage: 20,
filters: 'types.ts:"definitely-typed" OR types.ts:"included"',
filters: filters.default,
attributes: ATTRIBUTES.join(','),

@@ -36,2 +58,3 @@ 'x-algolia-agent': 'Algolia for vanilla JavaScript (lite) 3.27.1',

format: (hit: Hit) => string;
highlight?: (value: string, hit: Hit) => string;
importance: number;

@@ -56,2 +79,3 @@ mutexGroup?: string;

format: h => h.objectID,
highlight: (v, h) => highlightValue(v, h._highlightResult.name),
importance: 100,

@@ -72,4 +96,15 @@ },

{
header: 'npm',
format: h => makeInstallCommand('npm install', h),
importance: -1,
},
{
header: 'yarn',
format: h => makeInstallCommand('yarn add', h),
importance: -1,
},
{
header: 'description',
format: h => decode(h.description || ''),
highlight: (v, h) => highlightValue(v, h._highlightResult.description),
maxWidth: 40,

@@ -82,2 +117,3 @@ importance: 25,

format: h => decode(h.description || ''),
highlight: (v, h) => highlightValue(v, h._highlightResult.description),
maxWidth: 60,

@@ -90,2 +126,3 @@ importance: 30,

format: h => decode(h.description || ''),
highlight: (v, h) => highlightValue(v, h._highlightResult.description),
importance: 35,

@@ -95,13 +132,12 @@ mutexGroup: 'desc',

{
header: 'date',
format: h => moment(h.modified).format('YYYY-MM-DD'),
importance: 1,
},
{
header: 'updated',
format: h => moment(h.modified).fromNow(),
importance: 5,
align: 'right',
},
{
header: 'date',
format: h => moment(h.modified).format('YYYY-MM-DD'),
importance: 1,
},
{
header: 'homepage',

@@ -111,16 +147,32 @@ format: h => h.homepage || (h.repository ? h.repository.url : ''),

},
{
header: 'repo',
format: h => h.repository ? h.repository.url : '',
importance: -1,
}
];
function makeInstallCommand(cmd: string, {types, objectID}: Hit): string {
const install = `${cmd} ${objectID}`;
if (types.ts === 'included') {
return install;
} else if (types.ts === 'definitely-typed') {
return `${install} && ${cmd} -D ${types.definitelyTyped}`;
}
return '';
}
function pickColumns(widths: number[]): number[] {
const mutexGroups = new Set(columns.map(c => c.mutexGroup).filter(isNonNullish));
const constraints = {width: {max: 1 + (process.stdout.columns || 80)}};
const constraints: solver.Model['constraints'] = {width: {max: 1 + (process.stdout.columns || 80)}};
const mutexes = [...mutexGroups.keys()];
for (const mutex of mutexes) {
(constraints as any)[mutex] = {max: 1};
constraints[mutex] = {max: 1};
}
columns.forEach((c, i) => {
(constraints as any)[i] = {max: 1};
// name is included here just for debugging.
(constraints as any)[i] = {max: 1, name: c.header + (c.maxWidth ? '/' + c.maxWidth : '')};
});
const model = {
const model: solver.Model = {
opType: 'max',

@@ -141,3 +193,10 @@ optimize: 'importance',

if (program.debug) {
console.log('Column LP model:', model);
}
const result = solver.Solve(model);
if (program.debug) {
console.log('LP result:', result);
}
if (result.feasible) {

@@ -164,2 +223,21 @@ return columns.map((c, i) => result[i] ? i : null).filter(isNonNullish);

function highlightValue(val: string, highlightResult: Description | null) {
if (!highlightResult || highlightResult.matchLevel === 'none') {
return val;
} else if (highlightResult.fullyHighlighted) {
return chalk.bold(val);
}
for (const word of highlightResult.matchedWords) {
val = val.replace(new RegExp(word, 'ig'), chalk.bold(word));
}
return val;
// Alternatively, this could use highlightResult.value.
// The problem there is that string padding has already happened.
// 'Foo <em>bar</em> baz <em>quux</em>' -->
// [ 'Foo ', '<em>bar', ' baz ', '<em>quux', '' ]
// const parts = highlightResult.value.split(/(<em>.*?)<\/em>/);
// return parts.map(p => p.startsWith('<em>') ? chalk.bold(p.slice(4)) : p).join('');
}
function isNonNullish<T>(x: T | null | undefined): x is T {

@@ -173,3 +251,3 @@ return x !== null && x !== undefined;

function printTable(rows: string[][]) {
function printTable(rows: string[][], hits: Hit[]) {
const cols = columns.map((c, j) => [

@@ -181,3 +259,11 @@ c.header.toUpperCase(), ...rows.map(r => r[j])

const colIndices = pickColumns(widths);
const pickedCols = colIndices.map(i => formattedCols[i]);
const pickedCols = colIndices.map(i => {
const spec = columns[i];
const {highlight} = spec;
if (!highlight) {
return formattedCols[i];
} else {
return formattedCols[i].map((v, j) => j > 0 ? highlight(v, hits[j - 1]) : v)
}
});

@@ -190,5 +276,54 @@ for (let i = 0; i <= rows.length; i++) {

function adjustImportance(header: string, newImportance: number) {
let adjusted = false;
for (const col of columns) {
if (col.header === header) {
col.importance = newImportance;
adjusted = true;
}
}
if (!adjusted) {
throw new Error(`Unable to find column with header ${header}`);
}
}
function applyFlags() {
// Add special coluns if the user asks for them.
if (program.yarn) {
adjustImportance('yarn', 1000);
}
if (program.npm) {
adjustImportance('npm', 1000);
}
if (program.yarn || program.npm) {
adjustImportance('types', 25);
}
if (program.repo) {
adjustImportance('repo', 1000);
adjustImportance('homepage', -1);
}
const flags = ['untyped', 'dt', 'bundled'];
if (_.sum(flags.map(flag => program[flag] ? 1 : 0)) > 1) {
throw new Error(`May only specify one of ${flags}`);
}
if (program.untyped) {
delete PARAMS.filters;
} else if (program.dt) {
PARAMS.filters = filters.dt;
} else if (program.bundled) {
PARAMS.filters = filters.bundled;
}
}
(async () => {
const [, , query] = process.argv;
const query = program.args.join(' ');
applyFlags();
// Overfetch a bit in case there are @types results.
const {num} = program;
PARAMS.hitsPerPage = Math.floor(num * 1.5);
const params = new URLSearchParams();

@@ -200,14 +335,43 @@ for (const [k, v] of Object.entries(PARAMS)) {

const qs = params.toString();
const url = `${SEARCH_ENDPOINT}?${qs}`;
const response = await fetch(`${SEARCH_ENDPOINT}?${qs}`);
if (program.debug) {
console.log('Algolia query params:', params);
console.log('Fetching', url);
}
const startMs = Date.now();
const response = await fetch(url);
if (!response.ok) {
throw new Error(`${response.status} ${response.statusText}`);
}
const elapsedMs = Date.now() - startMs;
if (program.debug) {
console.log('Algolia responded in', elapsedMs, 'ms');
}
const result: AlgoliaResponse = await response.json();
const hits = result.hits.filter(hit => !hit.objectID.startsWith('@types/'));
const table = hits.slice(0, 10).map(formatResult);
printTable(table);
let hits = result.hits.filter(hit => !hit.objectID.startsWith('@types/'));
if (hits.length === 0) {
console.log('No results. Try dtsearch -u to include packages without types.');
return;
}
if (program.debug) {
console.log(`Got ${result.hits.length} results, pared down to ${hits.length}`);
console.log(result);
}
hits = hits.slice(0, num);
const table = hits.map(formatResult);
printTable(table, hits);
if (hits.length < num) {
console.log(
`\nOnly ${hits.length} result${hits.length > 1 ? 's' : ''}. ` +
`Try dtsearch -u to include packages without types.`
);
}
})().catch(e => {
console.error(e);
});

3

src/response.ts

@@ -50,2 +50,3 @@ export interface AlgoliaResponse {

export interface Description {
/** Looks like "foo <em>bar</em> baz <em>quux</em>" */
value: string;

@@ -57,3 +58,3 @@ matchLevel: MatchLevel;

export type MatchLevel = "full" | "none";
export type MatchLevel = "full" | "partial" | "none";

@@ -60,0 +61,0 @@ export interface Owner {

@@ -0,5 +1,7 @@

To-do:
- [x] Show GitHub repo if homepage is missing
- [x] Fill width of terminal
- [x] Add a binary
- [ ] Highlight matching terms
- [x] Highlight matching terms
- [ ] Distribute it

@@ -9,12 +11,19 @@ - [x] Check on the name (dtsearch is good)

- [ ] Fill out the readme
- [ ] Flag parsing
- [ ] -y/--yarn and -n/--npm
- [ ] Number of results
- [ ] Allow results w/o types
- [x] Flag parsing
- [x] -y/--yarn and -n/--npm
- [x] Number of results
- [x] Allow results w/o types
- [ ] Nits
- [ ] Only use the flame character if the terminal supports it
- [ ] Exclude "POP" column if nothing is popular
- [x] Show "for untyped results, use -u"
- [x] Unescape html entities (dateformat / &#39;)
- [x] Refactor columns + formatting
- [ ] Only use the flame character if the terminal supports it
- [ ] Add a "no results" output
- [x] Add a "no results" output
Punt:
- [ ] Get my own Algolia API key (they only offer a 14 day free trial)
- [ ] Count emojis as double-wide for width (dtsearch --num 20 solar)
This seems hard, see https://github.com/xtermjs/xterm.js/pull/2568
Alternatively, uses curses to write text at a specific position.

@@ -50,2 +50,3 @@ {

"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "resolveJsonModule": true,
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */

@@ -52,0 +53,0 @@ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc