commit 395ae0eff3e1324bfc4d806f812ba127256553b3 Author: Divlo Date: Sun Jul 5 15:48:51 2020 +0200 🎉 Initial commit diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..dc13074 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,108 @@ +# 💡 Contributing + +## Types of contributions : + +- [Submit a challenge instructions](challenge-instructions) +- [Share a solution for an already existing challenge](solution-for-an-already-existing-challenge) +- [Add a language available for testing the code](language-available-for-testing-the-code) +- [Correct a spelling error](spelling-error) + +## Challenge instructions + +After running the command `npm run create-challenge` (see Installation & Usage part of [README.md](../README.md)). + +You should have a new folder called the name of your challenge in [challenges](./challenges) folder. + +### Structure and purpose of each files : + +- `README.md` : explain what the solution function should do (the instructions). +- `input-output.json` : An array of possible input/output. This file allows you to test solutions. + + Example of file : + ```json + [ + { + "input": ["arg"], + "output": "Hello world arg!" + }, + { + "input": ["Divlo"], + "output": "Hello world Divlo!" + } + ] + ``` + Each object has a "input" key, an array where each item is an argument passed to the solution function when it's executed. The "output" key is the expected output with the given input. +- `solutions` folder where there are all solutions for this specific challenge. + +## Solution for an already existing challenge + +After running the command `npm run create-solution` (see Installation & Usage part of [README.md](../README.md)). + +You should have a new folder called the name of your solution in the challenge folder then you can write your solution in the solution file. +You need to name your parameters of the function (see the instruction of the challenge). + +When you feel it's right, you would need to test your code against `input-output.json` file to see if it's a valid solution.. +Run this command `npm run test [challenge-name] [solution-name]`. + +## Language available for testing the code + +Before to add a new language, you should understand : + +[/scripts/languages-wrapper](../scripts/languages-wrapper) folder contains all the files needed to execute solutions. Each programming challenge has a execute file, this execute file will be copied in the `temp` folder with the solution file. + +Steps to add a new language : + +1. Code the execute file in the appropriate language + + Algorithm of the execute file : + + - Import the solution function (same directory, ignore errors) + - Read the `./input.json` files and convert it as JSON (not plain text string) + - Execute the solution function imported with the inputs as arguments + - Create and write a new file called `./output.json`, with the output result of the solution function + + Example in javascript with node.js ([execute.js](../scripts/languages-wrapper/execute.js)) : + ```javascript + const path = require('path') + const fs = require('fs').promises + const solution = require('./solution') + + const inputPath = path.join(__dirname, 'input.json') + const outputPath = path.join(__dirname, 'output.json') + + const main = async () => { + const inputFile = await fs.readFile(inputPath) + const inputJSON = JSON.parse(inputFile) + + const result = solution.apply(null, inputJSON) + await fs.writeFile(outputPath, JSON.stringify(result)) + } + + main() + ``` + +1. Add the language in the `_languages.json` file as a new object of the array. Example for JavaScript : + ```json + { + "name": "JavaScript", + "extension": ".js", + "launch": "node" + } + ``` +1. Create a new solution file with the default basic boilerplate code in `/scripts/languages-wrapper/templates`. Example : `solution.js`: + ```js + function solution () { + + } + + module.exports = solution + ``` +1. Add the language in the language available in [README.md](../README.md) file. + + + +## Spelling error + +Correct spelling errors, in the README or CONTRIBUTING files. + +Thank you for your support! diff --git a/.github/logo.png b/.github/logo.png new file mode 100644 index 0000000..395a3b4 Binary files /dev/null and b/.github/logo.png differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..bf8ab9a --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +

programming-challenges

+ +

+ Programming exercises and challenges to improve your algorithmic logic. +

+ +

+ Gitmoji + Licence MIT + Commit Activity + Stars +

+ programming-challenges Logo +

