├── resources └── screencasts │ ├── screencast-2.gif │ └── screencast-1.svg ├── .editorconfig ├── lib ├── theme.js ├── predict-progressbar-width.js ├── parse-ffmpeg-log.js ├── formatter.js ├── progress-bar.js └── main.js ├── .gitignore ├── .npmignore ├── .jsdoc.json ├── LICENSE ├── CONTRIBUTING.md ├── package.json ├── .eslintrc ├── README.md └── yarn.lock /resources/screencasts/screencast-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sidneys/ffmpeg-progressbar-cli/HEAD/resources/screencasts/screencast-2.gif -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [.eslintrc] 13 | indent_size = 2 14 | 15 | [*.json] 16 | indent_size = 2 17 | 18 | [*.md] 19 | trim_trailing_whitespace = false 20 | 21 | [*.yml] 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /lib/theme.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | 4 | /** 5 | * Modules 6 | * External 7 | * @constant 8 | */ 9 | const chalk = require('chalk') 10 | 11 | /** 12 | * @namespace chalk 13 | * @function chalk.rgb 14 | */ 15 | 16 | /** 17 | * @module theme 18 | */ 19 | module.exports = { 20 | filenameLabel: chalk.rgb(180, 255, 220), 21 | filename: chalk.rgb(0, 255, 155).bold, 22 | beam: chalk.rgb(64, 0, 255), 23 | percentage: chalk.rgb(226, 217, 255), 24 | etaLabel: chalk.rgb(255, 180, 150), 25 | eta: chalk.rgb(255, 55, 0).bold, 26 | prompt: chalk.rgb(255, 230, 0).bold, 27 | error: chalk.rgb(255, 0, 0).bold, 28 | warning: chalk.rgb(255, 165, 0).bold 29 | } 30 | 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## BASE 2 | 3 | # macOS 4 | .DS_Store 5 | 6 | # Logs 7 | logs 8 | *.log 9 | 10 | # Temporary 11 | temp 12 | tmp 13 | 14 | # Caches 15 | .cache 16 | cache 17 | .sass-cache 18 | 19 | # JetBrains 20 | .idea 21 | *.sln.iml 22 | 23 | # VSCode 24 | .vscode 25 | 26 | # Compiled 27 | build 28 | 29 | # Generated documentation 30 | RELEASENOTES.md 31 | 32 | 33 | ## NODE.JS 34 | 35 | # Modules 36 | node_modules 37 | jspm_packages 38 | bower_components 39 | 40 | # Runtime data 41 | pids 42 | *.pid 43 | *.seed 44 | 45 | # Directory for instrumented libs generated by jscoverage/JSCover 46 | lib-cov 47 | 48 | # Coverage directory used by tools like istanbul 49 | coverage 50 | 51 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 52 | .grunt 53 | 54 | # Generated documentation 55 | docs 56 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ## BASE 2 | 3 | # macOS 4 | .DS_Store 5 | 6 | # Logs 7 | logs 8 | *.log 9 | 10 | # Temporary 11 | temp 12 | tmp 13 | 14 | # Caches 15 | .cache 16 | cache 17 | .sass-cache 18 | 19 | # JetBrains 20 | .idea 21 | *.sln.iml 22 | 23 | # VSCode 24 | .vscode 25 | 26 | # Compiled 27 | build 28 | 29 | # Generated documentation 30 | RELEASENOTES.md 31 | 32 | 33 | ## NODE.JS 34 | 35 | # Modules 36 | node_modules 37 | jspm_packages 38 | bower_components 39 | 40 | # Runtime data 41 | pids 42 | *.pid 43 | *.seed 44 | 45 | # Directory for instrumented libs generated by jscoverage/JSCover 46 | lib-cov 47 | 48 | # Coverage directory used by tools like istanbul 49 | coverage 50 | 51 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 52 | .grunt 53 | 54 | # Generated documentation 55 | docs 56 | -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true 4 | }, 5 | "source": { 6 | "include": [ 7 | "./lib", 8 | "package.json", 9 | "README.md" 10 | ], 11 | "includePattern": ".js$", 12 | "excludePattern": "(node_modules/|docs)" 13 | }, 14 | "plugins": ["plugins/markdown"], 15 | "opts": { 16 | "destination": "./docs", 17 | "encoding": "utf8", 18 | "recurse": true, 19 | "template": "./node_modules/docdash", 20 | "verbose": true 21 | }, 22 | "markdown": { 23 | "parser": "gfm", 24 | "hardwrap": true 25 | }, 26 | "templates": { 27 | "cleverLinks": false, 28 | "monospaceLinks": false, 29 | "default": { 30 | "outputSourceFiles": true, 31 | "includeDate": false 32 | } 33 | }, 34 | "docdash": { 35 | "static": false, 36 | "sort": true 37 | } 38 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 sidneys.github.io 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Creating Issues 4 | 5 | To file bug reports and feature suggestions, use the ["Issues"](https://github.com/sidneys/ffmpeg-progressbar-cli/issues?q=is%3Aissue) page. 6 | 7 | 1. Make sure the issue has not been filed before. 8 | 1. Create a new issue by filling out [the issue form](https://github.com/sidneys/ffmpeg-progressbar-cli/issues/new). 9 | 1. If an issue requires more information and receives no further input, it will be closed. 10 | 11 | 12 | ## Creating Pull Requests 13 | 14 | To create pull requests, use the ["Pull Requests"](https://github.com/sidneys/ffmpeg-progressbar-cli/pulls) page. 15 | 16 | 1. [Create a new Issue](#creating-issues) describing the Bug or Feature you are addressing, to let others know you are working on it. 17 | 1. If a related issue exists, add a comment to let others know that you'll submit a pull request. 18 | 1. Create a new pull request by filling out [the pull request form](https://github.com/sidneys/ffmpeg-progressbar-cli/pulls/compare). 19 | 20 | 21 | ### Setup 22 | 23 | 1. Fork the repository. 24 | 1. Clone your fork. 25 | 1. Make a branch for your change. 26 | 1. Run `npm install`. 27 | 28 | ## Commit Messages 29 | 30 | Use the `Conventional Commits` format. 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ffmpeg-progressbar-cli", 3 | "version": "1.5.0", 4 | "description": "A colored progress bar for FFmpeg. Simply use `ffmpeg-bar` instead of `ffmpeg`.", 5 | "license": "MIT", 6 | "homepage": "https://github.com/sidneys/ffmpeg-progressbar-cli", 7 | "author": { 8 | "name": "sidneys", 9 | "email": "sidneys.github.io@outlook.com", 10 | "url": "https://sidneys.github.io" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/sidneys/ffmpeg-progressbar-cli.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/sidneys/ffmpeg-progressbar-cli/issues" 18 | }, 19 | "engines": { 20 | "node": ">=8.11.3" 21 | }, 22 | "os": [ 23 | "darwin", 24 | "win32", 25 | "linux" 26 | ], 27 | "keywords": [ 28 | "FFmpeg", 29 | "progress bar", 30 | "colored", 31 | "media", 32 | "commandline", 33 | "terminal", 34 | "cli", 35 | "video", 36 | "conversion" 37 | ], 38 | "preferGlobal": true, 39 | "dependencies": { 40 | "@sidneys/cli-progress": "^2.2.0", 41 | "app-root-path": "^2.1.0", 42 | "chalk": "^2.4.1", 43 | "ellipsize": "^0.1.0", 44 | "ini": "^1.3.5", 45 | "moment": "^2.22.2", 46 | "moment-duration-format": "^2.2.2", 47 | "string-width": "^2.1.1", 48 | "which": "^1.3.1", 49 | "window-size": "^1.1.1" 50 | }, 51 | "devDependencies": { 52 | "docdash": "^1.0.0", 53 | "eslint": "^5.9.0", 54 | "jsdoc": "^3.5.5" 55 | }, 56 | "main": "./lib/main.js", 57 | "bin": { 58 | "ffmpeg-bar": "./lib/main.js" 59 | }, 60 | "scripts": { 61 | "docs": "jsdoc --verbose --configure .jsdoc.json", 62 | "start": "node ./lib/main.js" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/predict-progressbar-width.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | 4 | /** 5 | * Modules 6 | * External 7 | * @constant 8 | */ 9 | const windowSize = require('window-size') 10 | 11 | 12 | /** 13 | * Calculate the progress bars' text components width (fixed width) 14 | * @param {Number} targetFilenameLength - Target filename length 15 | * @returns {Number} - String length 16 | * 17 | * @private 18 | */ 19 | let predictTextWidth = (targetFilenameLength) => { 20 | // console.debug('predictTextWidth()') 21 | 22 | // TEXT LABEL WIDTH 23 | // 🎬 Rendering 12 24 | // BAR_FILENAME_LENGTH 10 25 | // | {percentagePad}% | ETA {etaTimecode} 20 26 | 27 | const barTextWidth = 12 + targetFilenameLength + 20 28 | 29 | const correctionFactor = 4 30 | 31 | return barTextWidth - correctionFactor 32 | } 33 | 34 | /** 35 | * Calculate the progress bars' beam width (variable width) 36 | * @param {Number} targetFilenameLength - Target filename length 37 | * @param {Number} targetBarBeamRatio - Target bar beam ratio 38 | * @returns {Number} - String length 39 | * 40 | * @private 41 | */ 42 | let predictBeamWidth = (targetFilenameLength, targetBarBeamRatio) => { 43 | // console.debug('predictBeamWidth()') 44 | 45 | const barBeamWidth = (windowSize.get().width - predictTextWidth(targetFilenameLength)) * targetBarBeamRatio 46 | 47 | return Math.floor(barBeamWidth) 48 | } 49 | 50 | /** 51 | * Calculate the progress bars' beam total width 52 | * @param {Number} targetFilenameLength - Target filename length 53 | * @param {Number} targetBarBeamRatio - Target bar beam ratio 54 | * @returns {Number} - String length 55 | */ 56 | let predictTotalWidth = (targetFilenameLength, targetBarBeamRatio) => { 57 | // console.debug('approximateBarWidth()') 58 | 59 | return predictTextWidth(targetFilenameLength) + predictBeamWidth(targetFilenameLength, targetBarBeamRatio) 60 | } 61 | 62 | 63 | /** 64 | * @module predict-progressbar-width.js 65 | */ 66 | module.exports = { 67 | beam: predictBeamWidth, 68 | text: predictTextWidth, 69 | total: predictTotalWidth 70 | } 71 | 72 | -------------------------------------------------------------------------------- /lib/parse-ffmpeg-log.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Modules 5 | * Node 6 | * @constant 7 | */ 8 | const path = require('path') 9 | 10 | /** 11 | * Modules 12 | * External 13 | * @constant 14 | */ 15 | const moment = require('moment') 16 | /* eslint-disable no-unused-vars */ 17 | const momentDurationFormat = require('moment-duration-format') 18 | /* eslint-enable */ 19 | 20 | 21 | /** 22 | * Parse FFmpeg log for "y/N" question 23 | * @param {String} text - FFmpeg Log Output 24 | * @returns {String|void} - First output filepath 25 | */ 26 | let parseFFmpegLogForQuestion = (text) => { 27 | // console.debug('searchQuestionLog') 28 | 29 | let questionLine = text.match(/.*(\[y\/N\]).*$/gmi) 30 | 31 | if (!questionLine) { return } 32 | 33 | questionLine = questionLine[0] 34 | questionLine = questionLine.replace(/\'/gmi, '') 35 | 36 | return questionLine 37 | } 38 | 39 | /** 40 | * Parse FFmpeg log for "Duration: " 41 | * @param {String} text - FFmpeg Log Output 42 | * @returns {Number|void} - Duration (milliseconds) 43 | */ 44 | let parseFFmpegLogForDuration = (text) => { 45 | // console.debug('searchDurationLog') 46 | 47 | const durationList = text.match(/Duration: (.{2}):(.{2}):(.{2}).(.{2})/) 48 | 49 | if (!durationList) { return } 50 | 51 | // Grep 52 | const timecode = `${durationList[1]}:${durationList[2]}:${durationList[3]}.${durationList[4].padEnd(3, '0')}` 53 | 54 | // Format 55 | const timecodeMoment = moment.duration(timecode) 56 | 57 | return timecodeMoment.asMilliseconds() 58 | } 59 | 60 | /** 61 | * Parse FFmpeg log for "Output #0" 62 | * @param {String} text - FFmpeg Log Output 63 | * @returns {String|void} - First output filepath 64 | */ 65 | let parseFFmpegLogForOutput = (text) => { 66 | // console.debug('searchOutputLog') 67 | 68 | let outputLine = text.match(/(?:Output #0).*$/gmi) 69 | 70 | if (!outputLine) { return } 71 | 72 | outputLine = outputLine[0] 73 | outputLine = outputLine.match(/'(.*?)'/gmi)[0] 74 | outputLine = outputLine.replace(/\'/gmi, '') 75 | 76 | return path.resolve(outputLine) 77 | } 78 | 79 | 80 | /** 81 | * @exports parse-ffmpeg-log 82 | */ 83 | module.exports = { 84 | output: parseFFmpegLogForOutput, 85 | duration: parseFFmpegLogForDuration, 86 | question: parseFFmpegLogForQuestion 87 | } 88 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 6, 9 | "ecmaFeatures": { 10 | "globalReturn": true, 11 | "impliedStrict": true, 12 | "jsx": true 13 | } 14 | }, 15 | "root": true, 16 | "rules": { 17 | "brace-style": [ 18 | "error", 19 | "1tbs", 20 | { 21 | "allowSingleLine": true 22 | } 23 | ], 24 | "camelcase": "off", 25 | "comma-dangle": [ 26 | "error", 27 | "never" 28 | ], 29 | "comma-style": [ 30 | "error", 31 | "last" 32 | ], 33 | "curly": "error", 34 | "dot-notation": "off", 35 | "eol-last": "error", 36 | "eqeqeq": "error", 37 | "indent": [ 38 | "error", 39 | 4, 40 | { 41 | "MemberExpression": "off", 42 | "SwitchCase": 1 43 | } 44 | ], 45 | "key-spacing": [ 46 | "error", 47 | { 48 | "afterColon": true, 49 | "beforeColon": false 50 | } 51 | ], 52 | "keyword-spacing": [ 53 | "error", 54 | { 55 | "after": true, 56 | "before": true 57 | } 58 | ], 59 | "linebreak-style": [ 60 | "error", 61 | "unix" 62 | ], 63 | "no-bitwise": "off", 64 | "no-caller": "error", 65 | "no-mixed-spaces-and-tabs": "error", 66 | "no-multi-str": "error", 67 | "no-trailing-spaces": "error", 68 | "no-undef": "error", 69 | "no-unused-vars": [ 70 | "error", 71 | { 72 | "args": "none", 73 | "vars": "local" 74 | } 75 | ], 76 | "no-use-before-define": [ 77 | "error", 78 | { 79 | "functions": false 80 | } 81 | ], 82 | "no-with": "error", 83 | "object-curly-spacing": [ 84 | "error", 85 | "always" 86 | ], 87 | "object-shorthand": "off", 88 | "quotes": [ 89 | "error", 90 | "single", 91 | { 92 | "allowTemplateLiterals": true 93 | } 94 | ], 95 | "semi": [ 96 | "error", 97 | "never" 98 | ], 99 | "space-before-blocks": [ 100 | "error", 101 | "always" 102 | ], 103 | "space-before-function-paren": [ 104 | "error", 105 | { 106 | "anonymous": "ignore", 107 | "named": "never" 108 | } 109 | ], 110 | "space-in-parens": [ 111 | "error", 112 | "never" 113 | ], 114 | "space-infix-ops": "error", 115 | "space-unary-ops": [ 116 | "error", 117 | { 118 | "words": false, 119 | "nonwords": false 120 | } 121 | ], 122 | "valid-jsdoc": [ 123 | "warn", 124 | { 125 | "requireReturn": false, 126 | "requireReturnDescription": false 127 | } 128 | ], 129 | "wrap-iife": "off" 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/formatter.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | 4 | /** 5 | * Modules 6 | * Node 7 | * @constant 8 | */ 9 | const path = require('path') 10 | 11 | /** 12 | * Modules 13 | * External 14 | * @constant 15 | */ 16 | const appRootPath = require('app-root-path') 17 | appRootPath.setPath(path.join(__dirname, '..')) 18 | const ellipsize = require('ellipsize') 19 | const stringWidth = require('string-width') 20 | 21 | 22 | /** 23 | * Ellipse character 24 | * @type {string} 25 | * @constant 26 | * @default 27 | */ 28 | const ellipseCharacter = '…' 29 | 30 | /** 31 | * Convert file paths to prettified file name 32 | * @param {String} filePath - File path 33 | * @param {Number=} targetLength - File name length before adding ellipsis 34 | * @returns {String} - File name 35 | */ 36 | let filepathToFilename = (filePath, targetLength = 10) => { 37 | // console.debug('prettyFilenameForFilepath') 38 | 39 | let fileName 40 | 41 | // Format fileName 42 | const fileExtension = path.extname(filePath) 43 | const fileTitle = path.basename(filePath, fileExtension) 44 | 45 | // Lengths 46 | const fileExtensionLength = stringWidth(fileExtension) 47 | const fileTitleLength = stringWidth(fileTitle) 48 | const fileNameLength = fileTitleLength + fileExtensionLength 49 | 50 | /** 51 | * File name string formatter 52 | * Handles most edge cases of file name/file extension lengths 53 | */ 54 | if (fileNameLength > targetLength) { 55 | if (fileExtensionLength < (targetLength - 2)) { 56 | // Name too long: ellipse title, leave extension as-is 57 | // filetit….extension 58 | fileName = `${ellipsize(fileTitle, targetLength - fileExtensionLength, { ellipse: ellipseCharacter })}${fileExtension}` 59 | } else { 60 | // Name too long, extension too long: Ellipse title, hide extension 61 | // filetit… 62 | fileName = `${ellipsize(fileTitle, targetLength, { ellipse: ellipseCharacter })}` 63 | } 64 | } else if (fileNameLength < targetLength) { 65 | if (fileNameLength >= (targetLength - 1)) { 66 | // Name too short by less than 2 characters: pad title left 67 | // filetitle.extension 68 | fileName = `${fileTitle}${fileExtension}`.padStart(targetLength, ' ') 69 | } else { 70 | // Name too short by more than 2 characters: Prefix ellipsed directory tree 71 | // …ocuments/filetitle.extension 72 | fileName = `${ellipseCharacter}${filePath.substr(filePath.length - targetLength + 1, filePath.length - 1)}` 73 | } 74 | } else { 75 | // File name has perfect length 76 | fileName = `${fileTitle}${fileExtension}` 77 | } 78 | 79 | return fileName 80 | } 81 | 82 | 83 | /** 84 | * Convert fraction to zero-padded percentage 85 | * @param {String} fraction - Fraction 86 | * @param {String=} pad - Pad character 87 | * @returns {String} - percentage 88 | */ 89 | let fractionToPercentage = (fraction, pad = '0') => { 90 | // console.debug('fractionToPercentagePad') 91 | 92 | return String(Math.round(fraction * 100)).padStart(3, pad) 93 | } 94 | 95 | /** 96 | * @exports format-string 97 | */ 98 | module.exports = { 99 | filepathToFilename: filepathToFilename, 100 | fractionToPercentage: fractionToPercentage 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ffmpeg-progressbar-cli [![npm](https://img.shields.io/npm/v/ffmpeg-progressbar-cli.svg?style=flat-square)](https://npmjs.com/package/ffmpeg-progressbar-cli) 2 | 3 |

