From 98de9cb8f4aef596b8d2bf254d6551ee4cf96c0c Mon Sep 17 00:00:00 2001 From: "K.B.Dharun Krishna" Date: Tue, 14 Nov 2023 17:14:25 +0530 Subject: [PATCH] feat (3.4.0): add platform option, Android and BSD platforms (#421) * feat: add platform option, Android platform Signed-off-by: K.B.Dharun Krishna * feat/test: update code, add alias for old options Signed-off-by: K.B.Dharun Krishna * feat: rename platform lib to platforms Signed-off-by: K.B.Dharun Krishna * Update bin/tldr Co-authored-by: Matthew Peveler * feat: add support for OpenBSD and update code Signed-off-by: K.B.Dharun Krishna * fix: add back updated platform options Signed-off-by: K.B.Dharun Krishna * cleanup: update files Signed-off-by: K.B.Dharun Krishna * Update bin/completion/bash/tldr Co-authored-by: Matthew Peveler * feat: add support for FreeBSD and NetBSD Signed-off-by: K.B.Dharun Krishna * cleanup: rearrange options, prepare for 3.4.0 Signed-off-by: K.B.Dharun Krishna * README: minor fixes Signed-off-by: K.B.Dharun Krishna * Fix URL-encoded email highlighting Signed-off-by: K.B.Dharun Krishna * cleanup: add release CI, drop Node 14 support Signed-off-by: K.B.Dharun Krishna * cleanup: remove not working alias option Signed-off-by: K.B.Dharun Krishna --------- Signed-off-by: K.B.Dharun Krishna Co-authored-by: Matthew Peveler --- .github/workflows/publish.yml | 24 +++++++ .github/workflows/test.yml | 2 +- README.md | 43 +++++++------ bin/completion/bash/tldr | 15 +++-- bin/completion/zsh/_tldr | 17 +++-- bin/tldr | 42 +++++-------- lib/cache.js | 6 +- lib/config.js | 10 +-- lib/index.js | 20 +++--- lib/{platform.js => platforms.js} | 18 ++++-- lib/render.js | 12 +++- lib/search.js | 8 +-- lib/tldr.js | 16 +++-- package-lock.json | 9 ++- package.json | 10 ++- test/cache.spec.js | 33 +++++++--- test/functional-test.sh | 10 +-- test/index.spec.js | 101 ++++++++++++++++++++---------- test/platform.spec.js | 24 ++++--- 19 files changed, 261 insertions(+), 159 deletions(-) create mode 100644 .github/workflows/publish.yml rename lib/{platform.js => platforms.js} (70%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..c9994338 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,24 @@ +# This workflow automatically publishes the package to NPM when a new release is created. +# Before, creating a new release, make sure to update the package version in package.json +# and add a Granular Access Token (with read and write packages scope) +# to the repository secrets with the name NPM_TOKEN. +# Once, the release has been published remove it from the repository secrets. + +name: Publish Package to NPM +on: + release: + types: [published] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # Setup .npmrc file to publish to npm + - uses: actions/setup-node@v4 + with: + node-version: '20.x' + registry-url: 'https://registry.npmjs.org' + - run: npm ci + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0081cbd5..ea024644 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: ${{ matrix.os }}-npm- - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/README.md b/README.md index 935135ad..a06260ae 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,19 @@ npm install -g tldr To see tldr pages: - `tldr ` show examples for this command -- `tldr --os=` show command page for the given platform (`linux`, `osx`, `sunos`, `windows`) -- `tldr --search ""` search all pages for the query +- `tldr --platform=` show command page for the given platform +- `tldr --android ` show command page for Android +- `tldr --darwin ` show command page for darwin (macOS) +- `tldr --freebsd ` show command page for FreeBSD - `tldr --linux ` show command page for Linux -- `tldr --osx ` show command page for OSX +- `tldr --macos ` show command page for macOS +- `tldr --netbsd ` show command page for NetBSD +- `tldr --openbsd ` show command page for OpenBSD +- `tldr --osx ` show command page for osx (macOS) - `tldr --sunos ` show command page for SunOS +- `tldr --win32 ` show command page for win32 (Windows) - `tldr --windows ` show command page for Windows +- `tldr --search ""` search all pages for the query - `tldr --list` show all pages for current platform - `tldr --list-all` show all available pages - `tldr --random` show a page at random @@ -91,7 +98,7 @@ you can put it in the config file: The default platform value can be overwritten with command-line option: ```shell -tldr du --os=osx +tldr du --platform= ``` As a contributor, you can also point to your own fork containing the `tldr.zip` file. The file is just a zipped version of the entire tldr repo: @@ -122,7 +129,7 @@ It's easiest for [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh) users, so let's start with that. -``` +```zsh mkdir -p $ZSH_CUSTOM/plugins/tldr ln -s bin/completion/zsh/_tldr $ZSH_CUSTOM/plugins/tldr/_tldr ``` @@ -131,13 +138,13 @@ Then add tldr to your oh-my-zsh plugins, usually defined in `~/.zshrc`, resulting in something looking like this: -``` +```zsh plugins=(git tmux tldr) ``` Alternatively, using [zplug](https://github.com/zplug/zplug) -``` +```zsh zplug "tldr-pages/tldr-node-client", use:bin/completion/zsh ``` @@ -147,29 +154,29 @@ Copy or symlink `bin/completion/zsh/_tldr` to (note the filename). Then add the containing directory to your fpath: -``` +```zsh fpath=(my/completions $fpath) ``` ### Bash -``` +```bash ln -s bin/completion/bash/tldr ~/.tldr-completion.bash ``` Now add the following line to our bashrc file: -``` +```bash source ~/.tldr-completion.bash ``` ## FAQ -#### Installation Issues +### Installation Issues - If you are trying to install as non-root user (`npm install -g tldr`) and get something like: -``` +```text Error: EACCES: permission denied, access '/usr/local/lib/node_modules/tldr' ``` @@ -177,7 +184,7 @@ Then most probably your npm's default installation directory has improper permis - If you are trying to install as a root user (`sudo npm install -g tldr`) and get something like: -``` +```shell as root -> gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/local/lib/node_modules/tldr/node_modules/webworker-threads/.node-gyp" gyp WARN EACCES user "root" does not have permission to access the dev dir "/usr/local/lib/node_modules/tldr/node_modules/webworker-threads/.node-gyp/8.9.1" @@ -187,14 +194,14 @@ You need to add the option `--unsafe-perm` to your command. This is because when - If you see an error related to `webworker-threads` like: -``` +```text /usr/local/lib/node_modules/tldr/node_modules/natural/lib/natural/classifiers/classifier.js:32 if (e.code !== 'MODULE_NOT_FOUND') throw e; ``` Most probably you need to reinstall `node-gyp` and `webworker-threads`. Try this - -``` +```shell sudo -H npm uninstall -g tldr sudo -H npm uninstall -g webworker-threads npm install -g node-gyp @@ -236,10 +243,6 @@ for a few rough guidelines. [npm-url]: https://www.npmjs.com/package/tldr [npm-image]: https://img.shields.io/npm/v/tldr.svg [gh-actions-url]: https://github.com/tldr-pages/tldr-node-client/actions?query=workflow%3ATest+branch%3Amaster -[gh-actions-image]: https://img.shields.io/github/actions/workflow/status/tldr-pages/tldr-node-client/test.yml?branch=master -[dep-url]: https://david-dm.org/tldr-pages/tldr-node-client -[dep-image]: https://david-dm.org/tldr-pages/tldr-node-client.svg?theme=shields.io -[dev-dep-url]: https://david-dm.org/tldr-pages/tldr-node-client#info=devDependencies -[dev-dep-image]: https://david-dm.org/tldr-pages/tldr-node-client/dev-status.svg?theme=shields.io +[gh-actions-image]: https://img.shields.io/github/actions/workflow/status/tldr-pages/tldr-node-client/test.yml?branch=main [matrix-url]: https://matrix.to/#/#tldr-pages:matrix.org [matrix-image]: https://img.shields.io/matrix/tldr-pages:matrix.org?label=chat+on+matrix diff --git a/bin/completion/bash/tldr b/bin/completion/bash/tldr index d47a23c2..860b4788 100644 --- a/bin/completion/bash/tldr +++ b/bin/completion/bash/tldr @@ -2,7 +2,7 @@ BUILTIN_THEMES="single base16 ocean" -OS_TYPES="linux osx sunos windows" +PLATFORM_TYPES="android freebsd linux netbsd openbsd osx sunos windows" OPTIONS='-v --version @@ -20,10 +20,17 @@ OPTIONS='-v --render -m --markdown --o +-p +--android +--darwin +--freebsd --linux +--macos +--netbsd +--openbsd --osx --sunos +--win32 --windows -t --theme @@ -56,8 +63,8 @@ function _tldr_autocomplete { COMPREPLY=(`compgen -f $cur`) ;; - -o|--os) - COMPREPLY=(`compgen -W "$OS_TYPES" $cur`) + -p|--platform) + COMPREPLY=(`compgen -W "$PLATFORM_TYPES" $cur`) ;; -t|--theme) diff --git a/bin/completion/zsh/_tldr b/bin/completion/zsh/_tldr index 027e13e5..ad1e9ba6 100644 --- a/bin/completion/zsh/_tldr +++ b/bin/completion/zsh/_tldr @@ -1,8 +1,8 @@ #compdef tldr -local -a pages oses +local -a pages platforms pages=$(tldr -a1) -oses='( linux osx sunos windows )' +platforms='( android freebsd linux netbsd openbsd osx sunos windows )' _arguments \ '(- *)'{-h,--help}'[show help]' \ @@ -15,11 +15,18 @@ _arguments \ '(- *)'{-e,--random-example}'[show a random example]' \ '(- *)'{-m,--markdown}'[show the original markdown format page]' \ '(-f --render)'{-f,--render}'[render a specific markdown file]:markdown file:_files -/' \ - '(-o --os)'{-o,--os}"[override operating system]:os:${oses}" \ + '(-p --platform)'{-p,--platform}"[override platform]:platform:(${(j:|:)platforms})" \ + '(- *)'{-u,--update}'[update local cache]' \ + '--android[override operating system with Android]' \ + '--darwin[override operating system with macOS]' \ + '--freebsd[override operating system with FreeBSD]' \ '--linux[override operating system with Linux]' \ - '--osx[override operating system with OSX]' \ + '--macos[override operating system with macOS]' \ + '--netbsd[override operating system with NetBSD]' \ + '--openbsd[override operating system with OpenBSD]' \ + '--osx[override operating system with macOS]' \ '--sunos[override operating system with SunOS]' \ + '--win32[override operating system with Windows]' \ '--windows[override operating system with Windows]' \ - '(- *)'{-u,--update}'[update local cache]' \ '(- *)'{-c,--clear-cache}'[clear local cache]' \ "*:page:(${(b)pages})" && return 0 diff --git a/bin/tldr b/bin/tldr index f0f5c743..705d6ebc 100755 --- a/bin/tldr +++ b/bin/tldr @@ -4,9 +4,11 @@ const program = require('commander'); const pkg = require('../package'); const Tldr = require('../lib/tldr'); const config = require('../lib/config'); -const platform = require('../lib/platform'); +const platforms = require('../lib/platforms'); const { TldrError } = require('../lib/errors'); +pkg.version = `v${pkg.version}\nClient Specification: 2.0`; + program .version(pkg.version, '-v, --version', 'Display version') .helpOption('-h, --help', 'Show this help message') @@ -22,11 +24,13 @@ program .option('-e, --random-example', 'Show a random example') .option('-f, --render [file]', 'Render a specific markdown [file]') .option('-m, --markdown', 'Output in markdown format') - .option('-o, --os [type]', 'Override the operating system [linux, osx, sunos, windows]') - .option('--linux', 'Override the operating system with Linux') - .option('--osx', 'Override the operating system with OSX') - .option('--sunos', 'Override the operating system with SunOS') - .option('--windows', 'Override the operating system with Windows') + .option('-p, --platform [type]', `Override the current platform [${platforms.supportedPlatforms.join(', ')}]`); + +for (const platform of platforms.supportedPlatforms) { + program.option(`--${platform}`, `Override the platform with ${platform}`); +} + +program .option('-t, --theme [theme]', 'Color theme (simple, base16, ocean)') .option('-s, --search [keywords]', 'Search pages using keywords') // @@ -39,7 +43,7 @@ const help = ` Examples: $ tldr tar - $ tldr du --os=linux + $ tldr du --platform=linux $ tldr --search "create symbolic link to file" $ tldr --list $ tldr --list-all @@ -62,27 +66,15 @@ program.on('--help', () => { program.parse(process.argv); -if (program.linux) { - program.os = 'linux'; -} - -if (program.osx) { - program.os = 'osx'; -} - -if (program.sunos) { - program.os = 'sunos'; -} - -if (program.windows) { - program.os = 'windows'; +for (const platform of platforms.supportedPlatforms) { + if (program[platform]) { + program.platform = platform; + } } let cfg = config.get(); -if (program.os) { - if (platform.isSupported(program.os)) { - cfg.platform = program.os; - } +if (program.platform && platforms.isSupported(program.platform)) { + cfg.platform = program.platform; } if (program.theme) { diff --git a/lib/cache.js b/lib/cache.js index f5bbefdf..fab21480 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -4,14 +4,12 @@ const fs = require('fs-extra'); const os = require('os'); const path = require('path'); const remote = require('./remote'); -const platform = require('./platform'); +const platforms = require('./platforms'); const index = require('./index'); const utils = require('./utils'); class Cache { constructor(config) { - // TODO: replace this with a private field when it reaches enough maturity - // https://github.com/tc39/proposal-class-fields#private-fields this.config = config; this.cacheFolder = path.join(config.cache, 'cache'); } @@ -21,7 +19,7 @@ class Cache { } getPage(page) { - let preferredPlatform = platform.getPreferredPlatformFolder(this.config); + let preferredPlatform = platforms.getPreferredPlatformFolder(this.config); const preferredLanguage = process.env.LANG || 'en'; return index.findPage(page, preferredPlatform, preferredLanguage) .then((folder) => { diff --git a/lib/config.js b/lib/config.js index 0f2eb529..34c76276 100644 --- a/lib/config.js +++ b/lib/config.js @@ -5,6 +5,7 @@ const fs = require('fs'); const path = require('path'); const utils = require('./utils'); const osHomedir = require('os').homedir; +const platforms = require('./platforms'); exports.get = () => { const DEFAULT = path.join(__dirname, '..', 'config.json'); @@ -55,10 +56,9 @@ exports.get = () => { return merged; }; -function validatePlatform(os) { - let platform = require('./platform'); - if (os && !platform.isSupported(os)) { - return 'Unsupported platform : ' + os; +function validatePlatform(platform) { + if (platform && !platforms.isSupported(platform)) { + return 'Unsupported platform : ' + platform; } return null; } @@ -118,4 +118,4 @@ function validateThemeItem(field, key) { return null; } return errMsg.join('\n'); -} \ No newline at end of file +} diff --git a/lib/index.js b/lib/index.js index d116b5b5..74c9faca 100644 --- a/lib/index.js +++ b/lib/index.js @@ -59,12 +59,12 @@ function findPage(page, preferredPlatform, preferredLanguage) { targetPlatform = 'common'; } else if (targets.length > 0 && hasLang(targets, preferredLanguage)) { targetLanguage = preferredLanguage; - targetPlatform = targets[0].os; - console.log(`Command ${page} does not exist for the host platform. Displaying the page from ${targets[0].os} platform`); + targetPlatform = targets[0].platform; + console.log(`Command ${page} does not exist for the host platform. Displaying the page from ${targetPlatform} platform`); } else if (targets.length > 0 && hasLang(targets, 'en')) { targetLanguage = 'en'; - targetPlatform = targets[0].os; - console.log(`Command ${page} does not exist for the host platform. Displaying the page from ${targets[0].os} platform`); + targetPlatform = targets[0].platform; + console.log(`Command ${page} does not exist for the host platform. Displaying the page from ${targetPlatform} platform`); } if (!targetLanguage && !targetPlatform) { @@ -81,7 +81,7 @@ function findPage(page, preferredPlatform, preferredLanguage) { function hasPlatformLang(targets, preferredPlatform, preferredLanguage) { return targets.some((t) => { - return t.os === preferredPlatform && t.language === preferredLanguage; + return t.platform === preferredPlatform && t.language === preferredLanguage; }); } @@ -116,7 +116,7 @@ function commandsFor(platform) { let commands = Object.keys(idx) .filter((cmd) => { let targets = idx[cmd].targets; - let platforms = targets.map((t) => {return t.os;}); + let platforms = targets.map((t) => {return t.platform;}); return platforms.indexOf(platform) !== -1 || platforms.indexOf('common') !== -1; }) .sort(); @@ -187,24 +187,24 @@ function buildShortPagesIndex() { .then((files) => { files = files.filter(utils.isPage); let reducer = (index, file) => { - let os = utils.parsePlatform(file); + let platform = utils.parsePlatform(file); let page = utils.parsePagename(file); let language = utils.parseLanguage(file); if (index[page]) { let targets = index[page].targets; let needsPush = true; for (const target of targets) { - if (target.platform === os && target.language === language) { + if (target.platform === platform && target.language === language) { needsPush = false; continue; } } if (needsPush) { - targets.push({'os': os, 'language': language}); + targets.push({ platform, language }); index[page].targets = targets; } } else { - index[page] = {targets: [{'os': os, 'language': language}]}; + index[page] = {targets: [{ platform, language }]}; } return index; diff --git a/lib/platform.js b/lib/platforms.js similarity index 70% rename from lib/platform.js rename to lib/platforms.js index 6401eb96..2e5b8e50 100644 --- a/lib/platform.js +++ b/lib/platforms.js @@ -3,17 +3,24 @@ const os = require('os'); const folders = { - 'osx': 'osx', + 'android': 'android', 'darwin': 'osx', + 'freebsd': 'freebsd', 'linux': 'linux', + 'macos': 'osx', + 'netbsd': 'netbsd', + 'openbsd': 'openbsd', + 'osx': 'osx', 'sunos': 'sunos', - 'windows': 'windows', - 'win32': 'windows' + 'win32': 'windows', + 'windows': 'windows' }; +const supportedPlatforms = Object.keys(folders); + // Check if the platform is there in the list of platforms or not function isSupported(platform) { - return Object.prototype.hasOwnProperty.call(folders, platform); + return supportedPlatforms.includes(platform); } // If the platform given in config is present, return that. @@ -35,5 +42,6 @@ function getPreferredPlatformFolder(config) { module.exports = { isSupported, getPreferredPlatform, - getPreferredPlatformFolder + getPreferredPlatformFolder, + supportedPlatforms }; diff --git a/lib/render.js b/lib/render.js index b26b0295..aa0dd852 100644 --- a/lib/render.js +++ b/lib/render.js @@ -1,6 +1,7 @@ 'use strict'; const Theme = require('./theme'); +const he = require('he'); // Import the 'he' library // The page structure is passed to this function, and then the theme is applied // to different parts of the page and rendered to the console @@ -24,18 +25,23 @@ exports.toANSI = (page, config) => { }, ''); } + function decodeEntities(text) { + return he.decode(text); // Decode HTML entities + } + // Creating an array where each line is an element in it let output = []; // Pushing each line by extracting the page parts and applying the theme to it output.push(' ' + theme.renderCommandName(page.name)); output.push(''); - output.push(' ' + theme.renderMainDescription(page.description.replace(/\n/g, '\n '))); + output.push(' ' + theme.renderMainDescription(decodeEntities(page.description.replace(/mailto:/g, '')).replace(/\n/g, '\n '))); // Decode entities and remove "mailto:" prefix in the description output.push(''); page.examples.forEach((example) => { - output.push(theme.renderExampleDescription(' - ' + example.description)); - output.push(highlight(example.code)); + // Decode entities and remove "mailto:" prefix in the description and code + output.push(theme.renderExampleDescription(' - ' + decodeEntities(example.description.replace(/mailto:/g, '')))); + output.push(highlight(decodeEntities(example.code.replace(/mailto:/g, '')))); output.push(''); }); diff --git a/lib/search.js b/lib/search.js index 12900540..ef4b65a1 100644 --- a/lib/search.js +++ b/lib/search.js @@ -7,7 +7,7 @@ const natural = require('natural'); const config = require('./config'); const utils = require('./utils'); const index = require('./index'); -const platform = require('./platform'); +const platforms = require('./platforms'); const CACHE_FOLDER = path.join(config.get().cache, 'cache'); @@ -189,16 +189,16 @@ exports.printResults = (results, config) => { // Prints the passed results to the console. // If the command is not available for the current platform, // it lists the available platforms instead. - // Example: tldr --search print directory tree --os sunos prints: + // Example: tldr --search print directory tree --platform sunos prints: // $ tree (Available on: linux, osx) index.getShortIndex().then((shortIndex) => { let outputs = new Set(); - let preferredPlatform = platform.getPreferredPlatform(config); + let preferredPlatform = platforms.getPreferredPlatform(config); results.forEach((elem) => { let cmdname = utils.parsePagename(elem.file); let output = ' $ ' + cmdname; let targets = shortIndex[cmdname]['targets']; - let platforms = targets.map((t) => {return t.os;}); + let platforms = targets.map((t) => {return t.platform;}); if (platforms.indexOf('common') === -1 && platforms.indexOf(preferredPlatform) === -1) { output += ' (Available on: ' + platforms.join(', ') + ')'; } diff --git a/lib/tldr.js b/lib/tldr.js index 27b1adbd..5b06ed36 100644 --- a/lib/tldr.js +++ b/lib/tldr.js @@ -7,7 +7,7 @@ const ora = require('ora'); const { EmptyCacheError, MissingPageError, MissingRenderPathError } = require('./errors'); const Cache = require('./cache'); const search = require('./search'); -const platform = require('./platform'); +const platforms = require('./platforms'); const parser = require('./parser'); const render = require('./render'); const index = require('./index'); @@ -15,15 +15,13 @@ const index = require('./index'); class Tldr { constructor(config) { - // TODO: replace this with a private field when it reaches enough maturity - // https://github.com/tc39/proposal-class-fields#private-fields this.config = config; this.cache = new Cache(this.config); } list(singleColumn) { - let os = platform.getPreferredPlatformFolder(this.config); - return index.commandsFor(os) + let platform = platforms.getPreferredPlatformFolder(this.config); + return index.commandsFor(platform) .then((commands) => { return this.printPages(commands, singleColumn); }); @@ -41,8 +39,8 @@ class Tldr { } random(options) { - let os = platform.getPreferredPlatformFolder(this.config); - return index.commandsFor(os) + let platform = platforms.getPreferredPlatformFolder(this.config); + return index.commandsFor(platform) .then((pages) => { if (pages.length === 0) { throw new EmptyCacheError(); @@ -57,8 +55,8 @@ class Tldr { } randomExample() { - let os = platform.getPreferredPlatformFolder(this.config); - return index.commandsFor(os) + let platform = platforms.getPreferredPlatformFolder(this.config); + return index.commandsFor(platform) .then((pages) => { if (pages.length === 0) { throw new EmptyCacheError(); diff --git a/package-lock.json b/package-lock.json index b796926d..8360c74d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tldr", - "version": "3.3.8", + "version": "3.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tldr", - "version": "3.3.8", + "version": "3.4.0", "license": "MIT", "dependencies": { "adm-zip": "^0.5.10", @@ -15,6 +15,7 @@ "commander": "^6.1.0", "fs-extra": "^9.0.1", "glob": "^7.1.6", + "he": "^1.2.0", "lodash": "^4.17.20", "marked": "^4.0.10", "ms": "^2.1.2", @@ -3253,7 +3254,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, "bin": { "he": "bin/he" } @@ -10085,8 +10085,7 @@ "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, "hmac-drbg": { "version": "1.0.1", diff --git a/package.json b/package.json index 83415975..a75de6dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tldr", - "version": "3.3.8", + "version": "3.4.0", "description": "Simplified and community-driven man pages", "author": "Romain Prieto", "license": "MIT", @@ -14,15 +14,18 @@ "unix", "linux", "osx", + "openbsd", + "freebsd", + "netbsd", "commands", "command-line", "shell", "bash", "zsh" ], - "homepage": "http://tldr-pages.github.io", + "homepage": "https://tldr.sh", "engines": { - "node": ">=14" + "node": ">=16" }, "main": "bin/tldr", "files": [ @@ -56,6 +59,7 @@ "commander": "^6.1.0", "fs-extra": "^9.0.1", "glob": "^7.1.6", + "he": "^1.2.0", "lodash": "^4.17.20", "marked": "^4.0.10", "ms": "^2.1.2", diff --git a/test/cache.spec.js b/test/cache.spec.js index 2ab82706..77eff24c 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -8,7 +8,7 @@ const path = require('path'); const fs = require('fs-extra'); const index = require('../lib/index'); const remote = require('../lib/remote'); -const platform = require('../lib/platform'); +const platforms = require('../lib/platforms'); describe('Cache', () => { @@ -69,7 +69,9 @@ describe('Cache', () => { dd: ['linux', 'osx', 'sunos'], du: ['linux', 'osx', 'sunos'], top: ['linux', 'osx'], - svcs: ['sunos'] + svcs: ['sunos'], + pkg: ['android', 'freebsd', 'openbsd'], + pkgin: ['netbsd'] }); }); @@ -79,7 +81,7 @@ describe('Cache', () => { it('should return page contents for ls', () => { sinon.stub(fs, 'readFile').resolves('# ls\n> ls page'); - sinon.stub(platform, 'getPreferredPlatformFolder').returns('osx'); + sinon.stub(platforms, 'getPreferredPlatformFolder').returns('osx'); sinon.stub(index, 'findPage').resolves('osx'); const cache = new Cache(config.get()); return cache.getPage('ls') @@ -87,28 +89,28 @@ describe('Cache', () => { should.exist(content); content.should.startWith('# ls'); fs.readFile.restore(); - platform.getPreferredPlatformFolder.restore(); + platforms.getPreferredPlatformFolder.restore(); index.findPage.restore(); }); }); it('should return empty contents for svcs on OSX', () =>{ sinon.stub(fs, 'readFile').resolves('# svcs\n> svcs'); - sinon.stub(platform, 'getPreferredPlatformFolder').returns('osx'); + sinon.stub(platforms, 'getPreferredPlatformFolder').returns('osx'); sinon.stub(index, 'findPage').resolves(null); const cache = new Cache(config.get()); return cache.getPage('svc') .then((content) => { should.not.exist(content); fs.readFile.restore(); - platform.getPreferredPlatformFolder.restore(); + platforms.getPreferredPlatformFolder.restore(); index.findPage.restore(); }); }); it('should return page contents for svcs on SunOS', () => { sinon.stub(fs, 'readFile').resolves('# svcs\n> svcs'); - sinon.stub(platform, 'getPreferredPlatformFolder').returns('sunos'); + sinon.stub(platforms, 'getPreferredPlatformFolder').returns('sunos'); sinon.stub(index, 'findPage').resolves('svcs'); const cache = new Cache(config.get()); return cache.getPage('svcs') @@ -116,7 +118,22 @@ describe('Cache', () => { should.exist(content); content.should.startWith('# svcs'); fs.readFile.restore(); - platform.getPreferredPlatformFolder.restore(); + platforms.getPreferredPlatformFolder.restore(); + index.findPage.restore(); + }); + }); + + it('should return page contents for pkg on Android', () => { + sinon.stub(fs, 'readFile').resolves('# pkg\n> pkg'); + sinon.stub(platforms, 'getPreferredPlatformFolder').returns('android'); + sinon.stub(index, 'findPage').resolves('pkg'); + const cache = new Cache(config.get()); + return cache.getPage('pkg') + .then((content) => { + should.exist(content); + content.should.startWith('# pkg'); + fs.readFile.restore(); + platforms.getPreferredPlatformFolder.restore(); index.findPage.restore(); }); }); diff --git a/test/functional-test.sh b/test/functional-test.sh index 919f88db..17a96eca 100755 --- a/test/functional-test.sh +++ b/test/functional-test.sh @@ -6,11 +6,11 @@ alias tldr="node bin/tldr" function tldr-render-pages { tldr zip && \ - tldr du --os=linux && \ - tldr du --os=osx && \ - tldr du --os=linux --markdown && \ - tldr du --os=osx --markdown && \ - tldr du --os=windows --markdown && \ + tldr du --platform=linux && \ + tldr du --platform=osx && \ + tldr du --platform=linux --markdown && \ + tldr du --platform=osx --markdown && \ + tldr du --platform=windows --markdown && \ LANG= tldr --random && \ LANG= tldr --random-example && \ tldr --list && \ diff --git a/test/index.spec.js b/test/index.spec.js index f2cb7dd0..5961783c 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -17,15 +17,19 @@ const pages = [ ['/pages', 'common', 'git.md'], ['/pages', 'common', 'ln.md'], ['/pages', 'common', 'ls.md'], + ['/pages', 'freebsd', 'pkg.md'], ['/pages', 'linux', 'dd.md'], ['/pages', 'linux', 'du.md'], ['/pages', 'linux', 'top.md'], + ['/pages', 'netbsd', 'pkgin.md'], + ['/pages', 'openbsd', 'pkg.md'], ['/pages', 'osx', 'dd.md'], ['/pages', 'osx', 'du.md'], ['/pages', 'osx', 'top.md'], ['/pages', 'sunos', 'dd.md'], ['/pages', 'sunos', 'du.md'], - ['/pages', 'sunos', 'svcs.md'] + ['/pages', 'sunos', 'svcs.md'], + ['/pages', 'android', 'pkg.md'], ].map((x) => { return path.join(...x); }); @@ -84,139 +88,170 @@ describe('Index', () => { it('should find Linux platform for apk command for Chinese', () => { return index.findPage('apk', 'linux', 'zh') .then((folder) => { - return folder.should.equal(path.join('pages.zh', 'linux')); + should.equal(folder, path.join('pages.zh', 'linux')); + }); + }); + + it('should find platform android for pkg command for English', () => { + return index.findPage('pkg', 'android', 'en') + .then((folder) => { + should.equal(folder, path.join('pages', 'android')); + }); + }); + + it('should find platform freebsd for pkg command for English', () => { + return index.findPage('pkg', 'freebsd', 'en') + .then((folder) => { + should.equal(folder, path.join('pages', 'freebsd')); + }); + }); + + it('should find platform openbsd for pkg command for English', () => { + return index.findPage('pkg', 'openbsd', 'en') + .then((folder) => { + should.equal(folder, path.join('pages', 'openbsd')); + }); + }); + + it('should find platform netbsd for pkgin command for English', () => { + return index.findPage('pkgin', 'netbsd', 'en') + .then((folder) => { + should.equal(folder, path.join('pages', 'netbsd')); }); }); it('should find Linux platform for apk command for Chinese given Windows', () => { return index.findPage('apk', 'windows', 'zh') .then((folder) => { - return folder.should.equal(path.join('pages.zh', 'linux')); + should.equal(folder, path.join('pages.zh', 'linux')); }); }); it('should find Linux platform for dd command', () => { return index.findPage('dd', 'linux', 'en') .then((folder) => { - return folder.should.equal(path.join('pages', 'linux')); + should.equal(folder, path.join('pages', 'linux')); }); }); it('should find platform common for cp command for English', () => { return index.findPage('cp', 'linux', 'en') .then((folder) => { - return folder.should.equal(path.join('pages', 'common')); + should.equal(folder, path.join('pages', 'common')); }); }); it('should find platform common for cp command for Tamil', () => { return index.findPage('cp', 'linux', 'ta') .then((folder) => { - return folder.should.equal(path.join('pages.ta', 'common')); + should.equal(folder, path.join('pages.ta', 'common')); }); }); it('should find platform common for cp command for Italian', () => { return index.findPage('cp', 'linux', 'it') .then((folder) => { - return folder.should.equal(path.join('pages.it', 'common')); + should.equal(folder, path.join('pages.it', 'common')); }); }); it('should find platform common for cp command for Italian given Windows', () => { return index.findPage('cp', 'windows', 'it') .then((folder) => { - return folder.should.equal(path.join('pages.it', 'common')); + should.equal(folder, path.join('pages.it', 'common')); }); }); it('should find platform common for ls command for Italian', () => { return index.findPage('ls', 'linux', 'it') .then((folder) => { - return folder.should.equal(path.join('pages', 'common')); + should.equal(folder, path.join('pages', 'common')); }); }); + it('should find platform common for cp command for Italian given common platform', () => { return index.findPage('cp', 'common', 'it') .then((folder) => { - return folder.should.equal(path.join('pages.it', 'common')); + should.equal(folder, path.join('pages.it', 'common')); }); }); it('should find platform common for cp command for English given a bad language', () => { return index.findPage('cp', 'linux', 'notexist') .then((folder) => { - return folder.should.equal(path.join('pages', 'common')); + should.equal(folder, path.join('pages', 'common')); }); }); it('should find platform for svcs command on Linux', () => { return index.findPage('svcs', 'linux', 'en') .then((folder) => { - return folder.should.equal(path.join('pages', 'sunos')); + should.equal(folder, path.join('pages', 'sunos')); }); }); it('should not find platform for non-existing command', () => { return index.findPage('qwerty', 'linux', 'en') .then((folder) => { - return should.not.exist(folder); + should.not.exist(folder); }); }); }); - it('should return correct list of all pages', () => { + it('should return the correct list of all pages', () => { return index.commands() .then((commands) => { - commands.should.deepEqual([ - 'apk', 'cp', 'dd', 'du', 'git', 'ln', 'ls', 'svcs', 'top' + should.deepEqual(commands, [ + 'apk', 'cp', 'dd', 'du', 'git', 'ln', 'ls', 'pkg', 'pkgin', 'svcs', 'top' ]); }); }); describe('commandsFor()', () => { - it('should return correct list of pages for Linux', () => { + it('should return the correct list of pages for Linux', () => { return index.commandsFor('linux') .then((commands) => { - commands.should.deepEqual([ + should.deepEqual(commands, [ 'apk', 'cp', 'dd', 'du', 'git', 'ln', 'ls', 'top' ]); }); }); - it('should return correct list of pages for OSX', () => { + it('should return the correct list of pages for OSX', () => { return index.commandsFor('osx') .then((commands) => { - commands.should.deepEqual([ + should.deepEqual(commands, [ 'cp', 'dd', 'du', 'git', 'ln', 'ls', 'top' ]); }); }); - it('should return correct list of pages for SunOS', () => { + it('should return the correct list of pages for SunOS', () => { return index.commandsFor('sunos') .then((commands) => { - commands.should.deepEqual([ + should.deepEqual(commands, [ 'cp', 'dd', 'du', 'git', 'ln', 'ls', 'svcs' ]); }); }); }); - it('should return correct short index on getShortIndex()', () => { + it('should return the correct short index on getShortIndex()', () => { return index.getShortIndex() .then((idx) => { - idx.should.deepEqual({ - apk: {targets: [{language: 'en', os: 'linux'}, {language: 'zh', os: 'linux'}]}, - cp: {targets: [{language: 'en', os: 'common'}, {language: 'it', os: 'common'}, {language: 'ta', os: 'common'}]}, - dd: {targets: [{language: 'en', os: 'linux'}, {language: 'en', os: 'osx'}, {language: 'en', os: 'sunos'}]}, - du: {targets: [{language: 'en', os: 'linux'}, {language: 'en', os: 'osx'}, {language: 'en', os: 'sunos'}]}, - git: {targets: [{language: 'en', os: 'common'}]}, - ln: {targets: [{language: 'en', os: 'common'}]}, - ls: {targets: [{language: 'en', os: 'common'}]}, - svcs: {targets: [{language: 'en', os: 'sunos'}]}, - top: {targets: [{language: 'en', os: 'linux'}, {language: 'en', os: 'osx'}]}, + should.deepEqual(idx, { + apk: { targets: [{ language: 'en', platform: 'linux' }, { language: 'zh', platform: 'linux' }] }, + cp: { targets: [{ language: 'en', platform: 'common' }, { language: 'it', platform: 'common' }, { language: 'ta', platform: 'common' }] }, + dd: { targets: [{ language: 'en', platform: 'linux' }, { language: 'en', platform: 'osx' }, { language: 'en', platform: 'sunos' }] }, + du: { targets: [{ language: 'en', platform: 'linux' }, { language: 'en', platform: 'osx' }, { language: 'en', platform: 'sunos' }] }, + git: { targets: [{ language: 'en', platform: 'common' }] }, + ln: { targets: [{ language: 'en', platform: 'common' }] }, + ls: { targets: [{ language: 'en', platform: 'common' }] }, + pkg: { targets: [{ language: 'en', platform: 'freebsd' }, { language: 'en', platform: 'openbsd' }, { language: 'en', platform: 'android' }] }, + pkgin: { targets: [{ language: 'en', platform: 'netbsd' }] }, + svcs: { targets: [{ language: 'en', platform: 'sunos' }] }, + top: { targets: [{ language: 'en', platform: 'linux' }, { language: 'en', platform: 'osx' }] }, }); }); }); diff --git a/test/platform.spec.js b/test/platform.spec.js index aa0842d3..0ed77231 100644 --- a/test/platform.spec.js +++ b/test/platform.spec.js @@ -3,7 +3,7 @@ const os = require('os'); const config = require('../lib/config'); const sinon = require('sinon'); -const platform = require('../lib/platform'); +const platforms = require('../lib/platforms'); describe('Platform', () => { @@ -20,7 +20,7 @@ describe('Platform', () => { it('should return the running platform with no configuration', () => { os.platform.onCall(0).returns('darwin'); this.config = {}; - platform.getPreferredPlatform(this.config).should.eql('darwin'); + platforms.getPreferredPlatform(this.config).should.eql('darwin'); }); it('should overwrite the running platform if configured', () => { @@ -28,7 +28,7 @@ describe('Platform', () => { this.config = { platform: 'linux' }; - platform.getPreferredPlatform(this.config).should.eql('linux'); + platforms.getPreferredPlatform(this.config).should.eql('linux'); }); it('should return current system platform if configuration is wrong', () => { @@ -36,17 +36,21 @@ describe('Platform', () => { this.config = { platform: 'there_is_no_such_platform' }; - platform.getPreferredPlatform(this.config).should.eql('darwin'); + platforms.getPreferredPlatform(this.config).should.eql('darwin'); }); }); describe('isSupported', () => { - it('should tell that Linux, OSX, SunOS and Win32 are supported', () => { - platform.isSupported('osx').should.eql(true); - platform.isSupported('linux').should.eql(true); - platform.isSupported('sunos').should.eql(true); - platform.isSupported('windows').should.eql(true); - platform.isSupported('ios').should.eql(false); + it('should tell that Android, FreeBSD, Linux, NetBSD, OpenBSD, OSX, SunOS and Win32 are supported', () => { + platforms.isSupported('android').should.eql(true); + platforms.isSupported('osx').should.eql(true); + platforms.isSupported('freebsd').should.eql(true); + platforms.isSupported('linux').should.eql(true); + platforms.isSupported('netbsd').should.eql(true); + platforms.isSupported('openbsd').should.eql(true); + platforms.isSupported('sunos').should.eql(true); + platforms.isSupported('windows').should.eql(true); + platforms.isSupported('ios').should.eql(false); }); }); });