diff --git a/package.json b/package.json index 26a48ba..20cccbd 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "inquirer": "^8.2.0", "inquirer-autocomplete-prompt": "^1.4.0", "progress": "^2.0.3", + "table": "^6.8.1", "tar": "^6.1.11", "tempy": "^1.0.1", "tslib": "^2.3.1", diff --git a/src/docsets.ts b/src/docsets.ts index 4e1d2ba..89ee0c6 100644 --- a/src/docsets.ts +++ b/src/docsets.ts @@ -54,7 +54,11 @@ export async function getAvailableDocsets(mirror?: string): Promise { }); } -export async function downloadDocset(docset: Docset, metadata: Metadata): Promise { +export async function downloadDocset( + docset: Docset, + metadata: Metadata, + showProgress: boolean = true, +): Promise { return new Promise((resolve, reject) => { // By default a random url is chosen, just like how Zeal would download a docset // If a mirror is specified with --mirror, metadata.urls will only contain one url @@ -68,13 +72,19 @@ export async function downloadDocset(docset: Docset, metadata: Metadata): Promis .on('error', err => reject(err)); logger.info(`Downloading docset from ${archiveUrl}`); - const bar = logger.progress(); - - got - .stream(archiveUrl) - .on('downloadProgress', progress => bar.update(progress.percent)) - .on('error', err => reject(err)) - .pipe(writeStream); + if (showProgress) { + const bar = logger.progress(); + got + .stream(archiveUrl) + .on('downloadProgress', progress => bar.update(progress.percent)) + .on('error', err => reject(err)) + .pipe(writeStream); + } else { + got + .stream(archiveUrl) + .on('error', err => reject(err)) + .pipe(writeStream); + } }); } diff --git a/src/index.ts b/src/index.ts index 5d1b7b3..2df966a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import { Command, Option } from 'commander'; import * as fs from 'fs-extra'; import * as inquirer from 'inquirer'; +import { table, getBorderCharacters } from 'table'; import { Docset, downloadDocset, extractDocset, getAvailableDocsets } from './docsets'; import { saveIcons } from './icons'; import { logger } from './logger'; @@ -38,32 +39,71 @@ async function selectDocset(mirror?: string): Promise { return availableDocsets.find(docset => docset.name === selectedName); } +async function listAllDocsets(mirror?: string): Promise { + const availableDocsets = await getAvailableDocsets(mirror); + const docsetsTable = [['Name', 'ID']]; + availableDocsets + .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())) + .forEach(docset => docsetsTable.push([docset.name, docset.id])); + + const output = table(docsetsTable, { + border: getBorderCharacters('void'), + columnDefault: { + paddingLeft: 0, + paddingRight: 1, + }, + singleLine: true, + drawHorizontalLine: () => false, + }); + + console.log(output.trim()); +} + async function runWithOptions(options: any): Promise { let docsetsDirectory: string = options.outputDirectory; if (docsetsDirectory === undefined) { docsetsDirectory = await getDocsetsDirectory(); } - const docset = await selectDocset(); - const docsetDirectory = path.resolve(docsetsDirectory, `${docset.id}.docset`); + if (options.listAll) { + await listAllDocsets(); + return; + } + + let docsetsToInstall: Docset[] = []; + + if (options.install) { + const availableDocsets = await getAvailableDocsets(options.mirror); + docsetsToInstall = availableDocsets.filter(docset => options.install.includes(docset.id)); + } else { + const docset = await selectDocset(options.mirror); + docsetsToInstall = [docset]; + } + + const tasks = docsetsToInstall.map(async docset => { + const docsetDirectory = path.resolve(docsetsDirectory, `${docset.id}.docset`); - if (fs.existsSync(docsetDirectory)) { - if (!options.force) { - throw new Error(`${docsetDirectory} already exists, use --force to overwrite it`); + if (fs.existsSync(docsetDirectory)) { + if (!options.force) { + throw new Error(`${docsetDirectory} already exists, use --force to overwrite it`); + } + + logger.warn(`Removing existing docset at ${docsetDirectory}`); + fs.removeSync(docsetDirectory); } - logger.warn(`Removing existing docset at ${docsetDirectory}`); - fs.removeSync(docsetDirectory); - } + const metadata = getMetadata(docset, options.mirror); - const metadata = getMetadata(docset, options.mirror); + const tempPath = await downloadDocset(docset, metadata, docsetsToInstall.length === 1); + await extractDocset(tempPath, docsetDirectory); + saveIcons(docset, docsetDirectory); + saveMetadata(metadata, docsetDirectory); + + logger.success(`Successfully added the ${docset.name} docset to Zeal`); + }); - const tempPath = await downloadDocset(docset, metadata); - await extractDocset(tempPath, docsetDirectory); - saveIcons(docset, docsetDirectory); - saveMetadata(metadata, docsetDirectory); + await Promise.allSettled(tasks); - logger.success(`Successfully added the ${docset.name} docset to Zeal`); logger.info('If Zeal is running, make sure to restart it for the docset to show up'); } @@ -76,7 +116,9 @@ export async function run(): Promise { availableMirrors, ), ) + .option('-l, --list-all', 'List all available docsets without a pager') .option('-o, --output-directory ', "path to Zeal's docsets directory, overriding the default search for it") + .option('-i, --install ', 'Install packages for the provided IDs') .option('-f, --force', 'overwrite existing docsets') .parse(process.argv); diff --git a/yarn.lock b/yarn.lock index b9daaee..3082776 100644 --- a/yarn.lock +++ b/yarn.lock @@ -400,6 +400,16 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" @@ -2307,6 +2317,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -2492,6 +2507,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + lodash.zip@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" @@ -3333,6 +3353,11 @@ registry-url@^5.0.0, registry-url@^5.1.0: dependencies: rc "^1.2.8" +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + resolve-alpn@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" @@ -3661,7 +3686,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.2.2: +string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3813,6 +3838,17 @@ symbol-observable@^3.0.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-3.0.0.tgz#eea8f6478c651018e059044268375c408c15c533" integrity sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q== +table@^6.8.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tar@^6.1.11: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"