4 |
5 | ffmpeg-progressbar-cli is a colored progress bar for FFmpeg.
6 | Simply use ffmpeg-bar instead of ffmpeg.

7 |

8 | 9 | 10 | ## Contents 11 | 12 | 1. [Installation](#installation) 13 | 1. [Usage](#usage) 14 | 1. [Configuration](#configuration) 15 | 1. [Requirements](#requirements) 16 | 1. [Compatibility](#compatibility) 17 | 1. [Contribute](#contribute) 18 | 1. [Author](#author) 19 | 20 | 21 | ## Installation 22 | 23 | ```bash 24 | $ npm install --global ffmpeg-progressbar-cli 25 | ``` 26 | 27 | 28 | ## Usage 29 | 30 | The installation process adds the `ffmpeg-bar` command to your system. 31 | This is a transparent wrapper, passing all commands to `ffmpeg`. 32 | 33 | To use it, simply launch `ffmpeg-bar` instead of `ffmpeg`, or replace `ffmpeg` with `ffmpeg-bar` inside your scripts. 34 | 35 | 36 | As long as no errors are encountered, the output of `ffmpeg-bar` will consist of a progress bar, the estimated time until process completion and a percentage. 37 | 38 | ###### Examples 39 | 40 | ```bash 41 | $> ffmpeg-bar -i input.mp4 output.avi 42 | ``` 43 | 44 | ```bash 45 | $> ffmpeg-bar -i input.avi -b:v 64k -bufsize 64k output.avi 46 | ``` 47 | 48 | ```bash 49 | $> ffmpeg-bar -i in.mkv -map_metadata:s:a 0:g out.mkv 50 | ``` 51 | 52 | 53 | ## Configuration 54 | 55 | For configuration purposes, `ffmpeg-progressbar-cli` exposes these environmental variables: 56 | 57 | 58 | ##### `BAR_FILENAME_LENGTH` 59 | The maximum number of characters of the filename label displayed next to the progress bar beam *(default: 20)* 60 | 61 | ###### Example 62 | 63 | ```bash 64 | $> BAR_FILENAME_LENGTH=7 ffmpeg-bar -i in.mp4 output.mp4 65 | ``` 66 | 67 | ##### `BAR_BEAM_RATIO ` 68 | 69 | The share of (available) horizontal display real estate the progress bar beam should occupy *(default: 0.75)* 70 | 71 | ###### Example 72 | 73 | ```bash 74 | $> BAR_BAR_SIZE_RATIO=0.5 ffmpeg-bar -i in.mp4 output.mp4 75 | ``` 76 | 77 | ## Requirements 78 | 79 | - [Node.js](https://nodejs.org/), v8.11 or later 80 | - [FFmpeg](https://ffmpeg.org/), installed correctly 81 | 82 | 83 | ## Compatibility 84 | 85 | Tested on 86 | 87 | - macOS 10.13, 10.14 Beta 88 | - Windows 10 1803 89 | - Ubuntu 18.04 90 | 91 | 92 | ## Contribute ![Contributors Wanted](https://img.shields.io/badge/contributions-wanted-red.svg?style=flat-square) 93 | 94 | Read the contribution [documentation](https://github.com/sidneys/ffmpeg-progressbar-cli/blob/master/CONTRIBUTING.md). 95 | 96 | 97 | ## License 98 | 99 | MIT 100 | 101 | 102 | ## Author 103 | 104 | [sidneys](http://sidneys.github.io) 2018 105 | 106 | -------------------------------------------------------------------------------- /lib/progress-bar.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | 4 | /** 5 | * Modules 6 | * Node 7 | * @constant 8 | */ 9 | const path = require('path') 10 | 11 | /** 12 | * Modules 13 | * External 14 | * @constant 15 | */ 16 | const appRootPath = require('app-root-path') 17 | appRootPath.setPath(path.join(__dirname, '..')) 18 | const CliProgress = require('@sidneys/cli-progress') 19 | const moment = require('moment') 20 | /* eslint-disable no-unused-vars */ 21 | const momentDurationFormat = require('moment-duration-format') 22 | /* eslint-enable */ 23 | 24 | /** 25 | * Modules 26 | * Internal 27 | * @constant 28 | */ 29 | const formatter = require('./formatter') 30 | const theme = require('./theme') 31 | 32 | /** 33 | * Timecode format 34 | * @constant 35 | * @default 36 | */ 37 | const etaFormat = 'HH:mm:ss' 38 | 39 | /** 40 | * Convert seconds into timecode format 41 | * @param {Number} seconds - Duration in seconds 42 | * @returns {String} - Timecode / HH:mm:ss 43 | */ 44 | let convertSecondsToTimecode = (seconds) => { 45 | // console.debug('convertSecondsToTimecode') 46 | 47 | return moment.duration(seconds, 'seconds').format(etaFormat, { trim: false }) 48 | } 49 | 50 | /** 51 | * Progress Bar Singleton 52 | * @external {CliProgress.Bar} 53 | */ 54 | let progressBar 55 | 56 | /** 57 | * Wrapper for progressBar constructor 58 | * @param {Object=} options - Options 59 | * @returns {CliProgress.Bar} - progressBar 60 | */ 61 | let createBar = (options) => { 62 | // console.debug('createProgressbar') 63 | 64 | const baseOptions = { 65 | format: `🎞 ${theme.filenameLabel('Rendering')} ${theme.filename('{filename}')} | ${theme.beam('{bar}')} ${theme.percentage('{percentagePad}%')} | ${theme.etaLabel('ETA')} ${theme.eta('{etaTimecode}')}`, 66 | fps: 20, 67 | barsize: 60, 68 | barCompleteChar: '\u2593', 69 | barIncompleteChar: '\u2591', 70 | etaBuffer: 64, 71 | hideCursor: true, 72 | stream: process.stdout, 73 | align: 'center', 74 | stopOnComplete: true, 75 | clearOnComplete: false 76 | } 77 | 78 | const finalOptions = Object.assign(baseOptions, options) 79 | 80 | // DEBUG 81 | // console.debug('configuration', require('util').inspect(finalOptions, /** @type {InspectOptions} */ { showHidden: true, colors: true, compact: true })) 82 | 83 | return new CliProgress.Bar(finalOptions) 84 | } 85 | 86 | /** 87 | * Wrapper for progressBar.start 88 | * @param {Number} totalValue - Total value 89 | * @param {Object=} payload - Extra Payload 90 | * @param {String=} annotation - Annotation 91 | * @public 92 | */ 93 | let startBar = (totalValue, payload, annotation) => { 94 | // console.debug('startProgressbar') 95 | 96 | const startPayload = { 97 | etaTimecode: convertSecondsToTimecode(0), 98 | percentagePad: '00' 99 | } 100 | 101 | const finalPayload = Object.assign(startPayload, payload) 102 | 103 | // DEBUG 104 | // console.debug('startBar', 'payload', require('util').inspect(finalPayload, /** @type {InspectOptions} */ { showHidden: true, colors: true, compact: true })) 105 | 106 | progressBar.start(totalValue, 0, finalPayload, annotation) 107 | } 108 | 109 | /** 110 | * Wrapper for progressBar.update 111 | * @param {Number} currentValue - Current value 112 | * @param {Object=} payload - Payload 113 | * @param {String=} annotation - Annotation 114 | * @public 115 | */ 116 | let updateBar = (currentValue, payload, annotation) => { 117 | // console.debug('updateProgressbar') 118 | 119 | const updatePayload = { 120 | percentagePad: formatter.fractionToPercentage(progressBar.value / progressBar.total, ' '), 121 | etaTimecode: convertSecondsToTimecode(progressBar.eta.eta) 122 | } 123 | 124 | const finalPayload = Object.assign(updatePayload, payload) 125 | 126 | // DEBUG 127 | // console.debug('updateBar', 'payload', require('util').inspect(finalPayload, /** @type {InspectOptions} */ { showHidden: true, colors: true, compact: true })) 128 | 129 | progressBar.update(currentValue, finalPayload, annotation) 130 | } 131 | 132 | /** 133 | * @typedef {Object} progressBar 134 | * @extends {CliProgress} 135 | * @property {function} bar 136 | * @property {function} start 137 | * @property {function} update 138 | */ 139 | 140 | /** 141 | * @param {Object} options - Progress Bar options 142 | * @exports progressBar 143 | * @returns {Object} 144 | */ 145 | module.exports = (options) => { 146 | progressBar = createBar(options) 147 | 148 | return { 149 | bar: progressBar, 150 | start: startBar, 151 | update: updateBar 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | 4 | /** 5 | * The maximum number of characters of the filename label displayed next to the progress bar beam. 6 | * @constant 7 | * @default 8 | */ 9 | const BAR_FILENAME_LENGTH = process.env.BAR_FILENAME_LENGTH || 20 10 | 11 | /** 12 | * The share of (available) horizontal display real estate the progress bar beam should occupy. 13 | * @constant 14 | */ 15 | const BAR_BEAM_RATIO = process.env.BAR_SIZE_RATIO || 0.75 16 | 17 | 18 | /** 19 | * Modules 20 | * Node 21 | * @constant 22 | */ 23 | const childProcess = require('child_process') 24 | const os = require('os') 25 | const readline = require('readline') 26 | const path = require('path') 27 | 28 | /** 29 | * Modules 30 | * External 31 | * @constant 32 | */ 33 | const appRootPath = require('app-root-path') 34 | appRootPath.setPath(path.join(__dirname, '..')) 35 | const ellipsize = require('ellipsize') 36 | const ini = require('ini') 37 | const which = require('which') 38 | 39 | /** 40 | * Modules 41 | * Internal 42 | * @constant 43 | */ 44 | const progressBar = require('./progress-bar') 45 | const parseFFmpegLog = require('./parse-ffmpeg-log') 46 | const predictProgressbarWidth = require('./predict-progressbar-width') 47 | const formatter = require('./formatter') 48 | const theme = require('./theme') 49 | 50 | 51 | /** 52 | * FFmpeg Binary 53 | * @constant 54 | */ 55 | const ffmpegFilepath = which.sync('ffmpeg') 56 | 57 | /** 58 | * FFmpeg arguments 59 | * @constant 60 | */ 61 | const ffmpegUserArgsList = process.argv.slice(2) 62 | const ffmpegPreparationTaskArgsList = [...ffmpegUserArgsList] 63 | const ffmpegPrimaryTaskArgsList = ['-y', '-loglevel', 'error', '-progress', 'pipe:1', ...ffmpegUserArgsList] 64 | 65 | 66 | /** 67 | * @typedef {ChildProcess} Task 68 | * @inherits {NodeJS.EventEmitter} 69 | * @property {NodeJS.ReadableStream} stdout - stdout Stream 70 | * @property {NodeJS.ReadableStream} stderr - stderr Stream 71 | * @property {NodeJS.WritableStream} stdin - stdin Stream 72 | * @property {function} on - NodeJS.EventEmitter.on 73 | */ 74 | 75 | /** 76 | * Primary Task 77 | * This task runs the FFmpeg commands provided. 78 | * @param {Number} totalTimeMs - Total duration (ms) 79 | * @param {String} filePath - Output target filepath 80 | */ 81 | let startPrimaryTask = (totalTimeMs, filePath) => { 82 | // console.debug('startPrimaryTask()') 83 | 84 | const progressbarWidth = predictProgressbarWidth.beam(Number(BAR_FILENAME_LENGTH), BAR_BEAM_RATIO) 85 | 86 | /** 87 | * Create Progress Bar 88 | * @type {progressBar} 89 | */ 90 | const bar = progressBar({ 91 | barsize: progressbarWidth 92 | }) 93 | 94 | /** 95 | * Startup status 96 | * @type {Boolean} 97 | */ 98 | let didStart = false 99 | 100 | /** 101 | * Last error log 102 | * @type {String} 103 | */ 104 | let lastError 105 | 106 | // DEBUG 107 | // lastError = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzstuvwxyz' 108 | 109 | /** 110 | * Main Task 111 | * @type {Task} 112 | */ 113 | const mainTask = childProcess.spawn(ffmpegFilepath, ffmpegPrimaryTaskArgsList) 114 | 115 | /** 116 | * Handles mainTask.stdout 117 | * @param {Buffer|String} data - FFmpeg Live Log Output Buffer 118 | */ 119 | let onData = (data) => { 120 | if (!didStart) { 121 | // Bar::start() 122 | bar.start(totalTimeMs, { 123 | filename: formatter.filepathToFilename(filePath, BAR_FILENAME_LENGTH) 124 | }) 125 | didStart = true 126 | } 127 | 128 | // Parse sparse output of nearly undocumented `-progress` argument 🤬🤬🤬 129 | // That is, key-value-pairs in .ini format 130 | const progressDictionary = ini.decode(data.toString()) 131 | 132 | const currentTimeMicroseconds = Number(progressDictionary['out_time_ms']) 133 | const currentTimeMs = Math.round(currentTimeMicroseconds / 1000) 134 | 135 | // Bar::update() (noncritical errors are written to Bar.annotation) 136 | bar.update(currentTimeMs, null, !!lastError ? theme.warning(ellipsize(lastError, progressbarWidth + 7)) : null) 137 | 138 | // DEBUG 139 | // console.debug('progress (ms)', progressMilliseconds, 'duration (ms)', durationMilliseconds, 'fraction', (progressMilliseconds / durationMilliseconds)) 140 | } 141 | 142 | /** 143 | * @fires mainTask#onData 144 | * @interface mainTask.stdout 145 | */ 146 | mainTask.stdout.on('data', onData) 147 | 148 | /** 149 | * @fires mainTask#onData 150 | * @interface mainTask.stderr 151 | */ 152 | mainTask.stderr.on('data', (data) => { 153 | // Error: Persist last line fo error logs to lastError 154 | const dataLines = data.toString().trim().split(os.EOL) 155 | lastError = dataLines[dataLines.length - 1] 156 | }) 157 | 158 | /** 159 | * @interface Process.ReadStream 160 | */ 161 | process.stdin.pipe(mainTask.stdin) 162 | 163 | /** 164 | * @listens mainTask#close 165 | */ 166 | mainTask.on('close', (code, signal) => { 167 | // DEBUG 168 | // console.debug('mainTask#close', 'code:', code, 'signal:', signal) 169 | 170 | // Set bar to 100% 171 | bar.update(totalTimeMs) 172 | 173 | // Restore cursor 174 | process.stdout.write('\x1B[?25h') 175 | 176 | // Exit 177 | process.exit(code) 178 | }) 179 | } 180 | 181 | /** 182 | * Preparation Task 183 | * This spawns a preliminary process, which gathers information about the output (filename, duration) which cannot be gathered via "ffmpeg -progress" 184 | */ 185 | let startPreparationTask = () => { 186 | // console.debug('startPreparationTask()') 187 | 188 | /** 189 | * Preparation Task 190 | */ 191 | const prepTask = childProcess.spawn(ffmpegFilepath, ffmpegPreparationTaskArgsList) 192 | 193 | /** 194 | * Duration of output file 195 | * @type {Number} 196 | */ 197 | let outputDuration 198 | 199 | /** 200 | * Name of output file 201 | * @type {String} 202 | */ 203 | let outputFilename 204 | 205 | /** 206 | * Last error log 207 | * @type {String} 208 | */ 209 | let lastError 210 | 211 | /** 212 | * Question status 213 | * @type {Boolean} 214 | */ 215 | let didShowQuestion = false 216 | 217 | /** 218 | * Handles prepTask.stdout 219 | * @param {Buffer|String} data - FFmpeg Live Log Output Buffer 220 | */ 221 | let onData = (data) => { 222 | data = data.toString() 223 | if (parseFFmpegLog.duration(data)) { outputDuration = parseFFmpegLog.duration(data) } 224 | if (parseFFmpegLog.output(data)) { outputFilename = parseFFmpegLog.output(data) } 225 | 226 | if (outputDuration && outputFilename) { 227 | if (didShowQuestion) { 228 | readline.moveCursor(process.stderr, 0, -1) 229 | readline.clearLine(process.stderr, 0) 230 | } 231 | // Send SIGKILL: preparation task succeeded 232 | prepTask.kill('SIGKILL') 233 | startPrimaryTask(outputDuration, outputFilename) 234 | } 235 | } 236 | 237 | /** 238 | * @fires prepTask#onData 239 | * @interface prepTask.stdout 240 | */ 241 | prepTask.stdout.on('data', onData) 242 | 243 | /** 244 | * @fires prepTask#onData 245 | * @interface prepTask.stderr 246 | */ 247 | prepTask.stderr.on('data', (data) => { 248 | if ((parseFFmpegLog.question(data.toString()))) { 249 | // Question: Show Question + whitespace 250 | process.stdout.write(theme.prompt(parseFFmpegLog.question(data.toString().trim()))) 251 | process.stdout.write(' ') 252 | didShowQuestion = true 253 | } else { 254 | // Error: Persist last line fo error logs to lastError 255 | const dataLines = data.toString().trim().split(os.EOL) 256 | lastError = dataLines[dataLines.length - 1] 257 | } 258 | onData(data) 259 | }) 260 | 261 | /** 262 | * @interface Process.ReadStream 263 | */ 264 | process.stdin.pipe(prepTask.stdin) 265 | 266 | /** 267 | * @listens prepTask#close 268 | */ 269 | prepTask.on('close', (code, signal) => { 270 | // DEBUG 271 | // console.debug('prepTask#close', 'code:', code, 'signal:', signal) 272 | 273 | // SIGKILL (fired by us) caused the process to close: ignore 274 | if (signal === 'SIGKILL') { return } 275 | 276 | // Other SIGNAL caused the process to close: Abort 277 | if (lastError) { 278 | // Show last error message 279 | process.stderr.write(theme.error(lastError)) 280 | } 281 | process.stdout.write(os.EOL) 282 | process.exit(code) 283 | }) 284 | } 285 | 286 | 287 | /** 288 | * Main 289 | */ 290 | if (require.main === module) { 291 | startPreparationTask() 292 | } 293 | -------------------------------------------------------------------------------- /resources/screencasts/screencast-1.svg: -------------------------------------------------------------------------------- 1 | user@home(/~)>user@home(/~)>ffmpeg-bar-i$HOME/Movies/input.mp4-map0-ccopyoutput_filename.mp4🔴output_fi….mp4|░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░|0%|ETA0s🔴output_fi….mp4|███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░|4%|ETA0s🔴output_fi….mp4|████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░|20%|ETA7s🔴output_fi….mp4|██████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░|29%|ETA15s🔴output_fi….mp4|████████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░|46%|ETA20s🔴output_fi….mp4|███████████████████████████████████████░░░░░░░░░░░░░░░░░░░░░░|63%|ETA20s🔴output_fi….mp4|█████████████████████████████████████████████████░░░░░░░░░░░░|80%|ETA10s🔴output_fi….mp4|█████████████████████████████████████████████████████████████|99%|ETA1s🔴output_fi….mp4|█████████████████████████████████████████████████████████████|100%|ETA1suser@home(/~)>ffmpeg-baruser@home(/~)>ffmpeg-bar-i$HOME/Movies/input.mp4user@home(/~)>ffmpeg-bar-i$HOME/Movies/input.mp4-map0-ccopy🔴output_fi….mp4|████████████████████████████████████████████████████████████░|99%|ETA1ss🔴output_fi….mp4|████████████████████████████████████████████████████████████░|99%|ETA1s -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.0.0" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" 8 | integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== 9 | dependencies: 10 | "@babel/highlight" "^7.0.0" 11 | 12 | "@babel/highlight@^7.0.0": 13 | version "7.0.0" 14 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" 15 | integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== 16 | dependencies: 17 | chalk "^2.0.0" 18 | esutils "^2.0.2" 19 | js-tokens "^4.0.0" 20 | 21 | "@sidneys/cli-progress@^2.2.0": 22 | version "2.2.0" 23 | resolved "https://registry.yarnpkg.com/@sidneys/cli-progress/-/cli-progress-2.2.0.tgz#c7fc07f927b2f8e1f3d809a056043a26e36ee812" 24 | dependencies: 25 | colors "^1.3.2" 26 | string-width "^2.1.1" 27 | 28 | acorn-jsx@^4.1.1: 29 | version "4.1.1" 30 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e" 31 | dependencies: 32 | acorn "^5.0.3" 33 | 34 | acorn@^5.0.3, acorn@^5.6.0: 35 | version "5.7.1" 36 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" 37 | 38 | ajv@^6.5.3: 39 | version "6.5.5" 40 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.5.tgz#cf97cdade71c6399a92c6d6c4177381291b781a1" 41 | integrity sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg== 42 | dependencies: 43 | fast-deep-equal "^2.0.1" 44 | fast-json-stable-stringify "^2.0.0" 45 | json-schema-traverse "^0.4.1" 46 | uri-js "^4.2.2" 47 | 48 | ansi-escapes@^3.0.0: 49 | version "3.1.0" 50 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" 51 | 52 | ansi-regex@^3.0.0: 53 | version "3.0.0" 54 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 55 | 56 | ansi-styles@^3.2.1: 57 | version "3.2.1" 58 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 59 | dependencies: 60 | color-convert "^1.9.0" 61 | 62 | app-root-path@^2.1.0: 63 | version "2.1.0" 64 | resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.1.0.tgz#98bf6599327ecea199309866e8140368fd2e646a" 65 | 66 | argparse@^1.0.7: 67 | version "1.0.10" 68 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 69 | dependencies: 70 | sprintf-js "~1.0.2" 71 | 72 | array-union@^1.0.1: 73 | version "1.0.2" 74 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 75 | dependencies: 76 | array-uniq "^1.0.1" 77 | 78 | array-uniq@^1.0.1: 79 | version "1.0.3" 80 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 81 | 82 | arrify@^1.0.0: 83 | version "1.0.1" 84 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 85 | 86 | babylon@7.0.0-beta.19: 87 | version "7.0.0-beta.19" 88 | resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.19.tgz#e928c7e807e970e0536b078ab3e0c48f9e052503" 89 | 90 | balanced-match@^1.0.0: 91 | version "1.0.0" 92 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 93 | 94 | bluebird@~3.5.0: 95 | version "3.5.1" 96 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" 97 | 98 | brace-expansion@^1.1.7: 99 | version "1.1.11" 100 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 101 | dependencies: 102 | balanced-match "^1.0.0" 103 | concat-map "0.0.1" 104 | 105 | caller-path@^0.1.0: 106 | version "0.1.0" 107 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" 108 | dependencies: 109 | callsites "^0.2.0" 110 | 111 | callsites@^0.2.0: 112 | version "0.2.0" 113 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" 114 | 115 | catharsis@~0.8.9: 116 | version "0.8.9" 117 | resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.9.tgz#98cc890ca652dd2ef0e70b37925310ff9e90fc8b" 118 | dependencies: 119 | underscore-contrib "~0.3.0" 120 | 121 | chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1: 122 | version "2.4.1" 123 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 124 | dependencies: 125 | ansi-styles "^3.2.1" 126 | escape-string-regexp "^1.0.5" 127 | supports-color "^5.3.0" 128 | 129 | chardet@^0.7.0: 130 | version "0.7.0" 131 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" 132 | integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== 133 | 134 | circular-json@^0.3.1: 135 | version "0.3.3" 136 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" 137 | 138 | cli-cursor@^2.1.0: 139 | version "2.1.0" 140 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 141 | dependencies: 142 | restore-cursor "^2.0.0" 143 | 144 | cli-width@^2.0.0: 145 | version "2.2.0" 146 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" 147 | 148 | color-convert@^1.9.0: 149 | version "1.9.2" 150 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" 151 | dependencies: 152 | color-name "1.1.1" 153 | 154 | color-name@1.1.1: 155 | version "1.1.1" 156 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" 157 | 158 | colors@^1.3.2: 159 | version "1.3.2" 160 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.2.tgz#2df8ff573dfbf255af562f8ce7181d6b971a359b" 161 | 162 | concat-map@0.0.1: 163 | version "0.0.1" 164 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 165 | 166 | cross-spawn@^6.0.5: 167 | version "6.0.5" 168 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 169 | dependencies: 170 | nice-try "^1.0.4" 171 | path-key "^2.0.1" 172 | semver "^5.5.0" 173 | shebang-command "^1.2.0" 174 | which "^1.2.9" 175 | 176 | debug@^4.0.1: 177 | version "4.1.0" 178 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" 179 | integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== 180 | dependencies: 181 | ms "^2.1.1" 182 | 183 | deep-is@~0.1.3: 184 | version "0.1.3" 185 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 186 | 187 | define-property@^1.0.0: 188 | version "1.0.0" 189 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" 190 | dependencies: 191 | is-descriptor "^1.0.0" 192 | 193 | del@^2.0.2: 194 | version "2.2.2" 195 | resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" 196 | dependencies: 197 | globby "^5.0.0" 198 | is-path-cwd "^1.0.0" 199 | is-path-in-cwd "^1.0.0" 200 | object-assign "^4.0.1" 201 | pify "^2.0.0" 202 | pinkie-promise "^2.0.0" 203 | rimraf "^2.2.8" 204 | 205 | docdash@^1.0.0: 206 | version "1.0.0" 207 | resolved "https://registry.yarnpkg.com/docdash/-/docdash-1.0.0.tgz#5b7df10fed3d341fc4416a8978c65ad561869d18" 208 | 209 | doctrine@^2.1.0: 210 | version "2.1.0" 211 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" 212 | dependencies: 213 | esutils "^2.0.2" 214 | 215 | ellipsize@^0.1.0: 216 | version "0.1.0" 217 | resolved "https://registry.yarnpkg.com/ellipsize/-/ellipsize-0.1.0.tgz#9d43682d44b91ad16ebd84268ac103170a6553f8" 218 | 219 | escape-string-regexp@^1.0.5, escape-string-regexp@~1.0.5: 220 | version "1.0.5" 221 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 222 | 223 | eslint-scope@^4.0.0: 224 | version "4.0.0" 225 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" 226 | dependencies: 227 | esrecurse "^4.1.0" 228 | estraverse "^4.1.1" 229 | 230 | eslint-utils@^1.3.1: 231 | version "1.3.1" 232 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" 233 | 234 | eslint-visitor-keys@^1.0.0: 235 | version "1.0.0" 236 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" 237 | 238 | eslint@^5.9.0: 239 | version "5.9.0" 240 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.9.0.tgz#b234b6d15ef84b5849c6de2af43195a2d59d408e" 241 | integrity sha512-g4KWpPdqN0nth+goDNICNXGfJF7nNnepthp46CAlJoJtC5K/cLu3NgCM3AHu1CkJ5Hzt9V0Y0PBAO6Ay/gGb+w== 242 | dependencies: 243 | "@babel/code-frame" "^7.0.0" 244 | ajv "^6.5.3" 245 | chalk "^2.1.0" 246 | cross-spawn "^6.0.5" 247 | debug "^4.0.1" 248 | doctrine "^2.1.0" 249 | eslint-scope "^4.0.0" 250 | eslint-utils "^1.3.1" 251 | eslint-visitor-keys "^1.0.0" 252 | espree "^4.0.0" 253 | esquery "^1.0.1" 254 | esutils "^2.0.2" 255 | file-entry-cache "^2.0.0" 256 | functional-red-black-tree "^1.0.1" 257 | glob "^7.1.2" 258 | globals "^11.7.0" 259 | ignore "^4.0.6" 260 | imurmurhash "^0.1.4" 261 | inquirer "^6.1.0" 262 | is-resolvable "^1.1.0" 263 | js-yaml "^3.12.0" 264 | json-stable-stringify-without-jsonify "^1.0.1" 265 | levn "^0.3.0" 266 | lodash "^4.17.5" 267 | minimatch "^3.0.4" 268 | mkdirp "^0.5.1" 269 | natural-compare "^1.4.0" 270 | optionator "^0.8.2" 271 | path-is-inside "^1.0.2" 272 | pluralize "^7.0.0" 273 | progress "^2.0.0" 274 | regexpp "^2.0.1" 275 | require-uncached "^1.0.3" 276 | semver "^5.5.1" 277 | strip-ansi "^4.0.0" 278 | strip-json-comments "^2.0.1" 279 | table "^5.0.2" 280 | text-table "^0.2.0" 281 | 282 | espree@^4.0.0: 283 | version "4.0.0" 284 | resolved "https://registry.yarnpkg.com/espree/-/espree-4.0.0.tgz#253998f20a0f82db5d866385799d912a83a36634" 285 | dependencies: 286 | acorn "^5.6.0" 287 | acorn-jsx "^4.1.1" 288 | 289 | esprima@^4.0.0: 290 | version "4.0.1" 291 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 292 | 293 | esquery@^1.0.1: 294 | version "1.0.1" 295 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" 296 | dependencies: 297 | estraverse "^4.0.0" 298 | 299 | esrecurse@^4.1.0: 300 | version "4.2.1" 301 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" 302 | dependencies: 303 | estraverse "^4.1.0" 304 | 305 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: 306 | version "4.2.0" 307 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 308 | 309 | esutils@^2.0.2: 310 | version "2.0.2" 311 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 312 | 313 | external-editor@^3.0.0: 314 | version "3.0.3" 315 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" 316 | integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== 317 | dependencies: 318 | chardet "^0.7.0" 319 | iconv-lite "^0.4.24" 320 | tmp "^0.0.33" 321 | 322 | fast-deep-equal@^2.0.1: 323 | version "2.0.1" 324 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" 325 | 326 | fast-json-stable-stringify@^2.0.0: 327 | version "2.0.0" 328 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" 329 | 330 | fast-levenshtein@~2.0.4: 331 | version "2.0.6" 332 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 333 | 334 | figures@^2.0.0: 335 | version "2.0.0" 336 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" 337 | dependencies: 338 | escape-string-regexp "^1.0.5" 339 | 340 | file-entry-cache@^2.0.0: 341 | version "2.0.0" 342 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" 343 | dependencies: 344 | flat-cache "^1.2.1" 345 | object-assign "^4.0.1" 346 | 347 | flat-cache@^1.2.1: 348 | version "1.3.0" 349 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" 350 | dependencies: 351 | circular-json "^0.3.1" 352 | del "^2.0.2" 353 | graceful-fs "^4.1.2" 354 | write "^0.2.1" 355 | 356 | fs.realpath@^1.0.0: 357 | version "1.0.0" 358 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 359 | 360 | functional-red-black-tree@^1.0.1: 361 | version "1.0.1" 362 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 363 | 364 | glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: 365 | version "7.1.2" 366 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 367 | dependencies: 368 | fs.realpath "^1.0.0" 369 | inflight "^1.0.4" 370 | inherits "2" 371 | minimatch "^3.0.4" 372 | once "^1.3.0" 373 | path-is-absolute "^1.0.0" 374 | 375 | globals@^11.7.0: 376 | version "11.7.0" 377 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" 378 | 379 | globby@^5.0.0: 380 | version "5.0.0" 381 | resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" 382 | dependencies: 383 | array-union "^1.0.1" 384 | arrify "^1.0.0" 385 | glob "^7.0.3" 386 | object-assign "^4.0.1" 387 | pify "^2.0.0" 388 | pinkie-promise "^2.0.0" 389 | 390 | graceful-fs@^4.1.2, graceful-fs@^4.1.9: 391 | version "4.1.11" 392 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 393 | 394 | has-flag@^3.0.0: 395 | version "3.0.0" 396 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 397 | 398 | iconv-lite@^0.4.24: 399 | version "0.4.24" 400 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 401 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 402 | dependencies: 403 | safer-buffer ">= 2.1.2 < 3" 404 | 405 | ignore@^4.0.6: 406 | version "4.0.6" 407 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" 408 | integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== 409 | 410 | imurmurhash@^0.1.4: 411 | version "0.1.4" 412 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 413 | 414 | inflight@^1.0.4: 415 | version "1.0.6" 416 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 417 | dependencies: 418 | once "^1.3.0" 419 | wrappy "1" 420 | 421 | inherits@2: 422 | version "2.0.3" 423 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 424 | 425 | ini@^1.3.5: 426 | version "1.3.5" 427 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" 428 | 429 | inquirer@^6.1.0: 430 | version "6.2.0" 431 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" 432 | integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== 433 | dependencies: 434 | ansi-escapes "^3.0.0" 435 | chalk "^2.0.0" 436 | cli-cursor "^2.1.0" 437 | cli-width "^2.0.0" 438 | external-editor "^3.0.0" 439 | figures "^2.0.0" 440 | lodash "^4.17.10" 441 | mute-stream "0.0.7" 442 | run-async "^2.2.0" 443 | rxjs "^6.1.0" 444 | string-width "^2.1.0" 445 | strip-ansi "^4.0.0" 446 | through "^2.3.6" 447 | 448 | is-accessor-descriptor@^1.0.0: 449 | version "1.0.0" 450 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" 451 | dependencies: 452 | kind-of "^6.0.0" 453 | 454 | is-buffer@^1.1.5: 455 | version "1.1.6" 456 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 457 | 458 | is-data-descriptor@^1.0.0: 459 | version "1.0.0" 460 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" 461 | dependencies: 462 | kind-of "^6.0.0" 463 | 464 | is-descriptor@^1.0.0: 465 | version "1.0.2" 466 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" 467 | dependencies: 468 | is-accessor-descriptor "^1.0.0" 469 | is-data-descriptor "^1.0.0" 470 | kind-of "^6.0.2" 471 | 472 | is-fullwidth-code-point@^2.0.0: 473 | version "2.0.0" 474 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 475 | 476 | is-number@^3.0.0: 477 | version "3.0.0" 478 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" 479 | dependencies: 480 | kind-of "^3.0.2" 481 | 482 | is-path-cwd@^1.0.0: 483 | version "1.0.0" 484 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" 485 | 486 | is-path-in-cwd@^1.0.0: 487 | version "1.0.1" 488 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" 489 | dependencies: 490 | is-path-inside "^1.0.0" 491 | 492 | is-path-inside@^1.0.0: 493 | version "1.0.1" 494 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" 495 | dependencies: 496 | path-is-inside "^1.0.1" 497 | 498 | is-promise@^2.1.0: 499 | version "2.1.0" 500 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 501 | 502 | is-resolvable@^1.1.0: 503 | version "1.1.0" 504 | resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" 505 | 506 | isexe@^2.0.0: 507 | version "2.0.0" 508 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 509 | 510 | js-tokens@^4.0.0: 511 | version "4.0.0" 512 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 513 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 514 | 515 | js-yaml@^3.12.0: 516 | version "3.12.0" 517 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" 518 | integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== 519 | dependencies: 520 | argparse "^1.0.7" 521 | esprima "^4.0.0" 522 | 523 | js2xmlparser@~3.0.0: 524 | version "3.0.0" 525 | resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-3.0.0.tgz#3fb60eaa089c5440f9319f51760ccd07e2499733" 526 | dependencies: 527 | xmlcreate "^1.0.1" 528 | 529 | jsdoc@^3.5.5: 530 | version "3.5.5" 531 | resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.5.5.tgz#484521b126e81904d632ff83ec9aaa096708fa4d" 532 | dependencies: 533 | babylon "7.0.0-beta.19" 534 | bluebird "~3.5.0" 535 | catharsis "~0.8.9" 536 | escape-string-regexp "~1.0.5" 537 | js2xmlparser "~3.0.0" 538 | klaw "~2.0.0" 539 | marked "~0.3.6" 540 | mkdirp "~0.5.1" 541 | requizzle "~0.2.1" 542 | strip-json-comments "~2.0.1" 543 | taffydb "2.6.2" 544 | underscore "~1.8.3" 545 | 546 | json-schema-traverse@^0.4.1: 547 | version "0.4.1" 548 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 549 | 550 | json-stable-stringify-without-jsonify@^1.0.1: 551 | version "1.0.1" 552 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 553 | 554 | kind-of@^3.0.2: 555 | version "3.2.2" 556 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" 557 | dependencies: 558 | is-buffer "^1.1.5" 559 | 560 | kind-of@^6.0.0, kind-of@^6.0.2: 561 | version "6.0.2" 562 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" 563 | 564 | klaw@~2.0.0: 565 | version "2.0.0" 566 | resolved "https://registry.yarnpkg.com/klaw/-/klaw-2.0.0.tgz#59c128e0dc5ce410201151194eeb9cbf858650f6" 567 | dependencies: 568 | graceful-fs "^4.1.9" 569 | 570 | levn@^0.3.0, levn@~0.3.0: 571 | version "0.3.0" 572 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 573 | dependencies: 574 | prelude-ls "~1.1.2" 575 | type-check "~0.3.2" 576 | 577 | lodash@^4.17.10: 578 | version "4.17.11" 579 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" 580 | integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== 581 | 582 | lodash@^4.17.5: 583 | version "4.17.10" 584 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" 585 | 586 | marked@~0.3.6: 587 | version "0.3.19" 588 | resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790" 589 | 590 | mimic-fn@^1.0.0: 591 | version "1.2.0" 592 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" 593 | 594 | minimatch@^3.0.4: 595 | version "3.0.4" 596 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 597 | dependencies: 598 | brace-expansion "^1.1.7" 599 | 600 | minimist@0.0.8: 601 | version "0.0.8" 602 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 603 | 604 | mkdirp@^0.5.1, mkdirp@~0.5.1: 605 | version "0.5.1" 606 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 607 | dependencies: 608 | minimist "0.0.8" 609 | 610 | moment-duration-format@^2.2.2: 611 | version "2.2.2" 612 | resolved "https://registry.yarnpkg.com/moment-duration-format/-/moment-duration-format-2.2.2.tgz#b957612de26016c9ad9eb6087c054573e5127779" 613 | 614 | moment@^2.22.2: 615 | version "2.22.2" 616 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" 617 | 618 | ms@^2.1.1: 619 | version "2.1.1" 620 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 621 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 622 | 623 | mute-stream@0.0.7: 624 | version "0.0.7" 625 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 626 | 627 | natural-compare@^1.4.0: 628 | version "1.4.0" 629 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 630 | 631 | nice-try@^1.0.4: 632 | version "1.0.4" 633 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" 634 | 635 | object-assign@^4.0.1: 636 | version "4.1.1" 637 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 638 | 639 | once@^1.3.0: 640 | version "1.4.0" 641 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 642 | dependencies: 643 | wrappy "1" 644 | 645 | onetime@^2.0.0: 646 | version "2.0.1" 647 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 648 | dependencies: 649 | mimic-fn "^1.0.0" 650 | 651 | optionator@^0.8.2: 652 | version "0.8.2" 653 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 654 | dependencies: 655 | deep-is "~0.1.3" 656 | fast-levenshtein "~2.0.4" 657 | levn "~0.3.0" 658 | prelude-ls "~1.1.2" 659 | type-check "~0.3.2" 660 | wordwrap "~1.0.0" 661 | 662 | os-tmpdir@~1.0.2: 663 | version "1.0.2" 664 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 665 | 666 | path-is-absolute@^1.0.0: 667 | version "1.0.1" 668 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 669 | 670 | path-is-inside@^1.0.1, path-is-inside@^1.0.2: 671 | version "1.0.2" 672 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 673 | 674 | path-key@^2.0.1: 675 | version "2.0.1" 676 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 677 | 678 | pify@^2.0.0: 679 | version "2.3.0" 680 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 681 | 682 | pinkie-promise@^2.0.0: 683 | version "2.0.1" 684 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 685 | dependencies: 686 | pinkie "^2.0.0" 687 | 688 | pinkie@^2.0.0: 689 | version "2.0.4" 690 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 691 | 692 | pluralize@^7.0.0: 693 | version "7.0.0" 694 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" 695 | 696 | prelude-ls@~1.1.2: 697 | version "1.1.2" 698 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 699 | 700 | progress@^2.0.0: 701 | version "2.0.0" 702 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" 703 | 704 | punycode@^2.1.0: 705 | version "2.1.1" 706 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 707 | 708 | regexpp@^2.0.1: 709 | version "2.0.1" 710 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" 711 | integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== 712 | 713 | require-uncached@^1.0.3: 714 | version "1.0.3" 715 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" 716 | dependencies: 717 | caller-path "^0.1.0" 718 | resolve-from "^1.0.0" 719 | 720 | requizzle@~0.2.1: 721 | version "0.2.1" 722 | resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.1.tgz#6943c3530c4d9a7e46f1cddd51c158fc670cdbde" 723 | dependencies: 724 | underscore "~1.6.0" 725 | 726 | resolve-from@^1.0.0: 727 | version "1.0.1" 728 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" 729 | 730 | restore-cursor@^2.0.0: 731 | version "2.0.0" 732 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 733 | dependencies: 734 | onetime "^2.0.0" 735 | signal-exit "^3.0.2" 736 | 737 | rimraf@^2.2.8: 738 | version "2.6.2" 739 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 740 | dependencies: 741 | glob "^7.0.5" 742 | 743 | run-async@^2.2.0: 744 | version "2.3.0" 745 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" 746 | dependencies: 747 | is-promise "^2.1.0" 748 | 749 | rxjs@^6.1.0: 750 | version "6.3.3" 751 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55" 752 | integrity sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw== 753 | dependencies: 754 | tslib "^1.9.0" 755 | 756 | "safer-buffer@>= 2.1.2 < 3": 757 | version "2.1.2" 758 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 759 | 760 | semver@^5.5.0: 761 | version "5.5.0" 762 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" 763 | 764 | semver@^5.5.1: 765 | version "5.6.0" 766 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" 767 | integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== 768 | 769 | shebang-command@^1.2.0: 770 | version "1.2.0" 771 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 772 | dependencies: 773 | shebang-regex "^1.0.0" 774 | 775 | shebang-regex@^1.0.0: 776 | version "1.0.0" 777 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 778 | 779 | signal-exit@^3.0.2: 780 | version "3.0.2" 781 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 782 | 783 | slice-ansi@1.0.0: 784 | version "1.0.0" 785 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" 786 | dependencies: 787 | is-fullwidth-code-point "^2.0.0" 788 | 789 | sprintf-js@~1.0.2: 790 | version "1.0.3" 791 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 792 | 793 | string-width@^2.1.0, string-width@^2.1.1: 794 | version "2.1.1" 795 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 796 | dependencies: 797 | is-fullwidth-code-point "^2.0.0" 798 | strip-ansi "^4.0.0" 799 | 800 | strip-ansi@^4.0.0: 801 | version "4.0.0" 802 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 803 | dependencies: 804 | ansi-regex "^3.0.0" 805 | 806 | strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: 807 | version "2.0.1" 808 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 809 | 810 | supports-color@^5.3.0: 811 | version "5.4.0" 812 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 813 | dependencies: 814 | has-flag "^3.0.0" 815 | 816 | table@^5.0.2: 817 | version "5.1.0" 818 | resolved "https://registry.yarnpkg.com/table/-/table-5.1.0.tgz#69a54644f6f01ad1628f8178715b408dc6bf11f7" 819 | integrity sha512-e542in22ZLhD/fOIuXs/8yDZ9W61ltF8daM88rkRNtgTIct+vI2fTnAyu/Db2TCfEcI8i7mjZz6meLq0nW7TYg== 820 | dependencies: 821 | ajv "^6.5.3" 822 | lodash "^4.17.10" 823 | slice-ansi "1.0.0" 824 | string-width "^2.1.1" 825 | 826 | taffydb@2.6.2: 827 | version "2.6.2" 828 | resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268" 829 | 830 | text-table@^0.2.0: 831 | version "0.2.0" 832 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 833 | 834 | through@^2.3.6: 835 | version "2.3.8" 836 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 837 | 838 | tmp@^0.0.33: 839 | version "0.0.33" 840 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 841 | dependencies: 842 | os-tmpdir "~1.0.2" 843 | 844 | tslib@^1.9.0: 845 | version "1.9.3" 846 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" 847 | integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== 848 | 849 | type-check@~0.3.2: 850 | version "0.3.2" 851 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 852 | dependencies: 853 | prelude-ls "~1.1.2" 854 | 855 | underscore-contrib@~0.3.0: 856 | version "0.3.0" 857 | resolved "https://registry.yarnpkg.com/underscore-contrib/-/underscore-contrib-0.3.0.tgz#665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7" 858 | dependencies: 859 | underscore "1.6.0" 860 | 861 | underscore@1.6.0, underscore@~1.6.0: 862 | version "1.6.0" 863 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" 864 | 865 | underscore@~1.8.3: 866 | version "1.8.3" 867 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" 868 | 869 | uri-js@^4.2.2: 870 | version "4.2.2" 871 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" 872 | integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== 873 | dependencies: 874 | punycode "^2.1.0" 875 | 876 | which@^1.2.9, which@^1.3.1: 877 | version "1.3.1" 878 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 879 | dependencies: 880 | isexe "^2.0.0" 881 | 882 | window-size@^1.1.1: 883 | version "1.1.1" 884 | resolved "https://registry.yarnpkg.com/window-size/-/window-size-1.1.1.tgz#9858586580ada78ab26ecd6978a6e03115c1af20" 885 | dependencies: 886 | define-property "^1.0.0" 887 | is-number "^3.0.0" 888 | 889 | wordwrap@~1.0.0: 890 | version "1.0.0" 891 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 892 | 893 | wrappy@1: 894 | version "1.0.2" 895 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 896 | 897 | write@^0.2.1: 898 | version "0.2.1" 899 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 900 | dependencies: 901 | mkdirp "^0.5.1" 902 | 903 | xmlcreate@^1.0.1: 904 | version "1.0.2" 905 | resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-1.0.2.tgz#fa6bf762a60a413fb3dd8f4b03c5b269238d308f" 906 | --------------------------------------------------------------------------------