From f238061af7612e780e9b35a8a252fa95a3475d45 Mon Sep 17 00:00:00 2001 From: Paramtamtam Date: Wed, 8 Jul 2020 16:12:21 +0500 Subject: [PATCH] Basic repo files added --- .dockerignore | 3 + .editorconfig | 15 +++ .github/workflows/release.yml | 87 ++++++++++++++++ .github/workflows/tests.yml | 46 +++++++++ .gitignore | 18 ++++ CHANGELOG.md | 14 +++ Dockerfile | 22 +++++ LICENSE | 21 ++++ Makefile | 21 ++++ README.md | 93 +++++++++++++++++ bin/generator.js | 69 +++++++++++++ configuration.json | 108 ++++++++++++++++++++ docker/docker-entrypoint.sh | 16 +++ docker/nginx-server.conf | 9 ++ package.json | 10 ++ static/favicon.ico | 0 static/index.html | 22 +++++ static/robots.txt | 2 + templates/ghost.html | 42 ++++++++ yarn.lock | 181 ++++++++++++++++++++++++++++++++++ 20 files changed, 799 insertions(+) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100755 bin/generator.js create mode 100644 configuration.json create mode 100755 docker/docker-entrypoint.sh create mode 100644 docker/nginx-server.conf create mode 100644 package.json create mode 100644 static/favicon.ico create mode 100644 static/index.html create mode 100644 static/robots.txt create mode 100644 templates/ghost.html create mode 100644 yarn.lock diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c21a610 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +/out +/node_modules +*.log diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dd3885c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.{yml, yaml, sh, conf}] +indent_size = 2 + +[Makefile] +indent_style = tab diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4ff8e8e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,87 @@ +name: release + +on: + release: # Docs: + types: [published] + +jobs: + demo: + name: Update demonstration, hosted on github pages + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Setup NodeJS + uses: actions/setup-node@v1 # Action page: + with: + node-version: 12 + + - name: Generate version value + run: echo "::set-env name=PACKAGE_VERSION::${GITHUB_REF##*/v}" + + - uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + run: ./bin/generator.js -c ./configuration.json -o ./out + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: content + path: out/ + + - name: Switch to github pages branch + uses: actions/checkout@v2 + with: + ref: gh-pages + + - name: Download artifact + uses: actions/download-artifact@v2 + with: + name: content + + - name: Setup git + run: | + git config --global user.name "$GITHUB_ACTOR" + git config --global user.email 'actions@github.com' + git remote add github "https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git" + + - name: Stage changes + run: git add . + + - name: Commit changes + run: git commit -m "Deploying ${GITHUB_SHA} to Github Pages" + + - name: Push changes + run: git push github --force + + docker-image: + name: Build docker image + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + with: + fetch-depth: 1 + + - name: Generate version value + run: echo "::set-env name=APP_VERSION::${GITHUB_REF##*/}@`echo ${GITHUB_SHA} | cut -c1-8`" + + - name: Generate image tag value + run: echo "::set-env name=IMAGE_TAG::${GITHUB_REF##*/[vV]}" # `/refs/tags/v1.2.3` -> `1.2.3` + + - name: Make docker login + run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_LOGIN }}" --password-stdin &> /dev/null + + - name: Build image + run: docker build --build-arg "APP_VERSION=${APP_VERSION}" --tag "tarampampam/tinifier:${IMAGE_TAG}" --tag "tarampampam/tinifier:latest" -f ./Dockerfile . + + - name: Push version image + run: docker push "tarampampam/tinifier:${IMAGE_TAG}" + + - name: Push latest image + run: docker push "tarampampam/tinifier:latest" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..6616f10 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,46 @@ +name: tests + +on: + push: + branches: + - master + tags-ignore: + - '**' + pull_request: + +jobs: # Docs: + generate: + name: Try to run generator + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Setup NodeJS + uses: actions/setup-node@v1 # Action page: + with: + node-version: 12 + + - uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + run: yarn install + + - name: Run generator + run: ./bin/generator.js -c ./configuration.json -o ./out + + - name: Test file creation + run: test -f ./out/ghost/404.html + + docker-build: + name: Try to run generator + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Build docker image + run: docker build -f ./Dockerfile . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d0d539 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +## IDEs +/.vscode +/.idea + +## Vendors +/node_modules + +## Lock files (use yarn only) +/package-lock.json + +## Dist +/out + +## Temp dirs & trash +/npm-debug.log +/yarn-error.log +.DS_Store +.env* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..30642f4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog + +All notable changes to this package will be documented in this file. + +The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. + +## v1.0.0 + +### Changed + +- WIP + +[keepachangelog]:https://keepachangelog.com/en/1.0.0/ +[semver]:https://semver.org/spec/v2.0.0.html diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..387eba4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# Image page: +FROM node:12.16.2-alpine as builder + +WORKDIR /src + +COPY . . + +RUN set -x \ + && yarn install --frozen-lockfile \ + && ./bin/generator.js -c ./configuration.json -o ./out + +# Image page: +FROM nginx:1.18-alpine + +COPY --from=builder --chown=nginx /src/docker/docker-entrypoint.sh /docker-entrypoint.sh +COPY --from=builder --chown=nginx /src/docker/nginx-server.conf /etc/nginx/conf.d/default.conf +COPY --from=builder --chown=nginx /src/static /opt/html +COPY --from=builder --chown=nginx /src/out /opt/html + +ENTRYPOINT ["/docker-entrypoint.sh"] + +CMD ["nginx", "-g", "daemon off;"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e46ba3d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..35a3430 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +#!/usr/bin/make +# Makefile readme (ru): +# Makefile readme (en): + +SHELL = /bin/sh + +DOCKER_BIN = $(shell command -v docker 2> /dev/null) +APP_NAME = $(notdir $(CURDIR)) + +.DEFAULT_GOAL : help + +help: ## Show this help + @printf "\033[33m%s:\033[0m\n" 'Available commands' + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[32m%-15s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +image: ## Build docker image + $(DOCKER_BIN) build -f ./Dockerfile -t $(APP_NAME):local . + @printf "\n \e[30;42m %s \033[0m\n\n" 'Now you can use image like `docker run --rm -p 8080:8080 $(APP_NAME):local ...`' + +shell: ## Start shell into container with node + $(DOCKER_BIN) run --rm -ti -v "$(shell pwd):/src:rw" -w "/src" --user "$(shell id -u):$(shell id -g)" node:12.16.2-alpine sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..da9864f --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +

