├── .devcontainer ├── Dockerfile ├── base.Dockerfile └── devcontainer.json ├── .firebaserc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── deploy.yml ├── .gitignore ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── firebase.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json ├── markdown │ └── staging-area.md └── robots.txt ├── src ├── App.css ├── App.tsx ├── articles-list.tsx ├── components │ ├── Article.css │ ├── Article.tsx │ ├── Definition.css │ └── Definition.tsx ├── git-command-parsing.tsx ├── git-commands.ts ├── git-definitions.ts ├── index.css ├── index.tsx ├── logo.svg ├── nord.css ├── react-app-env.d.ts ├── types.ts └── utils.ts ├── tsconfig.json └── yarn.lock /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster 2 | ARG VARIANT=16-bullseye 3 | FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT} 4 | 5 | # [Optional] Uncomment this section to install additional OS packages. 6 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 7 | # && apt-get -y install --no-install-recommends 8 | 9 | # [Optional] Uncomment if you want to install an additional version of node using nvm 10 | # ARG EXTRA_NODE_VERSION=10 11 | # RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" 12 | 13 | # [Optional] Uncomment if you want to install more global node packages 14 | # RUN su node -c "npm install -g " 15 | -------------------------------------------------------------------------------- /.devcontainer/base.Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster 2 | ARG VARIANT=16-bullseye 3 | FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT} 4 | 5 | # Install tslint, typescript. eslint is installed by javascript image 6 | ARG NODE_MODULES="tslint-to-eslint-config typescript" 7 | COPY library-scripts/meta.env /usr/local/etc/vscode-dev-containers 8 | RUN su node -c "umask 0002 && npm install -g ${NODE_MODULES}" \ 9 | && npm cache clean --force > /dev/null 2>&1 10 | 11 | # [Optional] Uncomment this section to install additional OS packages. 12 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 13 | # && apt-get -y install --no-install-recommends 14 | 15 | # [Optional] Uncomment if you want to install an additional version of node using nvm 16 | # ARG EXTRA_NODE_VERSION=10 17 | # RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" 18 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/typescript-node 3 | { 4 | "name": "Node.js & TypeScript", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick a Node version: 18, 16, 14. 8 | // Append -bullseye or -buster to pin to an OS version. 9 | // Use -bullseye variants on local on arm64/Apple Silicon. 10 | "args": { 11 | "VARIANT": "16-bullseye" 12 | } 13 | }, 14 | 15 | // Configure tool-specific properties. 16 | "customizations": { 17 | // Configure properties specific to VS Code. 18 | "vscode": { 19 | // Add the IDs of extensions you want installed when the container is created. 20 | "extensions": [ 21 | "dbaeumer.vscode-eslint" 22 | ] 23 | } 24 | }, 25 | 26 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 27 | // "forwardPorts": [], 28 | 29 | // Use 'postCreateCommand' to run commands after the container is created. 30 | // "postCreateCommand": "yarn install", 31 | 32 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 33 | "remoteUser": "node" 34 | } 35 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": {}, 3 | "targets": { 4 | "portfolio-8991e": { 5 | "hosting": { 6 | "what-the-git": [ 7 | "what-the-git" 8 | ] 9 | } 10 | } 11 | }, 12 | "etags": {} 13 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: If you noticed a bug in the application, be welcome to report it 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE REQUEST]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to Firebase Hosting 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | jobs: 8 | build_and_deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-node@v3 13 | with: 14 | node-version: "lts/gallium" 15 | - run: yarn && yarn build 16 | - uses: w9jds/firebase-action@master 17 | with: 18 | args: deploy --only hosting 19 | env: 20 | FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .eslintcache 26 | 27 | # firebase related 28 | /.firebase/ 29 | .vscode 30 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Developer Manual 2 | 3 | This readme intends to help you understand the project better by explaining its structure. 4 | 5 | ## Project structure 6 | 7 | The project is a classic React application, created with create-react-app. It works by parsing a single .json file, which is located in [src/git-commands.ts](src/git-commands.ts). As the user enters an input in the bar and confirms, a single function is called (`getGitCommand`). The function works this way: 8 | 9 | - Gets the command from the user input 10 | - Find all the available flags for the command 11 | - Create arrays of flags to be used with the parsing librairies (separate flags that can have a string value and strictly boolean ones) 12 | - Parse the arguments using yargs-parser 13 | - Restructure the arguments in another object because yargs-parser has an awkward object structure to work with 14 | - Check for special tokens such as . and replace them by the corresponding description 15 | - Replace string tokens (%s) with their corresponding argument values 16 | - Return the final object with a parsed description using the parseDescription function, embedding articles of matching git jargon keywords as spans in the description 17 | 18 | The command is then rendered using renderCommandDescription. 19 | 20 | ## git-commands.ts structure 21 | 22 | The structure of the git-commands.ts is really simple. It's a json file with the following structure: 23 | 24 | - An array of commands 25 | - Command 26 | - A name property with the name of the git command 27 | - The description for the command 28 | - An array of flags 29 | - Flag 30 | - The name of the flag 31 | - A description of the flag 32 | - An array of valid aliases for the flag 33 | - A boolean that states if the flag is a string flag or not 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Anthony Rodriguez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | What The Git 3 |

