From 469fe2bcd4c2bed4288f9f545ea78f2903304011 Mon Sep 17 00:00:00 2001 From: Yann IRBAH Date: Wed, 2 Sep 2020 16:03:00 +0200 Subject: [PATCH] Implement the init command (#3) ## Description Implements the init command. When a student runs the init command it will do the following: - Ask for the batch ID and the API secret (since the CLI package will be public, it's still better if the secret is not hardcoded) - Check that the `Workspace` directory exists in the HOME of the machine - Clone the exercises directory as a cache in the CLI config root (`~/.config/sparta/exercises`) - Initialize the student's exercises repo in her `Workspace` - Display instructions for GitHub and open the new repository creation page in the browser ## Related Issue [Cu-7rp5br] ## Motivation and Context It will help students initialize their workspace for an easier start ## How Has This Been Tested? Manually for now. We're a little time bound. Tests will be added later once we're sure the CLI is ready for the upcoming session. ## Screenshots [![asciicast](https://asciinema.org/a/TIAby4UJNfzk69P4XHc03JhZo.svg)](https://asciinema.org/a/TIAby4UJNfzk69P4XHc03JhZo) ## Types of changes - ~Chore (non-breaking change which refactors / improves the existing code base)~ - ~Bug fix (non-breaking change which fixes an issue)~ - New feature (non-breaking change which adds functionality) - ~Breaking change (fix or feature that would cause existing functionality to change)~ ## Checklist: - :white_check_mark: My code follows the code style of this project. - :white_check_mark: My change requires a change to the documentation. - :white_check_mark: I have updated the documentation accordingly. - :white_check_mark: I have read the [**CONTRIBUTING**][CONTRIBUTING_FILE] document. - :red_circle: I have added tests to cover my changes. - :red_circle: All new and existing tests passed. [CONTRIBUTING_FILE]: https://github.com/fewlinesco/guidelines/blob/master/CONTRIBUTING.adoc --- .gitignore | 1 + README.md | 55 ++++++++- bin/run.cmd | 3 - package.json | 16 ++- src/base.ts | 9 ++ src/commands/hello.ts | 31 ----- src/commands/init.ts | 58 +++++++++ src/config/config.ts | 49 ++++++++ src/instructions/init.ts | 25 ++++ src/services/check-workspace.ts | 20 +++ src/services/errors/sparta-error.ts | 12 ++ src/services/init-exercises-repository.ts | 39 ++++++ src/services/render-instructions.ts | 10 ++ src/services/update-exercises-repo-cache.ts | 24 ++++ tsconfig.json | 4 +- yarn.lock | 130 ++++++++++++++++++-- 16 files changed, 428 insertions(+), 58 deletions(-) delete mode 100644 bin/run.cmd create mode 100644 src/base.ts delete mode 100644 src/commands/hello.ts create mode 100644 src/commands/init.ts create mode 100644 src/config/config.ts create mode 100644 src/instructions/init.ts create mode 100644 src/services/check-workspace.ts create mode 100644 src/services/errors/sparta-error.ts create mode 100644 src/services/init-exercises-repository.ts create mode 100644 src/services/render-instructions.ts create mode 100644 src/services/update-exercises-repo-cache.ts diff --git a/.gitignore b/.gitignore index afccc7d..9bbfb58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store *-debug.log *-error.log /.nyc_output diff --git a/README.md b/README.md index bf29f4e..92f30ab 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,58 @@ -sparta -====== - Sparta CLI +====== -## Installation +# Installation ``` $ asdf install $ yarn install ``` -## Usage +# Usage + +```sh-session +$ npm install -g sparta +$ sparta COMMAND +running command... +$ sparta (-v|--version|version) +sparta/1.0.0 darwin-x64 node-v14.6.0 +$ sparta --help [COMMAND] +USAGE + $ sparta COMMAND +... +``` + +# Commands + +* [`sparta help [COMMAND]`](#sparta-help-command) +* [`sparta init`](#sparta-init) + +## `sparta help [COMMAND]` + +display help for sparta + +``` +USAGE + $ sparta help [COMMAND] + +ARGUMENTS + COMMAND command to show help for -## Commands +OPTIONS + --all see all commands in CLI +``` + +_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.0/src/commands/help.ts)_ + +## `sparta init` + +Initializes the Sparta workspace + +``` +USAGE + $ sparta init + +EXAMPLE + $ sparta init +``` + diff --git a/bin/run.cmd b/bin/run.cmd deleted file mode 100644 index 968fc30..0000000 --- a/bin/run.cmd +++ /dev/null @@ -1,3 +0,0 @@ -@echo off - -node "%~dp0\run" %* diff --git a/package.json b/package.json index c6823b5..71c11ad 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,22 @@ "@oclif/command": "^1", "@oclif/config": "^1", "@oclif/plugin-help": "^3", + "fs-extra": "^9.0.1", + "marked": "^1.1.1", + "marked-terminal": "^4.1.0", + "node-emoji": "^1.10.0", + "simple-git": "^2.20.1", "tslib": "^1" }, "devDependencies": { "@fewlines/eslint-config": "^3.0.0", "@oclif/dev-cli": "^1", + "@types/fs-extra": "9.0.1", "@types/jest": "^26.0.10", + "@types/marked": "1.1.0", + "@types/marked-terminal": "3.1.1", + "@types/node": "^14", + "@types/node-emoji": "^1.8.1", "@typescript-eslint/eslint-plugin": "^3.7.0", "@typescript-eslint/parser": "^3.7.0", "eslint": "^7.5.0", @@ -61,9 +71,9 @@ "oclif" ], "license": "MIT", - "main": "lib/index.js", + "main": "dist/index.js", "oclif": { - "commands": "./lib/commands", + "commands": "./dist/commands", "bin": "sparta", "plugins": [ "@oclif/plugin-help" @@ -73,7 +83,7 @@ "scripts": { "postpack": "rm -f oclif.manifest.json", "posttest": "eslint . --ext .ts", - "prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme", + "prepack": "rm -rf dist && tsc -b && oclif-dev manifest && oclif-dev readme", "test": "echo NO TESTS", "version": "oclif-dev readme && git add README.md" }, diff --git a/src/base.ts b/src/base.ts new file mode 100644 index 0000000..fd0d7d0 --- /dev/null +++ b/src/base.ts @@ -0,0 +1,9 @@ +import Command, { flags } from "@oclif/command"; + +import { SpartaError } from "./services/errors/sparta-error"; + +export default abstract class extends Command { + async catch(error: SpartaError): Promise { + this.error(error.message, { suggestions: error.suggestions }); + } +} diff --git a/src/commands/hello.ts b/src/commands/hello.ts deleted file mode 100644 index 6f4800e..0000000 --- a/src/commands/hello.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Command, flags } from "@oclif/command"; - -export default class Hello extends Command { - static description = "describe the command here"; - - static examples = [ - `$ sparta hello -hello world from ./src/hello.ts! -`, - ]; - - static flags = { - help: flags.help({ char: "h" }), - // flag with a value (-n, --name=VALUE) - name: flags.string({ char: "n", description: "name to print" }), - // flag with no value (-f, --force) - force: flags.boolean({ char: "f" }), - }; - - static args = [{ name: "file" }]; - - async run(): Promise { - const { args, flags } = this.parse(Hello); - - const name = flags.name ?? "world"; - this.log(`hello ${name} from ./src/commands/hello.ts`); - if (args.file && flags.force) { - this.log(`you input --force and --file: ${args.file}`); - } - } -} diff --git a/src/commands/init.ts b/src/commands/init.ts new file mode 100644 index 0000000..5d594ab --- /dev/null +++ b/src/commands/init.ts @@ -0,0 +1,58 @@ +import cli from "cli-ux"; +import * as emoji from "node-emoji"; + +import Command from "../base"; +import { loadConfig, ConfigInput, writeConfig } from "../config/config"; +import initInstuctions from "../instructions/init"; +import checkWorkspace from "../services/check-workspace"; +import initExercicesRepository from "../services/init-exercises-repository"; +import renderInstructions from "../services/render-instructions"; +import updateExercisesRepoCache from "../services/update-exercises-repo-cache"; + +export default class Init extends Command { + static description = "Initializes the Sparta workspace"; + + static examples = ["$ sparta init"]; + + async run(): Promise { + const configDir = this.config.configDir; + const userInput = await getUserInput(); + + writeConfig(configDir, userInput); + + const config = loadConfig(configDir); + + this.log(emoji.emojify(":crossed_fingers: Checking Workspace directory")); + checkWorkspace(config); + + this.log(emoji.emojify(":robot_face: Initializing exercises repository")); + await initExercicesRepository(config); + + cli.action.start( + emoji.emojify(":robot_face: Preparing the Sparta configuration"), + ); + await updateExercisesRepoCache(configDir, { delete: true }); + cli.action.stop(); + + this.log(emoji.emojify(":rocket: All Good! Follow the instructions now")); + this.log(renderInstructions(initInstuctions)); + + await cli.anykey( + "Press a key when you are ready to create your GitHub repository", + ); + + await cli.open("https://github.com/new"); + } +} + +async function getUserInput(): Promise { + const batchID = await cli.prompt("What is the ID of your batch ?"); + const sharedSecret = await cli.prompt("Enter the Sparta secret token", { + type: "hide", + }); + + return { + batchID, + sharedSecret, + }; +} diff --git a/src/config/config.ts b/src/config/config.ts new file mode 100644 index 0000000..a33e341 --- /dev/null +++ b/src/config/config.ts @@ -0,0 +1,49 @@ +import * as fs from "fs-extra"; +import * as path from "path"; + +export interface Config { + workspaceDir: string; + exercicesDir: string; + batchID: string; + sharedSecret: string; +} + +export interface ConfigInput { + batchID: string; + sharedSecret: string; +} + +export function loadConfig(configDir: string): Config { + const configPath = path.join(configDir, "config.json"); + fs.ensureFileSync(configPath); + + const writtenConfig: ConfigInput = fs.readJSONSync(configPath); + + const homeDir = process.env.HOME; + + if (!homeDir) { + throw new Error("HOME env variable not set"); + } + + const workspaceDir = path.join(homeDir, "Workspace"); + const exercicesDir = path.join( + workspaceDir, + "fewlines-education", + "exercices", + ); + + return { + ...writtenConfig, + workspaceDir, + exercicesDir, + }; +} + +export function writeConfig(configDir: string, input: ConfigInput): void { + const configPath = path.join(configDir, "config.json"); + + fs.ensureFileSync(configPath); + fs.writeJsonSync(configPath, input, { + spaces: 2, + }); +} diff --git a/src/instructions/init.ts b/src/instructions/init.ts new file mode 100644 index 0000000..a10d9d8 --- /dev/null +++ b/src/instructions/init.ts @@ -0,0 +1,25 @@ +export default ` +--- +# Initialize your GitHub repository + +## Create the repository on GitHub + +When pressing a key, the GitHub repository creation will open. + +Create a \`public\` repository and fill the requested information. + +Check the \`Add .gitignore\` checkbox + +## Bind the repository to your local directory + +Go to your exercises directory and add your GitHub repo as a remote: + +\`\`\`bash +$ git remote add origin git@github.com:/.git +$ git pull origin master +\`\`\` + +Congratulations! Your exercises directory is now ready to be used. + +--- +`; diff --git a/src/services/check-workspace.ts b/src/services/check-workspace.ts new file mode 100644 index 0000000..c537165 --- /dev/null +++ b/src/services/check-workspace.ts @@ -0,0 +1,20 @@ +import * as fs from "fs-extra"; + +import { Config } from "../config/config"; +import { SpartaError } from "./errors/sparta-error"; + +export class WorkspaceMissingError extends SpartaError { + constructor(directory: string) { + const name = "WorkspaceMissingError"; + const message = `Workspace not found.`; + const suggestions = [`Make sure the "${directory}" directory exists.`]; + + super(name, message, suggestions); + } +} + +export default function checkWorkspace(config: Config): void { + if (!fs.existsSync(config.workspaceDir)) { + throw new WorkspaceMissingError(config.workspaceDir); + } +} diff --git a/src/services/errors/sparta-error.ts b/src/services/errors/sparta-error.ts new file mode 100644 index 0000000..8d8203a --- /dev/null +++ b/src/services/errors/sparta-error.ts @@ -0,0 +1,12 @@ +export class SpartaError extends Error { + name: string; + + suggestions: string[]; + + constructor(name: string, message: string, suggestions: string[]) { + super(message); + + this.name = name; + this.suggestions = suggestions; + } +} diff --git a/src/services/init-exercises-repository.ts b/src/services/init-exercises-repository.ts new file mode 100644 index 0000000..0ad0ee8 --- /dev/null +++ b/src/services/init-exercises-repository.ts @@ -0,0 +1,39 @@ +import * as fs from "fs-extra"; +import simpleGit, { SimpleGit, SimpleGitOptions } from "simple-git"; + +import { Config } from "../config/config"; +import { SpartaError } from "./errors/sparta-error"; + +export class ExercisesDirectoryExistsError extends SpartaError { + constructor(directory: string) { + const name = "ExercisesDirectoryExistsError"; + const message = "Exercises directory already exists"; + const suggestions = [ + `Delete the ${directory} directory if you want to start over`, + `Rename the ${directory} directory (e.g. ${directory}-backup) if you want to keep your progress`, + ]; + + super(name, message, suggestions); + } +} + +export default async function initExercicesRepository( + config: Config, +): Promise { + const directory = config.exercicesDir; + + if (fs.existsSync(directory)) { + throw new ExercisesDirectoryExistsError(directory); + } + + fs.ensureDirSync(directory); + + const gitOptions: SimpleGitOptions = { + baseDir: directory, + binary: "git", + maxConcurrentProcesses: 6, + }; + + const git: SimpleGit = simpleGit(gitOptions); + await git.init(); +} diff --git a/src/services/render-instructions.ts b/src/services/render-instructions.ts new file mode 100644 index 0000000..47f02bb --- /dev/null +++ b/src/services/render-instructions.ts @@ -0,0 +1,10 @@ +import * as marked from "marked"; +import * as TerminalRenderer from "marked-terminal"; + +export default function renderInstructions(markdown: string): string { + marked.setOptions({ + renderer: new TerminalRenderer(), + }); + + return marked(markdown); +} diff --git a/src/services/update-exercises-repo-cache.ts b/src/services/update-exercises-repo-cache.ts new file mode 100644 index 0000000..610363b --- /dev/null +++ b/src/services/update-exercises-repo-cache.ts @@ -0,0 +1,24 @@ +import * as fs from "fs-extra"; +import * as path from "path"; +import simpleGit, { SimpleGit } from "simple-git"; + +export interface UpdateExercisesRepoCacheOptions { + delete?: boolean; +} + +export default async function updateExercisesRepoCache( + configDir: string, + options: UpdateExercisesRepoCacheOptions, +): Promise { + const exercicesDir = path.join(configDir, "exercises"); + const git: SimpleGit = simpleGit(); + + if (options.delete) { + fs.removeSync(exercicesDir); + } + + await git.clone( + "git@github.com:fewlines-education/dev-bootcamp-exercises.git", + exercicesDir, + ); +} diff --git a/tsconfig.json b/tsconfig.json index 681cba6..323938a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,9 +7,9 @@ "outDir": "dist", "rootDir": "src", "strict": true, - "target": "es2019" + "target": "es2019", }, "include": [ - "src/**/*" + "src/**/*", ] } diff --git a/yarn.lock b/yarn.lock index 2811a63..845cfe1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -271,6 +271,22 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@eslint/eslintrc@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" + integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.19" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@fewlines/eslint-config@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@fewlines/eslint-config/-/eslint-config-3.0.0.tgz#2d8c2be46b8bfec8222942b8ccc999d6d6d714f3" @@ -473,6 +489,18 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@kwsites/file-exists@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" + integrity sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw== + dependencies: + debug "^4.1.1" + +"@kwsites/promise-deferred@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919" + integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -653,6 +681,13 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== +"@types/fs-extra@9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918" + integrity sha512-B42Sxuaz09MhC3DDeW5kubRcQ5by4iuVQ0cRRWM2lggLzAa/KVom0Aft/208NgMvNQQZ86s5rVcqDdn/SH0/mg== + dependencies: + "@types/node" "*" + "@types/glob@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -696,9 +731,9 @@ "@types/istanbul-lib-report" "*" "@types/jest@26.x", "@types/jest@^26.0.10": - version "26.0.10" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.10.tgz#8faf7e9756c033c39014ae76a7329efea00ea607" - integrity sha512-i2m0oyh8w/Lum7wWK/YOZJakYF8Mx08UaKA1CtbmFeDquVhAEdA7znacsVSf2hJ1OQ/OfVMGN90pw/AtzF8s/Q== + version "26.0.12" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.12.tgz#0f20fef9e74f55a312530284e6178f3b3254f501" + integrity sha512-vZOFjm562IPb1EmaKxMjdcouxVb1l3NqoUH4XC4tDQ2R/AWde+0HXBUhyfc6L+7vc3mJ393U+5vr3nH2CLSVVg== dependencies: jest-diff "^25.2.1" pretty-format "^25.2.1" @@ -713,12 +748,30 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/marked-terminal@3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/marked-terminal/-/marked-terminal-3.1.1.tgz#130214af1afafda611f69e8528c07230a00b32b2" + integrity sha512-nufBv756I0NrRQl0HjCRYycgLz3OcZRm/AMGufmWTEKO22XDWpiTuPwtAWXVj9BY51njLWKkhM9QSCId93M+1Q== + dependencies: + "@types/marked" "*" + chalk "^2.4.1" + +"@types/marked@*", "@types/marked@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/marked/-/marked-1.1.0.tgz#53509b5f127e0c05c19176fcf1d743a41e00ff19" + integrity sha512-j8XXj6/l9kFvCwMyVqozznqpd/nk80krrW+QiIJN60Uu9gX5Pvn4/qPJ2YngQrR3QREPwmrE1f9/EWKVTFzoEw== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/node@*": +"@types/node-emoji@^1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@types/node-emoji/-/node-emoji-1.8.1.tgz#689cb74fdf6e84309bcafce93a135dfecd01de3f" + integrity sha512-0fRfA90FWm6KJfw6P9QGyo0HDTCmthZ7cWaBQndITlaWLTZ6njRyKwrwpzpg+n6kBXBIGKeUHEQuBx7bphGJkA== + +"@types/node@*", "@types/node@^14": version "14.6.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.2.tgz#264b44c5a28dfa80198fc2f7b6d3c8a054b9491f" integrity sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A== @@ -881,7 +934,7 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3: +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: version "6.12.4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== @@ -901,7 +954,7 @@ ansi-escapes@^3.1.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== @@ -1366,6 +1419,13 @@ cli-progress@^3.4.0: colors "^1.1.2" string-width "^4.2.0" +cli-table@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" + integrity sha1-9TsFJmqLGguTSz0IIebi3FkUriM= + dependencies: + colors "1.0.3" + cli-ux@^5.2.1: version "5.5.0" resolved "https://registry.yarnpkg.com/cli-ux/-/cli-ux-5.5.0.tgz#5609fb7dd2561891cb88933721c92e7acd5785f1" @@ -1458,6 +1518,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= + colors@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -1986,11 +2051,12 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint@^7.5.0: - version "7.7.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.7.0.tgz#18beba51411927c4b64da0a8ceadefe4030d6073" - integrity sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg== + version "7.8.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.8.1.tgz#e59de3573fb6a5be8ff526c791571646d124a8fa" + integrity sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w== dependencies: "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.1.3" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -2000,7 +2066,7 @@ eslint@^7.5.0: eslint-scope "^5.1.0" eslint-utils "^2.1.0" eslint-visitor-keys "^1.3.0" - espree "^7.2.0" + espree "^7.3.0" esquery "^1.2.0" esutils "^2.0.2" file-entry-cache "^5.0.1" @@ -2027,7 +2093,7 @@ eslint@^7.5.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^7.2.0: +espree@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== @@ -2665,7 +2731,7 @@ ignore@^5.1.1, ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== -import-fresh@^3.0.0: +import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -3704,6 +3770,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.toarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" + integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= + lodash.upperfirst@^4.2.0: version "4.3.1" resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" @@ -3766,6 +3837,23 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +marked-terminal@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-4.1.0.tgz#01087372d3636dc7cb286475a1d6147187f500e0" + integrity sha512-5KllfAOW02WS6hLRQ7cNvGOxvKW1BKuXELH4EtbWfyWgxQhROoMxEvuQ/3fTgkNjledR0J48F4HbapvYp1zWkQ== + dependencies: + ansi-escapes "^4.3.1" + cardinal "^2.1.1" + chalk "^4.0.0" + cli-table "^0.3.1" + node-emoji "^1.10.0" + supports-hyperlinks "^2.1.0" + +marked@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/marked/-/marked-1.1.1.tgz#e5d61b69842210d5df57b05856e0c91572703e6a" + integrity sha512-mJzT8D2yPxoPh7h0UXkB+dBj4FykPJ2OIfxAWeIHrvoHDkFxukV/29QxoFQoPM6RLEwhIFdJpmKBlqVM3s2ZIw== + merge-source-map@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" @@ -3911,6 +3999,13 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-emoji@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" + integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== + dependencies: + lodash.toarray "^4.4.0" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -4784,6 +4879,15 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +simple-git@^2.20.1: + version "2.20.1" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-2.20.1.tgz#bab3f4d083ed6e1655a7c62ab8e8920eecae86f6" + integrity sha512-aa9s2ZLjXlHCVGbDXQLInMLvLkxKEclqMU9X5HMXi3tLWLxbWObz1UgtyZha6ocHarQtFp0OjQW9KHVR1g6wbA== + dependencies: + "@kwsites/file-exists" "^1.1.1" + "@kwsites/promise-deferred" "^1.1.1" + debug "^4.1.1" + sisteransi@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -5062,7 +5166,7 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@^3.1.0: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==