+ +

+ +# Static error pages in a Docker container + +[![Build Status][badge_build_status]][link_build_status] +[![License][badge_license]][link_license] + +This repository contains a very simple generator for server error pages _(like `404: Not found`)_ and ready docker image with web server for error pages serving. + +Generator ([`bin/generator.js`](./bin/generator.js)) allows you: + +- Use different templates (section `templates` in configuration file) +- Generate pages with arbitrary content according to a specific template + +Can be used for [Traefik error pages customization](https://docs.traefik.io/middlewares/errorpages/). + +### Usage + +Just execute (installed `nodejs` is required): + +```bash +$ bin/generator.js -c ./configuration.json -o ./out +``` + +And watch into `./out` directory: + +```text +./out +└── ghost + ├── 400.html + ├── 401.html + ├── 403.html + ├── 404.html + ├── ... + └── 505.html +``` + +Default configuration can be found in [`configuration.json`](./configuration.json) file. + +### Docker + +Start image (`nginx` inside): + +```bash +$ docker run --rm -p "8080:8080" tarampampam/error-pages:1.0.0 +``` + +And open in your browser `http://127.0.0.1:8080/ghost/400.html`. Additionally, you can set "default" pages theme by passing `TEMPLATE_NAME` environment variable (eg.: `-e "TEMPLATE_NAME=ghost"`) - in this case all error pages will be accessible in root directory (eg.: `http://127.0.0.1:8080/400.html`). + +Also you can use generated error pages in your own docker images: + +```dockerfile +FROM nginx:1.18-alpine + +COPY --from=tarampampam/error-pages:1.0.0 /opt/html/ghost /usr/share/nginx/html/error-pages +``` + +> [`error_page` for `nginx` configuration](http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page) + +## Changes log + +[![Release date][badge_release_date]][link_releases] +[![Commits since latest release][badge_commits_since_release]][link_commits] + +Changes log can be [found here][link_changes_log]. + +## Support + +[![Issues][badge_issues]][link_issues] +[![Issues][badge_pulls]][link_pulls] + +If you will find any package errors, please, [make an issue][link_create_issue] in current repository. + +## License + +This is open-sourced software licensed under the [MIT License][link_license]. + +[badge_build_status]:https://img.shields.io/github/workflow/status/tarampampam/error-pages-docker/tests/master +[badge_release_date]:https://img.shields.io/github/release-date/tarampampam/error-pages-docker.svg?style=flat-square&maxAge=180 +[badge_commits_since_release]:https://img.shields.io/github/commits-since/tarampampam/error-pages-docker/latest.svg?style=flat-square&maxAge=180 +[badge_issues]:https://img.shields.io/github/issues/tarampampam/error-pages-docker.svg?style=flat-square&maxAge=180 +[badge_pulls]:https://img.shields.io/github/issues-pr/tarampampam/error-pages-docker.svg?style=flat-square&maxAge=180 +[badge_license]:https://img.shields.io/github/license/tarampampam/error-pages-docker.svg?longCache=true +[link_releases]:https://github.com/tarampampam/error-pages-docker/releases +[link_commits]:https://github.com/tarampampam/error-pages-docker/commits +[link_changes_log]:https://github.com/tarampampam/error-pages-docker/blob/master/CHANGELOG.md +[link_issues]:https://github.com/tarampampam/error-pages-docker/issues +[link_pulls]:https://github.com/tarampampam/error-pages-docker/pulls +[link_build_status]:https://travis-ci.org/tarampampam/error-pages-docker +[link_create_issue]:https://github.com/tarampampam/error-pages-docker/issues/new +[link_license]:https://github.com/tarampampam/error-pages-docker/blob/master/LICENSE diff --git a/bin/generator.js b/bin/generator.js new file mode 100755 index 0000000..a5877fa --- /dev/null +++ b/bin/generator.js @@ -0,0 +1,69 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const yargs = require('yargs'); + +const options = yargs + .usage('Usage: -c -d ') + .option("c", {alias: "config", describe: "config file path", type: "string", demandOption: true}) + .option("o", {alias: "out", describe: "output directory path", type: "string", demandOption: true}) + .argv; + +const configFile = options.config; +const outDir = options.out; + +try { + // Make sure that config file exists + if (! fs.existsSync(configFile)) { + throw new Error(`Config file "${configFile}" was not found`); + } + + // Create output directory (if needed) + if (!fs.existsSync(outDir)){ + fs.mkdirSync(outDir); + } + + // Read JSON config file and parse into object + const configContent = JSON.parse(fs.readFileSync(configFile)); + + // Loop over all defined templates in configuration file + configContent.templates.forEach((templateConfig) => { + // Make sure that template layout file exists + if (! fs.existsSync(templateConfig.path)) { + throw new Error(`Template "${templateConfig.name}" was not found in "${templateConfig.path}"`); + } + + // Read layout content into memory prepare output directory for template + const layoutContent = String(fs.readFileSync(templateConfig.path)); + const templateOutDir = path.join(outDir, templateConfig.name); + + if (!fs.existsSync(templateOutDir)){ + fs.mkdirSync(templateOutDir); + } + + console.info(`Use template "${templateConfig.name}" located in "${templateConfig.path}"`); + + // Loop over all pages + configContent.pages.forEach((pageConfig) => { + console.info(`Page with code ${pageConfig.code} generation...`); + + // Make replaces + let result = layoutContent + .replace(/{{\s?code\s?}}/g, pageConfig.code) + .replace(/{{\s?message\s?}}/g, pageConfig.message) + .replace(/{{\s?description\s?}}/g, pageConfig.description); + + // And write into result file + fs.writeFileSync(path.join(templateOutDir, `${pageConfig.code}.${configContent.output.file_extension}`), result, { + encoding: "utf8", + flag: "w+", + mode: 0o644 + }) + }); + }) +} catch (err) { + console.error(err); + + process.exit(1); +} diff --git a/configuration.json b/configuration.json new file mode 100644 index 0000000..5916e55 --- /dev/null +++ b/configuration.json @@ -0,0 +1,108 @@ +{ + "templates": [ + { + "name": "ghost", + "path": "./templates/ghost.html" + } + ], + "output": { + "file_extension": "html" + }, + "pages": [ + { + "code": 400, + "message": "Bad Request", + "description": "The server did not understand the request" + }, + { + "code": 401, + "message": "Unauthorized", + "description": "The requested page needs a username and a password" + }, + { + "code": 403, + "message": "Forbidden", + "description": "Access is forbidden to the requested page" + }, + { + "code": 404, + "message": "Not Found", + "description": "The server can not find the requested page" + }, + { + "code": 405, + "message": "Method Not Allowed", + "description": "The method specified in the request is not allowed" + }, + { + "code": 407, + "message": "Proxy Authentication Required", + "description": "You must authenticate with a proxy server before this request can be served" + }, + { + "code": 408, + "message": "Request Timeout", + "description": "The request took longer than the server was prepared to wait" + }, + { + "code": 409, + "message": "Conflict", + "description": "The request could not be completed because of a conflict" + }, + { + "code": 410, + "message": "Gone", + "description": "The requested page is no longer available" + }, + { + "code": 411, + "message": "Length Required", + "description": "The \"Content-Length\" is not defined. The server will not accept the request without it" + }, + { + "code": 412, + "message": "Precondition Failed", + "description": "The pre condition given in the request evaluated to false by the server" + }, + { + "code": 413, + "message": "Payload Too Large", + "description": "The server will not accept the request, because the request entity is too large" + }, + { + "code": 416, + "message": "Requested Range Not Satisfiable", + "description": "The requested byte range is not available and is out of bounds" + }, + { + "code": 429, + "message": "Too Many Requests", + "description": "Too many requests in a given amount of time" + }, + { + "code": 500, + "message": "Internal Server Error", + "description": "The server met an unexpected condition" + }, + { + "code": 502, + "message": "Bad Gateway", + "description": "The server received an invalid response from the upstream server" + }, + { + "code": 503, + "message": "Service Unavailable", + "description": "The server is temporarily overloading or down" + }, + { + "code": 504, + "message": "Gateway Timeout", + "description": "The gateway has timed out" + }, + { + "code": 505, + "message": "HTTP Version Not Supported", + "description": "The server does not support the \"http protocol\" version" + } + ] +} diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100755 index 0000000..db7f2bd --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env sh +set -e + +TEMPLATE_NAME=${TEMPLATE_NAME:-} # string|empty + +if [ -n "$TEMPLATE_NAME" ]; then + echo "$0: set pages for template '$TEMPLATE_NAME' as default (make accessible in root directory)"; + + if [ ! -d "/opt/html/$TEMPLATE_NAME" ]; then + (>&2 echo "$0: template '$TEMPLATE_NAME' was not found!"); exit 1; + fi; + + ln -f -s "/opt/html/$TEMPLATE_NAME/"* /opt/html; +fi; + +exec "$@" diff --git a/docker/nginx-server.conf b/docker/nginx-server.conf new file mode 100644 index 0000000..23ad5e1 --- /dev/null +++ b/docker/nginx-server.conf @@ -0,0 +1,9 @@ +server { + listen 8080; + server_name _; + + location / { + root /opt/html; + index index.html index.htm; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..16f0f00 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "error-pages", + "repository": { + "type": "git", + "url": "git://github.com/tarampampam/error-pages-docker.git" + }, + "dependencies": { + "yargs": "15.3" + } +} diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..118d418 --- /dev/null +++ b/static/index.html @@ -0,0 +1,22 @@ + + + + + Error pages + + + + +
+

Error pages

+

+ For online documentation and support please refer to project repository. +

+
+ diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/templates/ghost.html b/templates/ghost.html new file mode 100644 index 0000000..7b397f4 --- /dev/null +++ b/templates/ghost.html @@ -0,0 +1,42 @@ + + + + + + {{ code }}: {{ message }} + + + + +
+ + + + + + + + + + + + +

+ + + +

+

Error {{ code }}

+

{{ description }}

+
+ diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..4dbee62 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,181 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@^18.1.1: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@15.3: + version "15.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" + integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.1"