4 | 5 |

A web application that makes it easy to find what a git command does by just typing it into the search bar.

6 | 7 |

8 | Features • 9 | Upcoming Features • 10 | Supported Commands • 11 | Contribute 12 |

13 | 14 | ## Introduction 15 | 16 | This project is written with Typescript using React. It works only with a front-end, using parsing of command-line arguments and a simple .json file containing all the descriptions of the commands and their flags. It is also easy to self-host since it doesn't contain a server. 17 | 18 | ## Features 19 | 20 | What The Git only contains really basic git commands and flags so far, but the list will grow with time. I expect to support at least git add, git commit and git push. Feel free to contribute to fix issues that you found in the app or to add more git commands to it ! As this is a personal project, I would really appreciate it. 21 | 22 | It also features a system that embeds descriptions of git jargon such as staging area, commit etc. as hyperlinks in the command description. You can either hover them to see a short description, or click the "read article" button to be redirected on the full article. 23 | 24 | ## Upcoming features 25 | 26 | - More verbose errors (telling you where the command went wrong and why instead of just showing a generic error message) 27 | 28 | Feel free to submit an issue if you have feature requests, I'll gladly take them. 29 | 30 | ## Supported commands 31 | 32 | So far, only a few commands are supported, with a few flags: 33 | 34 | - git 35 | - add 36 | - commit 37 | - push 38 | - rebase 39 | - diff 40 | - log 41 | - pull 42 | - status 43 | 44 | ## Contribute 45 | 46 | As I stated above, pull requests, bug reports and feature requests are very much appreciated. This is a big project for me, and any help would be truly appreciated! I setup a couple of templates in the issues section if you want to contribute. I don't feel like adding a code of conduct, I only expect contributors to be respectful to each other. 47 | 48 | It is advised to read the [developer manual](DEVELOPMENT.md) before intending to contribute on the code. It's here to help you understand the structure of the project! 49 | 50 | ## Development 51 | 52 | ### Disclaimer 53 | 54 | This project now uses yarn, which is an alternative package manager for NodeJS. You can find it at https://yarnpkg.com/ if you don't already have it installed on your system. 55 | 56 | ### Run the app on a local machine 57 | 58 | In the project directory, you can run: 59 | 60 | ### `yarn` 61 | 62 | Runs yarn and downloads the related packages, and it's dependencies for this project 63 | 64 | ### `yarn run start` 65 | 66 | Runs the app in the development mode.\ 67 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 68 | 69 | The page will reload if you make edits.\ 70 | You will also see any lint errors in the console. 71 | 72 | ### `yarn run build` 73 | 74 | Builds the app for production to the `build` folder.\ 75 | It correctly bundles React in production mode and optimizes the build for the best performance. 76 | 77 | The build is minified, and the filenames include the hashes.\ 78 | Your app is ready to be deployed! 79 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "target": "what-the-git", 4 | "public": "build", 5 | "ignore": [ 6 | "firebase.json", 7 | "**/.*", 8 | "**/node_modules/**" 9 | ], 10 | "rewrites": [ 11 | { 12 | "source": "**", 13 | "destination": "/index.html" 14 | } 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "what-the-git", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.9", 7 | "@testing-library/react": "^11.2.3", 8 | "@testing-library/user-event": "^12.6.2", 9 | "@types/jest": "^26.0.20", 10 | "@types/node": "^14.14.22", 11 | "@types/react": "^17.0.2", 12 | "@types/react-dom": "^17.0.1", 13 | "markdown-to-jsx": "^7.1.1", 14 | "react": "^17.0.1", 15 | "react-dom": "^17.0.1", 16 | "react-popper-tooltip": "^4.1.2", 17 | "react-router-dom": "^5.2.0", 18 | "react-scripts": "4.0.1", 19 | "typescript": "^4.1.3", 20 | "web-vitals": "^0.2.4", 21 | "yargs-parser": "^18.1.3" 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "build": "cross-env CI=false react-scripts build" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | }, 45 | "devDependencies": { 46 | "@types/react-router-dom": "^5.1.7", 47 | "cross-env": "^7.0.3" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nezia1/what-the-git/6ad1d6f4f81a5c7521cb1401c12fc374025e44fb/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | What The Git 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nezia1/what-the-git/6ad1d6f4f81a5c7521cb1401c12fc374025e44fb/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nezia1/what-the-git/6ad1d6f4f81a5c7521cb1401c12fc374025e44fb/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/markdown/staging-area.md: -------------------------------------------------------------------------------- 1 | # Staging area 2 | 3 | The staging area is the place where your files go after you add them with `git add`. 4 | 5 | Think of it as a box: you can use it to pack files together. It is mainly used to help you structure your commits, as you can, for instance, add a part of the files you changed to make small and meaningful commits, instead of adding everything at once. You can check what's in the staging area using `git status`. 6 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap'); 2 | 3 | html { 4 | box-sizing: border-box; 5 | } 6 | *, 7 | *:before, 8 | *:after { 9 | box-sizing: inherit; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | font-family: -apple-system, BlinkMacSystemFont, 'Source Code Pro', 'Roboto', 'Oxygen', 'Ubuntu', 15 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | } 19 | 20 | .App-container { 21 | margin: 0 0.7em; 22 | } 23 | 24 | .App-header { 25 | margin-bottom: 3.5rem; 26 | } 27 | 28 | .App-header h2 { 29 | font-size: 1.3em; 30 | } 31 | .git { 32 | color: var(--nord13); 33 | } 34 | 35 | .get-command-section { 36 | margin-top: 1em; 37 | width: 100%; 38 | font-size: 0.8em; 39 | } 40 | 41 | .input-command { 42 | height: 4rem; 43 | padding: 0.5em 1.5em; 44 | font-size: 0.9em; 45 | border-radius: 5em; 46 | border: 0; 47 | outline: 0; 48 | margin-bottom: 1.3em; 49 | font-family: inherit; 50 | width: inherit; 51 | } 52 | 53 | .get-command-button { 54 | font-size: 1em; 55 | height: 3em; 56 | border: 0; 57 | border-radius: 5rem; 58 | width: 60%; 59 | outline: 0; 60 | font-family: inherit; 61 | background-color: var(--nord13); 62 | margin-bottom: 1.5rem; 63 | } 64 | 65 | @media (min-width: 465px) { 66 | .input-command { 67 | height: 4.5em; 68 | } 69 | } 70 | 71 | @media (min-width: 768px) { 72 | .input-command { 73 | width: 80%; 74 | height: 4em; 75 | } 76 | 77 | .get-command-button { 78 | width: 40%; 79 | } 80 | } 81 | 82 | h1, 83 | h2, 84 | h3 { 85 | margin-block-start: 0; 86 | margin-block-end: 0; 87 | } 88 | 89 | .invalid-command-error { 90 | color: var(--nord11); 91 | font-size: 1.5em; 92 | } 93 | 94 | .App-header h1, 95 | .get-command-section h2, 96 | .get-command-section h3 { 97 | margin-block-end: 0.5em; 98 | } 99 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import parser from 'yargs-parser' 2 | import { useState } from 'react' 3 | import './App.css' 4 | import { gitCommands, specialTokens } from './git-commands' 5 | import { 6 | getAvailableFlagsAsArray, 7 | getMatchingFlags, 8 | getParsedFlagsDescriptions, 9 | getAliasesAsObject, 10 | replaceSpecialTokens, 11 | parseDescription, 12 | } from './git-command-parsing' 13 | import { GitCommand, InputFlag } from './types' 14 | import { definitions } from './git-definitions' 15 | 16 | // Gets the matching git command from the git-commands.js file, and formats the description using the arguments if needed. 17 | function getGitCommand(inputCommand: string): GitCommand | null { 18 | // Get the command name and check if it exists 19 | const inputCommandName = inputCommand.split(' ')[1] 20 | const matchingCommand = gitCommands.commands.find((command) => command.name === inputCommandName) 21 | if (!(inputCommand.split(' ')[0] === 'git') || !matchingCommand) { 22 | return null 23 | } 24 | 25 | // Get all the available flags 26 | const availableFlags = gitCommands.commands.find((command) => command.name === inputCommandName) 27 | ?.flags 28 | 29 | // These arrays exist so they can be used with yargs-parser 30 | let availableFlagsAsArrays 31 | let aliasesObject 32 | 33 | if (availableFlags) { 34 | availableFlagsAsArrays = getAvailableFlagsAsArray(availableFlags) 35 | aliasesObject = getAliasesAsObject(availableFlags) 36 | } 37 | 38 | // Parse the arguments 39 | const parsedArgs = parser(inputCommand, { 40 | boolean: availableFlagsAsArrays?.booleanFlagsArray, 41 | string: availableFlagsAsArrays?.stringFlagsArray, 42 | alias: aliasesObject, 43 | }) 44 | 45 | // Restructure the arguments to be easier to work with 46 | // Instead of the flags being as keys value pairs in the object, put them in their own property called flags with a name and a value 47 | const parsedArguments = Object.entries(parsedArgs).reduce( 48 | (acc, [argumentKey, argumentValue]) => { 49 | if (argumentKey === '_') { 50 | return { ...acc, _: argumentValue } 51 | } 52 | if (argumentValue) { 53 | acc.flags.push({ name: argumentKey, value: argumentValue }) 54 | } 55 | return acc 56 | }, 57 | { _: [] as string[], flags: [] as InputFlag[] } 58 | ) 59 | 60 | let matchingFlags 61 | if (availableFlags) { 62 | matchingFlags = getMatchingFlags(availableFlags, parsedArguments) 63 | } 64 | 65 | // Check if the arguments contain special tokens and replace them by the description to be displayed in the description 66 | const updatedParsedArguments = replaceSpecialTokens(parsedArguments, specialTokens) 67 | 68 | // Replace string tokens with arguments and add a list of flags descriptions if needed 69 | const updatedMatchingCommand = { 70 | ...matchingCommand, 71 | description: parseDescription( 72 | matchingCommand as GitCommand, 73 | definitions, 74 | updatedParsedArguments 75 | ), 76 | flagsDescriptions: getParsedFlagsDescriptions(matchingFlags || [], updatedParsedArguments), 77 | } 78 | 79 | return updatedMatchingCommand 80 | } 81 | 82 | function renderCommandDescription(command: GitCommand) { 83 | let flagsDescriptions 84 | if (command.flagsDescriptions.length > 0) { 85 | flagsDescriptions = command.flagsDescriptions.map((flag) => { 86 | return ( 87 |
88 |

