├── .eslintrc.json ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── resources ├── dark │ ├── copy-to-folder.svg │ ├── create.svg │ ├── edit.svg │ ├── folder.svg │ ├── refresh.svg │ ├── string.svg │ ├── thumbnail.svg │ ├── upload.svg │ └── workshop-item-hidden.svg ├── light │ ├── copy-to-folder.svg │ ├── create.svg │ ├── edit.svg │ ├── folder.svg │ ├── refresh.svg │ ├── string.svg │ ├── thumbnail.svg │ ├── upload.svg │ └── workshop-item-hidden.svg ├── menu-icon.svg ├── package-icon.png ├── readme │ ├── configuration.png │ ├── create-addon-1.png │ ├── create-addon-2.png │ ├── create-addon-3.png │ └── features-copy-to-gmod.png └── samples │ ├── weapons │ ├── weapon_sandbox_main_sample.lua │ ├── weapon_sandbox_toolgun_sample.lua │ ├── weapon_ttt_detective_sample.lua │ ├── weapon_ttt_grenade_sample.lua │ ├── weapon_ttt_primary_sample.lua │ ├── weapon_ttt_secondary_sample.lua │ └── weapon_ttt_traitor_sample.lua │ └── workshop-thumbnail.jpg ├── src ├── Services │ ├── GModAddonManager.ts │ ├── GModWeaponManager.ts │ ├── GModWorkshopManager.ts │ └── LocalGModManager.ts ├── Views │ ├── GModAddonInfoView.ts │ ├── GModAddonWeaponsView.ts │ └── GModWorkshopView.ts ├── Wizards │ ├── AddonTagsWizard.ts │ ├── AddonTypeWizard.ts │ ├── CreateWeaponWizard.ts │ ├── WorkshopThumbnailWizard.ts │ └── WorkshopUploadWizard.ts ├── extension.ts └── test │ ├── runTest.ts │ └── suite │ ├── extension.test.ts │ └── index.ts ├── tsconfig.json └── vsc-extension-quickstart.md /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "${defaultBuildTask}" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "${defaultBuildTask}" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/.eslintrc.json 9 | **/*.map 10 | **/*.ts 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "gmod-sdk" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Michael Hawkins 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 | # GMod SDK for Visual Studio Code 2 | 3 | A VS Code extension to make GMod addon development easier. 4 | 5 | With this, you can quickly create and upload Garry's Mod addons to the workshop. 6 | 7 |
8 | 9 | It is recommended that you also install the [GLua language support extension](https://marketplace.visualstudio.com/items?itemName=aStonedPenguin.glua), for improved syntax highlighting and suggestions. 10 | 11 | 12 |
13 | 14 | # Create your first Workshop item 15 | 1. Open a folder in VS Code and click Create Addon 16 | 17 | ![create-addon-button](resources/readme/create-addon-1.png) 18 | 19 | 2. Add your content to the addon.
20 | For example, create a weapon using the weapons interface. 21 | 22 | ![create-addon-button](resources/readme/create-addon-2.png) 23 | 24 | 3. Upload your addon to the workshop (Steam must be running). 25 | 26 | ![create-addon-button](resources/readme/create-addon-3.png) 27 | 28 |
29 |
30 | 31 | # Features 32 | 33 | ## Addon Development 34 | _Create and use an addon within minutes!_ 35 | 36 | * Create an addon from an empty folder 37 | * Copy an addon project to your Garry's Mod folder (and use it in-game!) 38 | * Create a Garry's Mod weapon from different templates for TTT and Sandbox 39 | 40 |
41 | 42 | _Copy addons to your Garry's Mod folder and test them in-game_
43 | ![copy-to-local-garrysmod-folder](resources/readme/features-copy-to-gmod.png) 44 | 45 | 46 |
47 | 48 | ## Workshop Tools 49 | _Manage your workshop files at the click of a button!_ 50 | 51 | * Upload an addon to your workshop- maps, weapons, gamemodes, etc. 52 | * Update an existing addon on the workshop 53 | * Change an addon's thumbnail 54 | 55 | 56 |
57 |
58 | 59 | 60 | # Configuration 61 | 1. Press `ctrl` + `,` or go to `File` -> `Preferences` -> `Settings` 62 | 2. Search for `gmod-sdk` 63 | 64 |
65 | 66 | ## Set your Garry's Mod folder location 67 | 68 | In order for this extension to work, it needs to know where your Garry's Mod folder is located.
69 | In the settings, you will need to set your 70 | 71 | * Addons folder location 72 | * gmpublish program location 73 | * gmad program location 74 | 75 | If you need help finding these things, there is a helpful guide [here](https://gist.github.com/BadgerCode/00600eab40556c6e8809590d263ea053). 76 | 77 | ![configuration-example](resources/readme/configuration.png) 78 | 79 | 80 |
81 |
82 | 83 | 84 | # Contributing 85 | * Ideas/Problems - Please create an issue on Github or find an existing thread. We want to make Garry's Mod addon development easier for you! 86 | * Code 87 | * Clone this repository and open it in VS Code 88 | * Open a terminal and run `npm install` 89 | * To debug, press `F5`. This will open a new window with the extension running. 90 | * Use `ctrl+r` to reload the window 91 | 92 | 93 | # Resources 94 | * Icons 95 | * [Font Awesome](https://fontawesome.com/icons/list) 96 | * [Azure Functions Extension](https://github.com/microsoft/vscode-azurefunctions) 97 | * [VS Code Extension Examples](https://github.com/microsoft/vscode-extension-samples/) 98 | * [Garry's Mod Logo](https://icons8.com/icons/set/garrys-mod) 99 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gmod-sdk", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.4", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 19 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "chalk": { 34 | "version": "2.4.2", 35 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 36 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 37 | "dev": true, 38 | "requires": { 39 | "ansi-styles": "^3.2.1", 40 | "escape-string-regexp": "^1.0.5", 41 | "supports-color": "^5.3.0" 42 | } 43 | } 44 | } 45 | }, 46 | "@types/color-name": { 47 | "version": "1.1.1", 48 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 49 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", 50 | "dev": true 51 | }, 52 | "@types/eslint-visitor-keys": { 53 | "version": "1.0.0", 54 | "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", 55 | "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", 56 | "dev": true 57 | }, 58 | "@types/fs-extra": { 59 | "version": "9.0.1", 60 | "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.1.tgz", 61 | "integrity": "sha512-B42Sxuaz09MhC3DDeW5kubRcQ5by4iuVQ0cRRWM2lggLzAa/KVom0Aft/208NgMvNQQZ86s5rVcqDdn/SH0/mg==", 62 | "dev": true, 63 | "requires": { 64 | "@types/node": "*" 65 | } 66 | }, 67 | "@types/glob": { 68 | "version": "7.1.3", 69 | "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", 70 | "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", 71 | "dev": true, 72 | "requires": { 73 | "@types/minimatch": "*", 74 | "@types/node": "*" 75 | } 76 | }, 77 | "@types/json-schema": { 78 | "version": "7.0.5", 79 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", 80 | "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", 81 | "dev": true 82 | }, 83 | "@types/minimatch": { 84 | "version": "3.0.3", 85 | "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", 86 | "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", 87 | "dev": true 88 | }, 89 | "@types/mocha": { 90 | "version": "8.0.2", 91 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.2.tgz", 92 | "integrity": "sha512-5cv8rmqT3KX9XtWDvSgGYfS4OwrKM2eei90GWLnTYz+AXRiBv5uYcKBjnkQ4katNvfYk3+o2bHGZUsDhdcoUyg==", 93 | "dev": true 94 | }, 95 | "@types/node": { 96 | "version": "14.0.27", 97 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", 98 | "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==", 99 | "dev": true 100 | }, 101 | "@types/vscode": { 102 | "version": "1.48.0", 103 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.48.0.tgz", 104 | "integrity": "sha512-sZJKzsJz1gSoFXcOJWw3fnKl2sseUgZmvB4AJZS+Fea+bC/jfGPVhmFL/FfQHld/TKtukVONsmoD3Pkyx9iadg==", 105 | "dev": true 106 | }, 107 | "@typescript-eslint/eslint-plugin": { 108 | "version": "3.9.0", 109 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.9.0.tgz", 110 | "integrity": "sha512-UD6b4p0/hSe1xdTvRCENSx7iQ+KR6ourlZFfYuPC7FlXEzdHuLPrEmuxZ23b2zW96KJX9Z3w05GE/wNOiEzrVg==", 111 | "dev": true, 112 | "requires": { 113 | "@typescript-eslint/experimental-utils": "3.9.0", 114 | "debug": "^4.1.1", 115 | "functional-red-black-tree": "^1.0.1", 116 | "regexpp": "^3.0.0", 117 | "semver": "^7.3.2", 118 | "tsutils": "^3.17.1" 119 | } 120 | }, 121 | "@typescript-eslint/experimental-utils": { 122 | "version": "3.9.0", 123 | "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.9.0.tgz", 124 | "integrity": "sha512-/vSHUDYizSOhrOJdjYxPNGfb4a3ibO8zd4nUKo/QBFOmxosT3cVUV7KIg8Dwi6TXlr667G7YPqFK9+VSZOorNA==", 125 | "dev": true, 126 | "requires": { 127 | "@types/json-schema": "^7.0.3", 128 | "@typescript-eslint/types": "3.9.0", 129 | "@typescript-eslint/typescript-estree": "3.9.0", 130 | "eslint-scope": "^5.0.0", 131 | "eslint-utils": "^2.0.0" 132 | } 133 | }, 134 | "@typescript-eslint/parser": { 135 | "version": "3.9.0", 136 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.9.0.tgz", 137 | "integrity": "sha512-rDHOKb6uW2jZkHQniUQVZkixQrfsZGUCNWWbKWep4A5hGhN5dLHMUCNAWnC4tXRlHedXkTDptIpxs6e4Pz8UfA==", 138 | "dev": true, 139 | "requires": { 140 | "@types/eslint-visitor-keys": "^1.0.0", 141 | "@typescript-eslint/experimental-utils": "3.9.0", 142 | "@typescript-eslint/types": "3.9.0", 143 | "@typescript-eslint/typescript-estree": "3.9.0", 144 | "eslint-visitor-keys": "^1.1.0" 145 | } 146 | }, 147 | "@typescript-eslint/types": { 148 | "version": "3.9.0", 149 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.9.0.tgz", 150 | "integrity": "sha512-rb6LDr+dk9RVVXO/NJE8dT1pGlso3voNdEIN8ugm4CWM5w5GimbThCMiMl4da1t5u3YwPWEwOnKAULCZgBtBHg==", 151 | "dev": true 152 | }, 153 | "@typescript-eslint/typescript-estree": { 154 | "version": "3.9.0", 155 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.9.0.tgz", 156 | "integrity": "sha512-N+158NKgN4rOmWVfvKOMoMFV5n8XxAliaKkArm/sOypzQ0bUL8MSnOEBW3VFIeffb/K5ce/cAV0yYhR7U4ALAA==", 157 | "dev": true, 158 | "requires": { 159 | "@typescript-eslint/types": "3.9.0", 160 | "@typescript-eslint/visitor-keys": "3.9.0", 161 | "debug": "^4.1.1", 162 | "glob": "^7.1.6", 163 | "is-glob": "^4.0.1", 164 | "lodash": "^4.17.15", 165 | "semver": "^7.3.2", 166 | "tsutils": "^3.17.1" 167 | } 168 | }, 169 | "@typescript-eslint/visitor-keys": { 170 | "version": "3.9.0", 171 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.9.0.tgz", 172 | "integrity": "sha512-O1qeoGqDbu0EZUC/MZ6F1WHTIzcBVhGqDj3LhTnj65WUA548RXVxUHbYhAW9bZWfb2rnX9QsbbP5nmeJ5Z4+ng==", 173 | "dev": true, 174 | "requires": { 175 | "eslint-visitor-keys": "^1.1.0" 176 | } 177 | }, 178 | "acorn": { 179 | "version": "7.4.0", 180 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", 181 | "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", 182 | "dev": true 183 | }, 184 | "acorn-jsx": { 185 | "version": "5.2.0", 186 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", 187 | "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", 188 | "dev": true 189 | }, 190 | "agent-base": { 191 | "version": "4.3.0", 192 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", 193 | "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", 194 | "dev": true, 195 | "requires": { 196 | "es6-promisify": "^5.0.0" 197 | } 198 | }, 199 | "ajv": { 200 | "version": "6.12.4", 201 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", 202 | "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", 203 | "dev": true, 204 | "requires": { 205 | "fast-deep-equal": "^3.1.1", 206 | "fast-json-stable-stringify": "^2.0.0", 207 | "json-schema-traverse": "^0.4.1", 208 | "uri-js": "^4.2.2" 209 | } 210 | }, 211 | "ansi-colors": { 212 | "version": "4.1.1", 213 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 214 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 215 | "dev": true 216 | }, 217 | "ansi-regex": { 218 | "version": "5.0.0", 219 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 220 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 221 | "dev": true 222 | }, 223 | "ansi-styles": { 224 | "version": "3.2.1", 225 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 226 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 227 | "dev": true, 228 | "requires": { 229 | "color-convert": "^1.9.0" 230 | } 231 | }, 232 | "anymatch": { 233 | "version": "3.1.1", 234 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 235 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 236 | "dev": true, 237 | "requires": { 238 | "normalize-path": "^3.0.0", 239 | "picomatch": "^2.0.4" 240 | } 241 | }, 242 | "argparse": { 243 | "version": "1.0.10", 244 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 245 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 246 | "dev": true, 247 | "requires": { 248 | "sprintf-js": "~1.0.2" 249 | } 250 | }, 251 | "array.prototype.map": { 252 | "version": "1.0.2", 253 | "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", 254 | "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", 255 | "dev": true, 256 | "requires": { 257 | "define-properties": "^1.1.3", 258 | "es-abstract": "^1.17.0-next.1", 259 | "es-array-method-boxes-properly": "^1.0.0", 260 | "is-string": "^1.0.4" 261 | } 262 | }, 263 | "astral-regex": { 264 | "version": "1.0.0", 265 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", 266 | "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", 267 | "dev": true 268 | }, 269 | "at-least-node": { 270 | "version": "1.0.0", 271 | "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", 272 | "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" 273 | }, 274 | "axios": { 275 | "version": "0.19.2", 276 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", 277 | "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", 278 | "requires": { 279 | "follow-redirects": "1.5.10" 280 | } 281 | }, 282 | "balanced-match": { 283 | "version": "1.0.0", 284 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 285 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 286 | "dev": true 287 | }, 288 | "binary-extensions": { 289 | "version": "2.1.0", 290 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", 291 | "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", 292 | "dev": true 293 | }, 294 | "brace-expansion": { 295 | "version": "1.1.11", 296 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 297 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 298 | "dev": true, 299 | "requires": { 300 | "balanced-match": "^1.0.0", 301 | "concat-map": "0.0.1" 302 | } 303 | }, 304 | "braces": { 305 | "version": "3.0.2", 306 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 307 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 308 | "dev": true, 309 | "requires": { 310 | "fill-range": "^7.0.1" 311 | } 312 | }, 313 | "browser-stdout": { 314 | "version": "1.3.1", 315 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 316 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 317 | "dev": true 318 | }, 319 | "callsites": { 320 | "version": "3.1.0", 321 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 322 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 323 | "dev": true 324 | }, 325 | "camelcase": { 326 | "version": "5.3.1", 327 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 328 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 329 | "dev": true 330 | }, 331 | "chalk": { 332 | "version": "4.1.0", 333 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 334 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 335 | "dev": true, 336 | "requires": { 337 | "ansi-styles": "^4.1.0", 338 | "supports-color": "^7.1.0" 339 | }, 340 | "dependencies": { 341 | "ansi-styles": { 342 | "version": "4.2.1", 343 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", 344 | "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", 345 | "dev": true, 346 | "requires": { 347 | "@types/color-name": "^1.1.1", 348 | "color-convert": "^2.0.1" 349 | } 350 | }, 351 | "color-convert": { 352 | "version": "2.0.1", 353 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 354 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 355 | "dev": true, 356 | "requires": { 357 | "color-name": "~1.1.4" 358 | } 359 | }, 360 | "color-name": { 361 | "version": "1.1.4", 362 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 363 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 364 | "dev": true 365 | }, 366 | "has-flag": { 367 | "version": "4.0.0", 368 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 369 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 370 | "dev": true 371 | }, 372 | "supports-color": { 373 | "version": "7.1.0", 374 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 375 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 376 | "dev": true, 377 | "requires": { 378 | "has-flag": "^4.0.0" 379 | } 380 | } 381 | } 382 | }, 383 | "chokidar": { 384 | "version": "3.3.1", 385 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", 386 | "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", 387 | "dev": true, 388 | "requires": { 389 | "anymatch": "~3.1.1", 390 | "braces": "~3.0.2", 391 | "fsevents": "~2.1.2", 392 | "glob-parent": "~5.1.0", 393 | "is-binary-path": "~2.1.0", 394 | "is-glob": "~4.0.1", 395 | "normalize-path": "~3.0.0", 396 | "readdirp": "~3.3.0" 397 | } 398 | }, 399 | "cliui": { 400 | "version": "5.0.0", 401 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 402 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 403 | "dev": true, 404 | "requires": { 405 | "string-width": "^3.1.0", 406 | "strip-ansi": "^5.2.0", 407 | "wrap-ansi": "^5.1.0" 408 | }, 409 | "dependencies": { 410 | "ansi-regex": { 411 | "version": "4.1.0", 412 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 413 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 414 | "dev": true 415 | }, 416 | "strip-ansi": { 417 | "version": "5.2.0", 418 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 419 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 420 | "dev": true, 421 | "requires": { 422 | "ansi-regex": "^4.1.0" 423 | } 424 | } 425 | } 426 | }, 427 | "color-convert": { 428 | "version": "1.9.3", 429 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 430 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 431 | "dev": true, 432 | "requires": { 433 | "color-name": "1.1.3" 434 | } 435 | }, 436 | "color-name": { 437 | "version": "1.1.3", 438 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 439 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 440 | "dev": true 441 | }, 442 | "concat-map": { 443 | "version": "0.0.1", 444 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 445 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 446 | "dev": true 447 | }, 448 | "cross-spawn": { 449 | "version": "7.0.3", 450 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 451 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 452 | "dev": true, 453 | "requires": { 454 | "path-key": "^3.1.0", 455 | "shebang-command": "^2.0.0", 456 | "which": "^2.0.1" 457 | } 458 | }, 459 | "debug": { 460 | "version": "4.1.1", 461 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 462 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 463 | "dev": true, 464 | "requires": { 465 | "ms": "^2.1.1" 466 | } 467 | }, 468 | "decamelize": { 469 | "version": "1.2.0", 470 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 471 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 472 | "dev": true 473 | }, 474 | "deep-is": { 475 | "version": "0.1.3", 476 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 477 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 478 | "dev": true 479 | }, 480 | "define-properties": { 481 | "version": "1.1.3", 482 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 483 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 484 | "dev": true, 485 | "requires": { 486 | "object-keys": "^1.0.12" 487 | } 488 | }, 489 | "diff": { 490 | "version": "4.0.2", 491 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 492 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 493 | "dev": true 494 | }, 495 | "doctrine": { 496 | "version": "3.0.0", 497 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 498 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 499 | "dev": true, 500 | "requires": { 501 | "esutils": "^2.0.2" 502 | } 503 | }, 504 | "emoji-regex": { 505 | "version": "7.0.3", 506 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 507 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 508 | "dev": true 509 | }, 510 | "enquirer": { 511 | "version": "2.3.6", 512 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 513 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 514 | "dev": true, 515 | "requires": { 516 | "ansi-colors": "^4.1.1" 517 | } 518 | }, 519 | "es-abstract": { 520 | "version": "1.17.6", 521 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", 522 | "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", 523 | "dev": true, 524 | "requires": { 525 | "es-to-primitive": "^1.2.1", 526 | "function-bind": "^1.1.1", 527 | "has": "^1.0.3", 528 | "has-symbols": "^1.0.1", 529 | "is-callable": "^1.2.0", 530 | "is-regex": "^1.1.0", 531 | "object-inspect": "^1.7.0", 532 | "object-keys": "^1.1.1", 533 | "object.assign": "^4.1.0", 534 | "string.prototype.trimend": "^1.0.1", 535 | "string.prototype.trimstart": "^1.0.1" 536 | } 537 | }, 538 | "es-array-method-boxes-properly": { 539 | "version": "1.0.0", 540 | "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", 541 | "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", 542 | "dev": true 543 | }, 544 | "es-get-iterator": { 545 | "version": "1.1.0", 546 | "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", 547 | "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", 548 | "dev": true, 549 | "requires": { 550 | "es-abstract": "^1.17.4", 551 | "has-symbols": "^1.0.1", 552 | "is-arguments": "^1.0.4", 553 | "is-map": "^2.0.1", 554 | "is-set": "^2.0.1", 555 | "is-string": "^1.0.5", 556 | "isarray": "^2.0.5" 557 | } 558 | }, 559 | "es-to-primitive": { 560 | "version": "1.2.1", 561 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 562 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 563 | "dev": true, 564 | "requires": { 565 | "is-callable": "^1.1.4", 566 | "is-date-object": "^1.0.1", 567 | "is-symbol": "^1.0.2" 568 | } 569 | }, 570 | "es6-promise": { 571 | "version": "4.2.8", 572 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", 573 | "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", 574 | "dev": true 575 | }, 576 | "es6-promisify": { 577 | "version": "5.0.0", 578 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 579 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 580 | "dev": true, 581 | "requires": { 582 | "es6-promise": "^4.0.3" 583 | } 584 | }, 585 | "escape-string-regexp": { 586 | "version": "1.0.5", 587 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 588 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 589 | "dev": true 590 | }, 591 | "eslint": { 592 | "version": "7.7.0", 593 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.7.0.tgz", 594 | "integrity": "sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==", 595 | "dev": true, 596 | "requires": { 597 | "@babel/code-frame": "^7.0.0", 598 | "ajv": "^6.10.0", 599 | "chalk": "^4.0.0", 600 | "cross-spawn": "^7.0.2", 601 | "debug": "^4.0.1", 602 | "doctrine": "^3.0.0", 603 | "enquirer": "^2.3.5", 604 | "eslint-scope": "^5.1.0", 605 | "eslint-utils": "^2.1.0", 606 | "eslint-visitor-keys": "^1.3.0", 607 | "espree": "^7.2.0", 608 | "esquery": "^1.2.0", 609 | "esutils": "^2.0.2", 610 | "file-entry-cache": "^5.0.1", 611 | "functional-red-black-tree": "^1.0.1", 612 | "glob-parent": "^5.0.0", 613 | "globals": "^12.1.0", 614 | "ignore": "^4.0.6", 615 | "import-fresh": "^3.0.0", 616 | "imurmurhash": "^0.1.4", 617 | "is-glob": "^4.0.0", 618 | "js-yaml": "^3.13.1", 619 | "json-stable-stringify-without-jsonify": "^1.0.1", 620 | "levn": "^0.4.1", 621 | "lodash": "^4.17.19", 622 | "minimatch": "^3.0.4", 623 | "natural-compare": "^1.4.0", 624 | "optionator": "^0.9.1", 625 | "progress": "^2.0.0", 626 | "regexpp": "^3.1.0", 627 | "semver": "^7.2.1", 628 | "strip-ansi": "^6.0.0", 629 | "strip-json-comments": "^3.1.0", 630 | "table": "^5.2.3", 631 | "text-table": "^0.2.0", 632 | "v8-compile-cache": "^2.0.3" 633 | } 634 | }, 635 | "eslint-scope": { 636 | "version": "5.1.0", 637 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", 638 | "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", 639 | "dev": true, 640 | "requires": { 641 | "esrecurse": "^4.1.0", 642 | "estraverse": "^4.1.1" 643 | } 644 | }, 645 | "eslint-utils": { 646 | "version": "2.1.0", 647 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 648 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 649 | "dev": true, 650 | "requires": { 651 | "eslint-visitor-keys": "^1.1.0" 652 | } 653 | }, 654 | "eslint-visitor-keys": { 655 | "version": "1.3.0", 656 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 657 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 658 | "dev": true 659 | }, 660 | "espree": { 661 | "version": "7.2.0", 662 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz", 663 | "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==", 664 | "dev": true, 665 | "requires": { 666 | "acorn": "^7.3.1", 667 | "acorn-jsx": "^5.2.0", 668 | "eslint-visitor-keys": "^1.3.0" 669 | } 670 | }, 671 | "esprima": { 672 | "version": "4.0.1", 673 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 674 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 675 | "dev": true 676 | }, 677 | "esquery": { 678 | "version": "1.3.1", 679 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", 680 | "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", 681 | "dev": true, 682 | "requires": { 683 | "estraverse": "^5.1.0" 684 | }, 685 | "dependencies": { 686 | "estraverse": { 687 | "version": "5.2.0", 688 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 689 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 690 | "dev": true 691 | } 692 | } 693 | }, 694 | "esrecurse": { 695 | "version": "4.2.1", 696 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 697 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 698 | "dev": true, 699 | "requires": { 700 | "estraverse": "^4.1.0" 701 | } 702 | }, 703 | "estraverse": { 704 | "version": "4.3.0", 705 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 706 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 707 | "dev": true 708 | }, 709 | "esutils": { 710 | "version": "2.0.3", 711 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 712 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 713 | "dev": true 714 | }, 715 | "fast-deep-equal": { 716 | "version": "3.1.3", 717 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 718 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 719 | "dev": true 720 | }, 721 | "fast-json-stable-stringify": { 722 | "version": "2.1.0", 723 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 724 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 725 | "dev": true 726 | }, 727 | "fast-levenshtein": { 728 | "version": "2.0.6", 729 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 730 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 731 | "dev": true 732 | }, 733 | "file-entry-cache": { 734 | "version": "5.0.1", 735 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", 736 | "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", 737 | "dev": true, 738 | "requires": { 739 | "flat-cache": "^2.0.1" 740 | } 741 | }, 742 | "fill-range": { 743 | "version": "7.0.1", 744 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 745 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 746 | "dev": true, 747 | "requires": { 748 | "to-regex-range": "^5.0.1" 749 | } 750 | }, 751 | "find-up": { 752 | "version": "4.1.0", 753 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 754 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 755 | "dev": true, 756 | "requires": { 757 | "locate-path": "^5.0.0", 758 | "path-exists": "^4.0.0" 759 | } 760 | }, 761 | "flat": { 762 | "version": "4.1.0", 763 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", 764 | "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", 765 | "dev": true, 766 | "requires": { 767 | "is-buffer": "~2.0.3" 768 | } 769 | }, 770 | "flat-cache": { 771 | "version": "2.0.1", 772 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", 773 | "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", 774 | "dev": true, 775 | "requires": { 776 | "flatted": "^2.0.0", 777 | "rimraf": "2.6.3", 778 | "write": "1.0.3" 779 | } 780 | }, 781 | "flatted": { 782 | "version": "2.0.2", 783 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", 784 | "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", 785 | "dev": true 786 | }, 787 | "follow-redirects": { 788 | "version": "1.5.10", 789 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", 790 | "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", 791 | "requires": { 792 | "debug": "=3.1.0" 793 | }, 794 | "dependencies": { 795 | "debug": { 796 | "version": "3.1.0", 797 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 798 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 799 | "requires": { 800 | "ms": "2.0.0" 801 | } 802 | }, 803 | "ms": { 804 | "version": "2.0.0", 805 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 806 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 807 | } 808 | } 809 | }, 810 | "fs-extra": { 811 | "version": "9.0.1", 812 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", 813 | "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", 814 | "requires": { 815 | "at-least-node": "^1.0.0", 816 | "graceful-fs": "^4.2.0", 817 | "jsonfile": "^6.0.1", 818 | "universalify": "^1.0.0" 819 | } 820 | }, 821 | "fs.realpath": { 822 | "version": "1.0.0", 823 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 824 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 825 | "dev": true 826 | }, 827 | "fsevents": { 828 | "version": "2.1.3", 829 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 830 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 831 | "dev": true, 832 | "optional": true 833 | }, 834 | "function-bind": { 835 | "version": "1.1.1", 836 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 837 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 838 | "dev": true 839 | }, 840 | "functional-red-black-tree": { 841 | "version": "1.0.1", 842 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 843 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 844 | "dev": true 845 | }, 846 | "get-caller-file": { 847 | "version": "2.0.5", 848 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 849 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 850 | "dev": true 851 | }, 852 | "glob": { 853 | "version": "7.1.6", 854 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 855 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 856 | "dev": true, 857 | "requires": { 858 | "fs.realpath": "^1.0.0", 859 | "inflight": "^1.0.4", 860 | "inherits": "2", 861 | "minimatch": "^3.0.4", 862 | "once": "^1.3.0", 863 | "path-is-absolute": "^1.0.0" 864 | } 865 | }, 866 | "glob-parent": { 867 | "version": "5.1.1", 868 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 869 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 870 | "dev": true, 871 | "requires": { 872 | "is-glob": "^4.0.1" 873 | } 874 | }, 875 | "globals": { 876 | "version": "12.4.0", 877 | "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", 878 | "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", 879 | "dev": true, 880 | "requires": { 881 | "type-fest": "^0.8.1" 882 | } 883 | }, 884 | "graceful-fs": { 885 | "version": "4.2.4", 886 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 887 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" 888 | }, 889 | "growl": { 890 | "version": "1.10.5", 891 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 892 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 893 | "dev": true 894 | }, 895 | "has": { 896 | "version": "1.0.3", 897 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 898 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 899 | "dev": true, 900 | "requires": { 901 | "function-bind": "^1.1.1" 902 | } 903 | }, 904 | "has-flag": { 905 | "version": "3.0.0", 906 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 907 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 908 | "dev": true 909 | }, 910 | "has-symbols": { 911 | "version": "1.0.1", 912 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", 913 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", 914 | "dev": true 915 | }, 916 | "he": { 917 | "version": "1.2.0", 918 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 919 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 920 | "dev": true 921 | }, 922 | "http-proxy-agent": { 923 | "version": "2.1.0", 924 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", 925 | "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", 926 | "dev": true, 927 | "requires": { 928 | "agent-base": "4", 929 | "debug": "3.1.0" 930 | }, 931 | "dependencies": { 932 | "debug": { 933 | "version": "3.1.0", 934 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 935 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 936 | "dev": true, 937 | "requires": { 938 | "ms": "2.0.0" 939 | } 940 | }, 941 | "ms": { 942 | "version": "2.0.0", 943 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 944 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 945 | "dev": true 946 | } 947 | } 948 | }, 949 | "https-proxy-agent": { 950 | "version": "2.2.4", 951 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", 952 | "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", 953 | "dev": true, 954 | "requires": { 955 | "agent-base": "^4.3.0", 956 | "debug": "^3.1.0" 957 | }, 958 | "dependencies": { 959 | "debug": { 960 | "version": "3.2.6", 961 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 962 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 963 | "dev": true, 964 | "requires": { 965 | "ms": "^2.1.1" 966 | } 967 | } 968 | } 969 | }, 970 | "ignore": { 971 | "version": "4.0.6", 972 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 973 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 974 | "dev": true 975 | }, 976 | "import-fresh": { 977 | "version": "3.2.1", 978 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", 979 | "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", 980 | "dev": true, 981 | "requires": { 982 | "parent-module": "^1.0.0", 983 | "resolve-from": "^4.0.0" 984 | } 985 | }, 986 | "imurmurhash": { 987 | "version": "0.1.4", 988 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 989 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 990 | "dev": true 991 | }, 992 | "inflight": { 993 | "version": "1.0.6", 994 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 995 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 996 | "dev": true, 997 | "requires": { 998 | "once": "^1.3.0", 999 | "wrappy": "1" 1000 | } 1001 | }, 1002 | "inherits": { 1003 | "version": "2.0.4", 1004 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1005 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1006 | "dev": true 1007 | }, 1008 | "is-arguments": { 1009 | "version": "1.0.4", 1010 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", 1011 | "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", 1012 | "dev": true 1013 | }, 1014 | "is-binary-path": { 1015 | "version": "2.1.0", 1016 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1017 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1018 | "dev": true, 1019 | "requires": { 1020 | "binary-extensions": "^2.0.0" 1021 | } 1022 | }, 1023 | "is-buffer": { 1024 | "version": "2.0.4", 1025 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", 1026 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", 1027 | "dev": true 1028 | }, 1029 | "is-callable": { 1030 | "version": "1.2.0", 1031 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", 1032 | "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", 1033 | "dev": true 1034 | }, 1035 | "is-date-object": { 1036 | "version": "1.0.2", 1037 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", 1038 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", 1039 | "dev": true 1040 | }, 1041 | "is-extglob": { 1042 | "version": "2.1.1", 1043 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1044 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1045 | "dev": true 1046 | }, 1047 | "is-fullwidth-code-point": { 1048 | "version": "2.0.0", 1049 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1050 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1051 | "dev": true 1052 | }, 1053 | "is-glob": { 1054 | "version": "4.0.1", 1055 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 1056 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 1057 | "dev": true, 1058 | "requires": { 1059 | "is-extglob": "^2.1.1" 1060 | } 1061 | }, 1062 | "is-map": { 1063 | "version": "2.0.1", 1064 | "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", 1065 | "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", 1066 | "dev": true 1067 | }, 1068 | "is-number": { 1069 | "version": "7.0.0", 1070 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1071 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1072 | "dev": true 1073 | }, 1074 | "is-plain-obj": { 1075 | "version": "1.1.0", 1076 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", 1077 | "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", 1078 | "dev": true 1079 | }, 1080 | "is-regex": { 1081 | "version": "1.1.1", 1082 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", 1083 | "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", 1084 | "dev": true, 1085 | "requires": { 1086 | "has-symbols": "^1.0.1" 1087 | } 1088 | }, 1089 | "is-set": { 1090 | "version": "2.0.1", 1091 | "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", 1092 | "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", 1093 | "dev": true 1094 | }, 1095 | "is-string": { 1096 | "version": "1.0.5", 1097 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", 1098 | "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", 1099 | "dev": true 1100 | }, 1101 | "is-symbol": { 1102 | "version": "1.0.3", 1103 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", 1104 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", 1105 | "dev": true, 1106 | "requires": { 1107 | "has-symbols": "^1.0.1" 1108 | } 1109 | }, 1110 | "isarray": { 1111 | "version": "2.0.5", 1112 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 1113 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 1114 | "dev": true 1115 | }, 1116 | "isexe": { 1117 | "version": "2.0.0", 1118 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1119 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1120 | "dev": true 1121 | }, 1122 | "iterate-iterator": { 1123 | "version": "1.0.1", 1124 | "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", 1125 | "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", 1126 | "dev": true 1127 | }, 1128 | "iterate-value": { 1129 | "version": "1.0.2", 1130 | "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", 1131 | "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", 1132 | "dev": true, 1133 | "requires": { 1134 | "es-get-iterator": "^1.0.2", 1135 | "iterate-iterator": "^1.0.1" 1136 | } 1137 | }, 1138 | "js-tokens": { 1139 | "version": "4.0.0", 1140 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1141 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1142 | "dev": true 1143 | }, 1144 | "js-yaml": { 1145 | "version": "3.14.0", 1146 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 1147 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 1148 | "dev": true, 1149 | "requires": { 1150 | "argparse": "^1.0.7", 1151 | "esprima": "^4.0.0" 1152 | } 1153 | }, 1154 | "json-schema-traverse": { 1155 | "version": "0.4.1", 1156 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1157 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1158 | "dev": true 1159 | }, 1160 | "json-stable-stringify-without-jsonify": { 1161 | "version": "1.0.1", 1162 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1163 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 1164 | "dev": true 1165 | }, 1166 | "jsonfile": { 1167 | "version": "6.0.1", 1168 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", 1169 | "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", 1170 | "requires": { 1171 | "graceful-fs": "^4.1.6", 1172 | "universalify": "^1.0.0" 1173 | } 1174 | }, 1175 | "levn": { 1176 | "version": "0.4.1", 1177 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1178 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1179 | "dev": true, 1180 | "requires": { 1181 | "prelude-ls": "^1.2.1", 1182 | "type-check": "~0.4.0" 1183 | } 1184 | }, 1185 | "locate-path": { 1186 | "version": "5.0.0", 1187 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 1188 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 1189 | "dev": true, 1190 | "requires": { 1191 | "p-locate": "^4.1.0" 1192 | } 1193 | }, 1194 | "lodash": { 1195 | "version": "4.17.20", 1196 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 1197 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", 1198 | "dev": true 1199 | }, 1200 | "log-symbols": { 1201 | "version": "3.0.0", 1202 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", 1203 | "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", 1204 | "dev": true, 1205 | "requires": { 1206 | "chalk": "^2.4.2" 1207 | }, 1208 | "dependencies": { 1209 | "chalk": { 1210 | "version": "2.4.2", 1211 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 1212 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 1213 | "dev": true, 1214 | "requires": { 1215 | "ansi-styles": "^3.2.1", 1216 | "escape-string-regexp": "^1.0.5", 1217 | "supports-color": "^5.3.0" 1218 | } 1219 | } 1220 | } 1221 | }, 1222 | "minimatch": { 1223 | "version": "3.0.4", 1224 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1225 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1226 | "dev": true, 1227 | "requires": { 1228 | "brace-expansion": "^1.1.7" 1229 | } 1230 | }, 1231 | "minimist": { 1232 | "version": "1.2.5", 1233 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1234 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 1235 | "dev": true 1236 | }, 1237 | "mkdirp": { 1238 | "version": "0.5.5", 1239 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 1240 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 1241 | "dev": true, 1242 | "requires": { 1243 | "minimist": "^1.2.5" 1244 | } 1245 | }, 1246 | "mocha": { 1247 | "version": "8.1.1", 1248 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.1.tgz", 1249 | "integrity": "sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ==", 1250 | "dev": true, 1251 | "requires": { 1252 | "ansi-colors": "4.1.1", 1253 | "browser-stdout": "1.3.1", 1254 | "chokidar": "3.3.1", 1255 | "debug": "3.2.6", 1256 | "diff": "4.0.2", 1257 | "escape-string-regexp": "1.0.5", 1258 | "find-up": "4.1.0", 1259 | "glob": "7.1.6", 1260 | "growl": "1.10.5", 1261 | "he": "1.2.0", 1262 | "js-yaml": "3.13.1", 1263 | "log-symbols": "3.0.0", 1264 | "minimatch": "3.0.4", 1265 | "ms": "2.1.2", 1266 | "object.assign": "4.1.0", 1267 | "promise.allsettled": "1.0.2", 1268 | "serialize-javascript": "4.0.0", 1269 | "strip-json-comments": "3.0.1", 1270 | "supports-color": "7.1.0", 1271 | "which": "2.0.2", 1272 | "wide-align": "1.1.3", 1273 | "workerpool": "6.0.0", 1274 | "yargs": "13.3.2", 1275 | "yargs-parser": "13.1.2", 1276 | "yargs-unparser": "1.6.1" 1277 | }, 1278 | "dependencies": { 1279 | "debug": { 1280 | "version": "3.2.6", 1281 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 1282 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 1283 | "dev": true, 1284 | "requires": { 1285 | "ms": "^2.1.1" 1286 | } 1287 | }, 1288 | "has-flag": { 1289 | "version": "4.0.0", 1290 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1291 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1292 | "dev": true 1293 | }, 1294 | "js-yaml": { 1295 | "version": "3.13.1", 1296 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 1297 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 1298 | "dev": true, 1299 | "requires": { 1300 | "argparse": "^1.0.7", 1301 | "esprima": "^4.0.0" 1302 | } 1303 | }, 1304 | "strip-json-comments": { 1305 | "version": "3.0.1", 1306 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", 1307 | "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", 1308 | "dev": true 1309 | }, 1310 | "supports-color": { 1311 | "version": "7.1.0", 1312 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 1313 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 1314 | "dev": true, 1315 | "requires": { 1316 | "has-flag": "^4.0.0" 1317 | } 1318 | } 1319 | } 1320 | }, 1321 | "ms": { 1322 | "version": "2.1.2", 1323 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1324 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1325 | "dev": true 1326 | }, 1327 | "natural-compare": { 1328 | "version": "1.4.0", 1329 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1330 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 1331 | "dev": true 1332 | }, 1333 | "normalize-path": { 1334 | "version": "3.0.0", 1335 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1336 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1337 | "dev": true 1338 | }, 1339 | "object-inspect": { 1340 | "version": "1.8.0", 1341 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", 1342 | "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", 1343 | "dev": true 1344 | }, 1345 | "object-keys": { 1346 | "version": "1.1.1", 1347 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 1348 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 1349 | "dev": true 1350 | }, 1351 | "object.assign": { 1352 | "version": "4.1.0", 1353 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 1354 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 1355 | "dev": true, 1356 | "requires": { 1357 | "define-properties": "^1.1.2", 1358 | "function-bind": "^1.1.1", 1359 | "has-symbols": "^1.0.0", 1360 | "object-keys": "^1.0.11" 1361 | } 1362 | }, 1363 | "once": { 1364 | "version": "1.4.0", 1365 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1366 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1367 | "dev": true, 1368 | "requires": { 1369 | "wrappy": "1" 1370 | } 1371 | }, 1372 | "optionator": { 1373 | "version": "0.9.1", 1374 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 1375 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 1376 | "dev": true, 1377 | "requires": { 1378 | "deep-is": "^0.1.3", 1379 | "fast-levenshtein": "^2.0.6", 1380 | "levn": "^0.4.1", 1381 | "prelude-ls": "^1.2.1", 1382 | "type-check": "^0.4.0", 1383 | "word-wrap": "^1.2.3" 1384 | } 1385 | }, 1386 | "p-limit": { 1387 | "version": "2.3.0", 1388 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 1389 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 1390 | "dev": true, 1391 | "requires": { 1392 | "p-try": "^2.0.0" 1393 | } 1394 | }, 1395 | "p-locate": { 1396 | "version": "4.1.0", 1397 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 1398 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 1399 | "dev": true, 1400 | "requires": { 1401 | "p-limit": "^2.2.0" 1402 | } 1403 | }, 1404 | "p-try": { 1405 | "version": "2.2.0", 1406 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1407 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 1408 | "dev": true 1409 | }, 1410 | "parent-module": { 1411 | "version": "1.0.1", 1412 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1413 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1414 | "dev": true, 1415 | "requires": { 1416 | "callsites": "^3.0.0" 1417 | } 1418 | }, 1419 | "path-exists": { 1420 | "version": "4.0.0", 1421 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1422 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1423 | "dev": true 1424 | }, 1425 | "path-is-absolute": { 1426 | "version": "1.0.1", 1427 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1428 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1429 | "dev": true 1430 | }, 1431 | "path-key": { 1432 | "version": "3.1.1", 1433 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1434 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1435 | "dev": true 1436 | }, 1437 | "picomatch": { 1438 | "version": "2.2.2", 1439 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 1440 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 1441 | "dev": true 1442 | }, 1443 | "prelude-ls": { 1444 | "version": "1.2.1", 1445 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1446 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1447 | "dev": true 1448 | }, 1449 | "progress": { 1450 | "version": "2.0.3", 1451 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 1452 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 1453 | "dev": true 1454 | }, 1455 | "promise.allsettled": { 1456 | "version": "1.0.2", 1457 | "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", 1458 | "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", 1459 | "dev": true, 1460 | "requires": { 1461 | "array.prototype.map": "^1.0.1", 1462 | "define-properties": "^1.1.3", 1463 | "es-abstract": "^1.17.0-next.1", 1464 | "function-bind": "^1.1.1", 1465 | "iterate-value": "^1.0.0" 1466 | } 1467 | }, 1468 | "punycode": { 1469 | "version": "2.1.1", 1470 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1471 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1472 | "dev": true 1473 | }, 1474 | "randombytes": { 1475 | "version": "2.1.0", 1476 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1477 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1478 | "dev": true, 1479 | "requires": { 1480 | "safe-buffer": "^5.1.0" 1481 | } 1482 | }, 1483 | "readdirp": { 1484 | "version": "3.3.0", 1485 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", 1486 | "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", 1487 | "dev": true, 1488 | "requires": { 1489 | "picomatch": "^2.0.7" 1490 | } 1491 | }, 1492 | "regexpp": { 1493 | "version": "3.1.0", 1494 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", 1495 | "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", 1496 | "dev": true 1497 | }, 1498 | "require-directory": { 1499 | "version": "2.1.1", 1500 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1501 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1502 | "dev": true 1503 | }, 1504 | "require-main-filename": { 1505 | "version": "2.0.0", 1506 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1507 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 1508 | "dev": true 1509 | }, 1510 | "resolve-from": { 1511 | "version": "4.0.0", 1512 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1513 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1514 | "dev": true 1515 | }, 1516 | "rimraf": { 1517 | "version": "2.6.3", 1518 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 1519 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 1520 | "dev": true, 1521 | "requires": { 1522 | "glob": "^7.1.3" 1523 | } 1524 | }, 1525 | "safe-buffer": { 1526 | "version": "5.2.1", 1527 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1528 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1529 | "dev": true 1530 | }, 1531 | "semver": { 1532 | "version": "7.3.2", 1533 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", 1534 | "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", 1535 | "dev": true 1536 | }, 1537 | "serialize-javascript": { 1538 | "version": "4.0.0", 1539 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", 1540 | "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", 1541 | "dev": true, 1542 | "requires": { 1543 | "randombytes": "^2.1.0" 1544 | } 1545 | }, 1546 | "set-blocking": { 1547 | "version": "2.0.0", 1548 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1549 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 1550 | "dev": true 1551 | }, 1552 | "shebang-command": { 1553 | "version": "2.0.0", 1554 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1555 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1556 | "dev": true, 1557 | "requires": { 1558 | "shebang-regex": "^3.0.0" 1559 | } 1560 | }, 1561 | "shebang-regex": { 1562 | "version": "3.0.0", 1563 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1564 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1565 | "dev": true 1566 | }, 1567 | "slice-ansi": { 1568 | "version": "2.1.0", 1569 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", 1570 | "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", 1571 | "dev": true, 1572 | "requires": { 1573 | "ansi-styles": "^3.2.0", 1574 | "astral-regex": "^1.0.0", 1575 | "is-fullwidth-code-point": "^2.0.0" 1576 | } 1577 | }, 1578 | "sprintf-js": { 1579 | "version": "1.0.3", 1580 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1581 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1582 | "dev": true 1583 | }, 1584 | "string-width": { 1585 | "version": "3.1.0", 1586 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1587 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1588 | "dev": true, 1589 | "requires": { 1590 | "emoji-regex": "^7.0.1", 1591 | "is-fullwidth-code-point": "^2.0.0", 1592 | "strip-ansi": "^5.1.0" 1593 | }, 1594 | "dependencies": { 1595 | "ansi-regex": { 1596 | "version": "4.1.0", 1597 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1598 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1599 | "dev": true 1600 | }, 1601 | "strip-ansi": { 1602 | "version": "5.2.0", 1603 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1604 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1605 | "dev": true, 1606 | "requires": { 1607 | "ansi-regex": "^4.1.0" 1608 | } 1609 | } 1610 | } 1611 | }, 1612 | "string.prototype.trimend": { 1613 | "version": "1.0.1", 1614 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", 1615 | "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", 1616 | "dev": true, 1617 | "requires": { 1618 | "define-properties": "^1.1.3", 1619 | "es-abstract": "^1.17.5" 1620 | } 1621 | }, 1622 | "string.prototype.trimstart": { 1623 | "version": "1.0.1", 1624 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", 1625 | "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", 1626 | "dev": true, 1627 | "requires": { 1628 | "define-properties": "^1.1.3", 1629 | "es-abstract": "^1.17.5" 1630 | } 1631 | }, 1632 | "strip-ansi": { 1633 | "version": "6.0.0", 1634 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1635 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1636 | "dev": true, 1637 | "requires": { 1638 | "ansi-regex": "^5.0.0" 1639 | } 1640 | }, 1641 | "strip-json-comments": { 1642 | "version": "3.1.1", 1643 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1644 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1645 | "dev": true 1646 | }, 1647 | "supports-color": { 1648 | "version": "5.5.0", 1649 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1650 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1651 | "dev": true, 1652 | "requires": { 1653 | "has-flag": "^3.0.0" 1654 | } 1655 | }, 1656 | "table": { 1657 | "version": "5.4.6", 1658 | "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", 1659 | "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", 1660 | "dev": true, 1661 | "requires": { 1662 | "ajv": "^6.10.2", 1663 | "lodash": "^4.17.14", 1664 | "slice-ansi": "^2.1.0", 1665 | "string-width": "^3.0.0" 1666 | } 1667 | }, 1668 | "text-table": { 1669 | "version": "0.2.0", 1670 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1671 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1672 | "dev": true 1673 | }, 1674 | "to-regex-range": { 1675 | "version": "5.0.1", 1676 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1677 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1678 | "dev": true, 1679 | "requires": { 1680 | "is-number": "^7.0.0" 1681 | } 1682 | }, 1683 | "tslib": { 1684 | "version": "1.13.0", 1685 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 1686 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", 1687 | "dev": true 1688 | }, 1689 | "tsutils": { 1690 | "version": "3.17.1", 1691 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", 1692 | "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", 1693 | "dev": true, 1694 | "requires": { 1695 | "tslib": "^1.8.1" 1696 | } 1697 | }, 1698 | "type-check": { 1699 | "version": "0.4.0", 1700 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1701 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1702 | "dev": true, 1703 | "requires": { 1704 | "prelude-ls": "^1.2.1" 1705 | } 1706 | }, 1707 | "type-fest": { 1708 | "version": "0.8.1", 1709 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 1710 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 1711 | "dev": true 1712 | }, 1713 | "typescript": { 1714 | "version": "3.9.7", 1715 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", 1716 | "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", 1717 | "dev": true 1718 | }, 1719 | "universalify": { 1720 | "version": "1.0.0", 1721 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", 1722 | "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" 1723 | }, 1724 | "uri-js": { 1725 | "version": "4.2.2", 1726 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1727 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1728 | "dev": true, 1729 | "requires": { 1730 | "punycode": "^2.1.0" 1731 | } 1732 | }, 1733 | "v8-compile-cache": { 1734 | "version": "2.1.1", 1735 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", 1736 | "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", 1737 | "dev": true 1738 | }, 1739 | "vscode-test": { 1740 | "version": "1.4.0", 1741 | "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.4.0.tgz", 1742 | "integrity": "sha512-Jt7HNGvSE0+++Tvtq5wc4hiXLIr2OjDShz/gbAfM/mahQpy4rKBnmOK33D+MR67ATWviQhl+vpmU3p/qwSH/Pg==", 1743 | "dev": true, 1744 | "requires": { 1745 | "http-proxy-agent": "^2.1.0", 1746 | "https-proxy-agent": "^2.2.4", 1747 | "rimraf": "^2.6.3" 1748 | } 1749 | }, 1750 | "which": { 1751 | "version": "2.0.2", 1752 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1753 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1754 | "dev": true, 1755 | "requires": { 1756 | "isexe": "^2.0.0" 1757 | } 1758 | }, 1759 | "which-module": { 1760 | "version": "2.0.0", 1761 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1762 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 1763 | "dev": true 1764 | }, 1765 | "wide-align": { 1766 | "version": "1.1.3", 1767 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1768 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1769 | "dev": true, 1770 | "requires": { 1771 | "string-width": "^1.0.2 || 2" 1772 | }, 1773 | "dependencies": { 1774 | "ansi-regex": { 1775 | "version": "3.0.0", 1776 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 1777 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 1778 | "dev": true 1779 | }, 1780 | "string-width": { 1781 | "version": "2.1.1", 1782 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1783 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1784 | "dev": true, 1785 | "requires": { 1786 | "is-fullwidth-code-point": "^2.0.0", 1787 | "strip-ansi": "^4.0.0" 1788 | } 1789 | }, 1790 | "strip-ansi": { 1791 | "version": "4.0.0", 1792 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1793 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1794 | "dev": true, 1795 | "requires": { 1796 | "ansi-regex": "^3.0.0" 1797 | } 1798 | } 1799 | } 1800 | }, 1801 | "word-wrap": { 1802 | "version": "1.2.3", 1803 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1804 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 1805 | "dev": true 1806 | }, 1807 | "workerpool": { 1808 | "version": "6.0.0", 1809 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", 1810 | "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", 1811 | "dev": true 1812 | }, 1813 | "wrap-ansi": { 1814 | "version": "5.1.0", 1815 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 1816 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 1817 | "dev": true, 1818 | "requires": { 1819 | "ansi-styles": "^3.2.0", 1820 | "string-width": "^3.0.0", 1821 | "strip-ansi": "^5.0.0" 1822 | }, 1823 | "dependencies": { 1824 | "ansi-regex": { 1825 | "version": "4.1.0", 1826 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1827 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1828 | "dev": true 1829 | }, 1830 | "strip-ansi": { 1831 | "version": "5.2.0", 1832 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1833 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1834 | "dev": true, 1835 | "requires": { 1836 | "ansi-regex": "^4.1.0" 1837 | } 1838 | } 1839 | } 1840 | }, 1841 | "wrappy": { 1842 | "version": "1.0.2", 1843 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1844 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1845 | "dev": true 1846 | }, 1847 | "write": { 1848 | "version": "1.0.3", 1849 | "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", 1850 | "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", 1851 | "dev": true, 1852 | "requires": { 1853 | "mkdirp": "^0.5.1" 1854 | } 1855 | }, 1856 | "y18n": { 1857 | "version": "4.0.0", 1858 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 1859 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 1860 | "dev": true 1861 | }, 1862 | "yargs": { 1863 | "version": "13.3.2", 1864 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 1865 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 1866 | "dev": true, 1867 | "requires": { 1868 | "cliui": "^5.0.0", 1869 | "find-up": "^3.0.0", 1870 | "get-caller-file": "^2.0.1", 1871 | "require-directory": "^2.1.1", 1872 | "require-main-filename": "^2.0.0", 1873 | "set-blocking": "^2.0.0", 1874 | "string-width": "^3.0.0", 1875 | "which-module": "^2.0.0", 1876 | "y18n": "^4.0.0", 1877 | "yargs-parser": "^13.1.2" 1878 | }, 1879 | "dependencies": { 1880 | "find-up": { 1881 | "version": "3.0.0", 1882 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 1883 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 1884 | "dev": true, 1885 | "requires": { 1886 | "locate-path": "^3.0.0" 1887 | } 1888 | }, 1889 | "locate-path": { 1890 | "version": "3.0.0", 1891 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 1892 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 1893 | "dev": true, 1894 | "requires": { 1895 | "p-locate": "^3.0.0", 1896 | "path-exists": "^3.0.0" 1897 | } 1898 | }, 1899 | "p-locate": { 1900 | "version": "3.0.0", 1901 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 1902 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 1903 | "dev": true, 1904 | "requires": { 1905 | "p-limit": "^2.0.0" 1906 | } 1907 | }, 1908 | "path-exists": { 1909 | "version": "3.0.0", 1910 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 1911 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 1912 | "dev": true 1913 | } 1914 | } 1915 | }, 1916 | "yargs-parser": { 1917 | "version": "13.1.2", 1918 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 1919 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 1920 | "dev": true, 1921 | "requires": { 1922 | "camelcase": "^5.0.0", 1923 | "decamelize": "^1.2.0" 1924 | } 1925 | }, 1926 | "yargs-unparser": { 1927 | "version": "1.6.1", 1928 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", 1929 | "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", 1930 | "dev": true, 1931 | "requires": { 1932 | "camelcase": "^5.3.1", 1933 | "decamelize": "^1.2.0", 1934 | "flat": "^4.1.0", 1935 | "is-plain-obj": "^1.1.0", 1936 | "yargs": "^14.2.3" 1937 | }, 1938 | "dependencies": { 1939 | "find-up": { 1940 | "version": "3.0.0", 1941 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 1942 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 1943 | "dev": true, 1944 | "requires": { 1945 | "locate-path": "^3.0.0" 1946 | } 1947 | }, 1948 | "locate-path": { 1949 | "version": "3.0.0", 1950 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 1951 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 1952 | "dev": true, 1953 | "requires": { 1954 | "p-locate": "^3.0.0", 1955 | "path-exists": "^3.0.0" 1956 | } 1957 | }, 1958 | "p-locate": { 1959 | "version": "3.0.0", 1960 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 1961 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 1962 | "dev": true, 1963 | "requires": { 1964 | "p-limit": "^2.0.0" 1965 | } 1966 | }, 1967 | "path-exists": { 1968 | "version": "3.0.0", 1969 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 1970 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 1971 | "dev": true 1972 | }, 1973 | "yargs": { 1974 | "version": "14.2.3", 1975 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", 1976 | "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", 1977 | "dev": true, 1978 | "requires": { 1979 | "cliui": "^5.0.0", 1980 | "decamelize": "^1.2.0", 1981 | "find-up": "^3.0.0", 1982 | "get-caller-file": "^2.0.1", 1983 | "require-directory": "^2.1.1", 1984 | "require-main-filename": "^2.0.0", 1985 | "set-blocking": "^2.0.0", 1986 | "string-width": "^3.0.0", 1987 | "which-module": "^2.0.0", 1988 | "y18n": "^4.0.0", 1989 | "yargs-parser": "^15.0.1" 1990 | } 1991 | }, 1992 | "yargs-parser": { 1993 | "version": "15.0.1", 1994 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", 1995 | "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", 1996 | "dev": true, 1997 | "requires": { 1998 | "camelcase": "^5.0.0", 1999 | "decamelize": "^1.2.0" 2000 | } 2001 | } 2002 | } 2003 | } 2004 | } 2005 | } 2006 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gmod-sdk", 3 | "displayName": "GMod SDK", 4 | "description": "Tools for Garry's Mod addon development", 5 | "version": "0.2.1", 6 | "publisher": "BadgerCode", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/BadgerCode/GMod-SDK-VSCode" 10 | }, 11 | "galleryBanner": { 12 | "color": "#3a3d41", 13 | "theme": "dark" 14 | }, 15 | "homepage": "https://github.com/BadgerCode/GMod-SDK-VSCode/blob/master/README.md", 16 | "qna": "https://github.com/BadgerCode/GMod-SDK-VSCode/issues", 17 | "license": "SEE LICENSE IN LICENSE", 18 | "icon": "resources/package-icon.png", 19 | "engines": { 20 | "vscode": "^1.48.0" 21 | }, 22 | "categories": [ 23 | "Extension Packs", 24 | "Other" 25 | ], 26 | "keywords": [ 27 | "gmod", 28 | "glua" 29 | ], 30 | "main": "./out/extension.js", 31 | "activationEvents": [ 32 | "onCommand:gmodSDK.createAddon", 33 | "onCommand:gmodSDK.createWeapon", 34 | "onCommand:gmodSDK.uploadAddon", 35 | "onCommand:gmodSDK.updateAddonThumbnail", 36 | "onCommand:gmodSDK.copyToLocalGarrysmod", 37 | "onView:gmodAddonInfo", 38 | "onView:gmodAddonWeapons", 39 | "onView:gmodWorkshop" 40 | ], 41 | "contributes": { 42 | "configuration": { 43 | "title": "GMod SDK", 44 | "properties": { 45 | "gmod-sdk.gmadExecutablePath": { 46 | "type": "string", 47 | "markdownDescription": "The path to the **gmad** program, found in your Garry's Mod bin folder.\n\nFor example: `C:\\Program Files (x86)\\Steam\\steamapps\\common\\garrysmod\\bin\\gmad.exe`\n\n[Help](https://gist.github.com/BadgerCode/00600eab40556c6e8809590d263ea053)", 48 | "default": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\garrysmod\\bin\\gmad.exe" 49 | }, 50 | "gmod-sdk.gmpublishExecutablePath": { 51 | "type": "string", 52 | "markdownDescription": "The path to the **gmpublish** program, found in your Garry's Mod bin folder.\n\nFor example: `C:\\Program Files (x86)\\Steam\\steamapps\\common\\garrysmod\\bin\\gmpublish.exe`\n\n[Help](https://gist.github.com/BadgerCode/00600eab40556c6e8809590d263ea053)", 53 | "default": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\garrysmod\\bin\\gmpublish.exe" 54 | }, 55 | "gmod-sdk.garrysmodAddonsPath": { 56 | "type": "string", 57 | "markdownDescription": "The path to the **addons** folder, found in your Garry's Mod folder.\n\nFor example: `C:\\Program Files (x86)\\Steam\\steamapps\\common\\garrysmod\\garrysmod\\addons`\n\n[Help](https://gist.github.com/BadgerCode/00600eab40556c6e8809590d263ea053)", 58 | "default": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\garrysmod\\garrysmod\\addons" 59 | }, 60 | "gmod-sdk.steamID": { 61 | "type": "string", 62 | "markdownDescription": "Your long (steamID64) Steam ID. Used to load your workshop items.\n\n[Help](https://steamid.io/)\n\nE.g. 70561198021123456", 63 | "default": "" 64 | } 65 | } 66 | }, 67 | "commands": [ 68 | { 69 | "command": "gmodSDK.createAddon", 70 | "title": "Create Garry's Mod Addon" 71 | }, 72 | { 73 | "command": "gmodSDK.createWeapon", 74 | "title": "Create Weapon", 75 | "icon": { 76 | "light": "resources/light/create.svg", 77 | "dark": "resources/dark/create.svg" 78 | } 79 | }, 80 | { 81 | "command": "gmodSDK.uploadAddon", 82 | "title": "Upload addon to workshop", 83 | "icon": { 84 | "light": "resources/light/upload.svg", 85 | "dark": "resources/dark/upload.svg" 86 | } 87 | }, 88 | { 89 | "command": "gmodSDK.updateAddonThumbnail", 90 | "title": "Set the thumbnail for a workshop addon", 91 | "icon": { 92 | "light": "resources/light/thumbnail.svg", 93 | "dark": "resources/dark/thumbnail.svg" 94 | } 95 | }, 96 | { 97 | "command": "gmodSDK.copyToLocalGarrysmod", 98 | "title": "Copy the addon to your Garry's Mod folder.", 99 | "icon": { 100 | "light": "resources/light/copy-to-folder.svg", 101 | "dark": "resources/dark/copy-to-folder.svg" 102 | } 103 | }, 104 | { 105 | "command": "gmodAddonInfo.setTitle", 106 | "title": "Edit addon title", 107 | "icon": { 108 | "light": "resources/light/edit.svg", 109 | "dark": "resources/dark/edit.svg" 110 | } 111 | }, 112 | { 113 | "command": "gmodAddonInfo.setDescription", 114 | "title": "Edit addon description", 115 | "icon": { 116 | "light": "resources/light/edit.svg", 117 | "dark": "resources/dark/edit.svg" 118 | } 119 | }, 120 | { 121 | "command": "gmodAddonInfo.setType", 122 | "title": "Set addon type", 123 | "icon": { 124 | "light": "resources/light/edit.svg", 125 | "dark": "resources/dark/edit.svg" 126 | } 127 | }, 128 | { 129 | "command": "gmodAddonInfo.setTags", 130 | "title": "Set addon tags", 131 | "icon": { 132 | "light": "resources/light/edit.svg", 133 | "dark": "resources/dark/edit.svg" 134 | } 135 | }, 136 | { 137 | "command": "gmodAddonInfo.refresh", 138 | "title": "Refresh", 139 | "icon": { 140 | "light": "resources/light/refresh.svg", 141 | "dark": "resources/dark/refresh.svg" 142 | } 143 | }, 144 | { 145 | "command": "gmodWeapon.edit", 146 | "title": "Edit", 147 | "icon": { 148 | "light": "resources/light/edit.svg", 149 | "dark": "resources/dark/edit.svg" 150 | } 151 | }, 152 | { 153 | "command": "gmodWorkshop.selectSteamID", 154 | "title": "Select Steam ID" 155 | } 156 | ], 157 | "menus": { 158 | "commandPalette": [ 159 | { 160 | "command": "gmodAddonInfo.setTitle", 161 | "when": "false" 162 | }, 163 | { 164 | "command": "gmodAddonInfo.setDescription", 165 | "when": "false" 166 | }, 167 | { 168 | "command": "gmodAddonInfo.setType", 169 | "when": "false" 170 | }, 171 | { 172 | "command": "gmodAddonInfo.setTags", 173 | "when": "false" 174 | }, 175 | { 176 | "command": "gmodAddonInfo.refresh", 177 | "when": "false" 178 | }, 179 | { 180 | "command": "gmodWeapon.edit", 181 | "when": "false" 182 | }, 183 | { 184 | "command": "gmodWorkshop.selectSteamID", 185 | "when": "false" 186 | } 187 | ], 188 | "view/title": [ 189 | { 190 | "command": "gmodAddonInfo.refresh", 191 | "when": "view == gmodAddonInfo || view == gmodAddonWeapons || view == gmodWorkshop", 192 | "group": "navigation" 193 | }, 194 | { 195 | "command": "gmodSDK.copyToLocalGarrysmod", 196 | "when": "view == gmodAddonInfo", 197 | "group": "navigation" 198 | }, 199 | { 200 | "command": "gmodSDK.createWeapon", 201 | "when": "view == gmodAddonWeapons", 202 | "group": "navigation" 203 | }, 204 | { 205 | "command": "gmodSDK.uploadAddon", 206 | "when": "view == gmodWorkshop", 207 | "group": "navigation" 208 | }, 209 | { 210 | "command": "gmodSDK.updateAddonThumbnail", 211 | "when": "view == gmodWorkshop", 212 | "group": "navigation" 213 | } 214 | ], 215 | "view/item/context": [ 216 | { 217 | "command": "gmodAddonInfo.setTitle", 218 | "when": "view == gmodAddonInfo && viewItem == title", 219 | "group": "inline" 220 | }, 221 | { 222 | "command": "gmodAddonInfo.setDescription", 223 | "when": "view == gmodAddonInfo && viewItem == description", 224 | "group": "inline" 225 | }, 226 | { 227 | "command": "gmodAddonInfo.setType", 228 | "when": "view == gmodAddonInfo && viewItem == type", 229 | "group": "inline" 230 | }, 231 | { 232 | "command": "gmodAddonInfo.setTags", 233 | "when": "view == gmodAddonInfo && viewItem == tags", 234 | "group": "inline" 235 | }, 236 | { 237 | "command": "gmodWeapon.edit", 238 | "when": "view == gmodAddonWeapons && viewItem =~ /^weapons\\..*/", 239 | "group": "inline" 240 | }, 241 | { 242 | "command": "gmodSDK.uploadAddon", 243 | "when": "view == gmodWorkshop && viewItem =~ /^item-.*/", 244 | "group": "inline" 245 | }, 246 | { 247 | "command": "gmodSDK.updateAddonThumbnail", 248 | "when": "view == gmodWorkshop && viewItem =~ /^item-.*/", 249 | "group": "inline" 250 | } 251 | ] 252 | }, 253 | "views": { 254 | "gmod-sdk": [ 255 | { 256 | "id": "gmodAddonInfo", 257 | "name": "Addon Overview", 258 | "icon": "./resources/menu-icon.svg", 259 | "contextualTitle": "Addon Information" 260 | }, 261 | { 262 | "id": "gmodAddonWeapons", 263 | "name": "Weapons", 264 | "icon": "./resources/menu-icon.svg", 265 | "contextualTitle": "Addon Weapons" 266 | }, 267 | { 268 | "id": "gmodWorkshop", 269 | "name": "Workshop", 270 | "icon": "./resources/menu-icon.svg", 271 | "contextualTitle": "Garry's Mod Workshop" 272 | } 273 | ] 274 | }, 275 | "viewsWelcome": [ 276 | { 277 | "view": "gmodAddonInfo", 278 | "contents": "You are not in a Garry's Mod addon folder.\n(Could not find addon.json)\n[Create addon](command:gmodSDK.createAddon)" 279 | }, 280 | { 281 | "view": "gmodWorkshop", 282 | "contents": "Please select your Steam account to view your workshop items.\n\nHow to get your Steam ID - [https://steamid.io/](https://steamid.io/)\n\n[Select Account](command:gmodWorkshop.selectSteamID)" 283 | } 284 | ], 285 | "viewsContainers": { 286 | "activitybar": [ 287 | { 288 | "id": "gmod-sdk", 289 | "title": "Garry's Mod", 290 | "icon": "./resources/menu-icon.svg" 291 | } 292 | ] 293 | } 294 | }, 295 | "scripts": { 296 | "vscode:prepublish": "npm run compile", 297 | "compile": "tsc -p ./", 298 | "lint": "eslint src --ext ts", 299 | "watch": "tsc -watch -p ./", 300 | "pretest": "npm run compile && npm run lint", 301 | "test": "node ./out/test/runTest.js" 302 | }, 303 | "devDependencies": { 304 | "@types/fs-extra": "^9.0.1", 305 | "@types/glob": "^7.1.3", 306 | "@types/mocha": "^8.0.0", 307 | "@types/node": "^14.0.27", 308 | "@types/vscode": "^1.48.0", 309 | "@typescript-eslint/eslint-plugin": "^3.8.0", 310 | "@typescript-eslint/parser": "^3.8.0", 311 | "eslint": "^7.6.0", 312 | "mocha": "^8.0.1", 313 | "typescript": "^3.8.3", 314 | "vscode-test": "^1.4.0" 315 | }, 316 | "dependencies": { 317 | "axios": "^0.19.2", 318 | "fs-extra": "^9.0.1", 319 | "glob": "^7.1.6" 320 | } 321 | } -------------------------------------------------------------------------------- /resources/dark/copy-to-folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/create.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/string.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/thumbnail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/upload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/workshop-item-hidden.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/copy-to-folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/create.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/string.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/thumbnail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/upload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/workshop-item-hidden.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/menu-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/package-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BadgerCode/GMod-SDK-VSCode/69a7d2f4d481324b9632485137ac7c7c2a7d011d/resources/package-icon.png -------------------------------------------------------------------------------- /resources/readme/configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BadgerCode/GMod-SDK-VSCode/69a7d2f4d481324b9632485137ac7c7c2a7d011d/resources/readme/configuration.png -------------------------------------------------------------------------------- /resources/readme/create-addon-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BadgerCode/GMod-SDK-VSCode/69a7d2f4d481324b9632485137ac7c7c2a7d011d/resources/readme/create-addon-1.png -------------------------------------------------------------------------------- /resources/readme/create-addon-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BadgerCode/GMod-SDK-VSCode/69a7d2f4d481324b9632485137ac7c7c2a7d011d/resources/readme/create-addon-2.png -------------------------------------------------------------------------------- /resources/readme/create-addon-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BadgerCode/GMod-SDK-VSCode/69a7d2f4d481324b9632485137ac7c7c2a7d011d/resources/readme/create-addon-3.png -------------------------------------------------------------------------------- /resources/readme/features-copy-to-gmod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BadgerCode/GMod-SDK-VSCode/69a7d2f4d481324b9632485137ac7c7c2a7d011d/resources/readme/features-copy-to-gmod.png -------------------------------------------------------------------------------- /resources/samples/weapons/weapon_sandbox_main_sample.lua: -------------------------------------------------------------------------------- 1 | AddCSLuaFile() 2 | 3 | SWEP.Base = "weapon_base" 4 | SWEP.PrintName = "AK47-Sample" 5 | SWEP.Category = "Other" 6 | SWEP.Author = "Your name here" 7 | SWEP.Instructions = [[Enter a description to let players know 8 | how to use your weapon]] 9 | 10 | SWEP.HoldType = "ar2" 11 | SWEP.Slot = 1 12 | SWEP.SlotPos = 0 13 | SWEP.Weight = 5 14 | SWEP.AutoSwitchTo = true 15 | SWEP.AutoSwitchFrom = false 16 | 17 | SWEP.Spawnable = true 18 | SWEP.AdminSpawnable = true 19 | 20 | SWEP.ViewModelFlip = true 21 | SWEP.UseHands = false 22 | SWEP.DrawCrosshair = true 23 | 24 | SWEP.Primary.Delay = 0.08 25 | SWEP.Primary.Recoil = 1.9 26 | SWEP.Primary.Automatic = true 27 | SWEP.Primary.Damage = 20 28 | SWEP.Primary.Cone = 0.025 29 | SWEP.Primary.Ammo = "smg1" 30 | SWEP.Primary.ClipSize = 45 31 | SWEP.Primary.ClipMax = 90 32 | SWEP.Primary.DefaultClip = 45 33 | SWEP.Primary.Sound = Sound("Weapon_AK47.Single") 34 | 35 | SWEP.Secondary.Delay = 1 36 | 37 | SWEP.AmmoEnt = "item_ammo_smg1_ttt" 38 | 39 | SWEP.ViewModelFOV = 72 40 | SWEP.ViewModel = "models/weapons/v_rif_ak47.mdl" 41 | SWEP.WorldModel = "models/weapons/w_rif_ak47.mdl" 42 | 43 | 44 | function SWEP:PrimaryAttack() 45 | -- Checks if we have enough ammo to shoot 46 | if (self:CanPrimaryAttack() == false) then return end 47 | 48 | -- weapon_base: SWEP:ShootBullet(damage, numberOfBullets, aimcone, ammoType, force, tracer) 49 | -- Shoots a bullet, handles player/weapon animations & applies recoil 50 | self:ShootBullet( 51 | self.Primary.Damage, 52 | 1, 53 | self.Primary.Cone, 54 | self.Primary.Ammo 55 | ) 56 | 57 | self:TakePrimaryAmmo(1) 58 | 59 | self:EmitSound(self.Primary.Sound) 60 | 61 | self:SetNextPrimaryFire(CurTime() + self.Primary.Delay) 62 | end 63 | 64 | 65 | function SWEP:SecondaryAttack() 66 | if(!self:CanSecondaryAttack()) then return end 67 | 68 | if SERVER then 69 | self:GetOwner():ChatPrint("Right click!") 70 | end 71 | 72 | -- Delay when the gun can next be fired (secondary attack) 73 | self:SetNextSecondaryFire(CurTime() + self.Secondary.Delay) 74 | end 75 | -------------------------------------------------------------------------------- /resources/samples/weapons/weapon_sandbox_toolgun_sample.lua: -------------------------------------------------------------------------------- 1 | 2 | -- IMPORTANT 3 | -- ========= 4 | -- Set this to your file name without the ".lua" on the end 5 | -- Sandbox tools use the filename for storing information and translations 6 | local FileName = "$WEAPON_NAME$" 7 | 8 | 9 | TOOL.Category = "My Category" 10 | TOOL.Name = "#tool." .. FileName .. ".name" 11 | 12 | if CLIENT then 13 | language.Add("tool." .. FileName .. ".name", "My example tool") 14 | language.Add("tool." .. FileName .. ".desc", "This is an example tool") 15 | language.Add("tool." .. FileName .. ".0", "Click to get started") 16 | end 17 | 18 | 19 | TOOL.ClientConVar["secretmessage"] = "0" 20 | 21 | 22 | 23 | function TOOL:LeftClick(trace) 24 | if SERVER then 25 | self:GetOwner():ChatPrint("Left click!") 26 | 27 | local showSecretMessage = self:GetClientNumber("secretmessage") == 1 28 | 29 | if showSecretMessage then 30 | self:GetOwner():ChatPrint("Here is the secret message!") 31 | end 32 | end 33 | end 34 | 35 | function TOOL:RightClick(trace) 36 | if SERVER then 37 | self:GetOwner():ChatPrint("Right click!") 38 | end 39 | end 40 | 41 | function TOOL:Reload(trace) 42 | if SERVER then 43 | self:GetOwner():ChatPrint("Reload!") 44 | end 45 | end 46 | 47 | 48 | -- This function/hook is called every frame on client and every tick on the server 49 | function TOOL:Think() 50 | end 51 | 52 | 53 | -- This controls the part of the UI which shows options for this tool 54 | function TOOL.BuildCPanel(panel) 55 | panel:AddControl("Header", { 56 | Text = "#tool." .. FileName .. ".name", 57 | Description = "#tool." .. FileName .. ".desc" 58 | }) 59 | 60 | panel:AddControl("Checkbox", { 61 | Label = "Enable secret message for left click", 62 | Command = FileName .. "_secretmessage" 63 | }) 64 | end 65 | -------------------------------------------------------------------------------- /resources/samples/weapons/weapon_ttt_detective_sample.lua: -------------------------------------------------------------------------------- 1 | AddCSLuaFile() 2 | 3 | SWEP.HoldType = "ar2" 4 | 5 | if CLIENT then 6 | SWEP.PrintName = "AK47" 7 | SWEP.Slot = 6 8 | 9 | SWEP.ViewModelFOV = 72 10 | SWEP.ViewModelFlip = true 11 | 12 | -- Traitor equipment menu settings 13 | SWEP.EquipMenuData = { 14 | type = "item_weapon", 15 | desc = "Example custom weapon." 16 | }; 17 | 18 | SWEP.Icon = "vgui/ttt/icon_nades" 19 | end 20 | 21 | SWEP.Base = "weapon_tttbase" 22 | 23 | SWEP.Primary.Delay = 0.08 24 | SWEP.Primary.Recoil = 1.9 25 | SWEP.Primary.Automatic = true 26 | SWEP.Primary.Damage = 20 27 | SWEP.Primary.Cone = 0.025 28 | SWEP.Primary.Ammo = "smg1" 29 | SWEP.Primary.ClipSize = 45 30 | SWEP.Primary.ClipMax = 90 31 | SWEP.Primary.DefaultClip = 45 32 | SWEP.Primary.Sound = Sound( "Weapon_AK47.Single" ) 33 | 34 | SWEP.AmmoEnt = "item_ammo_smg1_ttt" 35 | 36 | SWEP.UseHands = true 37 | SWEP.ViewModel = "models/weapons/v_rif_ak47.mdl" 38 | SWEP.WorldModel = "models/weapons/w_rif_ak47.mdl" 39 | 40 | SWEP.NoSights = false 41 | SWEP.IronSightsPos = Vector( 6.05, -5, 2.4 ) 42 | SWEP.IronSightsAng = Vector( 2.2, -0.1, 0 ) 43 | 44 | 45 | -- Detective equipment settings 46 | 47 | SWEP.Kind = WEAPON_EQUIP1 48 | SWEP.AutoSpawnable = false 49 | SWEP.CanBuy = { ROLE_DETECTIVE } 50 | SWEP.LimitedStock = false 51 | SWEP.AllowDrop = true 52 | SWEP.IsSilent = false 53 | 54 | 55 | 56 | function SWEP:PrimaryAttack() 57 | -- Checks if we have enough ammo to shoot 58 | if not self:CanPrimaryAttack() then return end 59 | 60 | -- weapon_tttbase: SWEP:ShootBullet(damage, recoil, numberOfBullets, cone) 61 | -- Shoots a bullet, handles player/weapon animations & applies recoil 62 | self:ShootBullet( 63 | self.Primary.Damage, 64 | self.Primary.Recoil, 65 | self.Primary.NumShots, 66 | self:GetPrimaryCone() 67 | ) 68 | 69 | self:TakePrimaryAmmo(self.Primary.NumShots) 70 | 71 | self:EmitSound(self.Primary.Sound) 72 | 73 | self:SetNextPrimaryFire(CurTime() + self.Primary.Delay) 74 | end 75 | 76 | 77 | function SWEP:SecondaryAttack() 78 | if self.NoSights or (not self.IronSightsPos) then return end 79 | 80 | self:SetIronsights(not self:GetIronsights()) 81 | 82 | self:SetNextSecondaryFire(CurTime() + 0.3) 83 | end 84 | -------------------------------------------------------------------------------- /resources/samples/weapons/weapon_ttt_grenade_sample.lua: -------------------------------------------------------------------------------- 1 | AddCSLuaFile() 2 | 3 | SWEP.HoldType = "grenade" 4 | 5 | if CLIENT then 6 | SWEP.PrintName = "Sample Grenade" 7 | SWEP.Slot = 3 8 | 9 | SWEP.ViewModelFlip = false 10 | SWEP.ViewModelFOV = 54 11 | 12 | SWEP.Icon = "vgui/ttt/icon_nades" 13 | end 14 | 15 | SWEP.Base = "weapon_tttbasegrenade" 16 | 17 | SWEP.Kind = WEAPON_NADE 18 | 19 | SWEP.UseHands = true 20 | SWEP.ViewModel = "models/weapons/cstrike/c_eq_smokegrenade.mdl" 21 | SWEP.WorldModel = "models/weapons/w_eq_smokegrenade.mdl" 22 | 23 | SWEP.Weight = 5 24 | SWEP.AutoSpawnable = true 25 | SWEP.Spawnable = true 26 | 27 | 28 | function SWEP:GetGrenadeName() 29 | return "ttt_smokegrenade_proj" 30 | end 31 | -------------------------------------------------------------------------------- /resources/samples/weapons/weapon_ttt_primary_sample.lua: -------------------------------------------------------------------------------- 1 | AddCSLuaFile() 2 | 3 | SWEP.HoldType = "crossbow" 4 | 5 | if CLIENT then 6 | SWEP.PrintName = "S.A.M.P.L.E." 7 | SWEP.Slot = 2 8 | 9 | SWEP.ViewModelFlip = false 10 | SWEP.ViewModelFOV = 54 11 | 12 | SWEP.Icon = "vgui/ttt/icon_m249" 13 | end 14 | 15 | SWEP.Base = "weapon_tttbase" 16 | 17 | SWEP.Spawnable = true 18 | SWEP.AutoSpawnable = true 19 | 20 | SWEP.Kind = WEAPON_HEAVY 21 | 22 | SWEP.Primary.Damage = 7 23 | SWEP.Primary.Delay = 0.06 24 | SWEP.Primary.Cone = 0.09 25 | SWEP.Primary.ClipSize = 150 26 | SWEP.Primary.ClipMax = 150 27 | SWEP.Primary.DefaultClip = 150 28 | SWEP.Primary.Automatic = true 29 | SWEP.Primary.Ammo = "AirboatGun" 30 | SWEP.Primary.Recoil = 1.9 31 | SWEP.Primary.Sound = Sound("Weapon_m249.Single") 32 | 33 | SWEP.UseHands = true 34 | SWEP.ViewModel = "models/weapons/cstrike/c_mach_m249para.mdl" 35 | SWEP.WorldModel = "models/weapons/w_mach_m249para.mdl" 36 | 37 | SWEP.HeadshotMultiplier = 2.2 38 | 39 | SWEP.IronSightsPos = Vector(-5.96, -5.119, 2.349) 40 | SWEP.IronSightsAng = Vector(0, 0, 0) 41 | 42 | 43 | function SWEP:PrimaryAttack() 44 | -- Checks if we have enough ammo to shoot 45 | if not self:CanPrimaryAttack() then return end 46 | 47 | -- weapon_tttbase: SWEP:ShootBullet(damage, recoil, numberOfBullets, cone) 48 | -- Shoots a bullet, handles player/weapon animations & applies recoil 49 | self:ShootBullet( 50 | self.Primary.Damage, 51 | self.Primary.Recoil, 52 | self.Primary.NumShots, 53 | self:GetPrimaryCone() 54 | ) 55 | 56 | self:TakePrimaryAmmo(self.Primary.NumShots) 57 | 58 | self:EmitSound(self.Primary.Sound) 59 | 60 | self:SetNextPrimaryFire(CurTime() + self.Primary.Delay) 61 | end 62 | 63 | 64 | function SWEP:SecondaryAttack() 65 | if self.NoSights or (not self.IronSightsPos) then return end 66 | 67 | self:SetIronsights(not self:GetIronsights()) 68 | 69 | self:SetNextSecondaryFire(CurTime() + 0.3) 70 | end 71 | -------------------------------------------------------------------------------- /resources/samples/weapons/weapon_ttt_secondary_sample.lua: -------------------------------------------------------------------------------- 1 | AddCSLuaFile() 2 | 3 | SWEP.HoldType = "pistol" 4 | 5 | if CLIENT then 6 | SWEP.PrintName = "Sample Pistol" 7 | SWEP.Slot = 1 8 | 9 | SWEP.ViewModelFlip = false 10 | SWEP.ViewModelFOV = 54 11 | 12 | SWEP.Icon = "vgui/ttt/icon_pistol" 13 | end 14 | 15 | SWEP.Base = "weapon_tttbase" 16 | 17 | SWEP.Kind = WEAPON_PISTOL 18 | 19 | SWEP.Primary.Recoil = 1.5 20 | SWEP.Primary.Damage = 25 21 | SWEP.Primary.Delay = 0.38 22 | SWEP.Primary.Cone = 0.02 23 | SWEP.Primary.ClipSize = 20 24 | SWEP.Primary.Automatic = true 25 | SWEP.Primary.DefaultClip = 20 26 | SWEP.Primary.ClipMax = 60 27 | SWEP.Primary.Ammo = "Pistol" 28 | SWEP.Primary.Sound = Sound( "Weapon_FiveSeven.Single" ) 29 | 30 | SWEP.AutoSpawnable = true 31 | SWEP.AmmoEnt = "item_ammo_pistol_ttt" 32 | 33 | SWEP.UseHands = true 34 | SWEP.ViewModel = "models/weapons/cstrike/c_pist_fiveseven.mdl" 35 | SWEP.WorldModel = "models/weapons/w_pist_fiveseven.mdl" 36 | 37 | SWEP.IronSightsPos = Vector(-5.95, -4, 2.799) 38 | SWEP.IronSightsAng = Vector(0, 0, 0) 39 | 40 | 41 | function SWEP:PrimaryAttack() 42 | -- Checks if we have enough ammo to shoot 43 | if not self:CanPrimaryAttack() then return end 44 | 45 | -- weapon_tttbase: SWEP:ShootBullet(damage, recoil, numberOfBullets, cone) 46 | -- Shoots a bullet, handles player/weapon animations & applies recoil 47 | self:ShootBullet( 48 | self.Primary.Damage, 49 | self.Primary.Recoil, 50 | self.Primary.NumShots, 51 | self:GetPrimaryCone() 52 | ) 53 | 54 | self:TakePrimaryAmmo(self.Primary.NumShots) 55 | 56 | self:EmitSound(self.Primary.Sound) 57 | 58 | self:SetNextPrimaryFire(CurTime() + self.Primary.Delay) 59 | end 60 | 61 | 62 | function SWEP:SecondaryAttack() 63 | if self.NoSights or (not self.IronSightsPos) then return end 64 | 65 | self:SetIronsights(not self:GetIronsights()) 66 | 67 | self:SetNextSecondaryFire(CurTime() + 0.3) 68 | end 69 | -------------------------------------------------------------------------------- /resources/samples/weapons/weapon_ttt_traitor_sample.lua: -------------------------------------------------------------------------------- 1 | AddCSLuaFile() 2 | 3 | SWEP.HoldType = "ar2" 4 | 5 | if CLIENT then 6 | SWEP.PrintName = "AK47" 7 | SWEP.Slot = 6 8 | 9 | SWEP.ViewModelFOV = 72 10 | SWEP.ViewModelFlip = true 11 | 12 | -- Traitor equipment menu settings 13 | SWEP.EquipMenuData = { 14 | type = "item_weapon", 15 | desc = "Example custom weapon." 16 | }; 17 | 18 | SWEP.Icon = "vgui/ttt/icon_nades" 19 | end 20 | 21 | SWEP.Base = "weapon_tttbase" 22 | 23 | SWEP.Primary.Delay = 0.08 24 | SWEP.Primary.Recoil = 1.9 25 | SWEP.Primary.Automatic = true 26 | SWEP.Primary.Damage = 20 27 | SWEP.Primary.Cone = 0.025 28 | SWEP.Primary.Ammo = "smg1" 29 | SWEP.Primary.ClipSize = 45 30 | SWEP.Primary.ClipMax = 90 31 | SWEP.Primary.DefaultClip = 45 32 | SWEP.Primary.Sound = Sound( "Weapon_AK47.Single" ) 33 | 34 | SWEP.AmmoEnt = "item_ammo_smg1_ttt" 35 | 36 | SWEP.UseHands = true 37 | SWEP.ViewModel = "models/weapons/v_rif_ak47.mdl" 38 | SWEP.WorldModel = "models/weapons/w_rif_ak47.mdl" 39 | 40 | SWEP.NoSights = false 41 | SWEP.IronSightsPos = Vector( 6.05, -5, 2.4 ) 42 | SWEP.IronSightsAng = Vector( 2.2, -0.1, 0 ) 43 | 44 | 45 | -- Traitor equipment settings 46 | 47 | SWEP.Kind = WEAPON_EQUIP1 48 | SWEP.AutoSpawnable = false 49 | SWEP.CanBuy = { ROLE_TRAITOR } 50 | SWEP.LimitedStock = false 51 | SWEP.AllowDrop = true 52 | SWEP.IsSilent = false 53 | 54 | 55 | function SWEP:PrimaryAttack() 56 | -- Checks if we have enough ammo to shoot 57 | if not self:CanPrimaryAttack() then return end 58 | 59 | -- weapon_tttbase: SWEP:ShootBullet(damage, recoil, numberOfBullets, cone) 60 | -- Shoots a bullet, handles player/weapon animations & applies recoil 61 | self:ShootBullet( 62 | self.Primary.Damage, 63 | self.Primary.Recoil, 64 | self.Primary.NumShots, 65 | self:GetPrimaryCone() 66 | ) 67 | 68 | self:TakePrimaryAmmo(self.Primary.NumShots) 69 | 70 | self:EmitSound(self.Primary.Sound) 71 | 72 | self:SetNextPrimaryFire(CurTime() + self.Primary.Delay) 73 | end 74 | 75 | 76 | function SWEP:SecondaryAttack() 77 | if self.NoSights or (not self.IronSightsPos) then return end 78 | 79 | self:SetIronsights(not self:GetIronsights()) 80 | 81 | self:SetNextSecondaryFire(CurTime() + 0.3) 82 | end 83 | -------------------------------------------------------------------------------- /resources/samples/workshop-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BadgerCode/GMod-SDK-VSCode/69a7d2f4d481324b9632485137ac7c7c2a7d011d/resources/samples/workshop-thumbnail.jpg -------------------------------------------------------------------------------- /src/Services/GModAddonManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | 6 | export class GModAddonManager { 7 | constructor(private workspaceRoot: string | undefined) { } 8 | 9 | getAddonInfo(): GModAddonInfo | undefined { 10 | if (!this.workspaceRoot) { 11 | return undefined; 12 | } 13 | 14 | const addonJSONPath = path.join(this.workspaceRoot, 'addon.json'); 15 | if (this.pathExists(addonJSONPath) == false) { 16 | return undefined; 17 | } 18 | 19 | var addonInfo = JSON.parse(fs.readFileSync(addonJSONPath, 'utf8')); 20 | return addonInfo; 21 | } 22 | 23 | create(): void { 24 | if (!this.workspaceRoot) { 25 | vscode.window.showErrorMessage('Please open a folder before creating an addon.'); 26 | return; 27 | } 28 | 29 | const addonJSONPath = path.join(this.workspaceRoot, 'addon.json'); 30 | if (this.pathExists(addonJSONPath)) { 31 | vscode.window.showErrorMessage('GMod addon already exists!'); 32 | return; 33 | } 34 | 35 | var addonInfo = new GModAddonInfo("My GMod Addon"); 36 | 37 | this.save(addonInfo); 38 | vscode.window.showInformationMessage('Created addon.json'); 39 | } 40 | 41 | save(addonInfo: GModAddonInfo) { 42 | if (!this.workspaceRoot) 43 | return; 44 | 45 | const addonJSONPath = path.join(this.workspaceRoot, 'addon.json'); 46 | fs.writeFileSync(addonJSONPath, JSON.stringify(addonInfo, null, 4)); 47 | } 48 | 49 | private pathExists(p: string): boolean { 50 | try { 51 | fs.accessSync(p); 52 | } catch (err) { 53 | return false; 54 | } 55 | return true; 56 | } 57 | } 58 | 59 | 60 | export class GModAddonInfo { 61 | /** 62 | * Name of addon. Can be changed after publishing to the Steam Workshop. 63 | */ 64 | title: string; 65 | 66 | /** 67 | * A short description of the addon. Can be changed after publishing to the Steam Workshop. 68 | */ 69 | description: string; 70 | 71 | /** 72 | * Type of addon. Used for filtering addons on the Steam Workshop. 73 | */ 74 | type: AddonType; 75 | 76 | /** 77 | * Up to two tags. Used for filtering addons on the Steam Workshop. 78 | */ 79 | tags: AddonTag[]; 80 | 81 | /** 82 | * A list of relative-paths to files that should not be included when publishing the addon. 83 | */ 84 | ignore: string[]; 85 | 86 | constructor(title: string) { 87 | this.title = title; 88 | this.description = ""; 89 | this.type = AddonType.ServerContent; 90 | this.tags = [AddonTag.Fun]; 91 | this.ignore = [".git/*", "README.md"]; 92 | } 93 | } 94 | 95 | export enum AddonType { 96 | ServerContent = "servercontent", 97 | Gamemode = "gamemode", 98 | Map = "map", 99 | Weapon = "weapon", 100 | Vehicle = "vehicle", 101 | NPC = "npc", 102 | Tool = "tool", 103 | Effects = "effects", 104 | Model = "model" 105 | } 106 | 107 | export enum AddonTag { 108 | Fun = "fun", 109 | Roleplay = "roleplay", 110 | Scenic = "scenic", 111 | Movie = "movie", 112 | Realism = "realism", 113 | Cartoon = "cartoon", 114 | Water = "water", 115 | Comic = "comic", 116 | Build = "build" 117 | } 118 | -------------------------------------------------------------------------------- /src/Services/GModWeaponManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as fs from "fs"; 3 | import * as path from "path"; 4 | import { glob } from "glob"; 5 | 6 | export class GModWeaponManager { 7 | constructor(private workspaceRoot: string | undefined, private samplesRoot: string) { } 8 | 9 | getWeapons(): GModWeaponOverview { 10 | var weaponsOverview = new GModWeaponOverview(); 11 | 12 | if (!this.workspaceRoot) return weaponsOverview; 13 | 14 | 15 | const weaponsPath = path.join(this.workspaceRoot, "lua/weapons"); 16 | if (this.pathExists(weaponsPath)) { 17 | weaponsOverview.addWeapons(this.findWeapons(weaponsPath) 18 | .map(fullPath => new GModWeapon(path.basename(fullPath, ".lua"), `lua/weapons`, fullPath)) 19 | ); 20 | } 21 | 22 | const sandboxToolsPath = path.join(this.workspaceRoot, "lua/weapons/gmod_tool/stools"); 23 | if (this.pathExists(sandboxToolsPath)) { 24 | weaponsOverview.addSandboxTools(this.findWeapons(sandboxToolsPath) 25 | .map(fullPath => new GModWeapon(path.basename(fullPath, ".lua"), `lua/weapons/gmod_tool/stools`, fullPath)) 26 | ); 27 | } 28 | 29 | const gamemodesPath = path.join(this.workspaceRoot, "gamemodes"); 30 | if (this.pathExists(gamemodesPath)) { 31 | var gamemodes = fs.readdirSync(gamemodesPath, { withFileTypes: true }) 32 | .filter((fileEntity) => fileEntity.isDirectory()) 33 | .map((directory) => directory.name); 34 | 35 | for (let i = 0; i < gamemodes.length; i++) { 36 | const gamemode = gamemodes[i]; 37 | var gamemodeWeaponsPath = path.join(gamemodesPath, gamemode, "entities/weapons/"); 38 | 39 | if (this.pathExists(gamemodeWeaponsPath)) { 40 | weaponsOverview.addWeapons(this.findWeapons(gamemodeWeaponsPath) 41 | .map(fullPath => new GModWeapon(path.basename(fullPath, ".lua"), `gamemodes/${gamemode}/entities/weapons`, fullPath)) 42 | ); 43 | } 44 | 45 | const gamemodeSandboxToolsPath = path.join(gamemodesPath, gamemode, "entities/weapons/gmod_tool/stools"); 46 | if (this.pathExists(gamemodeSandboxToolsPath)) { 47 | weaponsOverview.addSandboxTools(this.findWeapons(gamemodeSandboxToolsPath) 48 | .map(fullPath => new GModWeapon(path.basename(fullPath, ".lua"), `gamemodes/${gamemode}/entities/weapons/gmod_tool/stools`, fullPath)) 49 | ); 50 | } 51 | } 52 | } 53 | 54 | weaponsOverview.sort(); 55 | 56 | return weaponsOverview; 57 | } 58 | 59 | private findWeapons(directoryPath: string): string[] { 60 | var directoryItems = fs.readdirSync(directoryPath, { withFileTypes: true }); 61 | var weaponPaths: string[] = []; 62 | 63 | for (let i = 0; i < directoryItems.length; i++) { 64 | const dirItem = directoryItems[i]; 65 | var fullPath = path.join(directoryPath, dirItem.name); 66 | 67 | if (dirItem.isFile()) { 68 | if (dirItem.name.endsWith(".lua")) { 69 | weaponPaths.push(fullPath); 70 | } 71 | } 72 | else if (dirItem.isDirectory()) { 73 | var weaponLuaFiles = glob.sync(path.join(fullPath, "@(shared|init|cl_init).lua")); 74 | if (weaponLuaFiles.length != 0) 75 | weaponPaths.push(fullPath); 76 | } 77 | } 78 | 79 | return weaponPaths; 80 | } 81 | 82 | editWeapon(weaponPath: string): void { 83 | // TODO: If path is a folder, open folder 84 | if (this.pathExists(weaponPath) == false) 85 | return; 86 | 87 | if (fs.lstatSync(weaponPath).isDirectory()) { 88 | this.openDirectory(weaponPath); 89 | } 90 | else { 91 | this.openFile(weaponPath); 92 | } 93 | } 94 | 95 | createWeaponFromTemplate(weaponName: string, weaponTemplate: any): void { 96 | if (!this.workspaceRoot) { 97 | return; 98 | } 99 | 100 | const weaponsPath = path.join(this.workspaceRoot, weaponTemplate.DestinationPath); 101 | if (this.pathExists(weaponsPath) == false) { 102 | fs.mkdirSync(weaponsPath, { recursive: true }); 103 | } 104 | 105 | var newWeaponName = `${weaponTemplate.WeaponNamePrefix}${weaponName}`; 106 | var newWeaponPath = path.join(weaponsPath, `${newWeaponName}.lua`); 107 | var uniqueNumber = 1; 108 | while (this.pathExists(newWeaponPath)) { 109 | newWeaponName = `${weaponTemplate.WeaponNamePrefix}${weaponName}${uniqueNumber++}`; 110 | newWeaponPath = path.join(weaponsPath, `${newWeaponName}.lua`); 111 | } 112 | 113 | var sampleWeaponTemplatePath = path.join(this.samplesRoot, "weapons", weaponTemplate.Filename); 114 | if (this.pathExists(sampleWeaponTemplatePath) == false) { 115 | vscode.window.showErrorMessage(`Unable to find template '${weaponTemplate.Filename}'`); 116 | return; 117 | } 118 | 119 | var sampleWeaponLua = fs 120 | .readFileSync(sampleWeaponTemplatePath, "utf8") 121 | .replace(/\$WEAPON_NAME\$/g, newWeaponName); 122 | 123 | fs.writeFileSync(newWeaponPath, sampleWeaponLua); 124 | 125 | this.openFile(newWeaponPath); 126 | } 127 | 128 | private pathExists(p: string): boolean { 129 | try { 130 | fs.accessSync(p); 131 | } catch (err) { 132 | return false; 133 | } 134 | return true; 135 | } 136 | 137 | private openFile(filePath: string): void { 138 | vscode.workspace.openTextDocument(vscode.Uri.file(filePath)).then( 139 | (document: vscode.TextDocument) => { 140 | vscode.window.showTextDocument(document, vscode.ViewColumn.Active, false).then((e) => { }); 141 | }, 142 | (error: any) => { 143 | console.error(error); 144 | debugger; 145 | } 146 | ); 147 | } 148 | 149 | private openDirectory(directoryPath: string): void { 150 | var weaponLuaFiles = glob.sync(path.join(directoryPath, "@(shared|init|cl_init).lua")); 151 | if (weaponLuaFiles.length == 0) 152 | return; 153 | 154 | vscode.workspace.openTextDocument(vscode.Uri.file(weaponLuaFiles[0])).then( 155 | (document: vscode.TextDocument) => { 156 | vscode.window.showTextDocument(document, vscode.ViewColumn.Active, false).then((e) => { }); 157 | }, 158 | (error: any) => { 159 | console.error(error); 160 | debugger; 161 | } 162 | ); 163 | } 164 | } 165 | 166 | export class GModWeaponOverview { 167 | public weapons: GModWeapon[]; 168 | public sandboxTools: GModWeapon[]; 169 | 170 | constructor() { 171 | this.weapons = []; 172 | this.sandboxTools = []; 173 | } 174 | 175 | addWeapons(weapons: GModWeapon[]): void { 176 | this.weapons = this.weapons.concat(weapons); 177 | } 178 | 179 | addSandboxTools(tools: GModWeapon[]): void { 180 | this.sandboxTools = this.sandboxTools.concat(tools); 181 | } 182 | 183 | sort(): void { 184 | this.weapons.sort(this.sortFunc); 185 | this.sandboxTools.sort(this.sortFunc); 186 | } 187 | 188 | private sortFunc(a: GModWeapon, b: GModWeapon): number { 189 | if (a.name < b.name) { 190 | return -1; 191 | } 192 | 193 | if (a.name > b.name) { 194 | return 1; 195 | } 196 | 197 | if (a.relativePath < b.relativePath) { 198 | return -1; 199 | } 200 | 201 | if (a.relativePath > b.relativePath) { 202 | return 1; 203 | } 204 | 205 | return 0; 206 | } 207 | } 208 | 209 | export class GModWeapon { 210 | name: string; 211 | pathToFile: string; 212 | relativePath: string; 213 | 214 | constructor(name: string, relativePath: string, pathToFile: string) { 215 | this.name = name; 216 | this.relativePath = relativePath; 217 | this.pathToFile = pathToFile; 218 | } 219 | } 220 | 221 | export class GModWeaponTemplates { 222 | private static readonly WeaponSamples: any = { 223 | TTT: { 224 | Label: "TTT", 225 | Weapons: { 226 | Primary: { 227 | Filename: "weapon_ttt_primary_sample.lua", 228 | WeaponNamePrefix: "weapon_ttt_", 229 | DestinationPath: "lua/weapons/" 230 | }, 231 | Secondary: { 232 | Filename: "weapon_ttt_secondary_sample.lua", 233 | WeaponNamePrefix: "weapon_ttt_", 234 | DestinationPath: "lua/weapons/" 235 | }, 236 | Grenade: { 237 | Filename: "weapon_ttt_grenade_sample.lua", 238 | WeaponNamePrefix: "weapon_ttt_", 239 | DestinationPath: "lua/weapons/" 240 | }, 241 | Traitor: { 242 | Filename: "weapon_ttt_traitor_sample.lua", 243 | WeaponNamePrefix: "weapon_ttt_", 244 | DestinationPath: "lua/weapons/" 245 | }, 246 | Detective: { 247 | Filename: "weapon_ttt_detective_sample.lua", 248 | WeaponNamePrefix: "weapon_ttt_", 249 | DestinationPath: "lua/weapons/" 250 | } 251 | } 252 | }, 253 | Sandbox: { 254 | Label: "Sandbox", 255 | Weapons: { 256 | Main: { 257 | Filename: "weapon_sandbox_main_sample.lua", 258 | WeaponNamePrefix: "weapon_", 259 | DestinationPath: "lua/weapons/" 260 | }, 261 | ToolGun: { 262 | Filename: "weapon_sandbox_toolgun_sample.lua", 263 | WeaponNamePrefix: "", 264 | DestinationPath: "lua/weapons/gmod_tool/stools/" 265 | } 266 | } 267 | } 268 | }; 269 | 270 | public static GetGamemodes(): string[] { 271 | return Object.keys(GModWeaponTemplates.WeaponSamples); 272 | } 273 | 274 | public static GetTemplateNames(gamemode: string): string[] { 275 | return Object.keys(GModWeaponTemplates.WeaponSamples[gamemode].Weapons); 276 | } 277 | 278 | public static GetTemplate(gamemode: string, template: string): any { 279 | return GModWeaponTemplates.WeaponSamples[gamemode].Weapons[template]; 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/Services/GModWorkshopManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import axios from 'axios'; 5 | 6 | export class GModWorkshopManager { 7 | private static DEBUGMODE: boolean = false; 8 | 9 | private get gmadExecutablePath(): string { 10 | var path = vscode.workspace.getConfiguration('gmod-sdk') 11 | .get("gmadExecutablePath"); 12 | 13 | if (path == undefined || !this.pathExists(path)) { 14 | var errorMessage = `Unable to find gmad program. To fix this, Open Settings, search for gmod-sdk.gmadExecutablePath and follow the instructions.`; 15 | vscode.commands.executeCommand("workbench.action.openSettings2") 16 | .then(() => { vscode.window.showErrorMessage(errorMessage) }); 17 | throw errorMessage; 18 | } 19 | 20 | return path; 21 | } 22 | 23 | private get gmpublishExecutablePath(): string { 24 | var path = vscode.workspace.getConfiguration('gmod-sdk') 25 | .get("gmpublishExecutablePath"); 26 | 27 | if (path == undefined || !this.pathExists(path)) { 28 | var errorMessage = `Unable to find gmad program. To fix this, Open Settings, search for gmod-sdk.gmpublishExecutablePath and follow the instructions.`; 29 | vscode.commands.executeCommand("workbench.action.openSettings2") 30 | .then(() => { vscode.window.showErrorMessage(errorMessage) }); 31 | throw errorMessage; 32 | } 33 | 34 | return path; 35 | } 36 | 37 | constructor(private workspacePath: string | undefined, private samplesRoot: string) { } 38 | 39 | getAddonsForUser(steamID64: string): Thenable { 40 | return new Promise((resolver, rejector) => { 41 | axios.get(`https://steamuserinfoapi.azurewebsites.net/api/profiles/${steamID64}/workshopitems/4000?pageNumber=1`) 42 | .then(response => { 43 | resolver(response.data.workshopItems); 44 | // Loading multiple pages of items takes too long 45 | // This is something to fix in the future 46 | }) 47 | .catch(error => { 48 | console.log(error); 49 | rejector(); 50 | }); 51 | }); 52 | } 53 | 54 | validateConfigForAddonUpload(): void { 55 | var gmadPath = this.gmadExecutablePath; 56 | } 57 | 58 | validateConfigForThumbnailUpload(): void { 59 | var gmpublishPath = this.gmpublishExecutablePath; 60 | } 61 | 62 | upload(): void { 63 | if (this.workspacePath == undefined) 64 | return; 65 | 66 | var gmaPath = path.join(this.workspacePath!, "workshop-file.gma"); 67 | var thumbnailPath = path.join(this.samplesRoot, "workshop-thumbnail.jpg"); 68 | 69 | if (GModWorkshopManager.DEBUGMODE) { 70 | console.log("Pretending to upload GMA"); 71 | console.log(`GMA: ${gmaPath}`); 72 | console.log(`Thumbnail: ${thumbnailPath}`); 73 | return; 74 | } 75 | 76 | var gmadPath = this.gmadExecutablePath; 77 | var gmpublishPath = this.gmpublishExecutablePath; 78 | 79 | // TODO: Re-use terminal if exists/cleanup terminal on exit 80 | var terminal = vscode.window.createTerminal("GMod Publish"); 81 | terminal.show(); 82 | 83 | terminal.sendText(`& "${gmadPath}" create -folder "${this.workspacePath}" -out "${gmaPath}" -warninvalid`); 84 | 85 | terminal.sendText(`& "${gmpublishPath}" create -addon "${gmaPath}" -icon "${thumbnailPath}"`); 86 | 87 | // TODO: Wait for terminal to finish processing commands and delete the .gma file 88 | } 89 | 90 | updateAddon(fileID: string): void { 91 | if (this.workspacePath == undefined) 92 | return; 93 | 94 | var gmaPath = path.join(this.workspacePath!, "workshop-file.gma"); 95 | 96 | if (GModWorkshopManager.DEBUGMODE) { 97 | console.log(`Pretending to upload GMA to existing addon ${fileID}`); 98 | console.log(`GMA: ${gmaPath}`); 99 | return; 100 | } 101 | 102 | var gmadPath = this.gmadExecutablePath; 103 | var gmpublishPath = this.gmpublishExecutablePath; 104 | 105 | var terminal = vscode.window.createTerminal("GMod Publish"); 106 | terminal.show(); 107 | 108 | terminal.sendText(`& "${gmadPath}" create -folder "${this.workspacePath}" -out "${gmaPath}" -warninvalid`); 109 | 110 | terminal.sendText(`& "${gmpublishPath}" update -id ${fileID} -addon "${gmaPath}"`); 111 | 112 | // TODO: Wait for terminal to finish processing commands and delete the .gma file 113 | } 114 | 115 | updateAddonThumbnail(fileID: string, thumbnailPath: string): void { 116 | if (GModWorkshopManager.DEBUGMODE) { 117 | console.log(`Pretending to update existing addon ${fileID} thumbnail`); 118 | console.log(`Thumbnail: ${thumbnailPath}`); 119 | return; 120 | } 121 | 122 | var gmpublishPath = this.gmpublishExecutablePath; 123 | 124 | var terminal = vscode.window.createTerminal("GMod Publish"); 125 | terminal.show(); 126 | 127 | terminal.sendText(`& "${gmpublishPath}" update -id ${fileID} -icon "${thumbnailPath}"`); 128 | 129 | // TODO: Wait for terminal to finish processing commands and delete the .gma file 130 | } 131 | 132 | private pathExists(p: string): boolean { 133 | try { 134 | fs.accessSync(p); 135 | } catch (err) { 136 | return false; 137 | } 138 | return true; 139 | } 140 | } 141 | 142 | export enum WorkshopItemVisibility { 143 | Public = 0, 144 | FriendsOnly = 1, 145 | Hidden = 2, 146 | Unlisted = 3 147 | } 148 | -------------------------------------------------------------------------------- /src/Services/LocalGModManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as fs from 'fs-extra'; 3 | import * as path from 'path'; 4 | 5 | 6 | export class LocalGModManager { 7 | constructor(private workspaceRoot: string | undefined) { } 8 | 9 | syncWorkspaceWithAddon(destinationAddonFolderName: string): void { 10 | if (!this.workspaceRoot) { 11 | return; 12 | } 13 | 14 | var addonsFolderPath = vscode.workspace.getConfiguration('gmod-sdk') 15 | .get("garrysmodAddonsPath"); 16 | 17 | if (addonsFolderPath == undefined || !this.pathExists(addonsFolderPath)) { 18 | var errorMessage = `Unable to find addons folder. To fix this, Open Settings, search for gmod-sdk.garrysmodAddonsPath and follow the instructions.`; 19 | vscode.commands.executeCommand("workbench.action.openSettings2") 20 | .then(() => { vscode.window.showErrorMessage(errorMessage) }); 21 | 22 | return; 23 | } 24 | 25 | var destinationPath = path.join(addonsFolderPath, destinationAddonFolderName); 26 | try { 27 | if (this.pathExists(destinationPath) == true) 28 | fs.removeSync(destinationPath); // TODO: Warn user 29 | 30 | fs.copySync(this.workspaceRoot, destinationPath, { recursive: true }); 31 | 32 | vscode.window.showInformationMessage(`The addon has been copied to your Garry's Mod folder. ${destinationPath}`) 33 | } 34 | catch (err) { 35 | vscode.window.showErrorMessage(`Error copying addon to the Garry's Mod folder: ${destinationPath} ${err}`); 36 | } 37 | } 38 | 39 | private pathExists(p: string): boolean { 40 | try { 41 | fs.accessSync(p); 42 | } catch (err) { 43 | return false; 44 | } 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Views/GModAddonInfoView.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as path from 'path'; 3 | import { GModAddonManager, GModAddonInfo, AddonTag, AddonType } from '../Services/GModAddonManager'; 4 | 5 | export class GModAddonInfoView implements vscode.TreeDataProvider { 6 | private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); 7 | readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; 8 | 9 | private addonInfo: GModAddonInfo | undefined; 10 | 11 | constructor(private addonManager: GModAddonManager) { 12 | this.addonInfo = this.addonManager.getAddonInfo(); 13 | } 14 | 15 | getTreeItem(element: GModMenuItem): vscode.TreeItem { 16 | return element; 17 | } 18 | 19 | getChildren(element?: GModMenuItem): Thenable { 20 | if (this.addonInfo == undefined) 21 | return Promise.resolve([]); 22 | 23 | if (!element) { 24 | var typeDescription = this.isAddonTypeValid(this.addonInfo.type) ? this.addonInfo.type : "❌ INVALID TYPE"; 25 | var tagsDescription = this.areTagsValid(this.addonInfo.tags) 26 | ? (this.addonInfo.tags.length <= 2 ? this.addonInfo.tags.join(", ") : "❌ TOO MANY TAGS") 27 | : "❌ INVALID TAGS"; 28 | 29 | return Promise.resolve([ 30 | new GModMenuItem("title", "Title", this.addonInfo.title), 31 | new GModMenuItem("description", "Description", this.addonInfo.description), 32 | new GModMenuItem("type", "Type", typeDescription), 33 | new GModMenuItem("tags", "Tags", tagsDescription), 34 | new GModMenuItem("ignoredFiles", "Ignored files", "", undefined, vscode.TreeItemCollapsibleState.Collapsed) 35 | ]); 36 | } 37 | else if (element.id == "ignoredFiles") { 38 | return Promise.resolve( 39 | this.addonInfo.ignore.map((filePath, index) => new GModMenuItem(`ignoredFiles.${index}`, filePath, "")) 40 | ); 41 | } 42 | else { 43 | return Promise.resolve([]); 44 | } 45 | } 46 | 47 | refresh(): void { 48 | this.addonInfo = this.addonManager.getAddonInfo(); 49 | this._onDidChangeTreeData.fire(undefined); 50 | } 51 | 52 | private isAddonTypeValid(type: AddonType): boolean { 53 | return Object.values(AddonType).includes(type); 54 | } 55 | 56 | private areTagsValid(tags: AddonTag[]): boolean { 57 | for (let i = 0; i < tags.length; i++) { 58 | const tag = tags[i]; 59 | if (Object.values(AddonTag).includes(tag) == false) 60 | return false; 61 | } 62 | return true; 63 | } 64 | } 65 | 66 | export class GModMenuItem extends vscode.TreeItem { 67 | constructor( 68 | public readonly id: string, 69 | public readonly label: string, 70 | private value: string, 71 | iconName: string | undefined = undefined, 72 | public readonly collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None 73 | ) { 74 | super(label, collapsibleState) 75 | 76 | if (iconName) { 77 | this.iconPath = { 78 | light: path.join(__filename, '..', '..', 'resources', 'light', iconName), 79 | dark: path.join(__filename, '..', '..', 'resources', 'dark', iconName) 80 | }; 81 | } 82 | 83 | this.contextValue = id; 84 | 85 | this.tooltip = this.value; 86 | this.description = this.value; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Views/GModAddonWeaponsView.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from "path"; 3 | import { GModWeaponManager, GModWeapon, GModWeaponOverview } from "../Services/GModWeaponManager"; 4 | 5 | export class GModAddonWeaponsView implements vscode.TreeDataProvider { 6 | private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); 7 | readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; 8 | 9 | private weaponOverview: GModWeaponOverview; 10 | 11 | constructor(private workspaceRoot: string | undefined, private weaponManager: GModWeaponManager) { 12 | this.weaponOverview = this.weaponManager.getWeapons(); 13 | } 14 | 15 | 16 | getTreeItem(element: GModWeaponMenuItem): vscode.TreeItem { 17 | return element; 18 | } 19 | 20 | getChildren(element?: GModWeaponMenuItem): Thenable { 21 | if (this.workspaceRoot == undefined) return Promise.resolve([]); 22 | 23 | if (element == undefined) { 24 | var treeItems = []; 25 | 26 | if (this.weaponOverview.sandboxTools.length != 0) 27 | treeItems.push(GModWeaponMenuItem.CreateSubMenu('category.stools', "Sandbox Tools")); 28 | 29 | var weapons = this.weaponOverview.weapons.map((weapon, index) => GModWeaponMenuItem.CreateWeaponItem(weapon)); 30 | return Promise.resolve(treeItems.concat(weapons)); 31 | } 32 | else if (element.id == 'category.stools') { 33 | return Promise.resolve(this.weaponOverview.sandboxTools.map((tool, index) => GModWeaponMenuItem.CreateWeaponItem(tool))); 34 | } 35 | else { 36 | return Promise.resolve([]); 37 | } 38 | } 39 | 40 | refresh(): void { 41 | this.weaponOverview = this.weaponManager.getWeapons(); 42 | this._onDidChangeTreeData.fire(undefined); 43 | } 44 | } 45 | 46 | export class GModWeaponMenuItem extends vscode.TreeItem { 47 | static CreateWeaponItem(weapon: GModWeapon): GModWeaponMenuItem { 48 | var menuItem = new GModWeaponMenuItem(`weapons.${weapon.relativePath}.${weapon.name}`, weapon.name, weapon.relativePath); 49 | menuItem.weapon = weapon; 50 | return menuItem; 51 | } 52 | 53 | static CreateSubMenu(id: string, label: string): GModWeaponMenuItem { 54 | return new GModWeaponMenuItem(id, label, undefined, "folder.svg", vscode.TreeItemCollapsibleState.Expanded); 55 | } 56 | 57 | weapon: GModWeapon | undefined; 58 | relativePath: string | undefined; 59 | 60 | constructor( 61 | public readonly id: string, 62 | label: string, 63 | relativePath: string | undefined = undefined, 64 | iconName: string | undefined = undefined, 65 | collapsedState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None 66 | ) { 67 | super(label, collapsedState); 68 | 69 | this.relativePath = relativePath; 70 | this.contextValue = id; 71 | 72 | if (iconName) { 73 | this.iconPath = { 74 | light: path.join(__filename, "..", "..", "resources", "light", iconName), 75 | dark: path.join(__filename, "..", "..", "resources", "dark", iconName) 76 | }; 77 | } 78 | 79 | this.tooltip = this.weapon?.pathToFile; 80 | this.description = this.relativePath; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Views/GModWorkshopView.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as path from 'path'; 3 | import { GModWorkshopManager, WorkshopItemVisibility } from '../Services/GModWorkshopManager'; 4 | 5 | export class GModWorkshopView implements vscode.TreeDataProvider { 6 | private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); 7 | readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; 8 | 9 | private workshopItems: any[] = []; 10 | private steamID: string | undefined = undefined; 11 | private isLoading: boolean = false; 12 | 13 | constructor(private workshopManager: GModWorkshopManager) { 14 | this.refresh(); 15 | } 16 | 17 | getTreeItem(element: GModWorkshopMenuItem): vscode.TreeItem { 18 | return element; 19 | } 20 | 21 | getChildren(element?: GModWorkshopMenuItem): Thenable { 22 | if (this.steamID == undefined) { 23 | return Promise.resolve([]); 24 | } 25 | 26 | if (this.isLoading) { 27 | return Promise.resolve([ 28 | new GModWorkshopMenuItem("loading", "Loading...") 29 | ]); 30 | } 31 | 32 | if (this.workshopItems.length == 0) { 33 | return Promise.resolve([ 34 | new GModWorkshopMenuItem("no-items", "You don't have any workshop items") 35 | ]); 36 | } 37 | 38 | return Promise.resolve(this.workshopItems 39 | .map(item => { 40 | var icon = this.getIconForItemVisibility(item["visibility"] as WorkshopItemVisibility); 41 | var menuItem = new GModWorkshopMenuItem(`item-${item["publishedfileid"]}`, item["title"], item["publishedfileid"], icon); 42 | menuItem.workshopFileId = item["publishedfileid"]; 43 | 44 | return menuItem; 45 | }) 46 | ); 47 | } 48 | 49 | refresh(): void { 50 | this.steamID = this.getSteamID(); 51 | 52 | if (this.steamID == undefined) { 53 | this.workshopItems = []; 54 | this._onDidChangeTreeData.fire(undefined); 55 | return; 56 | } 57 | 58 | this.isLoading = true; 59 | this.workshopManager.getAddonsForUser(this.steamID) 60 | .then(response => { 61 | this.isLoading = false; 62 | this.workshopItems = response; 63 | this._onDidChangeTreeData.fire(undefined); 64 | }); 65 | } 66 | 67 | private getSteamID(): string | undefined { 68 | var config = vscode.workspace.getConfiguration('gmod-sdk').get("steamID"); 69 | 70 | if (config == undefined || config.length == 0) 71 | return undefined; 72 | 73 | return config 74 | } 75 | 76 | private getIconForItemVisibility(visibility: WorkshopItemVisibility): string | undefined { 77 | switch (visibility) { 78 | case WorkshopItemVisibility.FriendsOnly: 79 | case WorkshopItemVisibility.Hidden: 80 | case WorkshopItemVisibility.Unlisted: 81 | return "workshop-item-hidden.svg"; 82 | case WorkshopItemVisibility.Public: 83 | default: 84 | return undefined; 85 | } 86 | } 87 | } 88 | 89 | export class GModWorkshopMenuItem extends vscode.TreeItem { 90 | workshopFileId: string | undefined; 91 | 92 | constructor(id: string, 93 | label: string, 94 | description: string | undefined = undefined, 95 | iconName: string | undefined = undefined, 96 | collapsedState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None 97 | ) { 98 | super(label, collapsedState) 99 | 100 | this.contextValue = id; 101 | 102 | if (iconName) { 103 | this.iconPath = { 104 | light: path.join(__filename, '..', '..', 'resources', 'light', iconName), 105 | dark: path.join(__filename, '..', '..', 'resources', 'dark', iconName) 106 | }; 107 | } 108 | 109 | this.tooltip = label; 110 | this.description = description; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Wizards/AddonTagsWizard.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { GModAddonManager, AddonTag } from '../Services/GModAddonManager'; 3 | 4 | export class AddonTagsWizard { 5 | 6 | constructor(private addonManager: GModAddonManager) { } 7 | 8 | show(): Thenable { 9 | return new Promise((resolver, rejector) => { 10 | var types = Object.keys(AddonTag); 11 | 12 | vscode.window 13 | .showQuickPick(types, { placeHolder: "Select the first tag" }) 14 | .then(tag1 => { 15 | if (tag1 == undefined) { 16 | return; 17 | } 18 | 19 | var firstTag = AddonTag[tag1 as keyof typeof AddonTag]; 20 | 21 | types.splice(types.indexOf(tag1), 1); 22 | types.unshift("None"); 23 | 24 | vscode.window 25 | .showQuickPick(types, { placeHolder: "Select the second tag" }) 26 | .then(tag2 => { 27 | if (tag2 == undefined) { 28 | return; 29 | } 30 | 31 | var tags = [firstTag]; 32 | 33 | if (tag2 != "None") { 34 | var secondTag = AddonTag[tag2 as keyof typeof AddonTag]; 35 | tags.push(secondTag); 36 | } 37 | 38 | var addonInfo = this.addonManager.getAddonInfo(); 39 | if (addonInfo == undefined) { 40 | vscode.window.showErrorMessage("addon.json is missing.") 41 | return; 42 | } 43 | 44 | addonInfo.tags = tags; 45 | this.addonManager.save(addonInfo); 46 | resolver(); 47 | }); 48 | }); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Wizards/AddonTypeWizard.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { GModAddonManager, AddonType } from '../Services/GModAddonManager'; 3 | 4 | export class AddonTypeWizard { 5 | 6 | constructor(private addonManager: GModAddonManager) { } 7 | 8 | show(): Thenable { 9 | return new Promise((resolver, rejector) => { 10 | var types = Object.keys(AddonType); 11 | 12 | vscode.window 13 | .showQuickPick(types, { placeHolder: "Select what type of addon you are making" }) 14 | .then(type => { 15 | if (type == undefined) { 16 | return; 17 | } 18 | 19 | var addonInfo = this.addonManager.getAddonInfo(); 20 | if (addonInfo == undefined) { 21 | vscode.window.showErrorMessage("addon.json is missing.") 22 | return; 23 | } 24 | 25 | addonInfo.type = AddonType[type as keyof typeof AddonType]; 26 | this.addonManager.save(addonInfo); 27 | resolver(); 28 | }); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Wizards/CreateWeaponWizard.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { GModWeaponManager, GModWeaponTemplates } from '../Services/GModWeaponManager'; 3 | 4 | export class CreateWeaponWizard { 5 | 6 | constructor(private weaponManager: GModWeaponManager) { } 7 | 8 | show(): Thenable { 9 | return new Promise((resolver, rejector) => { 10 | vscode.window 11 | .showQuickPick(GModWeaponTemplates.GetGamemodes(), { placeHolder: "What gamemode is the weapon for?" }) 12 | .then(gamemode => { 13 | if (gamemode == undefined) { 14 | return; 15 | } 16 | 17 | vscode.window 18 | .showQuickPick(GModWeaponTemplates.GetTemplateNames(gamemode), { placeHolder: "Which type of weapon?" }) 19 | .then(weaponType => { 20 | if (weaponType == undefined) { 21 | return; 22 | } 23 | 24 | vscode.window.showInputBox({ prompt: "Enter a weapon name. e.g. cool_weapon", validateInput: this.weaponNameValidator }) 25 | .then(weaponName => { 26 | if (weaponName == undefined) { 27 | return; 28 | } 29 | 30 | var weaponTemplate = GModWeaponTemplates.GetTemplate(gamemode, weaponType); 31 | this.weaponManager.createWeaponFromTemplate(weaponName, weaponTemplate); 32 | resolver(); 33 | }); 34 | }); 35 | }); 36 | }); 37 | } 38 | 39 | private static readonly WeaponNameRegex: RegExp = /^[A-Za-z0-9_-]+$/; 40 | private weaponNameValidator(input: string): string { 41 | return CreateWeaponWizard.WeaponNameRegex.test(input) ? "" : "Please only use letters, numbers and underscores."; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Wizards/WorkshopThumbnailWizard.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as fs from 'fs'; 3 | import { GModWorkshopManager } from '../Services/GModWorkshopManager'; 4 | 5 | export class WorkshopThumbnailWizard { 6 | 7 | constructor(private workshopManager: GModWorkshopManager) { } 8 | 9 | show(targetWorkshopFileID: string | undefined): void { 10 | this.workshopManager.validateConfigForThumbnailUpload(); 11 | 12 | this.showThumbnailSelector() 13 | .then(thumbnailPath => { 14 | if (targetWorkshopFileID != undefined) { 15 | this.updateThumbnail(targetWorkshopFileID, thumbnailPath); 16 | return; 17 | } 18 | 19 | vscode.window.showInputBox({ prompt: "Enter the workshop file ID. This can be found at the end of the URL. E.g. https://steamcommunity.com/sharedfiles/filedetails/?id=2086515808 has a file ID of 2086515808" }) 20 | .then(fileID => { 21 | if (fileID == undefined) { 22 | vscode.window.showInformationMessage(`You cancelled the thumbnail upload`); 23 | return; 24 | } 25 | 26 | this.updateThumbnail(fileID, thumbnailPath); 27 | }); 28 | }); 29 | } 30 | 31 | private showThumbnailSelector(): Thenable { 32 | return new Promise((resolver, rejector) => { 33 | vscode.window.showOpenDialog({ 34 | canSelectFolders: false, 35 | canSelectMany: false, 36 | openLabel: "Select thumbnail", 37 | filters: { 38 | 'Images': ['jpg'] 39 | } 40 | }).then(uri => { 41 | if (uri == undefined) { 42 | return; 43 | } 44 | 45 | var thumbnailPath = uri[0].fsPath; 46 | if (this.pathExists(thumbnailPath) == false) { 47 | vscode.window.showErrorMessage(`Unable to find thumbnail- ${thumbnailPath}`); 48 | return; 49 | } 50 | 51 | resolver(thumbnailPath); 52 | }); 53 | }); 54 | } 55 | 56 | private updateThumbnail(targetFileID: string, thumbnailPath: string) { 57 | if (isNaN(Number(targetFileID))) { 58 | vscode.window.showErrorMessage(`"${targetFileID}" is not a valid workshop File ID. E.g. https://steamcommunity.com/sharedfiles/filedetails/?id=2086515808 has a file ID of 2086515808`); 59 | return; 60 | } 61 | 62 | vscode.window.showInputBox({ prompt: `This will UPDATE the thumbnail for addon ${targetFileID}. Type YES to continue`, placeHolder: "YES" }) 63 | .then(confirmation => { 64 | if (confirmation == undefined || confirmation.toLowerCase().trim() != "yes") { 65 | vscode.window.showInformationMessage(`You cancelled the workshop upload`); 66 | return; 67 | } 68 | 69 | this.workshopManager.updateAddonThumbnail(targetFileID, thumbnailPath); 70 | }); 71 | } 72 | 73 | private pathExists(p: string): boolean { 74 | try { 75 | fs.accessSync(p); 76 | } catch (err) { 77 | return false; 78 | } 79 | return true; 80 | } 81 | } -------------------------------------------------------------------------------- /src/Wizards/WorkshopUploadWizard.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { GModWorkshopManager } from '../Services/GModWorkshopManager'; 3 | 4 | export class WorkshopUploadWizard { 5 | 6 | constructor(private workshopManager: GModWorkshopManager) { } 7 | 8 | show(targetWorkshopFileID: string | undefined): void { 9 | this.workshopManager.validateConfigForAddonUpload(); 10 | 11 | if (targetWorkshopFileID != undefined) { 12 | this.updateExistingItem(targetWorkshopFileID); 13 | return; 14 | } 15 | 16 | vscode.window 17 | .showQuickPick(["New Addon", "Existing Addon"], { placeHolder: "Upload a new addon or update an existing addon?" }) 18 | .then(response => { 19 | if (response == undefined) 20 | return; 21 | 22 | var shouldUploadNewAddon = response == "New Addon"; 23 | if (shouldUploadNewAddon) { 24 | vscode.window.showInputBox({ prompt: "This will upload a NEW addon to the workshop. Type YES to continue", placeHolder: "YES" }) 25 | .then(confirmation => { 26 | if (confirmation == undefined || confirmation.toLowerCase().trim() != "yes") { 27 | vscode.window.showInformationMessage(`You cancelled the workshop upload`); 28 | return; 29 | } 30 | this.workshopManager.upload(); 31 | }); 32 | } 33 | else { 34 | vscode.window.showInputBox({ prompt: "Enter the workshop file ID. This can be found at the end of the URL. E.g. https://steamcommunity.com/sharedfiles/filedetails/?id=2086515808 has a file ID of 2086515808" }) 35 | .then(fileID => { 36 | if (fileID == undefined) { 37 | vscode.window.showInformationMessage(`You cancelled the workshop upload`); 38 | return; 39 | } 40 | 41 | this.updateExistingItem(fileID); 42 | }); 43 | } 44 | }); 45 | } 46 | 47 | private updateExistingItem(fileID: string): void { 48 | var trimmedFileID = fileID.trim(); 49 | if (isNaN(Number(trimmedFileID))) { 50 | vscode.window.showInformationMessage(`"${trimmedFileID}" is not a valid workshop File ID. E.g. https://steamcommunity.com/sharedfiles/filedetails/?id=2086515808 has a file ID of 2086515808`); 51 | return; 52 | } 53 | 54 | vscode.window.showInputBox({ prompt: `This will UPDATE the addon ${trimmedFileID}. Type YES to continue`, placeHolder: "YES" }) 55 | .then(confirmation => { 56 | if (confirmation == undefined || confirmation.toLowerCase().trim() != "yes") { 57 | vscode.window.showInformationMessage(`You cancelled the workshop upload`); 58 | return; 59 | } 60 | 61 | this.workshopManager.updateAddon(trimmedFileID); 62 | }); 63 | } 64 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as path from 'path'; 3 | import { WorkshopUploadWizard } from './Wizards/WorkshopUploadWizard'; 4 | import { WorkshopThumbnailWizard } from './Wizards/WorkshopThumbnailWizard'; 5 | import { CreateWeaponWizard } from './Wizards/CreateWeaponWizard'; 6 | import { GModAddonManager } from './Services/GModAddonManager'; 7 | import { GModWeaponManager } from './Services/GModWeaponManager'; 8 | import { GModWorkshopManager } from './Services/GModWorkshopManager'; 9 | import { LocalGModManager } from './Services/LocalGModManager'; 10 | import { GModAddonInfoView } from './Views/GModAddonInfoView'; 11 | import { GModAddonWeaponsView, GModWeaponMenuItem } from './Views/GModAddonWeaponsView'; 12 | import { GModWorkshopView, GModWorkshopMenuItem } from './Views/GModWorkshopView'; 13 | import { AddonTypeWizard } from './Wizards/AddonTypeWizard'; 14 | import { AddonTagsWizard } from './Wizards/AddonTagsWizard'; 15 | 16 | 17 | export function activate(context: vscode.ExtensionContext) { 18 | console.log('Loading extension "gmod-sdk"'); 19 | 20 | // DEPENDENCIES 21 | var addonManager = new GModAddonManager(vscode.workspace.rootPath); 22 | var weaponManager = new GModWeaponManager(vscode.workspace.rootPath, context.asAbsolutePath('resources/samples')); 23 | var workshopManager = new GModWorkshopManager(vscode.workspace.rootPath, context.asAbsolutePath('resources/samples')); 24 | var localGModManager = new LocalGModManager(vscode.workspace.rootPath); 25 | 26 | 27 | 28 | // SIDE BAR 29 | const gmodAddonInfoView = new GModAddonInfoView(addonManager); 30 | vscode.window.registerTreeDataProvider('gmodAddonInfo', gmodAddonInfoView); 31 | 32 | const gmodAddonWeaponView = new GModAddonWeaponsView(vscode.workspace.rootPath, weaponManager); 33 | vscode.window.registerTreeDataProvider('gmodAddonWeapons', gmodAddonWeaponView); 34 | 35 | const gmodWorkshopView = new GModWorkshopView(workshopManager); 36 | vscode.window.registerTreeDataProvider('gmodWorkshop', gmodWorkshopView); 37 | 38 | 39 | // COMMANDS 40 | let createAddonCommand = vscode.commands.registerCommand('gmodSDK.createAddon', () => { 41 | addonManager.create(); 42 | gmodAddonInfoView.refresh(); 43 | }); 44 | context.subscriptions.push(createAddonCommand); 45 | 46 | 47 | let copyToGModFolderCommand = vscode.commands.registerCommand('gmodSDK.copyToLocalGarrysmod', () => { 48 | if (vscode.workspace.rootPath == undefined) { 49 | vscode.window.showInformationMessage("Please open a folder.") 50 | return; 51 | } 52 | 53 | localGModManager.syncWorkspaceWithAddon(path.basename(vscode.workspace.rootPath)); 54 | }); 55 | context.subscriptions.push(copyToGModFolderCommand); 56 | 57 | 58 | let createWeaponCommand = vscode.commands.registerCommand('gmodSDK.createWeapon', () => { 59 | if (vscode.workspace.rootPath == undefined) { 60 | vscode.window.showInformationMessage("Please open a folder.") 61 | return; 62 | } 63 | 64 | new CreateWeaponWizard(weaponManager) 65 | .show() 66 | .then(() => { 67 | gmodAddonWeaponView.refresh(); 68 | }); 69 | }); 70 | context.subscriptions.push(createWeaponCommand); 71 | 72 | 73 | let uploadWorkshopCommand = vscode.commands.registerCommand('gmodSDK.uploadAddon', (targetWorkshopItem: GModWorkshopMenuItem | undefined) => { 74 | if (addonManager.getAddonInfo() == undefined) { 75 | vscode.window.showInformationMessage("addon.json missing. Please create an addon first.") 76 | return; 77 | } 78 | 79 | var targetWorkshopItemID = undefined; 80 | if (targetWorkshopItem != undefined && targetWorkshopItem.workshopFileId != undefined) { 81 | targetWorkshopItemID = targetWorkshopItem.workshopFileId; 82 | } 83 | 84 | new WorkshopUploadWizard(workshopManager).show(targetWorkshopItemID); 85 | }); 86 | context.subscriptions.push(uploadWorkshopCommand); 87 | 88 | 89 | let updateWorkshopThumbnailCommand = vscode.commands.registerCommand('gmodSDK.updateAddonThumbnail', (targetWorkshopItem: GModWorkshopMenuItem | undefined) => { 90 | var targetWorkshopItemID = undefined; 91 | if (targetWorkshopItem != undefined && targetWorkshopItem.workshopFileId != undefined) { 92 | targetWorkshopItemID = targetWorkshopItem.workshopFileId; 93 | } 94 | 95 | new WorkshopThumbnailWizard(workshopManager).show(targetWorkshopItemID); 96 | }); 97 | context.subscriptions.push(updateWorkshopThumbnailCommand); 98 | 99 | 100 | 101 | // INTERNAL COMMANDS 102 | let refreshAddonInfoCommand = vscode.commands.registerCommand('gmodAddonInfo.refresh', () => { 103 | gmodAddonInfoView.refresh(); 104 | gmodAddonWeaponView.refresh(); 105 | gmodWorkshopView.refresh(); 106 | }); 107 | context.subscriptions.push(refreshAddonInfoCommand); 108 | 109 | let setAddonTitle = vscode.commands.registerCommand('gmodAddonInfo.setTitle', () => { 110 | vscode.window.showInputBox({ prompt: "Enter the title of your addon. This is only used the first time you upload your addon. E.g. AK47 TTT weapon" }) 111 | .then(response => { 112 | if (response == undefined) 113 | return; 114 | 115 | var addonInfo = addonManager.getAddonInfo(); 116 | if (addonInfo == undefined) { 117 | vscode.window.showErrorMessage("addon.json is missing"); 118 | return; 119 | } 120 | 121 | addonInfo.title = response; 122 | addonManager.save(addonInfo); 123 | gmodAddonInfoView.refresh(); 124 | }) 125 | }); 126 | context.subscriptions.push(setAddonTitle); 127 | 128 | let setAddonDescription = vscode.commands.registerCommand('gmodAddonInfo.setDescription', () => { 129 | vscode.window.showInputBox({ prompt: "Enter the description of your addon. This is only used the first time you upload your addon." }) 130 | .then(response => { 131 | if (response == undefined) 132 | return; 133 | 134 | var addonInfo = addonManager.getAddonInfo(); 135 | if (addonInfo == undefined) { 136 | vscode.window.showErrorMessage("addon.json is missing"); 137 | return; 138 | } 139 | 140 | addonInfo.description = response; 141 | addonManager.save(addonInfo); 142 | gmodAddonInfoView.refresh(); 143 | }) 144 | }); 145 | context.subscriptions.push(setAddonDescription); 146 | 147 | let setAddonTypeCommand = vscode.commands.registerCommand('gmodAddonInfo.setType', () => { 148 | new AddonTypeWizard(addonManager) 149 | .show() 150 | .then(() => { gmodAddonInfoView.refresh(); }); 151 | }); 152 | context.subscriptions.push(setAddonTypeCommand); 153 | 154 | let setAddonTagsCommand = vscode.commands.registerCommand('gmodAddonInfo.setTags', () => { 155 | new AddonTagsWizard(addonManager) 156 | .show() 157 | .then(() => { gmodAddonInfoView.refresh(); }); 158 | }); 159 | context.subscriptions.push(setAddonTagsCommand); 160 | 161 | 162 | let editWeaponCommand = vscode.commands.registerCommand('gmodWeapon.edit', (item: GModWeaponMenuItem) => { 163 | if (item.weapon == undefined) return; 164 | weaponManager.editWeapon(item.weapon.pathToFile); 165 | }); 166 | context.subscriptions.push(editWeaponCommand); 167 | 168 | let selectSteamIDCommand = vscode.commands.registerCommand('gmodWorkshop.selectSteamID', () => { 169 | vscode.window.showInputBox({ prompt: "Enter your long Steam ID (SteamID64)" }) 170 | .then(response => { 171 | if (response == undefined) 172 | return; 173 | 174 | var steamID = response.trim(); 175 | if (isNaN(Number(steamID))) { 176 | vscode.window.showInformationMessage(`"${steamID}" is not a valid Steam ID. E.g. 70561198021123456`); 177 | return; 178 | } 179 | 180 | vscode.workspace.getConfiguration('gmod-sdk') 181 | .update("steamID", steamID, vscode.ConfigurationTarget.Global) 182 | .then(() => { 183 | setTimeout(() => { 184 | gmodWorkshopView.refresh(); 185 | }, 1000); 186 | }); 187 | }); 188 | }); 189 | context.subscriptions.push(selectSteamIDCommand); 190 | } 191 | 192 | 193 | export function deactivate() { } 194 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from 'vscode-test'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.equal(-1, [1, 2, 3].indexOf(5)); 13 | assert.equal(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | * This folder contains all of the files necessary for your extension. 6 | * `package.json` - this is the manifest file in which you declare your extension and command. 7 | * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 10 | * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. 11 | 12 | ## Get up and running straight away 13 | 14 | * Press `F5` to open a new window with your extension loaded. 15 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. 16 | * Set breakpoints in your code inside `src/extension.ts` to debug your extension. 17 | * Find output from your extension in the debug console. 18 | 19 | ## Make changes 20 | 21 | * You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. 22 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 23 | 24 | 25 | ## Explore the API 26 | 27 | * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. 28 | 29 | ## Run tests 30 | 31 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. 32 | * Press `F5` to run the tests in a new window with your extension loaded. 33 | * See the output of the test result in the debug console. 34 | * Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder. 35 | * The provided test runner will only consider files matching the name pattern `**.test.ts`. 36 | * You can create folders inside the `test` folder to structure your tests any way you want. 37 | 38 | ## Go further 39 | 40 | * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension). 41 | * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. 42 | * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). 43 | --------------------------------------------------------------------------------