├── .gitignore ├── .npmignore ├── README.md ├── logo.png ├── package.json ├── src ├── commands │ ├── create.js │ ├── disable.js │ ├── enable.js │ ├── init.js │ └── list │ │ ├── index.js │ │ └── template.js ├── constant.js ├── default.conf ├── index.js ├── methods │ └── createConfig.js └── utils │ ├── createDir.js │ ├── createTable.js │ ├── getConfig.js │ ├── isAccessible.js │ ├── isExist.js │ ├── link.js │ ├── parseTemplate.js │ ├── readDir.js │ ├── readFile.js │ ├── slugify.js │ ├── templateReplace.js │ ├── unlink.js │ └── writeFile.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | preview.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-cli 2 | 3 | ![Version](https://img.shields.io/npm/v/nginx-cli?color=%23009639) 4 | 5 | ![Nginx-Cli Logo](https://raw.githubusercontent.com/aykutkardas/nginx-cli/master/logo.png) 6 | 7 | This tool makes it easier to manage nginx configurations and create new ones. It automates the linking of configurations between the `sites-available` and `sites-enabled` directories. It allows you to create templates to create new configurations and use them easily with cli. 8 | 9 | ## Install 10 | 11 | ```sh 12 | npm install -g nginx-cli 13 | ``` 14 | 15 | ### Initialize 16 | 17 | ``` 18 | ➜ nnx init 19 | 20 | ? What's the path of NGINX? /etc/nginx 21 | ? What's the path of sites-available? /etc/nginx/sites-available/ 22 | ? What's the path of sites-enabled? /etc/nginx/sites-enabled/ 23 | ``` 24 | 25 | ## Usage 26 | 27 | ### **Commands** 28 | 29 | `create` 30 | 31 | There are two ways to create a new configuration. Using the default template or by selecting a template. 32 | 33 | ### With Default Template 34 | 35 | ``` 36 | ➜ nnx create 37 | ? What's the value of DOMAIN? example.com 38 | ? What's the value of ROOT? /home/app/example 39 | ? Enable conf file? Yes 40 | ``` 41 | 42 | **Default Template Detail:** `/etc/.nginx-cli/templates/default.conf` 43 | 44 | ``` 45 | server { 46 | listen 80; 47 | server_name {{DOMAIN}}; 48 | 49 | access_log /var/log/nginx/{{DOMAIN}}_access.log; 50 | error_log /var/log/nginx/{{DOMAIN}}_error.log; 51 | 52 | root {{ROOT}}; 53 | index index.html index.htm index.php; 54 | 55 | location / { 56 | try_files $uri $uri/ =404; 57 | } 58 | } 59 | ``` 60 | 61 | ### With Custom Template 62 | 63 | When creating your own template so that it can be parsed, specify the parts you want to edit using the following format. 64 | 65 | `{{KEY}}` 66 | 67 | Copy this template file you created to the `/etc/.nginx-cli/templates` directory. 68 | 69 | You can now call it using the name of this template. 70 | 71 | ```sh 72 | ➜ nnx create template.conf 73 | ``` 74 | 75 | `list` 76 | 77 | ### List Nginx Confs 78 | 79 | ``` 80 | ➜ nnx list 81 | 82 | ┌──────────────────┬─────────┐ 83 | │ Conf Name │ Status │ 84 | ├──────────────────┼─────────┤ 85 | │ example_com.conf │ enabled │ 86 | └──────────────────┴─────────┘ 87 | ``` 88 | 89 | ### List Nginx Templates 90 | 91 | ``` 92 | ➜ nnx list template 93 | 94 | ┌───────────────┐ 95 | │ Template Name │ 96 | ├───────────────┤ 97 | │ default.conf │ 98 | └───────────────┘ 99 | ``` 100 | 101 | `enable` 102 | 103 | ``` 104 | ➜ nnx enable example_com.conf 105 | 106 | ┌──────────────────┬─────────┐ 107 | │ Conf Name │ Status │ 108 | ├──────────────────┼─────────┤ 109 | │ example_com.conf │ enabled │ 110 | └──────────────────┴─────────┘ 111 | ``` 112 | 113 | `disable` 114 | 115 | ``` 116 | ➜ nnx disable example_com.conf 117 | 118 | ┌──────────────────┬────────┐ 119 | │ Conf Name │ Status │ 120 | ├──────────────────┼────────┤ 121 | │ example_com.conf │ │ 122 | └──────────────────┴────────┘ 123 | ``` 124 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykutkardas/nginx-cli/22621a2f977845d7248e9e1953d6861df8f799d7/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nginx-cli", 3 | "version": "0.3.4", 4 | "description": "Allows viewing and managing Nginx configuration files.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/aykutkardas/nginx-cli.git" 12 | }, 13 | "bin": { 14 | "nnx": "src/index.js" 15 | }, 16 | "keywords": [ 17 | "nginx", 18 | "cli", 19 | "tool" 20 | ], 21 | "author": "Aykut Kardaş", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/aykutkardas/nginx-cli/issues" 25 | }, 26 | "homepage": "https://github.com/aykutkardas/nginx-cli#readme", 27 | "dependencies": { 28 | "cli-table3": "^0.6.0", 29 | "colors": "^1.4.0", 30 | "commander": "^6.2.1", 31 | "inquirer": "^7.3.3", 32 | "slugify": "^1.4.6" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/commands/create.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const fs = require("fs"); 3 | 4 | const { errors } = require("../constant"); 5 | 6 | const link = require("../utils/link"); 7 | const slugify = require("../utils/slugify"); 8 | const getConfig = require("../utils/getConfig"); 9 | const parseTemplate = require("../utils/parseTemplate"); 10 | const templateReplace = require("../utils/templateReplace"); 11 | const list = require("./list"); 12 | 13 | async function create(params) { 14 | let confName = (params && params.args[0]) || "default.conf"; 15 | 16 | const config = await getConfig(); 17 | if (!config) return; 18 | 19 | const { paths } = config; 20 | let templateContent = ""; 21 | let templateParams; 22 | 23 | try { 24 | const path = __dirname + "/../" + confName; 25 | templateContent = await fs.readFileSync(path, "utf8"); 26 | templateParams = parseTemplate(templateContent); 27 | } catch (err) { 28 | console.error(errors.createError.red); 29 | return null; 30 | } 31 | 32 | if (templateParams) 33 | (async () => { 34 | let answers = []; 35 | 36 | for (let i = 0; i < templateParams.length; i++) { 37 | const answer = await inquirer.prompt([ 38 | { 39 | message: `What's the value of ${templateParams[i]}?`, 40 | name: templateParams[i], 41 | type: "input", 42 | }, 43 | ]); 44 | 45 | answers.push(answer); 46 | } 47 | 48 | const confirm = await inquirer.prompt([ 49 | { 50 | default: true, 51 | message: "Enable conf file?", 52 | name: "enable", 53 | type: "confirm", 54 | }, 55 | ]); 56 | 57 | const data = { answers: { ...confirm } }; 58 | 59 | answers.forEach( 60 | (answer) => (data.answers = { ...data.answers, ...answer }) 61 | ); 62 | 63 | return data.answers; 64 | })() 65 | .then((asnwers) => { 66 | const confContent = templateReplace(templateContent, { 67 | ...asnwers, 68 | serverNameSlug: slugify(asnwers.DOMAIN), 69 | }); 70 | 71 | fs.promises 72 | .access("/etc", fs.constants.R_OK | fs.constants.W_OK) 73 | .then(async () => { 74 | const conf_name = slugify(asnwers.DOMAIN, "_") + ".conf"; 75 | const currentPath = paths.sitesAvailable + conf_name; 76 | const newPath = paths.sitesEnabled + conf_name; 77 | 78 | await fs.writeFileSync(currentPath, confContent); 79 | if (asnwers.enable) { 80 | await link(currentPath, newPath); 81 | } 82 | list(); 83 | }) 84 | .catch((err) => console.error("cannot access", err)); 85 | }) 86 | .catch(console.error); 87 | } 88 | 89 | module.exports = create; 90 | -------------------------------------------------------------------------------- /src/commands/disable.js: -------------------------------------------------------------------------------- 1 | const getConfig = require("../utils/getConfig"); 2 | const unlink = require("../utils/unlink"); 3 | const list = require("./list"); 4 | const { errors } = require("../constant"); 5 | 6 | module.exports = async function disable(confName) { 7 | const config = await getConfig(); 8 | if (!config) return; 9 | 10 | const { paths } = config; 11 | const path = paths.sitesEnabled + confName; 12 | 13 | const unlinked = await unlink(path); 14 | 15 | if (unlinked) { 16 | list(); 17 | } else { 18 | console.log(errors.unlinkError.red); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/commands/enable.js: -------------------------------------------------------------------------------- 1 | const getConfig = require("../utils/getConfig"); 2 | const link = require("../utils/link"); 3 | const list = require("./list"); 4 | 5 | module.exports = async function enable(confName) { 6 | const config = await getConfig(); 7 | if (!config) return; 8 | 9 | const { paths } = config; 10 | const currentPath = paths.sitesAvailable + confName; 11 | const newPath = paths.sitesEnabled + confName; 12 | 13 | const isLinked = await link(currentPath, newPath); 14 | 15 | if (isLinked) { 16 | list(); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/commands/init.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | 3 | const { paths } = require("../constant"); 4 | const writeFile = require("../utils/writeFile"); 5 | const createConfig = require("../methods/createConfig"); 6 | 7 | function init() { 8 | (async () => { 9 | const nginxPath = await inquirer.prompt([ 10 | { 11 | default: paths.nginxBasePath, 12 | message: "What's the path of NGINX?", 13 | name: "base", 14 | type: "input", 15 | }, 16 | ]); 17 | 18 | const sitesAvailable = await inquirer.prompt([ 19 | { 20 | default: nginxPath.base + paths.sitesAvailable, 21 | message: "What's the path of sites-available?", 22 | name: "sitesAvailable", 23 | type: "input", 24 | }, 25 | ]); 26 | 27 | const sitesEnabled = await inquirer.prompt([ 28 | { 29 | default: nginxPath.base + paths.sitesEnabled, 30 | message: "What's the path of sites-enabled?", 31 | name: "sitesEnabled", 32 | type: "input", 33 | }, 34 | ]); 35 | return { ...nginxPath, ...sitesAvailable, ...sitesEnabled }; 36 | })() 37 | .then(async (asnwers) => { 38 | const isCreatedConfig = await createConfig(); 39 | 40 | if (isCreatedConfig) { 41 | const content = JSON.stringify({ paths: asnwers }, null, 2) + "\n"; 42 | const isWritedConfig = await writeFile(paths.config, content); 43 | 44 | if (isWritedConfig) { 45 | console.log("Success".green); 46 | } else { 47 | console.log("Unexpected Error!".red); 48 | } 49 | } 50 | }) 51 | .catch(console.error); 52 | } 53 | 54 | module.exports = init; 55 | -------------------------------------------------------------------------------- /src/commands/list/index.js: -------------------------------------------------------------------------------- 1 | const readDir = require("../../utils/readDir"); 2 | const getConfig = require("../../utils/getConfig"); 3 | const createTable = require("../../utils/createTable"); 4 | const listTemplate = require("./template"); 5 | const { table } = require("../../constant"); 6 | 7 | module.exports = async function list(listName) { 8 | if (listName && listName.args[0] === "template") { 9 | listTemplate(); 10 | return; 11 | } 12 | 13 | const config = await getConfig(); 14 | if (!config) return; 15 | 16 | const { paths } = config; 17 | const data = []; 18 | 19 | readDir(paths.sitesAvailable) 20 | .then(function (sitesAvailable) { 21 | readDir(paths.sitesEnabled) 22 | .then(function (sitesEnabled) { 23 | const result = {}; 24 | 25 | sitesAvailable.forEach(function (item) { 26 | const isEnabled = sitesEnabled.includes(item); 27 | result[item] = isEnabled ? "enabled" : ""; 28 | }); 29 | 30 | Object.entries(result).forEach(([name, status]) => { 31 | data.push([name, status.green]); 32 | }); 33 | 34 | const tableData = createTable(table.listHead, data); 35 | console.log(tableData); 36 | }) 37 | .catch(console.error); 38 | }) 39 | .catch(console.error); 40 | }; 41 | -------------------------------------------------------------------------------- /src/commands/list/template.js: -------------------------------------------------------------------------------- 1 | const readDir = require("../../utils/readDir"); 2 | const createTable = require("../../utils/createTable"); 3 | const { table, paths } = require("../../constant"); 4 | 5 | async function template() { 6 | readDir(paths.templates) 7 | .then(function (temlpates) { 8 | const data = []; 9 | 10 | temlpates.forEach((template) => { 11 | data.push([template]); 12 | }); 13 | 14 | const tableData = createTable(table.templateHead, data); 15 | console.log(tableData); 16 | }) 17 | .catch(console.error); 18 | } 19 | 20 | module.exports = template; 21 | -------------------------------------------------------------------------------- /src/constant.js: -------------------------------------------------------------------------------- 1 | const errors = { 2 | nonExist: "File or directory not found.", 3 | notAccessible: "File or directory not accessible.", 4 | notFoundSourceFile: "The file to link was not found.", 5 | alreadyLinkedFile: "This file is already linked.", 6 | unlinkError: "There was a problem removing the file.", 7 | createError: "Try again as sudo. Make sure the template file you specified exists." 8 | }; 9 | 10 | const table = { 11 | listHead: ["Conf Name".blue, "Status".blue], 12 | templateHead: ["Template Name".blue], 13 | }; 14 | 15 | const paths = { 16 | config: "/etc/.nginx-cli/config.json", 17 | templates: "/etc/.nginx-cli/templates", 18 | nginxBasePath: "/etc/nginx", 19 | sitesAvailable: "/sites-available/", 20 | sitesEnabled: "/sites-enabled/", 21 | }; 22 | 23 | module.exports = { errors, paths, table }; 24 | -------------------------------------------------------------------------------- /src/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name {{DOMAIN}}; 4 | 5 | access_log /var/log/nginx/{{DOMAIN}}_access.log; 6 | error_log /var/log/nginx/{{DOMAIN}}_error.log; 7 | 8 | root {{ROOT}}; 9 | index index.html index.htm index.php; 10 | 11 | location / { 12 | try_files $uri $uri/ =404; 13 | } 14 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require("colors"); 4 | 5 | const program = require("commander"); 6 | 7 | program.command("init").action(require("./commands/init")); 8 | program.command("list").action(require("./commands/list")); 9 | program.command("create").action(require("./commands/create")); 10 | program.command("enable ").action(require("./commands/enable")); 11 | program.command("disable ").action(require("./commands/disable")); 12 | 13 | program.parse(process.argv); 14 | -------------------------------------------------------------------------------- /src/methods/createConfig.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const getConfig = require("../utils/getConfig"); 3 | const createDir = require("../utils/createDir"); 4 | const link = require("../utils/link"); 5 | 6 | async function createConfig() { 7 | const olConfig = await getConfig(); 8 | const configPath = "/etc/.nginx-cli"; 9 | 10 | if (!olConfig) { 11 | const isCreatedDir = await createDir(configPath); 12 | 13 | if (isCreatedDir) { 14 | await fs.writeFileSync(configPath + "/config.json", ""); 15 | const templatePath = configPath + "/templates"; 16 | const isCreatedTemplateDir = await createDir(templatePath); 17 | 18 | if (isCreatedTemplateDir) { 19 | await link( 20 | __dirname + "/../default.conf", 21 | templatePath + "/default.conf" 22 | ); 23 | } 24 | 25 | return true; 26 | } 27 | } 28 | 29 | return false; 30 | } 31 | 32 | module.exports = createConfig; 33 | -------------------------------------------------------------------------------- /src/utils/createDir.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const isAccessible = require("./isAccessible"); 3 | 4 | async function createDir(path, authorization = 0777) { 5 | const accesible = await isAccessible(path); 6 | 7 | if (!accesible) { 8 | fs.mkdirSync(path, authorization); 9 | return true; 10 | } 11 | 12 | return false; 13 | } 14 | 15 | module.exports = createDir; 16 | 17 | -------------------------------------------------------------------------------- /src/utils/createTable.js: -------------------------------------------------------------------------------- 1 | const Table = require("cli-table3"); 2 | 3 | function createTable(head, data) { 4 | if (!Array.isArray(head) || !Array.isArray(data)) { 5 | return null; 6 | } 7 | 8 | const table = new Table({ head }); 9 | table.push(...data); 10 | return table.toString(); 11 | } 12 | 13 | module.exports = createTable; 14 | -------------------------------------------------------------------------------- /src/utils/getConfig.js: -------------------------------------------------------------------------------- 1 | const readFile = require("./readFile"); 2 | const { paths } = require("../constant"); 3 | 4 | async function getConfig() { 5 | const config = await readFile(paths.config); 6 | 7 | if (config) { 8 | return JSON.parse(config); 9 | } 10 | 11 | return null; 12 | } 13 | 14 | module.exports = getConfig; 15 | -------------------------------------------------------------------------------- /src/utils/isAccessible.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const isExist = require("./isExist"); 3 | 4 | async function isAccessible(path) { 5 | const exist = await isExist(path); 6 | 7 | if (exist) { 8 | try { 9 | fs.accessSync(path, fs.constants.R_OK | fs.constants.W_OK); 10 | return true; 11 | } catch (err) {} 12 | } 13 | 14 | return false; 15 | } 16 | 17 | module.exports = isAccessible; 18 | -------------------------------------------------------------------------------- /src/utils/isExist.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | async function isExist(path) { 4 | const isExist = await fs.existsSync(path); 5 | 6 | if (isExist) { 7 | return true; 8 | } else { 9 | return false; 10 | } 11 | } 12 | 13 | module.exports = isExist; 14 | -------------------------------------------------------------------------------- /src/utils/link.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const isExist = require("./isExist"); 3 | const { errors } = require("../constant"); 4 | 5 | module.exports = async function link(sourcePath, newPath) { 6 | const isExistSource = await isExist(sourcePath); 7 | 8 | if (isExistSource) { 9 | const isExistTarget = await isExist(newPath); 10 | 11 | if (isExistTarget) { 12 | console.log(errors.alreadyLinkedFile.red); 13 | return false; 14 | } 15 | 16 | try { 17 | await fs.linkSync(sourcePath, newPath); 18 | return true; 19 | } catch (err) { 20 | return false; 21 | } 22 | } else { 23 | console.log(errors.notFoundSourceFile.red); 24 | return false; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/utils/parseTemplate.js: -------------------------------------------------------------------------------- 1 | const parseTemplate = (template) => { 2 | const regex = new RegExp("{{[A-Z]*}}", "gmi"); 3 | 4 | const params = {}; 5 | 6 | while ((param = regex.exec(template)) !== null) { 7 | params[param] = param[0].replace(/[{}]/g, ''); 8 | } 9 | 10 | return Object.values(params); 11 | }; 12 | 13 | module.exports = parseTemplate; 14 | -------------------------------------------------------------------------------- /src/utils/readDir.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | module.exports = async function readDir(path) { 4 | const files = await fs.promises.readdir(path); 5 | return files; 6 | }; 7 | -------------------------------------------------------------------------------- /src/utils/readFile.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const isAccessible = require("./isAccessible"); 3 | 4 | async function readFile(path, flag) { 5 | const accesible = await isAccessible(path); 6 | 7 | if (accesible) { 8 | const file = await fs.readFileSync(path, flag); 9 | if (file) { 10 | return file; 11 | } 12 | } 13 | 14 | return null; 15 | } 16 | 17 | module.exports = readFile; 18 | -------------------------------------------------------------------------------- /src/utils/slugify.js: -------------------------------------------------------------------------------- 1 | const slugify = require("slugify"); 2 | 3 | module.exports = function (str) { 4 | return slugify(str, { 5 | replacement: "-", 6 | lower: true, 7 | remove: /[*+~,()'"!:@]/g, 8 | }).replace(/\./g, "_"); 9 | }; 10 | -------------------------------------------------------------------------------- /src/utils/templateReplace.js: -------------------------------------------------------------------------------- 1 | module.exports = function (str, values) { 2 | let newStr = str; 3 | 4 | for (key in values) { 5 | const regex = new RegExp(`\{\{${key}\}\}`, "gmi"); 6 | newStr = newStr.replace(regex, values[key]); 7 | } 8 | 9 | return newStr; 10 | }; 11 | -------------------------------------------------------------------------------- /src/utils/unlink.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const isAccessible = require("./isAccessible"); 3 | 4 | module.exports = async function unlink(path) { 5 | const accessible = await isAccessible(path); 6 | 7 | if (accessible) { 8 | try { 9 | await fs.unlinkSync(path); 10 | return true; 11 | } catch (err) { 12 | return false; 13 | } 14 | } else { 15 | return false; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/utils/writeFile.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const isAccessible = require("./isAccessible"); 4 | const { paths } = require("../constant"); 5 | 6 | async function writeFile(path, body) { 7 | const accesible = await isAccessible(path); 8 | 9 | if (accesible) { 10 | fs.writeFileSync(paths.config, body); 11 | return true; 12 | } 13 | 14 | return false; 15 | } 16 | 17 | module.exports = writeFile; 18 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-escapes@^4.2.1: 6 | version "4.3.1" 7 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" 8 | integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== 9 | dependencies: 10 | type-fest "^0.11.0" 11 | 12 | ansi-regex@^5.0.0: 13 | version "5.0.0" 14 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" 15 | integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== 16 | 17 | ansi-styles@^4.1.0: 18 | version "4.3.0" 19 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 20 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 21 | dependencies: 22 | color-convert "^2.0.1" 23 | 24 | chalk@^4.1.0: 25 | version "4.1.0" 26 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" 27 | integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== 28 | dependencies: 29 | ansi-styles "^4.1.0" 30 | supports-color "^7.1.0" 31 | 32 | chardet@^0.7.0: 33 | version "0.7.0" 34 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" 35 | integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== 36 | 37 | cli-cursor@^3.1.0: 38 | version "3.1.0" 39 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" 40 | integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== 41 | dependencies: 42 | restore-cursor "^3.1.0" 43 | 44 | cli-table3@^0.6.0: 45 | version "0.6.0" 46 | resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee" 47 | integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ== 48 | dependencies: 49 | object-assign "^4.1.0" 50 | string-width "^4.2.0" 51 | optionalDependencies: 52 | colors "^1.1.2" 53 | 54 | cli-width@^3.0.0: 55 | version "3.0.0" 56 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" 57 | integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== 58 | 59 | color-convert@^2.0.1: 60 | version "2.0.1" 61 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 62 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 63 | dependencies: 64 | color-name "~1.1.4" 65 | 66 | color-name@~1.1.4: 67 | version "1.1.4" 68 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 69 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 70 | 71 | colors@^1.1.2, colors@^1.4.0: 72 | version "1.4.0" 73 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" 74 | integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== 75 | 76 | commander@^6.2.1: 77 | version "6.2.1" 78 | resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" 79 | integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== 80 | 81 | emoji-regex@^8.0.0: 82 | version "8.0.0" 83 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 84 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 85 | 86 | escape-string-regexp@^1.0.5: 87 | version "1.0.5" 88 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 89 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 90 | 91 | external-editor@^3.0.3: 92 | version "3.1.0" 93 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" 94 | integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== 95 | dependencies: 96 | chardet "^0.7.0" 97 | iconv-lite "^0.4.24" 98 | tmp "^0.0.33" 99 | 100 | figures@^3.0.0: 101 | version "3.2.0" 102 | resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" 103 | integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== 104 | dependencies: 105 | escape-string-regexp "^1.0.5" 106 | 107 | has-flag@^4.0.0: 108 | version "4.0.0" 109 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 110 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 111 | 112 | iconv-lite@^0.4.24: 113 | version "0.4.24" 114 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 115 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 116 | dependencies: 117 | safer-buffer ">= 2.1.2 < 3" 118 | 119 | inquirer@^7.3.3: 120 | version "7.3.3" 121 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" 122 | integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== 123 | dependencies: 124 | ansi-escapes "^4.2.1" 125 | chalk "^4.1.0" 126 | cli-cursor "^3.1.0" 127 | cli-width "^3.0.0" 128 | external-editor "^3.0.3" 129 | figures "^3.0.0" 130 | lodash "^4.17.19" 131 | mute-stream "0.0.8" 132 | run-async "^2.4.0" 133 | rxjs "^6.6.0" 134 | string-width "^4.1.0" 135 | strip-ansi "^6.0.0" 136 | through "^2.3.6" 137 | 138 | is-fullwidth-code-point@^3.0.0: 139 | version "3.0.0" 140 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 141 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 142 | 143 | lodash@^4.17.19: 144 | version "4.17.20" 145 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" 146 | integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== 147 | 148 | mimic-fn@^2.1.0: 149 | version "2.1.0" 150 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" 151 | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== 152 | 153 | mute-stream@0.0.8: 154 | version "0.0.8" 155 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" 156 | integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== 157 | 158 | object-assign@^4.1.0: 159 | version "4.1.1" 160 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 161 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 162 | 163 | onetime@^5.1.0: 164 | version "5.1.2" 165 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" 166 | integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== 167 | dependencies: 168 | mimic-fn "^2.1.0" 169 | 170 | os-tmpdir@~1.0.2: 171 | version "1.0.2" 172 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 173 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= 174 | 175 | restore-cursor@^3.1.0: 176 | version "3.1.0" 177 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" 178 | integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== 179 | dependencies: 180 | onetime "^5.1.0" 181 | signal-exit "^3.0.2" 182 | 183 | run-async@^2.4.0: 184 | version "2.4.1" 185 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" 186 | integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== 187 | 188 | rxjs@^6.6.0: 189 | version "6.6.3" 190 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" 191 | integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== 192 | dependencies: 193 | tslib "^1.9.0" 194 | 195 | "safer-buffer@>= 2.1.2 < 3": 196 | version "2.1.2" 197 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 198 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 199 | 200 | signal-exit@^3.0.2: 201 | version "3.0.3" 202 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" 203 | integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== 204 | 205 | slugify@^1.4.6: 206 | version "1.4.6" 207 | resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.6.tgz#ef288d920a47fb01c2be56b3487b6722f5e34ace" 208 | integrity sha512-ZdJIgv9gdrYwhXqxsH9pv7nXxjUEyQ6nqhngRxoAAOlmMGA28FDq5O4/5US4G2/Nod7d1ovNcgURQJ7kHq50KQ== 209 | 210 | string-width@^4.1.0, string-width@^4.2.0: 211 | version "4.2.0" 212 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" 213 | integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== 214 | dependencies: 215 | emoji-regex "^8.0.0" 216 | is-fullwidth-code-point "^3.0.0" 217 | strip-ansi "^6.0.0" 218 | 219 | strip-ansi@^6.0.0: 220 | version "6.0.0" 221 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" 222 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== 223 | dependencies: 224 | ansi-regex "^5.0.0" 225 | 226 | supports-color@^7.1.0: 227 | version "7.2.0" 228 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 229 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 230 | dependencies: 231 | has-flag "^4.0.0" 232 | 233 | through@^2.3.6: 234 | version "2.3.8" 235 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 236 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= 237 | 238 | tmp@^0.0.33: 239 | version "0.0.33" 240 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 241 | integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== 242 | dependencies: 243 | os-tmpdir "~1.0.2" 244 | 245 | tslib@^1.9.0: 246 | version "1.14.1" 247 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" 248 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 249 | 250 | type-fest@^0.11.0: 251 | version "0.11.0" 252 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" 253 | integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== 254 | --------------------------------------------------------------------------------