89 | --{flag.name} 90 | {flag.aliases ? ` (${flag.aliases.map((alias) => `-${alias}`).join(' / ')})` : ''} 91 |

92 |

{flag.description}

93 |
94 | ) 95 | }) 96 | } 97 | 98 | return ( 99 |
100 |

git {command.name}

101 |

{command.description}

102 | {flagsDescriptions &&

Flags

} 103 | {flagsDescriptions && flagsDescriptions} 104 |
105 | ) 106 | } 107 | 108 | function App() { 109 | const [matchingCommand, setMatchingCommand] = useState({} as GitCommand) 110 | const [inputCommand, setInputCommand] = useState('') 111 | const [isInvalid, setIsInvalid] = useState(false) 112 | 113 | const handleGetCommandClick = (inputCommand: string) => { 114 | const gitCommand = getGitCommand(inputCommand.toLowerCase()) 115 | if (!gitCommand) { 116 | setIsInvalid(true) 117 | setMatchingCommand({} as GitCommand) 118 | } else { 119 | setIsInvalid(false) 120 | setMatchingCommand(gitCommand) 121 | } 122 | } 123 | 124 | return ( 125 |
126 |
127 |

128 | >what the git 129 |

130 |

enter a git command and have it explained to you

131 |
132 |
133 | setInputCommand(e.target.value)} 140 | onKeyDown={(e) => { 141 | if (e.code === 'Enter') { 142 | handleGetCommandClick(inputCommand) 143 | } 144 | }} 145 | /> 146 | 149 | {isInvalid && ( 150 |

error: the command is not a valid git command

151 | )} 152 | {Object.entries(matchingCommand).length > 0 && renderCommandDescription(matchingCommand)} 153 |
154 |
155 | ) 156 | } 157 | 158 | export default App 159 | -------------------------------------------------------------------------------- /src/articles-list.tsx: -------------------------------------------------------------------------------- 1 | const articlesList = ['staging-area'] 2 | 3 | export { articlesList } 4 | -------------------------------------------------------------------------------- /src/components/Article.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 0 10%; 3 | text-align: left; 4 | } 5 | 6 | p { 7 | font-size: 0.9em; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Article.tsx: -------------------------------------------------------------------------------- 1 | import { useParams } from 'react-router-dom' 2 | import { useEffect, useState } from 'react' 3 | import Markdown from 'markdown-to-jsx' 4 | import { articlesList } from '../articles-list' 5 | import './Article.css' 6 | 7 | export default function Article() { 8 | const { article } = useParams<{ article: string }>() 9 | const [articleText, setArticleText] = useState('') 10 | const [articleExists, setArticleExists] = useState() 11 | useEffect(() => { 12 | async function getArticle() { 13 | if (articlesList.includes(article)) { 14 | setArticleExists(true) 15 | const response = await fetch(`${process.env.PUBLIC_URL}/markdown/${article}.md`) 16 | const text = await response.text() 17 | setArticleText(text) 18 | } 19 | } 20 | getArticle() 21 | }, [article]) 22 | return ( 23 |
24 | {articleExists ? {articleText} :

This article doesn't exist

} 25 |
26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/components/Definition.css: -------------------------------------------------------------------------------- 1 | .definition { 2 | display: inline-block; 3 | } 4 | 5 | .definition-term { 6 | color: var(--nord8); 7 | text-decoration: underline dotted; 8 | text-underline-offset: 0.3em; 9 | cursor: help; 10 | } 11 | 12 | .definition-box { 13 | width: 80%; 14 | background-color: var(--nord1); 15 | padding: 1em; 16 | text-align: left; 17 | font-size: 0.8em; 18 | } 19 | 20 | @media (min-width: 768px) { 21 | .definition-box { 22 | width: 40%; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Definition.tsx: -------------------------------------------------------------------------------- 1 | import { usePopperTooltip } from 'react-popper-tooltip' 2 | import { Link } from 'react-router-dom' 3 | import { GitDefinition } from '../types' 4 | import { toSnakeCase } from '../utils' 5 | import './Definition.css' 6 | 7 | export default function Definition({ definition }: { definition: GitDefinition }) { 8 | const { setTooltipRef, setTriggerRef, getTooltipProps, visible } = usePopperTooltip({ 9 | interactive: true, 10 | }) 11 | return ( 12 |
13 | 14 | {definition.name} 15 | 16 | {visible && ( 17 |
18 |
{definition.description}
19 |
20 | Read article 21 |
22 |
23 | )} 24 |
25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/git-command-parsing.tsx: -------------------------------------------------------------------------------- 1 | import { snakeToCamel } from './utils' 2 | import { 3 | AvailableFlagsArray, 4 | Flag, 5 | GitCommand, 6 | GitDefinition, 7 | ParsedArguments, 8 | SpecialTokens, 9 | } from './types' 10 | import Definition from './components/Definition' 11 | import { joinWithFinalAnd } from './utils' 12 | 13 | function getAvailableFlagsAsArray(availableFlagsObject: Flag[]): AvailableFlagsArray { 14 | const booleanFlagsArray = availableFlagsObject.reduce((acc, flag) => { 15 | if (!flag.isString) { 16 | if (flag.aliases) { 17 | return [...acc, flag.name, ...flag.aliases] 18 | } 19 | 20 | return [...acc, flag.name] 21 | } 22 | 23 | return acc 24 | }, [] as string[]) 25 | 26 | const stringFlagsArray = availableFlagsObject.reduce((acc, flag) => { 27 | if (flag.isString) { 28 | if (flag.aliases) { 29 | return [...acc, flag.name, ...flag.aliases] 30 | } 31 | 32 | return [...acc, flag.name] 33 | } 34 | 35 | return acc 36 | }, [] as string[]) 37 | 38 | return { booleanFlagsArray, stringFlagsArray } 39 | } 40 | 41 | function getMatchingFlags(availableFlags: Flag[], parsedArguments: ParsedArguments) { 42 | const matchingFlags = parsedArguments.flags.flatMap((inputFlag) => { 43 | // Filters out the boolean flags based on : 44 | // If the flag exists 45 | // If it's not _ (minimist prefix to store everything that's not a flag) 46 | // If the flag is either stored as its full name or the alias (only checks for aliases if the property exists) 47 | // TODO: add a check for duplicate 48 | return availableFlags.filter((availableFlag) => availableFlag.name === inputFlag.name) 49 | }) 50 | 51 | return matchingFlags 52 | } 53 | 54 | // Parse the flags descriptions if needed 55 | // TODO: So far if a command has two times the same string value, it just joins them with a space. Needs to be improved for specific flags such as -m writing the message on two paragraphs. 56 | function getParsedFlagsDescriptions(flagsDescriptions: Flag[], commandArguments: ParsedArguments) { 57 | return flagsDescriptions.map((flag) => { 58 | if (flag.isString) { 59 | // Gets the matching string value for the current flag 60 | let stringFlagValue = commandArguments.flags.find((inputFlag) => inputFlag.name === flag.name) 61 | ?.value 62 | if (Array.isArray(stringFlagValue)) { 63 | stringFlagValue = stringFlagValue.join(' ') 64 | } 65 | 66 | if (typeof stringFlagValue === 'string') { 67 | return { 68 | ...flag, 69 | description: flag.description.replace('%s', stringFlagValue), 70 | } 71 | } 72 | } 73 | return flag 74 | }) 75 | } 76 | 77 | function getAliasesAsObject(availableFlags: Flag[]) { 78 | return availableFlags.reduce((flagsList, flag) => { 79 | if (flag.aliases) { 80 | flagsList[snakeToCamel(flag.name)] = flag.aliases 81 | } 82 | return flagsList 83 | }, {} as any) 84 | } 85 | 86 | function replaceSpecialTokens(parsedArguments: ParsedArguments, specialTokens: SpecialTokens) { 87 | const updatedArguments = parsedArguments._.slice(2).map((argument) => { 88 | return ( 89 | Object.entries(specialTokens).find(([tokenKey]) => tokenKey === argument)?.[1] ?? argument 90 | ) 91 | }) 92 | 93 | return { ...parsedArguments, _: updatedArguments } 94 | } 95 | 96 | // Transform a description string into an array of strings and Definition components based on the term is a git definition or not. 97 | function parseDescriptionWithGitDefinitions( 98 | text: string | (string | JSX.Element)[], 99 | definition: GitDefinition 100 | ) { 101 | let updatedDescription 102 | // This conditional is here because text can be either a string or an array, and it needs to be handled accordingly. 103 | if (typeof text === 'string') { 104 | updatedDescription = text.split(definition.regex).map((str) => { 105 | if (definition.regex.test(str)) { 106 | return 107 | } 108 | return str 109 | }) 110 | } else { 111 | updatedDescription = text.flatMap((str) => { 112 | // This conditional checks the regex only if the array element is a string, and runs the loop on the part of the array again (probably a terrible solution, might need some refactoring later). 113 | if (typeof str === 'string') { 114 | return str.split(definition.regex).map((str) => { 115 | if (definition.regex.test(str)) { 116 | return 117 | } 118 | return str 119 | }) 120 | } 121 | return str 122 | }) 123 | } 124 | return updatedDescription 125 | } 126 | 127 | function parseDescription( 128 | command: GitCommand, 129 | definitions: GitDefinition[], 130 | parsedArguments: ParsedArguments 131 | ) { 132 | const descriptionWithDef = definitions.reduce>( 133 | (newDescription, definition) => { 134 | newDescription = parseDescriptionWithGitDefinitions(newDescription, definition) 135 | return newDescription 136 | }, 137 | command.description 138 | ) 139 | 140 | // Parse the descriptionWithDef variable accordingly whether it's a string or an array of elements. 141 | if (typeof descriptionWithDef === 'string') { 142 | return descriptionWithDef 143 | .split(' ') 144 | .map((str) => 145 | typeof str === 'string' ? str.replace('%s', joinWithFinalAnd(parsedArguments._)) : str 146 | ) 147 | } 148 | return descriptionWithDef.map((str) => 149 | typeof str === 'string' ? str.replace('%s', joinWithFinalAnd(parsedArguments._)) : str 150 | ) 151 | } 152 | 153 | export { 154 | getAvailableFlagsAsArray, 155 | getMatchingFlags, 156 | getParsedFlagsDescriptions, 157 | getAliasesAsObject, 158 | replaceSpecialTokens, 159 | parseDescriptionWithGitDefinitions, 160 | parseDescription, 161 | } 162 | -------------------------------------------------------------------------------- /src/git-commands.ts: -------------------------------------------------------------------------------- 1 | const gitCommands = { 2 | commands: [ 3 | { 4 | name: 'add', 5 | description: 'Adds %s to the staging area, ready to be committed.', 6 | flags: [ 7 | { 8 | name: 'verbose', 9 | aliases: ['v'], 10 | description: 'Be verbose (allows the command to say more about what it does).', 11 | isString: false, 12 | }, 13 | { 14 | name: 'dry-run', 15 | aliases: ['n'], 16 | description: 'Don’t actually add the file(s), just show if they exist.', 17 | isString: false, 18 | }, 19 | { 20 | name: 'force', 21 | aliases: ['f'], 22 | description: 'Allow adding ignored files.', 23 | isString: false, 24 | }, 25 | { 26 | name: 'pathspec-from-file', 27 | description: 28 | 'Adds the files to the staging area from %s instead of command-line arguments (separated by line breaks). If the value of the flag is -, then standard input is used instead.', 29 | isString: true, 30 | }, 31 | { 32 | name: 'patch', 33 | aliases: ['p'], 34 | description: 'Allows the user to add some part of the edited code interactively.', 35 | isString: false, 36 | }, 37 | ], 38 | }, 39 | { 40 | name: 'commit', 41 | description: 'Saves changes added to the staging area to the local git repository.', 42 | flags: [ 43 | { 44 | name: 'all', 45 | aliases: ['a'], 46 | description: 47 | 'Automatically adds every file that has been changed or deleted to the commit, but not the files that you just created.', 48 | isString: false, 49 | }, 50 | { 51 | name: 'message', 52 | aliases: ['m'], 53 | description: 'Set the commit message to %s', 54 | isString: true, 55 | }, 56 | ], 57 | }, 58 | { 59 | name: 'push', 60 | description: 'Updates your remote branch with your local commits.', 61 | flags: [ 62 | { 63 | name: 'all', 64 | description: 'Push all branches.', 65 | isString: false, 66 | }, 67 | { 68 | name: 'prune', 69 | description: 70 | 'Remove remote branches that don’t have a local counterpart. For example, a branch foo will be removed if a local branch with the same name does not exist anymore.', 71 | isString: false, 72 | }, 73 | { 74 | name: 'dry-run', 75 | aliases: ['n'], 76 | description: 'Do everything except send the updates (effectively simulating a push).', 77 | isString: false, 78 | }, 79 | { 80 | name: 'porcelain', 81 | description: 82 | 'Outputs machine-readable and parsable output (The output status line for each ref will be tab-separated and sent to stdout instead of stderr).', 83 | isString: false, 84 | }, 85 | ], 86 | }, 87 | { 88 | name: 'fetch', 89 | description: 'Fetch all the new commits from a remote branch locally.', 90 | flags: [] 91 | }, 92 | { 93 | name: 'rebase', 94 | description: 'Reapply commits on another branch', 95 | flags: [ 96 | { 97 | name: 'verbose', 98 | aliases: ['v'], 99 | description: 'Be verbose (allows the command to say more about what it does). Implies --stat.', 100 | isString: false, 101 | }, 102 | { 103 | name: 'quiet', 104 | aliases: ['q'], 105 | description: 'Be quiet (Opposite of verbose, silences the command output). Implies --no-stat.', 106 | isString: false, 107 | }, 108 | { 109 | name: 'interactive', 110 | aliases: ['i'], 111 | description: 'Make a list of the commits which are about to be rebased. (This list can be edited before continuing to rebase)', 112 | isString: false, 113 | }, 114 | { 115 | name: 'force-rebase', 116 | aliases: ['f'], 117 | description: 'Individually replay all rebased commits instead of fast-forwarding over the unchanged ones.', 118 | isString: false, 119 | }, 120 | { 121 | name: 'continue', 122 | description: 'Restart the rebasing process after having resolved a merge conflict.', 123 | isString: false, 124 | }, 125 | { 126 | name: 'abort', 127 | description: 'Abort the rebase operation and reset HEAD to the original branch.', 128 | isString: false, 129 | }, 130 | ] 131 | }, 132 | { 133 | name: 'diff', 134 | description: 'Show changes between commits, commit and working tree, and more.', 135 | isString: false, 136 | flags: [ 137 | { 138 | name: 'raw', 139 | description: 'Generates the differance in a raw format.', 140 | isString: false, 141 | }, 142 | { 143 | name: 'summary', 144 | description: 'Summarize the extended header information output (Such as creations and renames)', 145 | isString: false, 146 | }, 147 | { 148 | name: 'name-only', 149 | description: 'Only shows the name of changed files.', 150 | isString: false, 151 | }, 152 | ] 153 | }, 154 | { 155 | name: 'log', 156 | description: 'Lists commits and its information in reverse chronological order', 157 | flags: [ 158 | { 159 | name: 'source', 160 | description: 'Print out the ref information of each commit (Such as tags/branches).', 161 | isString: false, 162 | }, 163 | { 164 | name: 'log-size', 165 | description: 'Include the lenght of each commit message in bytes.', 166 | isString: false, 167 | }, 168 | { 169 | name: 'abbrev-commit', 170 | description: 'Shows a unique object prefix that substitutes the full 40-byte hexadecimal commit object name', 171 | isString: false, 172 | }, 173 | { 174 | name: 'oneline', 175 | description: 'Displays the information of each commit in only one line and abbreviates the object\'s name (The same way as --abbrev-commit).', 176 | isString: false, 177 | }, 178 | ] 179 | }, 180 | { 181 | name: 'pull', 182 | description: 'Merge changes from a remote repository into the current branch', 183 | flags: [ 184 | { 185 | name: 'commit', 186 | description: 'Commits the result of the merge.', 187 | isString: false, 188 | }, 189 | { 190 | name: 'edit', 191 | aliases: ['e'], 192 | description: 'Opens an editor to modify the auto-generated merge message before commiting to further explain the merge.', 193 | isString: false, 194 | }, 195 | ] 196 | }, 197 | { 198 | name: 'status', 199 | description: 'Displays the paths that have differences between the index file and staged, tracked or untracked files', 200 | flags: [ 201 | { 202 | name: 'short', 203 | aliases: ['s'], 204 | description: 'Outputs the status in the short format.', 205 | isString: false, 206 | }, 207 | { 208 | name: 'long', 209 | description: 'Outputs the status in the long format (Default).', 210 | isString: false, 211 | }, 212 | { 213 | name: 'verbose', 214 | aliases: ['v'], 215 | description: 'Shows the textual changes that are staged to be commited as well as the name of the files (The same way as git diff --cached).', 216 | isString: false, 217 | }, 218 | ] 219 | }, 220 | ], 221 | } 222 | 223 | const specialTokens = { 224 | '.': 'all files in the current directory', 225 | '..': 'all files in the previous directory', 226 | } 227 | 228 | export { gitCommands, specialTokens } -------------------------------------------------------------------------------- /src/git-definitions.ts: -------------------------------------------------------------------------------- 1 | import { GitDefinition } from './types' 2 | 3 | const definitions: GitDefinition[] = [ 4 | { 5 | name: 'staging area', 6 | description: 7 | 'The area of Git where your files end up after adding them using git add. The staging area can be used to craft commits, stash files etc.', 8 | regex: /(staging area)/, 9 | }, 10 | ] 11 | 12 | export { definitions } 13 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import 'nord.css'; 2 | 3 | .App { 4 | display: flex; 5 | min-width: 100vw; 6 | min-height: 100vh; 7 | text-align: center; 8 | flex-direction: column; 9 | align-items: center; 10 | justify-content: center; 11 | font-size: calc(10px + 2vmin); 12 | background-color: var(--nord0); 13 | color: var(--nord6); 14 | } 15 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' 4 | import App from './App' 5 | import Article from './components/Article' 6 | import './index.css' 7 | 8 | ReactDOM.render( 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 |
22 |
, 23 | document.getElementById('root') 24 | ) 25 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/nord.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-present Arctic Ice Studio 3 | * Copyright (c) 2016-present Sven Greb 4 | * 5 | * Project: Nord 6 | * Version: 0.2.0 7 | * Repository: https://github.com/arcticicestudio/nord 8 | * License: MIT 9 | * References: 10 | * https://www.w3.org/TR/css-variables 11 | * https://www.w3.org/TR/selectors/#root-pseudo 12 | * https://drafts.csswg.org/css-variables 13 | * https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables 14 | * http://warpspire.com/kss 15 | * https://github.com/kss-node/kss-node 16 | */ 17 | 18 | /* 19 | An arctic, north-bluish color palette. 20 | Created for the clean- and minimal flat design pattern to achieve a optimal focus and readability for code syntax 21 | highlighting and UI. 22 | It consists of a total of sixteen, carefully selected, dimmed pastel colors for a eye-comfortable, but yet colorful 23 | ambiance. 24 | Styleguide Nord 25 | */ 26 | 27 | :root { 28 | /* 29 | Base component color of "Polar Night". 30 | Used for texts, backgrounds, carets and structuring characters like curly- and square brackets. 31 | Markup: 32 |
33 | Styleguide Nord - Polar Night 34 | */ 35 | --nord0: #2e3440; 36 | 37 | /* 38 | Lighter shade color of the base component color. 39 | Used as a lighter background color for UI elements like status bars. 40 | Markup: 41 |
42 | Styleguide Nord - Polar Night 43 | */ 44 | --nord1: #3b4252; 45 | 46 | /* 47 | Lighter shade color of the base component color. 48 | Used as line highlighting in the editor. 49 | In the UI scope it may be used as selection- and highlight color. 50 | Markup: 51 |
52 | Styleguide Nord - Polar Night 53 | */ 54 | --nord2: #434c5e; 55 | 56 | /* 57 | Lighter shade color of the base component color. 58 | Used for comments, invisibles, indent- and wrap guide marker. 59 | In the UI scope used as pseudoclass color for disabled elements. 60 | Markup: 61 |
62 | Styleguide Nord - Polar Night 63 | */ 64 | --nord3: #4c566a; 65 | 66 | /* 67 | Base component color of "Snow Storm". 68 | Main color for text, variables, constants and attributes. 69 | In the UI scope used as semi-light background depending on the theme shading design. 70 | Markup: 71 |
72 | Styleguide Nord - Snow Storm 73 | */ 74 | --nord4: #d8dee9; 75 | 76 | /* 77 | Lighter shade color of the base component color. 78 | Used as a lighter background color for UI elements like status bars. 79 | Used as semi-light background depending on the theme shading design. 80 | Markup: 81 |
82 | Styleguide Nord - Snow Storm 83 | */ 84 | --nord5: #e5e9f0; 85 | 86 | /* 87 | Lighter shade color of the base component color. 88 | Used for punctuations, carets and structuring characters like curly- and square brackets. 89 | In the UI scope used as background, selection- and highlight color depending on the theme shading design. 90 | Markup: 91 |
92 | Styleguide Nord - Snow Storm 93 | */ 94 | --nord6: #eceff4; 95 | 96 | /* 97 | Bluish core color. 98 | Used for classes, types and documentation tags. 99 | Markup: 100 |
101 | Styleguide Nord - Frost 102 | */ 103 | --nord7: #8fbcbb; 104 | 105 | /* 106 | Bluish core accent color. 107 | Represents the accent color of the color palette. 108 | Main color for primary UI elements and methods/functions. 109 | Can be used for 110 | - Markup quotes 111 | - Markup link URLs 112 | Markup: 113 |
114 | Styleguide Nord - Frost 115 | */ 116 | --nord8: #88c0d0; 117 | 118 | /* 119 | Bluish core color. 120 | Used for language-specific syntactic/reserved support characters and keywords, operators, tags, units and 121 | punctuations like (semi)colons,commas and braces. 122 | Markup: 123 |
124 | Styleguide Nord - Frost 125 | */ 126 | --nord9: #81a1c1; 127 | 128 | /* 129 | Bluish core color. 130 | Used for markup doctypes, import/include/require statements, pre-processor statements and at-rules (`@`). 131 | Markup: 132 |
133 | Styleguide Nord - Frost 134 | */ 135 | --nord10: #5e81ac; 136 | 137 | /* 138 | Colorful component color. 139 | Used for errors, git/diff deletion and linter marker. 140 | Markup: 141 |
142 | Styleguide Nord - Aurora 143 | */ 144 | --nord11: #bf616a; 145 | 146 | /* 147 | Colorful component color. 148 | Used for annotations. 149 | Markup: 150 |
151 | Styleguide Nord - Aurora 152 | */ 153 | --nord12: #d08770; 154 | 155 | /* 156 | Colorful component color. 157 | Used for escape characters, regular expressions and markup entities. 158 | In the UI scope used for warnings and git/diff renamings. 159 | Markup: 160 |
161 | Styleguide Nord - Aurora 162 | */ 163 | --nord13: #ebcb8b; 164 | 165 | /* 166 | Colorful component color. 167 | Main color for strings and attribute values. 168 | In the UI scope used for git/diff additions and success visualizations. 169 | Markup: 170 |
171 | Styleguide Nord - Aurora 172 | */ 173 | --nord14: #a3be8c; 174 | 175 | /* 176 | Colorful component color. 177 | Used for numbers. 178 | Markup: 179 |
180 | Styleguide Nord - Aurora 181 | */ 182 | --nord15: #b48ead; 183 | } 184 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface GitCommand { 2 | name: string 3 | description: string | (string | JSX.Element)[] 4 | flagsDescriptions: Flag[] 5 | flags: Flag[] 6 | } 7 | export interface Flag { 8 | name: string 9 | aliases?: string[] 10 | description: string 11 | isString: boolean 12 | } 13 | 14 | export interface AvailableFlagsArray { 15 | booleanFlagsArray: string[] 16 | stringFlagsArray: string[] 17 | } 18 | 19 | export interface ParsedArguments { 20 | _: string[] 21 | flags: InputFlag[] 22 | } 23 | 24 | export interface InputFlag { 25 | name: string 26 | value: boolean | string | string[] 27 | } 28 | 29 | export interface SpecialTokens { 30 | [key: string]: string 31 | } 32 | 33 | export interface GitDefinition { 34 | name: string 35 | description: string 36 | regex: RegExp 37 | } 38 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | function snakeToCamel(str: string) { 2 | return str.replace(/([-_][a-z])/g, (group) => 3 | group.toUpperCase().replace('-', '').replace('_', '') 4 | ) 5 | } 6 | 7 | function joinWithFinalAnd(arrayToJoin: string[]) { 8 | if (arrayToJoin.length <= 1) { 9 | return arrayToJoin.join() 10 | } 11 | return arrayToJoin.slice(0, -1).join(', ') + ' and ' + arrayToJoin.slice(-1) 12 | } 13 | 14 | function toSnakeCase(str: string) { 15 | return str.trim().replace(' ', '-') 16 | } 17 | 18 | export { snakeToCamel, joinWithFinalAnd, toSnakeCase } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx", 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------