+ +## 📜 About + +**programming-challenges** brings together lots of programming exercises and challenges to improve your algorithmic logic. + +Each challenge has its **solutions**, its **instructions** and **input/output examples** so you can try to solve them on your own. See [challenges](./challenges) folder. + +## ✅ Programming languages available + +`npm run test` command will only work with these languages : + +- JavaScript and TypeScript (Node.js >= 12) +- Python >= 3.8 + +## 🚀 Installation & Usage (CLI) + +To easily create **new challenges instructions, solutions and test** your code, I made a **CLI tool** made with Node.js and TypeScript. + +### Requirements : + +- Node.js >= 12 + +Then you need to run `npm install` in the root folder to install needed packages, you can now use one of these commands : + +- ### `npm run create-challenge` + + Create the basic files needed for a new challenge. It will ask you some questions and you will be ready to write the instructions and `input-output.json`. Please read [CONTRIBUTING.md](./.github/CONTRIBUTING.md). + +- ### `npm run create-solution` + + Create the basic files needed for a new solution for a challenge. It will ask you some questions and you will be ready to write your solution in the available programming languages (see above). If you wish to submit to everyone your solution. Please read [CONTRIBUTING.md](./.github/CONTRIBUTING.md). + +- ### `npm run test [challenge-name] [solution-name]` + + Test if the solution is correct and display where it succeeds and fails with the inputs provided, the output of your function and the expected output. + + Example : `npm run test hello-world python-hello` + +## 💡 Contributing + +Feel free to submit your challenges, your solutions or even a simple spelling mistake. + +Everyone can contribute to the improvement of the project! The steps to contribute can be found in the [CONTRIBUTING.md](./.github/CONTRIBUTING.md) file. + +## 📄 License + +[MIT](./LICENSE) diff --git a/challenges/hello-world/README.md b/challenges/hello-world/README.md new file mode 100644 index 0000000..7e36589 --- /dev/null +++ b/challenges/hello-world/README.md @@ -0,0 +1,11 @@ +# hello-world + +Created by @Divlo at 5 July 2020. + +## Instructions : + +Your function should return Hello depending on the parameter. + +## Examples : + +See the `input-output.json` file for examples of input/output. diff --git a/challenges/hello-world/input-output.json b/challenges/hello-world/input-output.json new file mode 100644 index 0000000..f20ac45 --- /dev/null +++ b/challenges/hello-world/input-output.json @@ -0,0 +1,14 @@ +[ + { + "input": ["world"], + "output": "Hello world!" + }, + { + "input": ["everyone"], + "output": "Hello everyone!" + }, + { + "input": ["Divlo"], + "output": "Hello Divlo!" + } +] diff --git a/challenges/hello-world/solutions/.gitkeep b/challenges/hello-world/solutions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/challenges/hello-world/solutions/javascript-hello/README.md b/challenges/hello-world/solutions/javascript-hello/README.md new file mode 100644 index 0000000..e84a0de --- /dev/null +++ b/challenges/hello-world/solutions/javascript-hello/README.md @@ -0,0 +1,4 @@ +# javascript-hello - hello-world + +Programming language : JavaScript +Created by @Divlo at 5 July 2020. diff --git a/challenges/hello-world/solutions/javascript-hello/solution.js b/challenges/hello-world/solutions/javascript-hello/solution.js new file mode 100644 index 0000000..f06558f --- /dev/null +++ b/challenges/hello-world/solutions/javascript-hello/solution.js @@ -0,0 +1,5 @@ +function solution (arg) { + return 'Hello ' + arg + '!' +} + +module.exports = solution diff --git a/challenges/hello-world/solutions/python-hello/README.md b/challenges/hello-world/solutions/python-hello/README.md new file mode 100644 index 0000000..ff210d3 --- /dev/null +++ b/challenges/hello-world/solutions/python-hello/README.md @@ -0,0 +1,4 @@ +# python-hello - hello-world + +Programming language : Python +Created by @Divlo at 5 July 2020. diff --git a/challenges/hello-world/solutions/python-hello/solution.py b/challenges/hello-world/solutions/python-hello/solution.py new file mode 100644 index 0000000..945103e --- /dev/null +++ b/challenges/hello-world/solutions/python-hello/solution.py @@ -0,0 +1,2 @@ +def solution(arg): + return 'Hello ' + arg + '!' diff --git a/challenges/hello-world/solutions/typescript-hello/README.md b/challenges/hello-world/solutions/typescript-hello/README.md new file mode 100644 index 0000000..2de0cb0 --- /dev/null +++ b/challenges/hello-world/solutions/typescript-hello/README.md @@ -0,0 +1,4 @@ +# typescript-hello - hello-world + +Programming language : TypeScript +Created by @Divlo at 5 July 2020. diff --git a/challenges/hello-world/solutions/typescript-hello/solution.ts b/challenges/hello-world/solutions/typescript-hello/solution.ts new file mode 100644 index 0000000..f8c1fd1 --- /dev/null +++ b/challenges/hello-world/solutions/typescript-hello/solution.ts @@ -0,0 +1,5 @@ +function solution (arg: string) { + return 'Hello ' + arg + '!' +} + +export default solution diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4a52d93 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,899 @@ +{ + "name": "programming-challenges", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@babel/runtime-corejs3": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.4.tgz", + "integrity": "sha512-BFlgP2SoLO9HJX9WBwN67gHWMBhDX/eDz64Jajd6mR/UAUzqrNMm99d4qHnVaKscAElZoFiPv+JpR/Siud5lXw==", + "dev": true, + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/date-and-time": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/date-and-time/-/date-and-time-0.6.0.tgz", + "integrity": "sha512-gCNLSTK8SnHqNQo1MIy4fwhsufZU5zFXsJzmTdjvRsp6Xpb28G2ODFNSoZNSsf2kB9J0hPSzHCEaQqYusIwUqQ==", + "dev": true + }, + "@types/inquirer": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", + "integrity": "sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==", + "dev": true, + "requires": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "@types/node": { + "version": "14.0.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.14.tgz", + "integrity": "sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ==", + "dev": true + }, + "@types/node-emoji": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@types/node-emoji/-/node-emoji-1.8.1.tgz", + "integrity": "sha512-0fRfA90FWm6KJfw6P9QGyo0HDTCmthZ7cWaBQndITlaWLTZ6njRyKwrwpzpg+n6kBXBIGKeUHEQuBx7bphGJkA==", + "dev": true + }, + "@types/table": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/table/-/table-5.0.0.tgz", + "integrity": "sha512-fQLtGLZXor264zUPWI95WNDsZ3QV43/c0lJpR/h1hhLJumXRmHNsrvBfEzW2YMhb0EWCsn4U6h82IgwsajAuTA==", + "dev": true + }, + "@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-iFNNIrEaJH1lbPiyX+O/QyxSbKxrTjdNBVZGckt+iEL9So0hdZNBL68sOfHnt2txuUD8UJXvmKv/1DkgkebgUg==", + "dev": true + }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==", + "dev": true + }, + "date-and-time": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.13.1.tgz", + "integrity": "sha512-/Uge9DJAT+s+oAcDxtBhyR8+sKjUnZbYmyhbmWjTHNtX7B7oWD8YyYdeXcBRbwSj6hVvj+IQegJam7m7czhbFw==", + "dev": true + }, + "decamelize": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz", + "integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==", + "dev": true, + "requires": { + "xregexp": "^4.2.4" + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.0.tgz", + "integrity": "sha512-K+LZp6L/6eE5swqIcVXrxl21aGDU4S50gKH0/d96OMQnSBCyGyZl/oZhbkVmdp5sBoINHd4xZvFSARh2dk6DWA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node-emoji": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "dev": true, + "requires": { + "lodash.toarray": "^4.4.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pretty-ms": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.0.tgz", + "integrity": "sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg==", + "dev": true, + "requires": { + "parse-ms": "^2.1.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + }, + "replace-in-file": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.1.0.tgz", + "integrity": "sha512-URzjyF3nucvejuY13HFd7O+Q6tFJRLKGHLYVvSh+LiZj3gFXzSYGnIkQflnJJulCAI2/RTZaZkpOtdVdW0EhQA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "glob": "^7.1.6", + "yargs": "^15.3.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "rxjs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + }, + "typescript": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", + "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xregexp": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz", + "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==", + "dev": true, + "requires": { + "@babel/runtime-corejs3": "^7.8.3" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.0.tgz", + "integrity": "sha512-D3fRFnZwLWp8jVAAhPZBsmeIHY8tTsb8ItV9KaAaopmC6wde2u6Yw29JBIZHXw14kgkRnYmDgmQU4FVMDlIsWw==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^3.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.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9e87351 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "programming-challenges", + "private": true, + "scripts": { + "create-challenge": "ts-node ./scripts/create-challenge.ts", + "create-solution": "ts-node ./scripts/create-solution.ts", + "test": "ts-node ./scripts/test.ts" + }, + "devDependencies": { + "@types/date-and-time": "^0.6.0", + "@types/inquirer": "^6.5.0", + "@types/node": "^14.0.14", + "@types/node-emoji": "^1.8.1", + "@types/table": "^5.0.0", + "@types/validate-npm-package-name": "^3.0.0", + "chalk": "^4.1.0", + "date-and-time": "^0.13.1", + "inquirer": "^7.3.0", + "make-dir": "^3.1.0", + "node-emoji": "^1.10.0", + "pretty-ms": "^7.0.0", + "replace-in-file": "^6.1.0", + "table": "^5.4.6", + "ts-node": "^8.10.2", + "typescript": "^3.9.6", + "validate-npm-package-name": "^3.0.0" + } +} diff --git a/scripts/create-challenge.ts b/scripts/create-challenge.ts new file mode 100644 index 0000000..262475f --- /dev/null +++ b/scripts/create-challenge.ts @@ -0,0 +1,84 @@ +import path from 'path' +import * as fsWithCallbacks from 'fs' +import chalk from 'chalk' +import makeDir from 'make-dir' +import inquirer from 'inquirer' +import { replaceInFile } from 'replace-in-file' +import validateProjectName from 'validate-npm-package-name' +import copyDirPromise from './utils/copyDirPromise' +import date from 'date-and-time' +;(async () => { + const fs = fsWithCallbacks.promises + const QUESTIONS = [ + { + name: 'challengeName', + type: 'input', + message: 'Challenge name:' + }, + { + name: 'userGitHub', + type: 'input', + message: 'Your GitHub name:' + } + ] + const answers = await inquirer.prompt(QUESTIONS) + const { challengeName, userGitHub } = answers as { + [key: string]: string + } + console.log() + + if (!challengeName || challengeName === '') { + console.log(chalk.cyan('Please specify the challenge name you want to create.')) + process.exit(0) + } + + const validChallengeName = validateProjectName(challengeName) + if (!validChallengeName.validForNewPackages) { + console.log(` + Invalid challenge name: ${chalk.red(challengeName)} + ${validChallengeName.errors != undefined && + validChallengeName.errors[0]} + `) + process.exit(0) + } + + const challengePath = path.resolve( + __dirname, + '..', + 'challenges', + challengeName + ) + const templatePath = path.resolve(__dirname, 'templates', 'challenge') + + // Challenge valid ? + if (fsWithCallbacks.existsSync(challengePath)) { + console.log(`The challenge already exists: ${chalk.red(challengeName)}`) + process.exit(0) + } + + const createdChallengeTemplatePath = await makeDir(challengePath) + const solutionsFolderPath = path.join(createdChallengeTemplatePath, 'solutions') + await copyDirPromise(templatePath, createdChallengeTemplatePath) + await makeDir(solutionsFolderPath) + await fs.writeFile(path.join(solutionsFolderPath, '.gitkeep'), '') + + // Replace {{ challengeName }} in README.md + const readmePath = path.join(createdChallengeTemplatePath, 'README.md') + await replaceInFile({ + files: [readmePath], + from: /{{ challengeName }}/g, + to: challengeName + }) + + // Replace {{ challengeInfo }} in README.md + await replaceInFile({ + files: [readmePath], + from: /{{ challengeInfo }}/g, + to: `Created${(userGitHub !== '') ? ` by @${userGitHub}` : ''} at ${date.format(new Date(), 'D MMMM Y', true)}.` + }) + + console.log(` + ${chalk.green('Success:')} "${challengeName}" challenge created. + ${chalk.cyan('You can now edit README.md and input-output.json files.')} + `) +})() diff --git a/scripts/create-solution.ts b/scripts/create-solution.ts new file mode 100644 index 0000000..88a7874 --- /dev/null +++ b/scripts/create-solution.ts @@ -0,0 +1,113 @@ +import path from 'path' +import * as fsWithCallbacks from 'fs' +import chalk from 'chalk' +import inquirer from 'inquirer' +import { replaceInFile } from 'replace-in-file' +import makeDir from 'make-dir' +import date from 'date-and-time' +import validateProjectName from 'validate-npm-package-name' +import copyDirPromise from './utils/copyDirPromise' +;(async () => { + const fs = fsWithCallbacks.promises + const challengesPath = path.resolve(__dirname, '..', 'challenges') + const challengesAvailable = await fs.readdir(challengesPath) + const languagesAvailable: { + name: string + extension: string + launch: string + }[] = require('./languages-wrapper/_languages.json') + + const QUESTIONS = [ + { + name: 'challengeName', + type: 'list', + message: 'Select a challenge:', + choices: challengesAvailable + }, + { + name: 'programmingLanguage', + type: 'list', + message: 'Select a programming language:', + choices: languagesAvailable.map(language => ({ name: language.name, value: language })) + }, + { + name: 'solutionName', + type: 'input', + message: 'Solution name:' + }, + { + name: 'userGitHub', + type: 'input', + message: 'Your GitHub name:' + } + ] + + const answers = await inquirer.prompt(QUESTIONS) + console.log() + const { challengeName, solutionName, userGitHub } = answers as { + [key: string]: string + } + const { programmingLanguage } = answers as { + programmingLanguage: { + extension: string + name: string + } + } + + const validSolutionName = validateProjectName(solutionName) + if (!validSolutionName.validForNewPackages) { + console.log(` + Invalid solution name: ${chalk.red(solutionName)} + ${validSolutionName.errors != undefined && + validSolutionName.errors[0]} + `) + process.exit(0) + } + + const solutionPath = path.resolve( + __dirname, + '..', + 'challenges', + challengeName, + 'solutions', + solutionName + ) + const templatePath = path.resolve(__dirname, 'templates', 'solutions') + const templateSolutionPath = path.resolve(__dirname, 'languages-wrapper', 'templates') + + // Solution valid ? + if (fsWithCallbacks.existsSync(solutionPath)) { + console.log(`The solution already exists: ${chalk.red(solutionName)}`) + process.exit(0) + } + + const createdSolutionTemplatePath = await makeDir(solutionPath) + await copyDirPromise(templatePath, createdSolutionTemplatePath) + + const languageSolutionTemplate = path.join(templateSolutionPath, `solution${programmingLanguage.extension}`) + await fs.copyFile(languageSolutionTemplate, path.join(createdSolutionTemplatePath, `solution${programmingLanguage.extension}`)) + + // Replace {{ solutionName }} in README.md + const readmePath = path.join(createdSolutionTemplatePath, 'README.md') + await replaceInFile({ + files: [readmePath], + from: /{{ solutionName }}/g, + to: `${solutionName} - ${challengeName}` + }) + + // Replace {{ solutionInfo }} in README.md + const createdByString = `Created${(userGitHub !== '') ? ` by @${userGitHub}` : ''} at ${date.format(new Date(), 'D MMMM Y', true)}.` + await replaceInFile({ + files: [readmePath], + from: /{{ solutionInfo }}/g, + to: 'Programming language : ' + programmingLanguage.name + '\n' + createdByString + }) + + console.log(` + ${chalk.green('Success:')} "${solutionName}" created. + ${chalk.cyan(`Edit your solution${programmingLanguage.extension} file and try to solve "${challengeName}" challenge (see README.md).`)} + + Don't forget to test your solution attempt : + ${chalk.green(`npm run test ${challengeName} ${solutionName}`)} + `) +})() diff --git a/scripts/languages-wrapper/_languages.json b/scripts/languages-wrapper/_languages.json new file mode 100644 index 0000000..ef5f9bc --- /dev/null +++ b/scripts/languages-wrapper/_languages.json @@ -0,0 +1,17 @@ +[ + { + "name": "Python", + "extension": ".py", + "launch": "python" + }, + { + "name": "JavaScript", + "extension": ".js", + "launch": "node" + }, + { + "name": "TypeScript", + "extension": ".ts", + "launch": "ts-node" + } +] diff --git a/scripts/languages-wrapper/execute.js b/scripts/languages-wrapper/execute.js new file mode 100644 index 0000000..70892af --- /dev/null +++ b/scripts/languages-wrapper/execute.js @@ -0,0 +1,16 @@ +const path = require('path') +const fs = require('fs').promises +const solution = require('./solution') + +const inputPath = path.join(__dirname, 'input.json') +const outputPath = path.join(__dirname, 'output.json') + +const main = async () => { + const inputFile = await fs.readFile(inputPath) + const inputJSON = JSON.parse(inputFile) + + const result = solution.apply(null, inputJSON) + await fs.writeFile(outputPath, JSON.stringify(result)) +} + +main() diff --git a/scripts/languages-wrapper/execute.py b/scripts/languages-wrapper/execute.py new file mode 100644 index 0000000..a8ff28b --- /dev/null +++ b/scripts/languages-wrapper/execute.py @@ -0,0 +1,13 @@ +import os +import json +from solution import solution + +current_directory = os.path.dirname(__file__) +input_path = os.path.join(current_directory, "input.json") +output_path = os.path.join(current_directory, "output.json") + +with open(input_path, "r") as file_content: + input_json = json.load(file_content) + +with open(output_path, "w") as file_content: + json.dump(solution(*input_json), file_content) diff --git a/scripts/languages-wrapper/execute.ts b/scripts/languages-wrapper/execute.ts new file mode 100644 index 0000000..f60d46a --- /dev/null +++ b/scripts/languages-wrapper/execute.ts @@ -0,0 +1,18 @@ +import path from 'path' +import * as fsWithCallbacks from 'fs' +// @ts-ignore +import solution from './solution' + +const fs = fsWithCallbacks.promises +const inputPath = path.join(__dirname, 'input.json') +const outputPath = path.join(__dirname, 'output.json') + +const main = async () => { + const inputFile = await fs.readFile(inputPath) + const inputJSON = JSON.parse(inputFile.toString()) + + const result = solution.apply(null, inputJSON) + await fs.writeFile(outputPath, JSON.stringify(result)) +} + +main() diff --git a/scripts/languages-wrapper/templates/solution.js b/scripts/languages-wrapper/templates/solution.js new file mode 100644 index 0000000..47ed582 --- /dev/null +++ b/scripts/languages-wrapper/templates/solution.js @@ -0,0 +1,5 @@ +function solution () { + +} + +module.exports = solution diff --git a/scripts/languages-wrapper/templates/solution.py b/scripts/languages-wrapper/templates/solution.py new file mode 100644 index 0000000..4d6454c --- /dev/null +++ b/scripts/languages-wrapper/templates/solution.py @@ -0,0 +1,2 @@ +def solution(): + pass diff --git a/scripts/languages-wrapper/templates/solution.ts b/scripts/languages-wrapper/templates/solution.ts new file mode 100644 index 0000000..3482076 --- /dev/null +++ b/scripts/languages-wrapper/templates/solution.ts @@ -0,0 +1,5 @@ +function solution () { + +} + +export default solution diff --git a/scripts/templates/challenge/README.md b/scripts/templates/challenge/README.md new file mode 100644 index 0000000..b66535e --- /dev/null +++ b/scripts/templates/challenge/README.md @@ -0,0 +1,11 @@ +# {{ challengeName }} + +{{ challengeInfo }} + +## Instructions : + +Description of the challenge... + +## Examples : + +See the `input-output.json` file for examples of input/output. diff --git a/scripts/templates/challenge/input-output.json b/scripts/templates/challenge/input-output.json new file mode 100644 index 0000000..7d7ec61 --- /dev/null +++ b/scripts/templates/challenge/input-output.json @@ -0,0 +1,6 @@ +[ + { + "input": [], + "output": null + } +] diff --git a/scripts/templates/solutions/README.md b/scripts/templates/solutions/README.md new file mode 100644 index 0000000..6590f87 --- /dev/null +++ b/scripts/templates/solutions/README.md @@ -0,0 +1,3 @@ +# {{ solutionName }} + +{{ solutionInfo }} diff --git a/scripts/test.ts b/scripts/test.ts new file mode 100644 index 0000000..1207eff --- /dev/null +++ b/scripts/test.ts @@ -0,0 +1,195 @@ +import util from 'util' +import path from 'path' +import * as fsWithCallbacks from 'fs' +import childProcess from 'child_process' +import { performance } from 'perf_hooks' +import chalk from 'chalk' +import deleteAllFilesExceptOne from './utils/deleteAllFilesExceptOne' +import emoji from 'node-emoji' +import prettyMilliseconds from 'pretty-ms' +import { table } from 'table' +;(async () => { + const fs = fsWithCallbacks.promises + const exec = util.promisify(childProcess.exec) + const args = process.argv.slice(2) + const [challengeName, solutionName] = args + + if (!challengeName || !solutionName) { + console.log(` + Please specify the challenge and solution name: + ${chalk.cyan(`npm run test [challenge-name] [solution-name]`)} + + For example: + ${chalk.cyan('npm run test hello-world python-hello')} + `) + process.exit(0) + } + + const challengePath = path.resolve( + __dirname, + '..', + 'challenges', + challengeName + ) + const solutionFolderPath = path.resolve( + challengePath, + 'solutions', + solutionName + ) + + // Challenge valid ? + try { + await fs.access(challengePath) + } catch { + console.log(`The challenge was not found: ${chalk.red(challengeName)}`) + process.exit(0) + } + + // Solution valid ? + try { + await fs.access(solutionFolderPath) + } catch { + console.log(`The solution was not found: ${chalk.red(solutionName)}`) + process.exit(0) + } + + // Determinate the language to execute + const solutionFilesName = await fs.readdir(solutionFolderPath) + let solutionFilePath + for (const solutionFileName of solutionFilesName) { + const fileName = solutionFileName + .split('.') + .slice(0, -1) + .join('.') + if (fileName === 'solution') { + solutionFilePath = solutionFileName + break + } + } + if (!solutionFilePath) { + console.log(`The ${chalk.red('solution')} file was not found.`) + process.exit(0) + } + const languages: { + name: string + extension: string + launch: string + }[] = require('./languages-wrapper/_languages.json') + const extensionSolution = path.extname(solutionFilePath) + const languageToExecute = languages.find( + language => language.extension === extensionSolution + ) + if (!languageToExecute) { + console.log(`Sadly, this ${chalk.red('language')} is not supported yet.`) + process.exit(0) + } + + // Copy 'solution' and 'execute' files in temp + const inputOutputJSON: { input: any[]; output: any }[] = require(path.join( + __dirname, + '..', + 'challenges', + challengeName, + 'input-output.json' + )) + const tempPath = path.join(__dirname, '..', 'temp') + const executeFile = `execute${languageToExecute.extension}` + const executeLanguagePath = path.resolve( + __dirname, + 'languages-wrapper', + executeFile + ) + const executeLanguageTempPath = path.join(tempPath, executeFile) + const inputPath = path.join(tempPath, 'input.json') + const outputPath = path.join(tempPath, 'output.json') + await fs.copyFile( + path.resolve(solutionFolderPath, solutionFilePath), + path.join(tempPath, solutionFilePath) + ) + await fs.copyFile(executeLanguagePath, executeLanguageTempPath) + + // Console.log & Tests + const totalCorrect = { + total: 0, + correct: 0 + } + const tableResult = [ + [ + chalk.cyan('Result'), + chalk.cyan('Input'), + chalk.cyan('Output'), + chalk.cyan('Expected output') + ] + ] + const startTest = performance.now() + + // Loop I/O + for (const { input, output } of inputOutputJSON) { + // Write input.json + const inputStringify = JSON.stringify(input) + await fs.writeFile(inputPath, inputStringify) + + // Execute script (create output.json) + try { + await exec(`${languageToExecute.launch} ${executeLanguageTempPath}`) + } catch (error) { + console.log(chalk.bgRedBright.black(error.stderr)) + await deleteAllFilesExceptOne(tempPath, '.gitignore') + process.exit(0) + } + + // Read output.json + const data = await fs.readFile(outputPath) + const outputJSON = JSON.parse(data.toString()) + + // Tests + totalCorrect.total += 1 + const outputJSONStringify = JSON.stringify(outputJSON) + const outputStringify = JSON.stringify(output) + const isCorrect = outputJSONStringify === outputStringify + + if (isCorrect) { + tableResult.push([ + emoji.get('white_check_mark'), + inputStringify, + outputJSONStringify, + outputStringify + ]) + totalCorrect.correct += 1 + } else { + tableResult.push([ + emoji.get('x'), + inputStringify, + outputJSONStringify, + outputStringify + ]) + } + + // Delete I/O file + await fs.unlink(inputPath) + await fs.unlink(outputPath) + } + + const endTest = performance.now() + + console.log( + table(tableResult, { + columns: { + 0: { width: 6, alignment: 'center' }, + 1: { width: 20, wrapWord: true }, + 2: { width: 30, wrapWord: true }, + 3: { width: 30, wrapWord: true } + } + }) + ) + console.log(` +Challenge : ${challengeName} +Solution : ${solutionName} +Tests : ${chalk.green(`${totalCorrect.correct} passed`)}, ${ + totalCorrect.total + } total +Time : ${chalk.yellow(prettyMilliseconds(endTest - startTest))} +`) + + await deleteAllFilesExceptOne(tempPath, '.gitignore') +})() diff --git a/scripts/utils/copyDirPromise.ts b/scripts/utils/copyDirPromise.ts new file mode 100644 index 0000000..993cf43 --- /dev/null +++ b/scripts/utils/copyDirPromise.ts @@ -0,0 +1,26 @@ +import fs from 'fs' +import path from 'path' + +function copyDirPromise (source: string, destination: string) { + return new Promise(next => { + const filesToCreate = fs.readdirSync(source) + filesToCreate.forEach(async file => { + const originalFilePath = path.join(source, file) + const stats = fs.statSync(originalFilePath) + if (stats.isFile()) { + if (file === '.npmignore') file = '.gitignore' + const writePath = path.join(destination, file) + fs.copyFileSync(originalFilePath, writePath) + } else if (stats.isDirectory()) { + fs.mkdirSync(path.join(destination, file)) + await copyDirPromise( + path.join(source, file), + path.join(destination, file) + ) + } + }) + next() + }) +} + +export default copyDirPromise diff --git a/scripts/utils/deleteAllFilesExceptOne.ts b/scripts/utils/deleteAllFilesExceptOne.ts new file mode 100644 index 0000000..48956ae --- /dev/null +++ b/scripts/utils/deleteAllFilesExceptOne.ts @@ -0,0 +1,18 @@ +import path from 'path' +import * as fsWithCallbacks from 'fs' +const fs = fsWithCallbacks.promises + +async function deleteAllFilesExceptOne (directoryPath: string, fileNameToNotDelete: string) { + const fileNames = await fs.readdir(path.resolve(directoryPath)) + for (const name of fileNames) { + const fileNamePath = path.resolve(directoryPath, name) + const stats = await fs.stat(fileNamePath) + if (stats.isDirectory()) { + await fs.rmdir(fileNamePath, { recursive: true }) + } else if (name !== fileNameToNotDelete) { + await fs.unlink(fileNamePath) + } + } +} + +export default deleteAllFilesExceptOne diff --git a/temp/.gitignore b/temp/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/temp/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9d577a3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,70 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "resolveJsonModule": true, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +}