├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── needs_more_info.yml └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .prettierrc.json ├── .vscode ├── launch.json └── tasks.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE.txt ├── README.md ├── documentation └── preview.gif ├── e2e ├── disabled-emmet-project-fixture │ ├── index.ts │ ├── main.ts │ └── tsconfig.json ├── package-lock.json ├── package.json ├── project-fixture │ ├── .vscode │ │ └── settings.json │ ├── index.ts │ ├── main.ts │ └── tsconfig.json ├── server-fixture │ └── index.js └── tests │ ├── _helpers.js │ ├── completionEntryDetails.js │ ├── completions.js │ ├── emmetCompletions.js │ ├── errors.js │ ├── outliningSpans.js │ └── quickFix.js ├── package-lock.json ├── package.json ├── src ├── _config.ts ├── _configuration.ts ├── _language-service.ts ├── _logger.ts ├── _plugin.ts ├── _substituter.ts ├── _virtual-document-provider.ts ├── api.ts ├── index.ts └── test │ └── substituter.test.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | trim_trailing_whitespace = true 7 | 8 | [{npm-shrinkwrap.json,package.json}] 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib 2 | e2e 3 | .eslintrc.js 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es6: true, 4 | node: true, 5 | }, 6 | extends: ['plugin:@typescript-eslint/recommended'], 7 | parser: '@typescript-eslint/parser', 8 | parserOptions: { 9 | project: 'tsconfig.json', 10 | sourceType: 'module', 11 | }, 12 | plugins: ['@typescript-eslint'], 13 | rules: { 14 | '@typescript-eslint/no-explicit-any': 'off', 15 | '@typescript-eslint/explicit-module-boundary-types': 'off', 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /.github/needs_more_info.yml: -------------------------------------------------------------------------------- 1 | { 2 | daysUntilClose: 7, 3 | needsMoreInfoLabel: 'needs more info', 4 | perform: true, 5 | closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity." 6 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | pull_request: 6 | branches: 7 | - main 8 | name: Continuous Integration 9 | 10 | jobs: 11 | lint: 12 | name: Lint 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | - name: Install Node.js 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: 14.x 21 | - run: npm install 22 | - name: Linting 23 | run: npm run lint 24 | compile: 25 | name: Compile 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v2 30 | - name: Install Node.js 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: 14.x 34 | - run: npm install 35 | - name: Compiling 36 | run: npm run compile 37 | unit: 38 | name: Unit Tests 39 | runs-on: ubuntu-latest 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v2 43 | - name: Install Node.js 44 | uses: actions/setup-node@v1 45 | with: 46 | node-version: 14.x 47 | - run: npm install 48 | - name: Compiling 49 | run: npm run compile 50 | - run: npm run unit 51 | e2e: 52 | name: E2E Tests 53 | runs-on: ubuntu-latest 54 | steps: 55 | - name: Checkout 56 | uses: actions/checkout@v2 57 | - name: Install Node.js 58 | uses: actions/setup-node@v1 59 | with: 60 | node-version: 14.x 61 | - run: npm install 62 | - run: npm install 63 | working-directory: ./e2e 64 | - name: Compiling 65 | run: npm run compile 66 | - run: npm run e2e 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log 3 | *.swp 4 | *.swo 5 | .DS_Store 6 | *.log 7 | log.txt 8 | 9 | test-workspace 10 | lib -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log 3 | *.swp 4 | *.swo 5 | .DS_Store 6 | *.map 7 | log.txt 8 | 9 | test-workspace 10 | e2e 11 | *.test.js -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "attach", 7 | "name": "Debug Styled Plugin", 8 | "skipFiles": ["/**"], 9 | "outFiles": ["${workspaceFolder}/lib/**/*.js"], 10 | "port": 9229, 11 | "preLaunchTask": "npm: watch:compile" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch:compile", 9 | "problemMatcher": [ 10 | { 11 | "base": "$tsc-watch", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": "Starting compilation in watch mode", 15 | "endsPattern": "Watching\\sfor\\sfile\\schanges\\." 16 | } 17 | } 18 | ], 19 | "isBackground": true 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.18.3 - April 4, 2023 4 | - Pick up latest `typescript-template-language-service-decorator` with fix for TS 5.0 5 | - Mark package as deprecated in favor of https://github.com/styled-components/typescript-styled-plugin. 6 | 7 | ## 0.18.2 - December 06, 2021 8 | - Include completion spans in responses. Thanks @jasonwilliams! 9 | - Don't trigger completions when opening template tags. Thanks @jasonwilliams! 10 | 11 | ## 0.18.1 - August 05, 2021 12 | - Update emmet. Thanks @jasonwilliams! 13 | 14 | ## 0.18.0 - June 03, 2021 15 | - Enable IntelliSense in `keyframes`. Thanks @jasonwilliams! 16 | 17 | ## 0.17.0 - May 07, 2021 18 | - Apply in `keyframes` by default. Thanks @jasonwilliams! 19 | 20 | ## 0.16.0 - April 28, 2021 21 | - Pick up new language service version. Thanks @hantatsang and @jasonwilliams! 22 | 23 | ## 0.15.0 - October 16, 2019 24 | - Pick up new language service version. Thanks @apust! 25 | 26 | ## 0.14.0 - February 26, 2019 27 | - Pick up new language service version. 28 | - Support for dynamically changing configuration. 29 | - Only enable plugin for TS 3.0+ in order to support automatically enabling plugin for workspace TS versions. 30 | 31 | ## 0.13.0 - November 8, 2018 32 | - Mark color completions with the `'color'` `kindModifier`. This allows editors to render the color previews inline. 33 | - Fix more false positive errors. 34 | 35 | ## 0.12.0 - October 15, 2018 36 | - Pick up new decorator library version to fix a possible state corruption error. 37 | 38 | ## 0.11.0 - September 11, 2018 39 | - Fixed some false positive errors when using a placeholder in a contexual selector. Thanks @lukyth! 40 | - Apply in `injectGlobal` or `createGlobalStyle` by default. Thanks @scf4! 41 | 42 | ## 0.10.0 - July 10, 2018 43 | - Add folding support. 44 | 45 | ## 0.9.2 - July 9, 2018 46 | - Remove TS as peerDep. 47 | 48 | ## 0.9.1 - July 9, 2018 49 | - Allow language service to be consumed by other libraries. 50 | 51 | ## 0.8.1 - July 2, 2018 52 | - Fix some false error reports around creative uses of placeholders. 53 | 54 | ## 0.8.0 - July 2, 2018 55 | - Support for emotion style typescript declarations. 56 | 57 | ## 0.7.0 - June 25, 2018 58 | - Picked up new CSS version. Brings improved suggestions and better documentation. 59 | 60 | ## 0.6.3 - April 20, 2018 61 | - Fixed `width: ${1}%;` incorrectly reported as an error. 62 | 63 | ## 0.6.2 - April 18, 2018 64 | - Fixed case where a placeholder that looked like a mixin was incorrectly reported as an error. 65 | 66 | ## 0.6.1 - April 16, 2018 67 | - Fixed some cases where placeholder usage was incorrectly reported as an error. 68 | 69 | ## 0.6.0 - February 16, 2018 70 | - Added emmet suggestions. Thanks @ramya-rao-a! 71 | 72 | ## 0.5.1 - February 13, 2018 73 | - Small fix for suggestions inside of nested selectors. 74 | 75 | ## 0.5.0 - February 12, 2018 76 | - Add quick fixes for misspelled property names. 77 | 78 | ## 0.4.1 - February 12, 2018 79 | - Fixed false error when placeholder is used as a selector. 80 | 81 | ## 0.4.0 - January 16, 2018 82 | - Fix suggestions inside of nested selectors. Thanks @aczekajski! 83 | 84 | ## 0.3.1 - January 11, 2018 85 | - Cache completion entries so we don't recompute them as often. 86 | 87 | ## 0.3.0 - January 9, 2018 88 | - Added basic support for completion entry details 89 | 90 | ## 0.2.2 - November 29, 2017 91 | - Fix auto import completions not showing up when using plugin with TS 2.6.2+ 92 | 93 | ## 0.2.1 - November 27, 2017 94 | - Fix cases where placeholder is followed by a trailing semicolon. Thanks @kingdaro! 95 | 96 | ## 0.2.0 - November 9, 2017 97 | - Do not take runtime dependecy on TypeScript. 98 | 99 | ## 0.1.2 — October 24, 2017 100 | - Fix bug that could cause errors not to be reported when on the last line of a block. 101 | 102 | ## 0.1.1 — October 24, 2017 103 | - Compile to ES5 to support regular Visual Studio 104 | 105 | ## 0.1.0 106 | - Support for nested classes. Thanks @asvetliakov! 107 | - Support for styled properties, such as `MyButton.extend...`. Thanks @asvetliakov! 108 | - Fix a bug that could cause errors to stop being reported. 109 | 110 | ### 0.0.5 - September 29, 2017 111 | - Fix empty value error being showing when using placeholder for value in multiline template strings. 112 | 113 | ### 0.0.4 - September 29, 2017 114 | - Fix multiline strings with placeholders. 115 | 116 | ### 0.0.3 - September 29, 2017 117 | - Initial support for strings with placeholders. 118 | 119 | ### 0.0.2 - September 29, 2017 120 | - Disable empty ruleset lint error by default 121 | - Fix styled completions showing on character immediately before start of string 122 | - Supprt `css` tag by default. 123 | - Remove a bunch of files from published npm package. 124 | 125 | ### 0.0.1 - September 28, 2017 126 | - Initial release -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) base project [2017] [Quramy], Modifications copyright Microsoft 2017 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 | # TypeScript Styled Plugin 2 | 3 | > ❗️ This package has been deprecated in favor of [Styled component's fork](https://github.com/styled-components/typescript-styled-plugin). 4 | 5 | TypeScript server plugin that adds intellisense to [styled component](https://styled-components.com) css strings 6 | 7 | ![](documentation/preview.gif) 8 | 9 | ![Build Status](https://github.com/microsoft/typescript-styled-plugin/actions/workflows/ci.yml/badge.svg) 10 | 11 | **Features** 12 | 13 | - IntelliSense for CSS property names and values. 14 | - Syntax error reporting. 15 | - Quick fixes for misspelled property names. 16 | 17 | ## Usage 18 | This plugin requires TypeScript 2.4 or later. It can provide intellisense in both JavaScript and TypeScript files within any editor that uses TypeScript to power their language features. This includes [VS Code](https://code.visualstudio.com), [Sublime with the TypeScript plugin](https://github.com/Microsoft/TypeScript-Sublime-Plugin), [Atom with the TypeScript plugin](https://atom.io/packages/atom-typescript), [Visual Studio](https://www.visualstudio.com), and others. 19 | 20 | ### With VS Code 21 | Just install the [VS Code Styled Components extension](https://github.com/styled-components/vscode-styled-components). This extension adds syntax highlighting and IntelliSense for styled components in JavaScript and TypeScript files. 22 | 23 | If you are using a [workspace version of TypeScript]((https://code.visualstudio.com/Docs/languages/typescript#_using-newer-typescript-versions)) however, you must manually install the plugin along side the version of TypeScript in your workspace: 24 | 25 | ```bash 26 | npm install --save-dev typescript-styled-plugin typescript 27 | ``` 28 | 29 | Then add a `plugins` section to your [`tsconfig.json`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) or [`jsconfig.json`](https://code.visualstudio.com/Docs/languages/javascript#_javascript-project-jsconfigjson) 30 | 31 | ```json 32 | { 33 | "compilerOptions": { 34 | "plugins": [ 35 | { 36 | "name": "typescript-styled-plugin" 37 | } 38 | ] 39 | } 40 | } 41 | ``` 42 | 43 | Finally, run the `Select TypeScript version` command in VS Code to switch to use the workspace version of TypeScript for VS Code's JavaScript and TypeScript language support. You can find more information about managing typescript versions [in the VS Code documentation](https://code.visualstudio.com/Docs/languages/typescript#_using-newer-typescript-versions). 44 | 45 | ### With Sublime 46 | This plugin works with the [Sublime TypeScript plugin](https://github.com/Microsoft/TypeScript-Sublime-Plugin). 47 | 48 | First install the plugin and a copy of TypeScript in your workspace: 49 | 50 | ```bash 51 | npm install --save-dev typescript-styled-plugin typescript 52 | ``` 53 | 54 | And configure Sublime to use the workspace version of TypeScript by [setting the `typescript_tsdk`](https://github.com/Microsoft/TypeScript-Sublime-Plugin#note-using-different-versions-of-typescript) setting in Sublime: 55 | 56 | ```json 57 | { 58 | "typescript_tsdk": "/Users/matb/my-amazing-project/node_modules/typescript/lib" 59 | } 60 | ``` 61 | 62 | Finally add a `plugins` section to your [`tsconfig.json`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) or [`jsconfig.json`](https://code.visualstudio.com/Docs/languages/javascript#_javascript-project-jsconfigjson) and restart Sublime. 63 | 64 | ```json 65 | { 66 | "compilerOptions": { 67 | "plugins": [ 68 | { 69 | "name": "typescript-styled-plugin" 70 | } 71 | ] 72 | } 73 | } 74 | ``` 75 | 76 | ### With Atom 77 | This plugin works with the [Atom TypeScript plugin](https://atom.io/packages/atom-typescript). 78 | 79 | First install the plugin and a copy of TypeScript in your workspace: 80 | 81 | ```bash 82 | npm install --save-dev typescript-styled-plugin typescript 83 | ``` 84 | 85 | Then add a `plugins` section to your [`tsconfig.json`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) or [`jsconfig.json`](https://code.visualstudio.com/Docs/languages/javascript#_javascript-project-jsconfigjson) and restart Atom. 86 | 87 | ```json 88 | { 89 | "compilerOptions": { 90 | "plugins": [ 91 | { 92 | "name": "typescript-styled-plugin" 93 | } 94 | ] 95 | } 96 | } 97 | ``` 98 | 99 | To get sytnax highlighting for styled strings in Atom, consider installing the [language-babel](https://atom.io/packages/language-babel) extension. 100 | 101 | 102 | ### With Visual Studio 103 | This plugin works [Visual Studio 2017](https://www.visualstudio.com) using the TypeScript 2.5+ SDK. 104 | 105 | First install the plugin in your project: 106 | 107 | ```bash 108 | npm install --save-dev typescript-styled-plugin 109 | ``` 110 | 111 | Then add a `plugins` section to your [`tsconfig.json`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html). 112 | 113 | ```json 114 | { 115 | "compilerOptions": { 116 | "plugins": [ 117 | { 118 | "name": "typescript-styled-plugin" 119 | } 120 | ] 121 | } 122 | } 123 | ``` 124 | 125 | Then reload your project to make sure the plugin has been loaded properly. Note that `jsconfig.json` projects are currently not supported in VS. 126 | 127 | 128 | ## Configuration 129 | 130 | ### Tags 131 | This plugin adds styled component IntelliSense to any template literal [tagged](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) with `styled`, `css`, `injectGlobal`, `keyframes` or `createGlobalStyle`: 132 | 133 | ```js 134 | import styled from 'styled-components' 135 | 136 | styled.button` 137 | color: blue; 138 | ` 139 | ``` 140 | 141 | You can enable IntelliSense for other tag names by configuring `"tags"`: 142 | 143 | ```json 144 | { 145 | "compilerOptions": { 146 | "plugins": [ 147 | { 148 | "name": "typescript-styled-plugin", 149 | "tags": [ 150 | "styled", 151 | "css", 152 | "sty" 153 | ] 154 | } 155 | ] 156 | } 157 | } 158 | ``` 159 | 160 | Now strings tagged with either `styled`, `css`, or `sty` will have styled component IntelliSense: 161 | 162 | ```js 163 | import sty from 'styled-components' 164 | 165 | sty.button` 166 | color: blue; 167 | ` 168 | ``` 169 | 170 | Tags also apply to methods on styled components. This is enabled for `extend` by default: 171 | 172 | ```js 173 | import sty from 'styled-components' 174 | 175 | const BlueButton = sty.button` 176 | color: blue; 177 | ` 178 | 179 | const MyFancyBlueButton = BlueButton.extend` 180 | border: 10px solid hotpink; 181 | ` 182 | ``` 183 | 184 | ### Linting 185 | 186 | To disable error reporting, set `"validate": false` in the plugin configuration: 187 | 188 | ```json 189 | { 190 | "compilerOptions": { 191 | "plugins": [ 192 | { 193 | "name": "typescript-styled-plugin", 194 | "validate": false 195 | } 196 | ] 197 | } 198 | } 199 | ``` 200 | 201 | You can also configure how errors are reported using linter settings. 202 | 203 | ```json 204 | { 205 | "compilerOptions": { 206 | "plugins": [ 207 | { 208 | "name": "typescript-styled-plugin", 209 | "lint": { 210 | "vendorPrefix": "error", 211 | "zeroUnits": "ignore" 212 | } 213 | } 214 | ] 215 | } 216 | } 217 | ``` 218 | 219 | The following lint options are supported: 220 | 221 | #### validProperties 222 | ``` 223 | ["property1", "property2", ....] 224 | ``` 225 | 226 | List of properties that are treated as valid. 227 | 228 | #### unknownProperties 229 | ``` 230 | "ignore" | "warning" | "error" 231 | ``` 232 | 233 | Should unknown properties show an error or warning? Default is `"warning"`. 234 | 235 | #### compatibleVendorPrefixes 236 | ``` 237 | "ignore" | "warning" | "error" 238 | ``` 239 | 240 | When using a vendor-specific prefix make sure to also include all other vendor-specific properties. Default is `"ignore"`. 241 | 242 | #### vendorPrefix 243 | ``` 244 | "ignore" | "warning" | "error" 245 | ``` 246 | 247 | When using a vendor-specific prefix also include the standard property. Default is `"warning"`. 248 | 249 | #### duplicateProperties 250 | ``` 251 | "ignore" | "warning" | "error" 252 | ``` 253 | 254 | Do not use duplicate style definitions. Default is `"ignore"`. 255 | 256 | #### emptyRules 257 | ``` 258 | "ignore" | "warning" | "error" 259 | ``` 260 | 261 | Do not use empty rulesets. Default is `"ignore"`. 262 | 263 | #### importStatement 264 | ``` 265 | "ignore" | "warning" | "error" 266 | ``` 267 | 268 | Import statements do not load in parallel. Default is `"ignore"`. 269 | 270 | #### boxModel 271 | ``` 272 | "ignore" | "warning" | "error" 273 | ``` 274 | 275 | Do not use width or height when using padding or border. Default is `"ignore"`. 276 | 277 | #### universalSelector 278 | ``` 279 | "ignore" | "warning" | "error" 280 | ``` 281 | 282 | The universal selector (*) is known to be slow. Default is `"ignore"`. 283 | 284 | #### zeroUnits 285 | ``` 286 | "ignore" | "warning" | "error" 287 | ``` 288 | 289 | No unit for zero needed. Default is `"ignore"`. 290 | 291 | #### fontFaceProperties 292 | ``` 293 | "ignore" | "warning" | "error" 294 | ``` 295 | 296 | @font-face rule must define 'src' and 'font-family' properties. Default is `"warning"`. 297 | 298 | #### hexColorLength 299 | ``` 300 | "ignore" | "warning" | "error" 301 | ``` 302 | 303 | Hex colors must consist of three or six hex numbers. Default is `"error"`. 304 | 305 | #### argumentsInColorFunction 306 | ``` 307 | "ignore" | "warning" | "error" 308 | ``` 309 | 310 | Invalid number of parameters. Default is `"error"`. 311 | 312 | #### ieHack 313 | ``` 314 | "ignore" | "warning" | "error" 315 | ``` 316 | 317 | IE hacks are only necessary when supporting IE7 and older. Default is `"ignore"`. 318 | 319 | #### unknownVendorSpecificProperties 320 | ``` 321 | "ignore" | "warning" | "error" 322 | ``` 323 | 324 | Unknown vendor specific property. Default is `"ignore"`. 325 | 326 | #### propertyIgnoredDueToDisplay 327 | ``` 328 | "ignore" | "warning" | "error" 329 | ``` 330 | 331 | Property is ignored due to the display. E.g. with 'display: inline', the width, height, margin-top, margin-bottom, and float properties have no effect. Default is `"warning"` 332 | 333 | #### important 334 | ``` 335 | "ignore" | "warning" | "error" 336 | ``` 337 | 338 | Avoid using !important. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored. Default is `"ignore"`. 339 | 340 | #### float 341 | ``` 342 | "ignore" | "warning" | "error" 343 | ``` 344 | 345 | Avoid using 'float'. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes. Default is `"ignore"`. 346 | 347 | #### idSelector 348 | ``` 349 | "ignore" | "warning" | "error" 350 | ``` 351 | 352 | Selectors should not contain IDs because these rules are too tightly coupled with the HTML. Default is `"ignore"`. 353 | 354 | ### Emmet in completion list 355 | 356 | You can now see your Emmet abbreviations expanded and included in the completion list. 357 | An [upstream issue](https://github.com/Microsoft/TypeScript/issues/21999) with typescript blocks the Emmet entry in the completion list to get updated as you type. 358 | So for now you will have to press `Ctrl+Space` after typing out the abbreviation. 359 | 360 | The below settings which are in sync with general Emmet settings in VS Code control the expanded Emmet abbreviations in the auto-completion list. 361 | 362 | #### showExpandedAbbreviation 363 | ``` 364 | "always" | "never" 365 | ``` 366 | 367 | Controls whether or not expanded Emmet abbreviations should show up in the completion list 368 | 369 | #### showSuggestionsAsSnippets 370 | ``` 371 | `true` | `false` 372 | ``` 373 | 374 | If true, then Emmet suggestions will show up as snippets allowing you to order them as per editor.snippetSuggestions setting. 375 | 376 | #### preferences 377 | 378 | Preferences used to modify behavior of some actions and resolvers of Emmet. 379 | 380 | 381 | ## Contributing 382 | 383 | To build the typescript-styled-plugin, you'll need [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/). 384 | 385 | First, [fork](https://help.github.com/articles/fork-a-repo/) the typescript-styled-plugin repo and clone your fork: 386 | 387 | ```bash 388 | git clone https://github.com/YOUR_GITHUB_ACCOUNT_NAME/typescript-styled-plugin.git 389 | cd typescript-styled-plugin 390 | ``` 391 | 392 | Then install dev dependencies: 393 | 394 | ```bash 395 | npm install 396 | ``` 397 | 398 | The plugin is written in [TypeScript](http://www.typescriptlang.org). The source code is in the `src/` directory with the compiled JavaScript output to the `lib/` directory. Kick off a build using the `compile` script: 399 | 400 | ```bash 401 | npm run compile 402 | ``` 403 | 404 | switch to `e2` to install or update test dependencies: 405 | 406 | ```bash 407 | (cd e2e && npm install) 408 | ``` 409 | 410 | and then navigate back to the project root and run the end to end tests with the `e2e` script: 411 | 412 | ```bash 413 | cd .. 414 | npm run e2e 415 | ``` 416 | 417 | You can submit bug fixes and features through [pull requests](https://help.github.com/articles/about-pull-requests/). To get started, first checkout a new feature branch on your local repo: 418 | 419 | ```bash 420 | git checkout -b my-awesome-new-feature-branch 421 | ``` 422 | 423 | Make the desired code changes, commit them, and then push the changes up to your forked repository: 424 | 425 | ```bash 426 | git push origin my-awesome-new-feature-branch 427 | ``` 428 | 429 | Then [submit a pull request](https://help.github.com/articles/creating-a-pull-request/ 430 | ) against the Microsoft typescript-styled-plugin repository. 431 | 432 | Please also see our [Code of Conduct](CODE_OF_CONDUCT.md). 433 | 434 | 435 | ## Credits 436 | 437 | Code originally forked from: https://github.com/Quramy/ts-graphql-plugin -------------------------------------------------------------------------------- /documentation/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/typescript-styled-plugin/3409ef468b309505b6e18d90211034f8ebaaf6fe/documentation/preview.gif -------------------------------------------------------------------------------- /e2e/disabled-emmet-project-fixture/index.ts: -------------------------------------------------------------------------------- 1 | css` 2 | 3 | ` -------------------------------------------------------------------------------- /e2e/disabled-emmet-project-fixture/main.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/typescript-styled-plugin/3409ef468b309505b6e18d90211034f8ebaaf6fe/e2e/disabled-emmet-project-fixture/main.ts -------------------------------------------------------------------------------- /e2e/disabled-emmet-project-fixture/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "commonjs", 5 | "plugins": [ 6 | { "name": "typescript-styled-plugin", "tags": ["css"], "emmet": { "showExpandedAbbreviation": "never"} } 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /e2e/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e2e", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "typescript": { 8 | "version": "4.2.4", 9 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", 10 | "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==" 11 | }, 12 | "typescript-styled-plugin": { 13 | "version": "file:..", 14 | "requires": { 15 | "typescript-template-language-service-decorator": "^2.2.0", 16 | "vscode-css-languageservice": "^5.1.1", 17 | "vscode-emmet-helper": "1.2.11", 18 | "vscode-languageserver-types": "^3.16.0" 19 | }, 20 | "dependencies": { 21 | "@babel/helper-validator-identifier": { 22 | "version": "7.12.11", 23 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", 24 | "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" 25 | }, 26 | "@babel/highlight": { 27 | "version": "7.12.13", 28 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", 29 | "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", 30 | "requires": { 31 | "@babel/helper-validator-identifier": "^7.12.11", 32 | "chalk": "^2.0.0", 33 | "js-tokens": "^4.0.0" 34 | }, 35 | "dependencies": { 36 | "ansi-styles": { 37 | "version": "3.2.1", 38 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 39 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 40 | "requires": { 41 | "color-convert": "^1.9.0" 42 | } 43 | }, 44 | "chalk": { 45 | "version": "2.4.2", 46 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 47 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 48 | "requires": { 49 | "ansi-styles": "^3.2.1", 50 | "escape-string-regexp": "^1.0.5", 51 | "supports-color": "^5.3.0" 52 | } 53 | }, 54 | "color-convert": { 55 | "version": "1.9.3", 56 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 57 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 58 | "requires": { 59 | "color-name": "1.1.3" 60 | } 61 | }, 62 | "color-name": { 63 | "version": "1.1.3", 64 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 65 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 66 | }, 67 | "escape-string-regexp": { 68 | "version": "1.0.5", 69 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 70 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 71 | }, 72 | "has-flag": { 73 | "version": "3.0.0", 74 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 75 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 76 | }, 77 | "supports-color": { 78 | "version": "5.5.0", 79 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 80 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 81 | "requires": { 82 | "has-flag": "^3.0.0" 83 | } 84 | } 85 | } 86 | }, 87 | "@emmetio/extract-abbreviation": { 88 | "version": "0.1.6", 89 | "resolved": "https://registry.npmjs.org/@emmetio/extract-abbreviation/-/extract-abbreviation-0.1.6.tgz", 90 | "integrity": "sha512-Ce3xE2JvTSEbASFbRbA1gAIcMcZWdS2yUYRaQbeM0nbOzaZrUYfa3ePtcriYRZOZmr+CkKA+zbjhvTpIOAYVcw==" 91 | }, 92 | "@eslint/eslintrc": { 93 | "version": "0.4.0", 94 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", 95 | "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", 96 | "requires": { 97 | "ajv": "^6.12.4", 98 | "debug": "^4.1.1", 99 | "espree": "^7.3.0", 100 | "globals": "^12.1.0", 101 | "ignore": "^4.0.6", 102 | "import-fresh": "^3.2.1", 103 | "js-yaml": "^3.13.1", 104 | "minimatch": "^3.0.4", 105 | "strip-json-comments": "^3.1.1" 106 | }, 107 | "dependencies": { 108 | "argparse": { 109 | "version": "1.0.10", 110 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 111 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 112 | "requires": { 113 | "sprintf-js": "~1.0.2" 114 | } 115 | }, 116 | "globals": { 117 | "version": "12.4.0", 118 | "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", 119 | "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", 120 | "requires": { 121 | "type-fest": "^0.8.1" 122 | } 123 | }, 124 | "ignore": { 125 | "version": "4.0.6", 126 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 127 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" 128 | }, 129 | "js-yaml": { 130 | "version": "3.14.1", 131 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 132 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 133 | "requires": { 134 | "argparse": "^1.0.7", 135 | "esprima": "^4.0.0" 136 | } 137 | } 138 | } 139 | }, 140 | "@nodelib/fs.scandir": { 141 | "version": "2.1.4", 142 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", 143 | "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", 144 | "requires": { 145 | "@nodelib/fs.stat": "2.0.4", 146 | "run-parallel": "^1.1.9" 147 | } 148 | }, 149 | "@nodelib/fs.stat": { 150 | "version": "2.0.4", 151 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", 152 | "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==" 153 | }, 154 | "@nodelib/fs.walk": { 155 | "version": "1.2.6", 156 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", 157 | "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", 158 | "requires": { 159 | "@nodelib/fs.scandir": "2.1.4", 160 | "fastq": "^1.6.0" 161 | } 162 | }, 163 | "@types/chai": { 164 | "version": "4.2.3", 165 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.3.tgz", 166 | "integrity": "sha512-VRw2xEGbll3ZiTQ4J02/hUjNqZoue1bMhoo2dgM2LXjDdyaq4q80HgBDHwpI0/VKlo4Eg+BavyQMv/NYgTetzA==" 167 | }, 168 | "@types/json-schema": { 169 | "version": "7.0.7", 170 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", 171 | "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" 172 | }, 173 | "@types/mocha": { 174 | "version": "5.2.7", 175 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", 176 | "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==" 177 | }, 178 | "@types/node": { 179 | "version": "7.10.7", 180 | "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.7.tgz", 181 | "integrity": "sha512-4I7+hXKyq7e1deuzX9udu0hPIYqSSkdKXtjow6fMnQ3OR9qkxIErGHbGY08YrfZJrCS1ajK8lOuzd0k3n2WM4A==" 182 | }, 183 | "@typescript-eslint/eslint-plugin": { 184 | "version": "4.22.0", 185 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz", 186 | "integrity": "sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==", 187 | "requires": { 188 | "@typescript-eslint/experimental-utils": "4.22.0", 189 | "@typescript-eslint/scope-manager": "4.22.0", 190 | "debug": "^4.1.1", 191 | "functional-red-black-tree": "^1.0.1", 192 | "lodash": "^4.17.15", 193 | "regexpp": "^3.0.0", 194 | "semver": "^7.3.2", 195 | "tsutils": "^3.17.1" 196 | }, 197 | "dependencies": { 198 | "semver": { 199 | "version": "7.3.5", 200 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 201 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 202 | "requires": { 203 | "lru-cache": "^6.0.0" 204 | } 205 | }, 206 | "tsutils": { 207 | "version": "3.21.0", 208 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 209 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 210 | "requires": { 211 | "tslib": "^1.8.1" 212 | } 213 | } 214 | } 215 | }, 216 | "@typescript-eslint/experimental-utils": { 217 | "version": "4.22.0", 218 | "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz", 219 | "integrity": "sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==", 220 | "requires": { 221 | "@types/json-schema": "^7.0.3", 222 | "@typescript-eslint/scope-manager": "4.22.0", 223 | "@typescript-eslint/types": "4.22.0", 224 | "@typescript-eslint/typescript-estree": "4.22.0", 225 | "eslint-scope": "^5.0.0", 226 | "eslint-utils": "^2.0.0" 227 | } 228 | }, 229 | "@typescript-eslint/parser": { 230 | "version": "4.22.0", 231 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.0.tgz", 232 | "integrity": "sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==", 233 | "requires": { 234 | "@typescript-eslint/scope-manager": "4.22.0", 235 | "@typescript-eslint/types": "4.22.0", 236 | "@typescript-eslint/typescript-estree": "4.22.0", 237 | "debug": "^4.1.1" 238 | } 239 | }, 240 | "@typescript-eslint/scope-manager": { 241 | "version": "4.22.0", 242 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz", 243 | "integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==", 244 | "requires": { 245 | "@typescript-eslint/types": "4.22.0", 246 | "@typescript-eslint/visitor-keys": "4.22.0" 247 | } 248 | }, 249 | "@typescript-eslint/types": { 250 | "version": "4.22.0", 251 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz", 252 | "integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==" 253 | }, 254 | "@typescript-eslint/typescript-estree": { 255 | "version": "4.22.0", 256 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz", 257 | "integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==", 258 | "requires": { 259 | "@typescript-eslint/types": "4.22.0", 260 | "@typescript-eslint/visitor-keys": "4.22.0", 261 | "debug": "^4.1.1", 262 | "globby": "^11.0.1", 263 | "is-glob": "^4.0.1", 264 | "semver": "^7.3.2", 265 | "tsutils": "^3.17.1" 266 | }, 267 | "dependencies": { 268 | "semver": { 269 | "version": "7.3.5", 270 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 271 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 272 | "requires": { 273 | "lru-cache": "^6.0.0" 274 | } 275 | }, 276 | "tsutils": { 277 | "version": "3.21.0", 278 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 279 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 280 | "requires": { 281 | "tslib": "^1.8.1" 282 | } 283 | } 284 | } 285 | }, 286 | "@typescript-eslint/visitor-keys": { 287 | "version": "4.22.0", 288 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz", 289 | "integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==", 290 | "requires": { 291 | "@typescript-eslint/types": "4.22.0", 292 | "eslint-visitor-keys": "^2.0.0" 293 | } 294 | }, 295 | "@ungap/promise-all-settled": { 296 | "version": "1.1.2", 297 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 298 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==" 299 | }, 300 | "acorn": { 301 | "version": "7.4.1", 302 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 303 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" 304 | }, 305 | "acorn-jsx": { 306 | "version": "5.3.1", 307 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", 308 | "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" 309 | }, 310 | "ajv": { 311 | "version": "6.12.6", 312 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 313 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 314 | "requires": { 315 | "fast-deep-equal": "^3.1.1", 316 | "fast-json-stable-stringify": "^2.0.0", 317 | "json-schema-traverse": "^0.4.1", 318 | "uri-js": "^4.2.2" 319 | } 320 | }, 321 | "ansi-colors": { 322 | "version": "4.1.1", 323 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 324 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" 325 | }, 326 | "ansi-regex": { 327 | "version": "3.0.0", 328 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 329 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 330 | }, 331 | "ansi-styles": { 332 | "version": "4.3.0", 333 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 334 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 335 | "requires": { 336 | "color-convert": "^2.0.1" 337 | } 338 | }, 339 | "anymatch": { 340 | "version": "3.1.1", 341 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 342 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 343 | "requires": { 344 | "normalize-path": "^3.0.0", 345 | "picomatch": "^2.0.4" 346 | } 347 | }, 348 | "argparse": { 349 | "version": "2.0.1", 350 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 351 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 352 | }, 353 | "array-union": { 354 | "version": "2.1.0", 355 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 356 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" 357 | }, 358 | "assertion-error": { 359 | "version": "1.1.0", 360 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 361 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" 362 | }, 363 | "astral-regex": { 364 | "version": "2.0.0", 365 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", 366 | "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" 367 | }, 368 | "balanced-match": { 369 | "version": "1.0.0", 370 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 371 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 372 | }, 373 | "binary-extensions": { 374 | "version": "2.2.0", 375 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 376 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 377 | }, 378 | "brace-expansion": { 379 | "version": "1.1.11", 380 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 381 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 382 | "requires": { 383 | "balanced-match": "^1.0.0", 384 | "concat-map": "0.0.1" 385 | } 386 | }, 387 | "braces": { 388 | "version": "3.0.2", 389 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 390 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 391 | "requires": { 392 | "fill-range": "^7.0.1" 393 | } 394 | }, 395 | "browser-stdout": { 396 | "version": "1.3.1", 397 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 398 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" 399 | }, 400 | "callsites": { 401 | "version": "3.1.0", 402 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 403 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" 404 | }, 405 | "camelcase": { 406 | "version": "6.2.0", 407 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", 408 | "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==" 409 | }, 410 | "chai": { 411 | "version": "4.2.0", 412 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 413 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 414 | "requires": { 415 | "assertion-error": "^1.1.0", 416 | "check-error": "^1.0.2", 417 | "deep-eql": "^3.0.1", 418 | "get-func-name": "^2.0.0", 419 | "pathval": "^1.1.0", 420 | "type-detect": "^4.0.5" 421 | } 422 | }, 423 | "chalk": { 424 | "version": "4.1.0", 425 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 426 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 427 | "requires": { 428 | "ansi-styles": "^4.1.0", 429 | "supports-color": "^7.1.0" 430 | }, 431 | "dependencies": { 432 | "supports-color": { 433 | "version": "7.2.0", 434 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 435 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 436 | "requires": { 437 | "has-flag": "^4.0.0" 438 | } 439 | } 440 | } 441 | }, 442 | "check-error": { 443 | "version": "1.0.2", 444 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 445 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" 446 | }, 447 | "chokidar": { 448 | "version": "3.5.1", 449 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", 450 | "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", 451 | "requires": { 452 | "anymatch": "~3.1.1", 453 | "braces": "~3.0.2", 454 | "glob-parent": "~5.1.0", 455 | "is-binary-path": "~2.1.0", 456 | "is-glob": "~4.0.1", 457 | "normalize-path": "~3.0.0", 458 | "readdirp": "~3.5.0" 459 | } 460 | }, 461 | "cliui": { 462 | "version": "7.0.4", 463 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 464 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 465 | "requires": { 466 | "string-width": "^4.2.0", 467 | "strip-ansi": "^6.0.0", 468 | "wrap-ansi": "^7.0.0" 469 | }, 470 | "dependencies": { 471 | "ansi-regex": { 472 | "version": "5.0.0", 473 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 474 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 475 | }, 476 | "is-fullwidth-code-point": { 477 | "version": "3.0.0", 478 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 479 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 480 | }, 481 | "string-width": { 482 | "version": "4.2.0", 483 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 484 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 485 | "requires": { 486 | "emoji-regex": "^8.0.0", 487 | "is-fullwidth-code-point": "^3.0.0", 488 | "strip-ansi": "^6.0.0" 489 | } 490 | }, 491 | "strip-ansi": { 492 | "version": "6.0.0", 493 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 494 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 495 | "requires": { 496 | "ansi-regex": "^5.0.0" 497 | } 498 | } 499 | } 500 | }, 501 | "color-convert": { 502 | "version": "2.0.1", 503 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 504 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 505 | "requires": { 506 | "color-name": "~1.1.4" 507 | } 508 | }, 509 | "color-name": { 510 | "version": "1.1.4", 511 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 512 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 513 | }, 514 | "concat-map": { 515 | "version": "0.0.1", 516 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 517 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 518 | }, 519 | "cross-spawn": { 520 | "version": "7.0.3", 521 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 522 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 523 | "requires": { 524 | "path-key": "^3.1.0", 525 | "shebang-command": "^2.0.0", 526 | "which": "^2.0.1" 527 | } 528 | }, 529 | "debug": { 530 | "version": "4.3.1", 531 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 532 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 533 | "requires": { 534 | "ms": "2.1.2" 535 | }, 536 | "dependencies": { 537 | "ms": { 538 | "version": "2.1.2", 539 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 540 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 541 | } 542 | } 543 | }, 544 | "decamelize": { 545 | "version": "4.0.0", 546 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 547 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==" 548 | }, 549 | "deep-eql": { 550 | "version": "3.0.1", 551 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 552 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 553 | "requires": { 554 | "type-detect": "^4.0.0" 555 | } 556 | }, 557 | "deep-is": { 558 | "version": "0.1.3", 559 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 560 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" 561 | }, 562 | "diff": { 563 | "version": "5.0.0", 564 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 565 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" 566 | }, 567 | "dir-glob": { 568 | "version": "3.0.1", 569 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 570 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 571 | "requires": { 572 | "path-type": "^4.0.0" 573 | } 574 | }, 575 | "doctrine": { 576 | "version": "3.0.0", 577 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 578 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 579 | "requires": { 580 | "esutils": "^2.0.2" 581 | } 582 | }, 583 | "emoji-regex": { 584 | "version": "8.0.0", 585 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 586 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 587 | }, 588 | "enquirer": { 589 | "version": "2.3.6", 590 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 591 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 592 | "requires": { 593 | "ansi-colors": "^4.1.1" 594 | } 595 | }, 596 | "escalade": { 597 | "version": "3.1.1", 598 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 599 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" 600 | }, 601 | "escape-string-regexp": { 602 | "version": "4.0.0", 603 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 604 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" 605 | }, 606 | "eslint": { 607 | "version": "7.25.0", 608 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz", 609 | "integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==", 610 | "requires": { 611 | "@babel/code-frame": "7.12.11", 612 | "@eslint/eslintrc": "^0.4.0", 613 | "ajv": "^6.10.0", 614 | "chalk": "^4.0.0", 615 | "cross-spawn": "^7.0.2", 616 | "debug": "^4.0.1", 617 | "doctrine": "^3.0.0", 618 | "enquirer": "^2.3.5", 619 | "eslint-scope": "^5.1.1", 620 | "eslint-utils": "^2.1.0", 621 | "eslint-visitor-keys": "^2.0.0", 622 | "espree": "^7.3.1", 623 | "esquery": "^1.4.0", 624 | "esutils": "^2.0.2", 625 | "file-entry-cache": "^6.0.1", 626 | "functional-red-black-tree": "^1.0.1", 627 | "glob-parent": "^5.0.0", 628 | "globals": "^13.6.0", 629 | "ignore": "^4.0.6", 630 | "import-fresh": "^3.0.0", 631 | "imurmurhash": "^0.1.4", 632 | "is-glob": "^4.0.0", 633 | "js-yaml": "^3.13.1", 634 | "json-stable-stringify-without-jsonify": "^1.0.1", 635 | "levn": "^0.4.1", 636 | "lodash": "^4.17.21", 637 | "minimatch": "^3.0.4", 638 | "natural-compare": "^1.4.0", 639 | "optionator": "^0.9.1", 640 | "progress": "^2.0.0", 641 | "regexpp": "^3.1.0", 642 | "semver": "^7.2.1", 643 | "strip-ansi": "^6.0.0", 644 | "strip-json-comments": "^3.1.0", 645 | "table": "^6.0.4", 646 | "text-table": "^0.2.0", 647 | "v8-compile-cache": "^2.0.3" 648 | }, 649 | "dependencies": { 650 | "@babel/code-frame": { 651 | "version": "7.12.11", 652 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", 653 | "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", 654 | "requires": { 655 | "@babel/highlight": "^7.10.4" 656 | } 657 | }, 658 | "ansi-regex": { 659 | "version": "5.0.0", 660 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 661 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 662 | }, 663 | "argparse": { 664 | "version": "1.0.10", 665 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 666 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 667 | "requires": { 668 | "sprintf-js": "~1.0.2" 669 | } 670 | }, 671 | "ignore": { 672 | "version": "4.0.6", 673 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 674 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" 675 | }, 676 | "js-yaml": { 677 | "version": "3.14.1", 678 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 679 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 680 | "requires": { 681 | "argparse": "^1.0.7", 682 | "esprima": "^4.0.0" 683 | } 684 | }, 685 | "semver": { 686 | "version": "7.3.5", 687 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 688 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 689 | "requires": { 690 | "lru-cache": "^6.0.0" 691 | } 692 | }, 693 | "strip-ansi": { 694 | "version": "6.0.0", 695 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 696 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 697 | "requires": { 698 | "ansi-regex": "^5.0.0" 699 | } 700 | } 701 | } 702 | }, 703 | "eslint-plugin-prettier": { 704 | "version": "3.4.0", 705 | "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz", 706 | "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==", 707 | "requires": { 708 | "prettier-linter-helpers": "^1.0.0" 709 | } 710 | }, 711 | "eslint-scope": { 712 | "version": "5.1.1", 713 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 714 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 715 | "requires": { 716 | "esrecurse": "^4.3.0", 717 | "estraverse": "^4.1.1" 718 | } 719 | }, 720 | "eslint-utils": { 721 | "version": "2.1.0", 722 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 723 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 724 | "requires": { 725 | "eslint-visitor-keys": "^1.1.0" 726 | }, 727 | "dependencies": { 728 | "eslint-visitor-keys": { 729 | "version": "1.3.0", 730 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 731 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" 732 | } 733 | } 734 | }, 735 | "eslint-visitor-keys": { 736 | "version": "2.0.0", 737 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", 738 | "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==" 739 | }, 740 | "espree": { 741 | "version": "7.3.1", 742 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", 743 | "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", 744 | "requires": { 745 | "acorn": "^7.4.0", 746 | "acorn-jsx": "^5.3.1", 747 | "eslint-visitor-keys": "^1.3.0" 748 | }, 749 | "dependencies": { 750 | "eslint-visitor-keys": { 751 | "version": "1.3.0", 752 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 753 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" 754 | } 755 | } 756 | }, 757 | "esprima": { 758 | "version": "4.0.1", 759 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 760 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 761 | }, 762 | "esquery": { 763 | "version": "1.4.0", 764 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 765 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 766 | "requires": { 767 | "estraverse": "^5.1.0" 768 | }, 769 | "dependencies": { 770 | "estraverse": { 771 | "version": "5.2.0", 772 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 773 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" 774 | } 775 | } 776 | }, 777 | "esrecurse": { 778 | "version": "4.3.0", 779 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 780 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 781 | "requires": { 782 | "estraverse": "^5.2.0" 783 | }, 784 | "dependencies": { 785 | "estraverse": { 786 | "version": "5.2.0", 787 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 788 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" 789 | } 790 | } 791 | }, 792 | "estraverse": { 793 | "version": "4.3.0", 794 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 795 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" 796 | }, 797 | "esutils": { 798 | "version": "2.0.3", 799 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 800 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 801 | }, 802 | "fast-deep-equal": { 803 | "version": "3.1.3", 804 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 805 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 806 | }, 807 | "fast-diff": { 808 | "version": "1.2.0", 809 | "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", 810 | "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" 811 | }, 812 | "fast-glob": { 813 | "version": "3.2.5", 814 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", 815 | "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", 816 | "requires": { 817 | "@nodelib/fs.stat": "^2.0.2", 818 | "@nodelib/fs.walk": "^1.2.3", 819 | "glob-parent": "^5.1.0", 820 | "merge2": "^1.3.0", 821 | "micromatch": "^4.0.2", 822 | "picomatch": "^2.2.1" 823 | } 824 | }, 825 | "fast-json-stable-stringify": { 826 | "version": "2.1.0", 827 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 828 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 829 | }, 830 | "fast-levenshtein": { 831 | "version": "2.0.6", 832 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 833 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" 834 | }, 835 | "fastq": { 836 | "version": "1.11.0", 837 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", 838 | "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", 839 | "requires": { 840 | "reusify": "^1.0.4" 841 | } 842 | }, 843 | "file-entry-cache": { 844 | "version": "6.0.1", 845 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 846 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 847 | "requires": { 848 | "flat-cache": "^3.0.4" 849 | } 850 | }, 851 | "fill-range": { 852 | "version": "7.0.1", 853 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 854 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 855 | "requires": { 856 | "to-regex-range": "^5.0.1" 857 | } 858 | }, 859 | "find-up": { 860 | "version": "5.0.0", 861 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 862 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 863 | "requires": { 864 | "locate-path": "^6.0.0", 865 | "path-exists": "^4.0.0" 866 | } 867 | }, 868 | "flat": { 869 | "version": "5.0.2", 870 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 871 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" 872 | }, 873 | "flat-cache": { 874 | "version": "3.0.4", 875 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 876 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 877 | "requires": { 878 | "flatted": "^3.1.0", 879 | "rimraf": "^3.0.2" 880 | } 881 | }, 882 | "flatted": { 883 | "version": "3.1.1", 884 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", 885 | "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" 886 | }, 887 | "fs.realpath": { 888 | "version": "1.0.0", 889 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 890 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 891 | }, 892 | "functional-red-black-tree": { 893 | "version": "1.0.1", 894 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 895 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" 896 | }, 897 | "get-caller-file": { 898 | "version": "2.0.5", 899 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 900 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 901 | }, 902 | "get-func-name": { 903 | "version": "2.0.0", 904 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 905 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" 906 | }, 907 | "glob": { 908 | "version": "7.1.4", 909 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 910 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 911 | "requires": { 912 | "fs.realpath": "^1.0.0", 913 | "inflight": "^1.0.4", 914 | "inherits": "2", 915 | "minimatch": "^3.0.4", 916 | "once": "^1.3.0", 917 | "path-is-absolute": "^1.0.0" 918 | } 919 | }, 920 | "glob-parent": { 921 | "version": "5.1.1", 922 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 923 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 924 | "requires": { 925 | "is-glob": "^4.0.1" 926 | } 927 | }, 928 | "globals": { 929 | "version": "13.8.0", 930 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", 931 | "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", 932 | "requires": { 933 | "type-fest": "^0.20.2" 934 | }, 935 | "dependencies": { 936 | "type-fest": { 937 | "version": "0.20.2", 938 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 939 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" 940 | } 941 | } 942 | }, 943 | "globby": { 944 | "version": "11.0.3", 945 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", 946 | "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", 947 | "requires": { 948 | "array-union": "^2.1.0", 949 | "dir-glob": "^3.0.1", 950 | "fast-glob": "^3.1.1", 951 | "ignore": "^5.1.4", 952 | "merge2": "^1.3.0", 953 | "slash": "^3.0.0" 954 | } 955 | }, 956 | "growl": { 957 | "version": "1.10.5", 958 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 959 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" 960 | }, 961 | "has-flag": { 962 | "version": "4.0.0", 963 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 964 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 965 | }, 966 | "he": { 967 | "version": "1.2.0", 968 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 969 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" 970 | }, 971 | "ignore": { 972 | "version": "5.1.8", 973 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", 974 | "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" 975 | }, 976 | "import-fresh": { 977 | "version": "3.3.0", 978 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 979 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 980 | "requires": { 981 | "parent-module": "^1.0.0", 982 | "resolve-from": "^4.0.0" 983 | } 984 | }, 985 | "imurmurhash": { 986 | "version": "0.1.4", 987 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 988 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 989 | }, 990 | "inflight": { 991 | "version": "1.0.6", 992 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 993 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 994 | "requires": { 995 | "once": "^1.3.0", 996 | "wrappy": "1" 997 | } 998 | }, 999 | "inherits": { 1000 | "version": "2.0.4", 1001 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1002 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1003 | }, 1004 | "is-binary-path": { 1005 | "version": "2.1.0", 1006 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1007 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1008 | "requires": { 1009 | "binary-extensions": "^2.0.0" 1010 | } 1011 | }, 1012 | "is-extglob": { 1013 | "version": "2.1.1", 1014 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1015 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" 1016 | }, 1017 | "is-fullwidth-code-point": { 1018 | "version": "2.0.0", 1019 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1020 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 1021 | }, 1022 | "is-glob": { 1023 | "version": "4.0.1", 1024 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 1025 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 1026 | "requires": { 1027 | "is-extglob": "^2.1.1" 1028 | } 1029 | }, 1030 | "is-number": { 1031 | "version": "7.0.0", 1032 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1033 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 1034 | }, 1035 | "is-plain-obj": { 1036 | "version": "2.1.0", 1037 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1038 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" 1039 | }, 1040 | "isexe": { 1041 | "version": "2.0.0", 1042 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1043 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 1044 | }, 1045 | "js-tokens": { 1046 | "version": "4.0.0", 1047 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1048 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 1049 | }, 1050 | "js-yaml": { 1051 | "version": "4.0.0", 1052 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", 1053 | "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", 1054 | "requires": { 1055 | "argparse": "^2.0.1" 1056 | } 1057 | }, 1058 | "json-schema-traverse": { 1059 | "version": "0.4.1", 1060 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1061 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 1062 | }, 1063 | "json-stable-stringify-without-jsonify": { 1064 | "version": "1.0.1", 1065 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1066 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" 1067 | }, 1068 | "jsonc-parser": { 1069 | "version": "1.0.3", 1070 | "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-1.0.3.tgz", 1071 | "integrity": "sha512-hk/69oAeaIzchq/v3lS50PXuzn5O2ynldopMC+SWBql7J2WtdptfB9dy8Y7+Og5rPkTCpn83zTiO8FMcqlXJ/g==" 1072 | }, 1073 | "levn": { 1074 | "version": "0.4.1", 1075 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1076 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1077 | "requires": { 1078 | "prelude-ls": "^1.2.1", 1079 | "type-check": "~0.4.0" 1080 | } 1081 | }, 1082 | "locate-path": { 1083 | "version": "6.0.0", 1084 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1085 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1086 | "requires": { 1087 | "p-locate": "^5.0.0" 1088 | } 1089 | }, 1090 | "lodash": { 1091 | "version": "4.17.21", 1092 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1093 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 1094 | }, 1095 | "lodash.clonedeep": { 1096 | "version": "4.5.0", 1097 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 1098 | "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" 1099 | }, 1100 | "lodash.flatten": { 1101 | "version": "4.4.0", 1102 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 1103 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" 1104 | }, 1105 | "lodash.truncate": { 1106 | "version": "4.4.2", 1107 | "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", 1108 | "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=" 1109 | }, 1110 | "log-symbols": { 1111 | "version": "4.0.0", 1112 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", 1113 | "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", 1114 | "requires": { 1115 | "chalk": "^4.0.0" 1116 | } 1117 | }, 1118 | "lru-cache": { 1119 | "version": "6.0.0", 1120 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1121 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1122 | "requires": { 1123 | "yallist": "^4.0.0" 1124 | } 1125 | }, 1126 | "merge2": { 1127 | "version": "1.4.1", 1128 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1129 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" 1130 | }, 1131 | "micromatch": { 1132 | "version": "4.0.4", 1133 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", 1134 | "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", 1135 | "requires": { 1136 | "braces": "^3.0.1", 1137 | "picomatch": "^2.2.3" 1138 | }, 1139 | "dependencies": { 1140 | "picomatch": { 1141 | "version": "2.2.3", 1142 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", 1143 | "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==" 1144 | } 1145 | } 1146 | }, 1147 | "minimatch": { 1148 | "version": "3.0.4", 1149 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1150 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1151 | "requires": { 1152 | "brace-expansion": "^1.1.7" 1153 | } 1154 | }, 1155 | "mocha": { 1156 | "version": "8.3.0", 1157 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.0.tgz", 1158 | "integrity": "sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q==", 1159 | "requires": { 1160 | "@ungap/promise-all-settled": "1.1.2", 1161 | "ansi-colors": "4.1.1", 1162 | "browser-stdout": "1.3.1", 1163 | "chokidar": "3.5.1", 1164 | "debug": "4.3.1", 1165 | "diff": "5.0.0", 1166 | "escape-string-regexp": "4.0.0", 1167 | "find-up": "5.0.0", 1168 | "glob": "7.1.6", 1169 | "growl": "1.10.5", 1170 | "he": "1.2.0", 1171 | "js-yaml": "4.0.0", 1172 | "log-symbols": "4.0.0", 1173 | "minimatch": "3.0.4", 1174 | "ms": "2.1.3", 1175 | "nanoid": "3.1.20", 1176 | "serialize-javascript": "5.0.1", 1177 | "strip-json-comments": "3.1.1", 1178 | "supports-color": "8.1.1", 1179 | "which": "2.0.2", 1180 | "wide-align": "1.1.3", 1181 | "workerpool": "6.1.0", 1182 | "yargs": "16.2.0", 1183 | "yargs-parser": "20.2.4", 1184 | "yargs-unparser": "2.0.0" 1185 | }, 1186 | "dependencies": { 1187 | "glob": { 1188 | "version": "7.1.6", 1189 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 1190 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 1191 | "requires": { 1192 | "fs.realpath": "^1.0.0", 1193 | "inflight": "^1.0.4", 1194 | "inherits": "2", 1195 | "minimatch": "^3.0.4", 1196 | "once": "^1.3.0", 1197 | "path-is-absolute": "^1.0.0" 1198 | } 1199 | } 1200 | } 1201 | }, 1202 | "ms": { 1203 | "version": "2.1.3", 1204 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1205 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1206 | }, 1207 | "nanoid": { 1208 | "version": "3.1.20", 1209 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", 1210 | "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==" 1211 | }, 1212 | "natural-compare": { 1213 | "version": "1.4.0", 1214 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1215 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" 1216 | }, 1217 | "normalize-path": { 1218 | "version": "3.0.0", 1219 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1220 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 1221 | }, 1222 | "once": { 1223 | "version": "1.4.0", 1224 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1225 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1226 | "requires": { 1227 | "wrappy": "1" 1228 | } 1229 | }, 1230 | "optionator": { 1231 | "version": "0.9.1", 1232 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 1233 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 1234 | "requires": { 1235 | "deep-is": "^0.1.3", 1236 | "fast-levenshtein": "^2.0.6", 1237 | "levn": "^0.4.1", 1238 | "prelude-ls": "^1.2.1", 1239 | "type-check": "^0.4.0", 1240 | "word-wrap": "^1.2.3" 1241 | } 1242 | }, 1243 | "p-limit": { 1244 | "version": "3.1.0", 1245 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1246 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1247 | "requires": { 1248 | "yocto-queue": "^0.1.0" 1249 | } 1250 | }, 1251 | "p-locate": { 1252 | "version": "5.0.0", 1253 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1254 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1255 | "requires": { 1256 | "p-limit": "^3.0.2" 1257 | } 1258 | }, 1259 | "parent-module": { 1260 | "version": "1.0.1", 1261 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1262 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1263 | "requires": { 1264 | "callsites": "^3.0.0" 1265 | } 1266 | }, 1267 | "path-exists": { 1268 | "version": "4.0.0", 1269 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1270 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" 1271 | }, 1272 | "path-is-absolute": { 1273 | "version": "1.0.1", 1274 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1275 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1276 | }, 1277 | "path-key": { 1278 | "version": "3.1.1", 1279 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1280 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" 1281 | }, 1282 | "path-type": { 1283 | "version": "4.0.0", 1284 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 1285 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" 1286 | }, 1287 | "pathval": { 1288 | "version": "1.1.0", 1289 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 1290 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" 1291 | }, 1292 | "picomatch": { 1293 | "version": "2.2.2", 1294 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 1295 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" 1296 | }, 1297 | "prelude-ls": { 1298 | "version": "1.2.1", 1299 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1300 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" 1301 | }, 1302 | "prettier": { 1303 | "version": "2.2.1", 1304 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", 1305 | "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==" 1306 | }, 1307 | "prettier-linter-helpers": { 1308 | "version": "1.0.0", 1309 | "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", 1310 | "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", 1311 | "requires": { 1312 | "fast-diff": "^1.1.2" 1313 | } 1314 | }, 1315 | "progress": { 1316 | "version": "2.0.3", 1317 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 1318 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" 1319 | }, 1320 | "punycode": { 1321 | "version": "2.1.1", 1322 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1323 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1324 | }, 1325 | "queue-microtask": { 1326 | "version": "1.2.3", 1327 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1328 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" 1329 | }, 1330 | "randombytes": { 1331 | "version": "2.1.0", 1332 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1333 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1334 | "requires": { 1335 | "safe-buffer": "^5.1.0" 1336 | } 1337 | }, 1338 | "readdirp": { 1339 | "version": "3.5.0", 1340 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", 1341 | "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", 1342 | "requires": { 1343 | "picomatch": "^2.2.1" 1344 | } 1345 | }, 1346 | "regexpp": { 1347 | "version": "3.1.0", 1348 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", 1349 | "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" 1350 | }, 1351 | "require-directory": { 1352 | "version": "2.1.1", 1353 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1354 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 1355 | }, 1356 | "require-from-string": { 1357 | "version": "2.0.2", 1358 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1359 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" 1360 | }, 1361 | "resolve-from": { 1362 | "version": "4.0.0", 1363 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1364 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" 1365 | }, 1366 | "reusify": { 1367 | "version": "1.0.4", 1368 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1369 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" 1370 | }, 1371 | "rimraf": { 1372 | "version": "3.0.2", 1373 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1374 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1375 | "requires": { 1376 | "glob": "^7.1.3" 1377 | } 1378 | }, 1379 | "run-parallel": { 1380 | "version": "1.2.0", 1381 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1382 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1383 | "requires": { 1384 | "queue-microtask": "^1.2.2" 1385 | } 1386 | }, 1387 | "safe-buffer": { 1388 | "version": "5.2.1", 1389 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1390 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1391 | }, 1392 | "serialize-javascript": { 1393 | "version": "5.0.1", 1394 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", 1395 | "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", 1396 | "requires": { 1397 | "randombytes": "^2.1.0" 1398 | } 1399 | }, 1400 | "shebang-command": { 1401 | "version": "2.0.0", 1402 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1403 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1404 | "requires": { 1405 | "shebang-regex": "^3.0.0" 1406 | } 1407 | }, 1408 | "shebang-regex": { 1409 | "version": "3.0.0", 1410 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1411 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" 1412 | }, 1413 | "slash": { 1414 | "version": "3.0.0", 1415 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 1416 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" 1417 | }, 1418 | "slice-ansi": { 1419 | "version": "4.0.0", 1420 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", 1421 | "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", 1422 | "requires": { 1423 | "ansi-styles": "^4.0.0", 1424 | "astral-regex": "^2.0.0", 1425 | "is-fullwidth-code-point": "^3.0.0" 1426 | }, 1427 | "dependencies": { 1428 | "is-fullwidth-code-point": { 1429 | "version": "3.0.0", 1430 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1431 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 1432 | } 1433 | } 1434 | }, 1435 | "sprintf-js": { 1436 | "version": "1.0.3", 1437 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1438 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 1439 | }, 1440 | "string-width": { 1441 | "version": "2.1.1", 1442 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1443 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1444 | "requires": { 1445 | "is-fullwidth-code-point": "^2.0.0", 1446 | "strip-ansi": "^4.0.0" 1447 | } 1448 | }, 1449 | "strip-ansi": { 1450 | "version": "4.0.0", 1451 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1452 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1453 | "requires": { 1454 | "ansi-regex": "^3.0.0" 1455 | } 1456 | }, 1457 | "strip-json-comments": { 1458 | "version": "3.1.1", 1459 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1460 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" 1461 | }, 1462 | "supports-color": { 1463 | "version": "8.1.1", 1464 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1465 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1466 | "requires": { 1467 | "has-flag": "^4.0.0" 1468 | } 1469 | }, 1470 | "table": { 1471 | "version": "6.6.0", 1472 | "resolved": "https://registry.npmjs.org/table/-/table-6.6.0.tgz", 1473 | "integrity": "sha512-iZMtp5tUvcnAdtHpZTWLPF0M7AgiQsURR2DwmxnJwSy8I3+cY+ozzVvYha3BOLG2TB+L0CqjIz+91htuj6yCXg==", 1474 | "requires": { 1475 | "ajv": "^8.0.1", 1476 | "lodash.clonedeep": "^4.5.0", 1477 | "lodash.flatten": "^4.4.0", 1478 | "lodash.truncate": "^4.4.2", 1479 | "slice-ansi": "^4.0.0", 1480 | "string-width": "^4.2.0", 1481 | "strip-ansi": "^6.0.0" 1482 | }, 1483 | "dependencies": { 1484 | "ajv": { 1485 | "version": "8.2.0", 1486 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.2.0.tgz", 1487 | "integrity": "sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA==", 1488 | "requires": { 1489 | "fast-deep-equal": "^3.1.1", 1490 | "json-schema-traverse": "^1.0.0", 1491 | "require-from-string": "^2.0.2", 1492 | "uri-js": "^4.2.2" 1493 | } 1494 | }, 1495 | "ansi-regex": { 1496 | "version": "5.0.0", 1497 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1498 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 1499 | }, 1500 | "is-fullwidth-code-point": { 1501 | "version": "3.0.0", 1502 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1503 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 1504 | }, 1505 | "json-schema-traverse": { 1506 | "version": "1.0.0", 1507 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 1508 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" 1509 | }, 1510 | "string-width": { 1511 | "version": "4.2.2", 1512 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 1513 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 1514 | "requires": { 1515 | "emoji-regex": "^8.0.0", 1516 | "is-fullwidth-code-point": "^3.0.0", 1517 | "strip-ansi": "^6.0.0" 1518 | } 1519 | }, 1520 | "strip-ansi": { 1521 | "version": "6.0.0", 1522 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1523 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1524 | "requires": { 1525 | "ansi-regex": "^5.0.0" 1526 | } 1527 | } 1528 | } 1529 | }, 1530 | "text-table": { 1531 | "version": "0.2.0", 1532 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1533 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" 1534 | }, 1535 | "to-regex-range": { 1536 | "version": "5.0.1", 1537 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1538 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1539 | "requires": { 1540 | "is-number": "^7.0.0" 1541 | } 1542 | }, 1543 | "tslib": { 1544 | "version": "1.14.1", 1545 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 1546 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 1547 | }, 1548 | "type-check": { 1549 | "version": "0.4.0", 1550 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1551 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1552 | "requires": { 1553 | "prelude-ls": "^1.2.1" 1554 | } 1555 | }, 1556 | "type-detect": { 1557 | "version": "4.0.8", 1558 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 1559 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" 1560 | }, 1561 | "type-fest": { 1562 | "version": "0.8.1", 1563 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 1564 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" 1565 | }, 1566 | "typescript": { 1567 | "version": "4.2.4", 1568 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", 1569 | "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==" 1570 | }, 1571 | "typescript-template-language-service-decorator": { 1572 | "version": "2.2.0", 1573 | "resolved": "https://registry.npmjs.org/typescript-template-language-service-decorator/-/typescript-template-language-service-decorator-2.2.0.tgz", 1574 | "integrity": "sha512-xiolqt1i7e22rpqMaprPgSFVgU64u3b9n6EJlAaUYE61jumipKAdI1+O5khPlWslpTUj80YzjUKjJ2jxT0D74w==" 1575 | }, 1576 | "uri-js": { 1577 | "version": "4.4.1", 1578 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1579 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1580 | "requires": { 1581 | "punycode": "^2.1.0" 1582 | } 1583 | }, 1584 | "v8-compile-cache": { 1585 | "version": "2.3.0", 1586 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 1587 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" 1588 | }, 1589 | "vscode-css-languageservice": { 1590 | "version": "5.1.1", 1591 | "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-5.1.1.tgz", 1592 | "integrity": "sha512-QW0oe/g2y5E2AbVqY7FJNr2Q8uHiAHNSFpptI6xB8Y0KgzVKppOcIVrgmBNzXhFp9IswAwptkdqr8ExSJbqPkQ==", 1593 | "requires": { 1594 | "vscode-languageserver-textdocument": "^1.0.1", 1595 | "vscode-languageserver-types": "^3.16.0", 1596 | "vscode-nls": "^5.0.0", 1597 | "vscode-uri": "^3.0.2" 1598 | } 1599 | }, 1600 | "vscode-emmet-helper": { 1601 | "version": "1.2.11", 1602 | "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.2.11.tgz", 1603 | "integrity": "sha512-ms6/Z9TfNbjXS8r/KgbGxrNrFlu4RcIfVJxTZ2yFi0K4gn+Ka9X1+8cXvb5+5IOBGUrOsPjR0BuefdDkG+CKbQ==", 1604 | "requires": { 1605 | "@emmetio/extract-abbreviation": "0.1.6", 1606 | "jsonc-parser": "^1.0.0", 1607 | "vscode-languageserver-types": "^3.6.0-next.1" 1608 | } 1609 | }, 1610 | "vscode-languageserver-textdocument": { 1611 | "version": "1.0.1", 1612 | "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz", 1613 | "integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==" 1614 | }, 1615 | "vscode-languageserver-types": { 1616 | "version": "3.16.0", 1617 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", 1618 | "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" 1619 | }, 1620 | "vscode-nls": { 1621 | "version": "5.0.0", 1622 | "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.0.tgz", 1623 | "integrity": "sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==" 1624 | }, 1625 | "vscode-uri": { 1626 | "version": "3.0.2", 1627 | "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.2.tgz", 1628 | "integrity": "sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA==" 1629 | }, 1630 | "which": { 1631 | "version": "2.0.2", 1632 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1633 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1634 | "requires": { 1635 | "isexe": "^2.0.0" 1636 | } 1637 | }, 1638 | "wide-align": { 1639 | "version": "1.1.3", 1640 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1641 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1642 | "requires": { 1643 | "string-width": "^1.0.2 || 2" 1644 | } 1645 | }, 1646 | "word-wrap": { 1647 | "version": "1.2.3", 1648 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1649 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" 1650 | }, 1651 | "workerpool": { 1652 | "version": "6.1.0", 1653 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", 1654 | "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==" 1655 | }, 1656 | "wrap-ansi": { 1657 | "version": "7.0.0", 1658 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1659 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1660 | "requires": { 1661 | "ansi-styles": "^4.0.0", 1662 | "string-width": "^4.1.0", 1663 | "strip-ansi": "^6.0.0" 1664 | }, 1665 | "dependencies": { 1666 | "ansi-regex": { 1667 | "version": "5.0.0", 1668 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1669 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 1670 | }, 1671 | "is-fullwidth-code-point": { 1672 | "version": "3.0.0", 1673 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1674 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 1675 | }, 1676 | "string-width": { 1677 | "version": "4.2.0", 1678 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 1679 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 1680 | "requires": { 1681 | "emoji-regex": "^8.0.0", 1682 | "is-fullwidth-code-point": "^3.0.0", 1683 | "strip-ansi": "^6.0.0" 1684 | } 1685 | }, 1686 | "strip-ansi": { 1687 | "version": "6.0.0", 1688 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1689 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1690 | "requires": { 1691 | "ansi-regex": "^5.0.0" 1692 | } 1693 | } 1694 | } 1695 | }, 1696 | "wrappy": { 1697 | "version": "1.0.2", 1698 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1699 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1700 | }, 1701 | "y18n": { 1702 | "version": "5.0.5", 1703 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", 1704 | "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" 1705 | }, 1706 | "yallist": { 1707 | "version": "4.0.0", 1708 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1709 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1710 | }, 1711 | "yargs": { 1712 | "version": "16.2.0", 1713 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1714 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1715 | "requires": { 1716 | "cliui": "^7.0.2", 1717 | "escalade": "^3.1.1", 1718 | "get-caller-file": "^2.0.5", 1719 | "require-directory": "^2.1.1", 1720 | "string-width": "^4.2.0", 1721 | "y18n": "^5.0.5", 1722 | "yargs-parser": "^20.2.2" 1723 | }, 1724 | "dependencies": { 1725 | "ansi-regex": { 1726 | "version": "5.0.0", 1727 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1728 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 1729 | }, 1730 | "is-fullwidth-code-point": { 1731 | "version": "3.0.0", 1732 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1733 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 1734 | }, 1735 | "string-width": { 1736 | "version": "4.2.0", 1737 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 1738 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 1739 | "requires": { 1740 | "emoji-regex": "^8.0.0", 1741 | "is-fullwidth-code-point": "^3.0.0", 1742 | "strip-ansi": "^6.0.0" 1743 | } 1744 | }, 1745 | "strip-ansi": { 1746 | "version": "6.0.0", 1747 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1748 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1749 | "requires": { 1750 | "ansi-regex": "^5.0.0" 1751 | } 1752 | } 1753 | } 1754 | }, 1755 | "yargs-parser": { 1756 | "version": "20.2.4", 1757 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1758 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" 1759 | }, 1760 | "yargs-unparser": { 1761 | "version": "2.0.0", 1762 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1763 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1764 | "requires": { 1765 | "camelcase": "^6.0.0", 1766 | "decamelize": "^4.0.0", 1767 | "flat": "^5.0.2", 1768 | "is-plain-obj": "^2.1.0" 1769 | } 1770 | }, 1771 | "yocto-queue": { 1772 | "version": "0.1.0", 1773 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1774 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" 1775 | } 1776 | } 1777 | } 1778 | } 1779 | } 1780 | -------------------------------------------------------------------------------- /e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e2e", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "index.js", 7 | "directories": { 8 | "test": "tests" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "typescript": "^4.2.4", 18 | "typescript-styled-plugin": "file:./.." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /e2e/project-fixture/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /e2e/project-fixture/index.ts: -------------------------------------------------------------------------------- 1 | css` 2 | 3 | ` -------------------------------------------------------------------------------- /e2e/project-fixture/main.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/typescript-styled-plugin/3409ef468b309505b6e18d90211034f8ebaaf6fe/e2e/project-fixture/main.ts -------------------------------------------------------------------------------- /e2e/project-fixture/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "commonjs", 5 | "plugins": [{ "name": "typescript-styled-plugin", "tags": ["css", "keyframes"] }] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /e2e/server-fixture/index.js: -------------------------------------------------------------------------------- 1 | const { fork } = require('child_process'); 2 | const path = require('path'); 3 | const readline = require('readline'); 4 | 5 | class TSServer { 6 | constructor(project) { 7 | const logfile = path.join(__dirname, 'log.txt'); 8 | const tsserverPath = path.join(__dirname, '..', 'node_modules', 'typescript', 'lib', 'tsserver'); 9 | const server = fork(tsserverPath, [ 10 | '--logVerbosity', 'verbose', 11 | '--logFile', logfile, 12 | '--pluginProbeLocations', path.join(__dirname, '..') 13 | ], { 14 | cwd: path.join(__dirname, '..', project), 15 | stdio: ['pipe', 'pipe', 'pipe', 'ipc'], 16 | }); 17 | this._exitPromise = new Promise((resolve, reject) => { 18 | server.on('exit', code => resolve(code)); 19 | server.on('error', reason => reject(reason)); 20 | }); 21 | server.stdout.setEncoding('utf-8'); 22 | readline.createInterface({ 23 | input: server.stdout 24 | }).on('line', line => { 25 | if (line[0] !== '{') { 26 | return; 27 | } 28 | try { 29 | const result = JSON.parse(line); 30 | if (result.type === 'response') { 31 | this.responses.push(result); 32 | --this._pendingResponses; 33 | 34 | if (this._pendingResponses <= 0 && this._isClosed) { 35 | this._shutdown(); 36 | } 37 | } 38 | } catch (e) { 39 | // noop 40 | } 41 | 42 | }); 43 | 44 | this._isClosed = false; 45 | this._server = server; 46 | this._seq = 0; 47 | this.responses = []; 48 | this._pendingResponses = 0; 49 | } 50 | 51 | send(command, responseExpected) { 52 | if (this._isClosed) { 53 | throw new Error('server is closed'); 54 | } 55 | if (responseExpected) { 56 | ++this._pendingResponses; 57 | } 58 | const seq = ++this._seq; 59 | const req = JSON.stringify(Object.assign({ seq: seq, type: 'request' }, command)) + '\n'; 60 | this._server.stdin.write(req); 61 | } 62 | 63 | sendCommand(name, args) { 64 | this.send({ command: name, arguments: args }, true); 65 | } 66 | 67 | close() { 68 | if (!this._isClosed) { 69 | this._isClosed = true; 70 | if (this._pendingResponses <= 0) { 71 | this._shutdown(); 72 | } 73 | } 74 | return this._exitPromise; 75 | } 76 | 77 | _shutdown() { 78 | this._server.stdin.end(); 79 | } 80 | } 81 | 82 | function createServer(project) { 83 | return new TSServer(project || 'project-fixture'); 84 | } 85 | 86 | module.exports = createServer; 87 | -------------------------------------------------------------------------------- /e2e/tests/_helpers.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | 3 | exports.openMockFile = (server, mockFileName, fileContent) => { 4 | server.send({ 5 | command: 'open', 6 | arguments: { 7 | file: mockFileName, 8 | fileContent, 9 | scriptKindName: 'TS' 10 | } 11 | }); 12 | return server; 13 | }; 14 | 15 | 16 | exports.getFirstResponseOfType = (command, server) => { 17 | const response = server.responses.find(response => response.command === command); 18 | assert.isTrue(response !== undefined); 19 | return response; 20 | }; 21 | 22 | exports.getResponsesOfType = (command, server) => { 23 | return server.responses.filter(response => response.command === command); 24 | }; -------------------------------------------------------------------------------- /e2e/tests/completionEntryDetails.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | const path = require('path'); 3 | const createServer = require('../server-fixture'); 4 | const { openMockFile, getFirstResponseOfType } = require('./_helpers'); 5 | 6 | const mockFileName = path.join(__dirname, '..', 'project-fixture', 'main.ts'); 7 | 8 | describe('CompletionEntryDetails', () => { 9 | it('should return details for color completion', () => { 10 | const server = createServer(); 11 | openMockFile(server, mockFileName, 'const q = css`color:`'); 12 | server.sendCommand('completionEntryDetails', { file: mockFileName, offset: 21, line: 1, entryNames: ['blue'] }); 13 | 14 | return server.close().then(() => { 15 | const completionsResponse = getFirstResponseOfType('completionEntryDetails', server); 16 | assert.isTrue(completionsResponse.success); 17 | assert.strictEqual(completionsResponse.body.length, 1); 18 | 19 | const firstDetails = completionsResponse.body[0] 20 | assert.strictEqual(firstDetails.name, 'blue'); 21 | assert.strictEqual(firstDetails.documentation[0].text, '#0000ff'); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /e2e/tests/completions.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | const path = require('path'); 3 | const createServer = require('../server-fixture'); 4 | const { openMockFile, getFirstResponseOfType } = require('./_helpers'); 5 | 6 | const mockFileName = path.join(__dirname, '..', 'project-fixture', 'main.ts'); 7 | 8 | const createServerWithMockFile = (fileContents) => { 9 | const server = createServer(); 10 | openMockFile(server, mockFileName, fileContents); 11 | return server; 12 | }; 13 | 14 | describe('Completions', () => { 15 | it('should return property value completions for single line string', () => { 16 | const server = createServerWithMockFile('const q = css`color:`'); 17 | server.sendCommand('completions', { file: mockFileName, offset: 21, line: 1 }); 18 | 19 | return server.close().then(() => { 20 | const completionsResponse = getFirstResponseOfType('completions', server); 21 | assert.isTrue(completionsResponse.success); 22 | assert.strictEqual(completionsResponse.body.length, 157); 23 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 24 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'rgba')); 25 | }); 26 | }); 27 | 28 | it('should not return SCSS functions in property value completions', () => { 29 | const server = createServerWithMockFile('const q = css`color:`'); 30 | server.sendCommand('completions', { file: mockFileName, offset: 21, line: 1 }); 31 | 32 | return server.close().then(() => { 33 | const completionsResponse = getFirstResponseOfType('completions', server); 34 | assert.isTrue(completionsResponse.success); 35 | assert.isFalse(completionsResponse.body.some((item) => item.name === 'darken')); 36 | }); 37 | }); 38 | 39 | it('should return property value completions for multiline string', () => { 40 | const server = createServerWithMockFile(['const q = css`', 'color:', '`'].join('\n')); 41 | server.sendCommand('completions', { file: mockFileName, offset: 22, line: 1 }); 42 | 43 | return server.close().then(() => { 44 | const completionsResponse = getFirstResponseOfType('completions', server); 45 | assert.isTrue(completionsResponse.success); 46 | assert.strictEqual(completionsResponse.body.length, 157); 47 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 48 | }); 49 | }); 50 | 51 | it('should return property value completions for nested selector', () => { 52 | const server = createServerWithMockFile('const q = css`position: relative; &:hover { color: }`'); 53 | server.sendCommand('completions', { file: mockFileName, offset: 51, line: 1 }); 54 | 55 | return server.close().then(() => { 56 | const completionsResponse = getFirstResponseOfType('completions', server); 57 | assert.isTrue(completionsResponse.success); 58 | assert.strictEqual(completionsResponse.body.length, 157); 59 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 60 | }); 61 | }); 62 | 63 | it('should not return css completions on tag', () => { 64 | const server = createServerWithMockFile('css.``'); 65 | server.sendCommand('completions', { file: mockFileName, offset: 5, line: 1 }); 66 | 67 | return server.close().then(() => { 68 | const completionsResponse = getFirstResponseOfType('completions', server); 69 | assert.isFalse(completionsResponse.success); 70 | assert.strictEqual(completionsResponse.message, 'No content available.'); 71 | }); 72 | }); 73 | 74 | it('should return completions when placeholder is used as property', () => { 75 | const server = createServerWithMockFile('css`color: ; boarder: 1px solid ${"red"};`'); 76 | server.sendCommand('completions', { file: mockFileName, offset: 11, line: 1 }); 77 | 78 | return server.close().then(() => { 79 | const completionsResponse = getFirstResponseOfType('completions', server); 80 | assert.isTrue(completionsResponse.success); 81 | assert.strictEqual(completionsResponse.body.length, 157); 82 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 83 | }); 84 | }); 85 | 86 | it('should return completions after where placeholder is used as property', () => { 87 | const server = createServerWithMockFile('css`border: 1px solid ${"red"}; color:`'); 88 | server.sendCommand('completions', { file: mockFileName, offset: 39, line: 1 }); 89 | 90 | return server.close().then(() => { 91 | const completionsResponse = getFirstResponseOfType('completions', server); 92 | assert.isTrue(completionsResponse.success); 93 | assert.strictEqual(completionsResponse.body.length, 157); 94 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 95 | }); 96 | }); 97 | 98 | it('should return completions between were placeholders are used as properties', () => { 99 | const server = createServerWithMockFile('css`boarder: 1px solid ${"red"}; color: ; margin: ${20}; `'); 100 | server.sendCommand('completions', { file: mockFileName, offset: 40, line: 1 }); 101 | 102 | return server.close().then(() => { 103 | const completionsResponse = getFirstResponseOfType('completions', server); 104 | assert.isTrue(completionsResponse.success); 105 | assert.strictEqual(completionsResponse.body.length, 157); 106 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 107 | }); 108 | }); 109 | 110 | it('should return completions on tagged template string with placeholder using dotted tag', () => { 111 | const server = createServerWithMockFile('css.x`color: ; boarder: 1px solid ${"red"};`'); 112 | server.sendCommand('completions', { file: mockFileName, offset: 13, line: 1 }); 113 | 114 | return server.close().then(() => { 115 | const completionsResponse = getFirstResponseOfType('completions', server); 116 | assert.isTrue(completionsResponse.success); 117 | assert.strictEqual(completionsResponse.body.length, 157); 118 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 119 | }); 120 | }); 121 | 122 | it('should return js completions inside placeholder', () => { 123 | const server = createServerWithMockFile('const abc = 123; css`color: ${};`'); 124 | server.sendCommand('completions', { file: mockFileName, offset: 31, line: 1 }); 125 | 126 | return server.close().then(() => { 127 | const completionsResponse = getFirstResponseOfType('completions', server); 128 | assert.isTrue(completionsResponse.success); 129 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'abc')); 130 | }); 131 | }); 132 | 133 | it('should return js completions at end of placeholder', () => { 134 | const server = createServerWithMockFile('css`color: ${"red".};`'); 135 | server.sendCommand('completions', { file: mockFileName, offset: 20, line: 1 }); 136 | 137 | return server.close().then(() => { 138 | const completionsResponse = getFirstResponseOfType('completions', server); 139 | assert.isTrue(completionsResponse.success); 140 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'substr')); 141 | }); 142 | }); 143 | 144 | it('should return styled completions inside of nested placeholder', () => { 145 | const server = createServerWithMockFile('styled`background: red; ${(() => css`color:`)()}`;'); 146 | server.sendCommand('completions', { file: mockFileName, offset: 44, line: 1 }); 147 | 148 | return server.close().then(() => { 149 | const completionsResponse = getFirstResponseOfType('completions', server); 150 | assert.isTrue(completionsResponse.success); 151 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 152 | }); 153 | }); 154 | 155 | it('should handle multiline value placeholder correctly ', () => { 156 | const server = createServerWithMockFile(['css`margin: ${', '0', '}; color: `'].join('\n')); 157 | server.sendCommand('completions', { file: mockFileName, offset: 10, line: 3 }); 158 | 159 | return server.close().then(() => { 160 | const completionsResponse = getFirstResponseOfType('completions', server); 161 | assert.isTrue(completionsResponse.success); 162 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 163 | }); 164 | }); 165 | 166 | it('should handle multiline rule placeholder correctly ', () => { 167 | const server = createServerWithMockFile(['css`', '${', 'css`margin: 0;`', '}', 'color: `'].join('\n')); 168 | server.sendCommand('completions', { file: mockFileName, offset: 8, line: 5 }); 169 | 170 | return server.close().then(() => { 171 | const completionsResponse = getFirstResponseOfType('completions', server); 172 | assert.isTrue(completionsResponse.success); 173 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 174 | }); 175 | }); 176 | 177 | it('should return completions when placeholder is used as a selector', () => { 178 | const server = createServerWithMockFile(['css`${"button"} {', ' color: ;', '}', 'color: ;', '`'].join('\n')); 179 | server.sendCommand('completions', { file: mockFileName, line: 2, offset: 11 }); 180 | 181 | return server.close().then(() => { 182 | const completionsResponse = getFirstResponseOfType('completions', server); 183 | assert.isTrue(completionsResponse.success); 184 | assert.strictEqual(completionsResponse.body.length, 157); 185 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 186 | }); 187 | }); 188 | 189 | it('should return completions inside of nested selector xx', () => { 190 | const server = createServerWithMockFile( 191 | ['css`', ' color: red;', ' &:hover {', ' color: ', ' }', '`'].join('\n') 192 | ); 193 | server.sendCommand('completions', { file: mockFileName, line: 4, offset: 15 }); 194 | 195 | return server.close().then(() => { 196 | const completionsResponse = getFirstResponseOfType('completions', server); 197 | assert.isTrue(completionsResponse.success); 198 | assert.strictEqual(completionsResponse.body.length, 157); 199 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 200 | }); 201 | }); 202 | 203 | it('should support tag that is a function call', () => { 204 | const server = createServerWithMockFile('const q = css("bla")`color:`'); 205 | server.sendCommand('completions', { file: mockFileName, offset: 28, line: 1 }); 206 | 207 | return server.close().then(() => { 208 | const completionsResponse = getFirstResponseOfType('completions', server); 209 | assert.isTrue(completionsResponse.success); 210 | assert.strictEqual(completionsResponse.body.length, 157); 211 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 212 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'rgba')); 213 | }); 214 | }); 215 | 216 | it('should support tag that is a templated function call', async () => { 217 | const server = createServerWithMockFile("const q = css('bla')`color:`"); 218 | server.sendCommand('completions', { file: mockFileName, offset: 36, line: 1 }); 219 | 220 | await server.close(); 221 | const completionsResponse = getFirstResponseOfType('completions', server); 222 | assert.isTrue(completionsResponse.success); 223 | assert.strictEqual(completionsResponse.body.length, 157); 224 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'aliceblue')); 225 | assert.isTrue(completionsResponse.body.some((item) => item.name === 'rgba')); 226 | }); 227 | 228 | it('should mark color completions with "color" kindModifier', async () => { 229 | const server = createServerWithMockFile('const q = css`color:`'); 230 | server.sendCommand('completions', { file: mockFileName, offset: 21, line: 1 }); 231 | 232 | await server.close(); 233 | const completionsResponse = getFirstResponseOfType('completions', server); 234 | assert.isTrue(completionsResponse.success); 235 | const aliceBlue = completionsResponse.body.find((item) => item.name === 'aliceblue'); 236 | assert.isTrue(aliceBlue.kindModifiers === 'color'); 237 | }); 238 | 239 | it('should get completions inside keyframes blocks', async () => { 240 | const server = createServerWithMockFile('const q = keyframes`0% {color:`'); 241 | server.sendCommand('completions', { file: mockFileName, offset: 31, line: 1 }); 242 | 243 | await server.close(); 244 | const completionsResponse = getFirstResponseOfType('completions', server); 245 | assert.isTrue(completionsResponse.success); 246 | const aliceBlue = completionsResponse.body.find((item) => item.name === 'aliceblue'); 247 | assert.isTrue(aliceBlue.kindModifiers === 'color'); 248 | }); 249 | }); 250 | -------------------------------------------------------------------------------- /e2e/tests/emmetCompletions.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const assert = require('chai').assert; 3 | const path = require('path'); 4 | const createServer = require('../server-fixture'); 5 | const { openMockFile, getFirstResponseOfType } = require('./_helpers'); 6 | 7 | const createMockFileForServer = (fileContents, project) => { 8 | project = project || 'project-fixture' 9 | const server = createServer(project); 10 | const mockFileName = path.join(__dirname, '..', project, 'main.ts'); 11 | openMockFile(server, mockFileName, fileContents); 12 | return { server, mockFileName }; 13 | } 14 | 15 | describe('Emmet Completions', () => { 16 | it('shouldnt return emmet property completions when disabled', async () => { 17 | const { server, mockFileName } = createMockFileForServer( 18 | 'const q = css`m10-20`', 'disabled-emmet-project-fixture'); 19 | server.sendCommand('completions', { file: mockFileName, offset: 21, line: 1 }); 20 | 21 | await server.close(); 22 | const completionsResponse = getFirstResponseOfType('completions', server); 23 | assert.isTrue(completionsResponse.body.every(item => item.name !== 'margin: 10px 20px;')); 24 | }); 25 | 26 | it('should return emmet property completions for single line string', async () => { 27 | const { server, mockFileName } = createMockFileForServer('const q = css`m10-20`'); 28 | server.sendCommand('completions', { file: mockFileName, offset: 21, line: 1 }); 29 | 30 | await server.close(); 31 | const completionsResponse = getFirstResponseOfType('completions', server); 32 | assert.isTrue(completionsResponse.body.some(item => item.name === 'margin: 10px 20px;')); 33 | }); 34 | 35 | it('should return emmet property completions for multiline string', async () => { 36 | const { server, mockFileName } = createMockFileForServer([ 37 | 'const q = css`', 38 | 'm10-20', 39 | '`' 40 | ].join('\n')); 41 | server.sendCommand('completions', { file: mockFileName, offset: 7, line: 2 }); 42 | 43 | await server.close(); 44 | const completionsResponse = getFirstResponseOfType('completions', server); 45 | assert.isTrue(completionsResponse.body.some(item => item.name === 'margin: 10px 20px;')); 46 | }); 47 | 48 | it('should return emmet property completions for nested selector', async () => { 49 | const { server, mockFileName } = createMockFileForServer( 50 | 'const q = css`position: relative; &:hover { m10-20 }`'); 51 | 52 | server.sendCommand('completions', { file: mockFileName, offset: 51, line: 1 }); 53 | 54 | await server.close(); 55 | const completionsResponse = getFirstResponseOfType('completions', server); 56 | assert.isTrue(completionsResponse.body.some(item => item.name === 'margin: 10px 20px;')); 57 | }); 58 | 59 | it('should return emmet completions when placeholder is used as property', async () => { 60 | const { server, mockFileName } = createMockFileForServer('css`m10-20 ; boarder: 1px solid ${"red"};`'); 61 | server.sendCommand('completions', { file: mockFileName, offset: 11, line: 1 }); 62 | 63 | await server.close(); 64 | const completionsResponse = getFirstResponseOfType('completions', server); 65 | assert.isTrue(completionsResponse.body.some(item => item.name === 'margin: 10px 20px;')); 66 | }); 67 | 68 | it('should return emmet completions after where placeholder is used as property', async () => { 69 | const { server, mockFileName } = createMockFileForServer('css`border: 1px solid ${"red"}; m10-20`'); 70 | server.sendCommand('completions', { file: mockFileName, offset: 39, line: 1 }); 71 | 72 | await server.close(); 73 | const completionsResponse = getFirstResponseOfType('completions', server); 74 | assert.isTrue(completionsResponse.body.some(item => item.name === 'margin: 10px 20px;')); 75 | }); 76 | 77 | it('should return emmet completions between were placeholders are used as properties', async () => { 78 | const { server, mockFileName } = createMockFileForServer('css`boarder: 1px solid ${"red"}; color: #12; margin: ${20}; `') 79 | server.sendCommand('completions', { file: mockFileName, offset: 44, line: 1 }); 80 | 81 | await server.close(); 82 | const completionsResponse = getFirstResponseOfType('completions', server); 83 | assert.isTrue(completionsResponse.body.some(item => item.name === '#121212')); 84 | }); 85 | 86 | it('should return emmet completions on tagged template string with placeholder using dotted tag', async () => { 87 | const { server, mockFileName } = createMockFileForServer('css.x`color: #12 ; boarder: 1px solid ${"red"};`'); 88 | server.sendCommand('completions', { file: mockFileName, offset: 17, line: 1 }); 89 | 90 | await server.close(); 91 | const completionsResponse = getFirstResponseOfType('completions', server); 92 | assert.isTrue(completionsResponse.success); 93 | assert.isTrue(completionsResponse.body.some(item => item.name === '#121212')); 94 | }); 95 | 96 | it('should return styled emmet completions inside of nested placeholder', async () => { 97 | const { server, mockFileName } = createMockFileForServer('styled`background: red; ${(() => css`color: #12`)()}`;'); 98 | server.sendCommand('completions', { file: mockFileName, offset: 48, line: 1 }); 99 | 100 | await server.close(); 101 | const completionsResponse = getFirstResponseOfType('completions', server); 102 | assert.isTrue(completionsResponse.body.some(item => item.name === '#121212')); 103 | }); 104 | 105 | it('should handle emmet completions in multiline value placeholder correctly ', async () => { 106 | const { server, mockFileName } = createMockFileForServer([ 107 | 'css`margin: ${', 108 | '0', 109 | "}; color: #12`"].join('\n')); 110 | server.sendCommand('completions', { file: mockFileName, offset: 14, line: 3 }); 111 | 112 | await server.close(); 113 | const completionsResponse = getFirstResponseOfType('completions', server); 114 | assert.isTrue(completionsResponse.body.some(item => item.name === '#121212')); 115 | }); 116 | 117 | it('should handle emmet completions in multiline rule placeholder correctly ', async () => { 118 | const { server, mockFileName } = createMockFileForServer([ 119 | 'css`', 120 | '${', 121 | 'css`margin: 0;`', 122 | '}', 123 | 'color: #12`'].join('\n')); 124 | server.sendCommand('completions', { file: mockFileName, offset: 11, line: 5 }); 125 | 126 | await server.close(); 127 | const completionsResponse = getFirstResponseOfType('completions', server); 128 | assert.isTrue(completionsResponse.body.some(item => item.name === '#121212')); 129 | }); 130 | 131 | it('should return emmet completions inside of nested selector xx', async () => { 132 | const { server, mockFileName } = createMockFileForServer([ 133 | 'css`', 134 | ' color: red;', 135 | ' &:hover {', 136 | ' color: #12 ', 137 | ' }', 138 | '`'].join('\n')); 139 | server.sendCommand('completions', { file: mockFileName, line: 4, offset: 19 }); 140 | 141 | await server.close(); 142 | const completionsResponse = getFirstResponseOfType('completions', server); 143 | assert.isTrue(completionsResponse.body.some(item => item.name === '#121212')); 144 | }); 145 | 146 | it('should mark emmet completions as isIncomplete', async () => { 147 | const { server, mockFileName } = createMockFileForServer('const q = css`m10-20`'); 148 | server.sendCommand('completions', { file: mockFileName, offset: 21, line: 1 }); 149 | 150 | await server.close(); 151 | const completionsResponse = getFirstResponseOfType('completions', server); 152 | assert.isTrue(completionsResponse.body.some(item => item.name === 'margin: 10px 20px;')); 153 | assert.isTrue(completionsResponse.metadata.isIncomplete); 154 | }); 155 | }); 156 | -------------------------------------------------------------------------------- /e2e/tests/errors.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const assert = require('chai').assert; 3 | const path = require('path'); 4 | const createServer = require('../server-fixture'); 5 | const { openMockFile, getFirstResponseOfType } = require('./_helpers'); 6 | 7 | const mockFileName = path.join(__dirname, '..', 'project-fixture', 'main.ts'); 8 | 9 | const getSemanticDiagnosticsForFile = (fileContents) => { 10 | const server = createServer(); 11 | openMockFile(server, mockFileName, fileContents); 12 | server.sendCommand('semanticDiagnosticsSync', { file: mockFileName }); 13 | 14 | return server.close().then(_ => { 15 | return getFirstResponseOfType('semanticDiagnosticsSync', server); 16 | }); 17 | } 18 | 19 | describe('Errors', () => { 20 | it('should return error for unknown property', async () => { 21 | const errorResponse = await getSemanticDiagnosticsForFile('function css(x) { return x; }; const q = css`boarder: 1px solid black;`'); 22 | assert.isTrue(errorResponse.success); 23 | assert.strictEqual(errorResponse.body.length, 1); 24 | const error = errorResponse.body[0]; 25 | assert.strictEqual(error.text, "Unknown property: 'boarder'"); 26 | assert.strictEqual(error.start.line, 1); 27 | assert.strictEqual(error.start.offset, 46); 28 | assert.strictEqual(error.end.line, 1); 29 | assert.strictEqual(error.end.offset, 53); 30 | }); 31 | 32 | it('should not return errors for empty rulesets', async () => { 33 | const errorResponse = await getSemanticDiagnosticsForFile('function css(x) { return x; }; const q = css``'); 34 | assert.isTrue(errorResponse.success); 35 | assert.strictEqual(errorResponse.body.length, 0); 36 | }); 37 | 38 | it('should not return errors for nested rulesets', async () => { 39 | const errorResponse = await getSemanticDiagnosticsForFile('function css(x) { return x; }; const q = css`&:hover { border: 1px solid black; }`'); 40 | assert.isTrue(errorResponse.success); 41 | assert.strictEqual(errorResponse.body.length, 0); 42 | }); 43 | 44 | it('should not return an error for a placeholder in a property', async () => { 45 | const errorResponse = await getSemanticDiagnosticsForFile('function css(strings, ...) { return ""; }; const q = css`color: ${"red"};`'); 46 | assert.isTrue(errorResponse.success); 47 | assert.strictEqual(errorResponse.body.length, 0); 48 | }); 49 | 50 | it('should not return an error for a placeholder in a property with a multiline string', async () => { 51 | const errorResponse = await getSemanticDiagnosticsForFile([ 52 | 'function css(strings, ...) { return ""; }; const q = css`', 53 | ' color: ${"red"};', 54 | '`' 55 | ].join('\n')); 56 | assert.isTrue(errorResponse.success); 57 | assert.strictEqual(errorResponse.body.length, 0); 58 | }); 59 | 60 | it('should return errors when error occurs in last position', async () => { 61 | const errorResponse = await getSemanticDiagnosticsForFile('function css(strings, ...) { return ""; }; const q = css`;`'); 62 | assert.isTrue(errorResponse.success); 63 | assert.strictEqual(errorResponse.body.length, 1); 64 | const error = errorResponse.body[0]; 65 | assert.strictEqual(error.text, '} expected'); 66 | assert.strictEqual(error.start.line, 1); 67 | assert.strictEqual(error.start.offset, 58); 68 | assert.strictEqual(error.end.line, 1); 69 | assert.strictEqual(error.end.offset, 59); 70 | }); 71 | 72 | it('should return error for multiline unknown property #20', async () => { 73 | const errorResponse = await getSemanticDiagnosticsForFile([ 74 | 'function css(x) { return x; };', 75 | 'const q = css`', 76 | 'boarder: 1px solid black;', 77 | '`' 78 | ].join('\n')); 79 | assert.isTrue(errorResponse.success); 80 | assert.strictEqual(errorResponse.body.length, 1); 81 | const error = errorResponse.body[0]; 82 | assert.strictEqual(error.text, "Unknown property: 'boarder'"); 83 | assert.strictEqual(error.start.line, 3); 84 | assert.strictEqual(error.start.offset, 1); 85 | assert.strictEqual(error.end.line, 3); 86 | assert.strictEqual(error.end.offset, 8); 87 | }); 88 | 89 | it('should not error with interpolation at start, followed by semicolon #22', async () => { 90 | const errorResponse = await getSemanticDiagnosticsForFile([ 91 | "function css(...args){}", 92 | "const mixin = ''", 93 | // test single-line 94 | "css`${mixin}; color: blue;`", 95 | // test multi-line (normal case) 96 | "css`", 97 | " ${mixin};", 98 | " color: blue;", 99 | "`", 100 | // test multiple spaces after semi 101 | "css`", 102 | " ${mixin} ;", 103 | " color: blue;", 104 | "`", 105 | // test hella semis - will this ever pop up? probably not, but screw it 106 | "css`", 107 | " ${mixin};;; ;; ;", 108 | " color: blue;", 109 | "`", 110 | ].join('\n')); 111 | assert.isTrue(errorResponse.success); 112 | assert.strictEqual(errorResponse.body.length, 0); 113 | }); 114 | 115 | it('should not return an error for a placeholder used as a selector (#30)', async () => { 116 | const errorResponse = await getSemanticDiagnosticsForFile('function css(strings, ...) { return ""; }; const q = css`${"button"} { color: red; }`'); 117 | assert.isTrue(errorResponse.success); 118 | assert.strictEqual(errorResponse.body.length, 0); 119 | }); 120 | 121 | it('should not return an error for a placeholder used as a complex selector (#30)', () => { 122 | return getSemanticDiagnosticsForFile(` 123 | function css(strings, ...) { return ""; }; 124 | function fullWidth() { }; 125 | const Button = {}; 126 | const q = css\` 127 | display: flex; 128 | \${fullWidth()}; 129 | 130 | \${Button} { 131 | width: 100%; 132 | 133 | &:not(:first-child):not(:last-child) { 134 | margin-left: 0; 135 | margin-right: 0; 136 | border-radius: 0; 137 | } 138 | } 139 | \`` 140 | ).then(errorResponse => { 141 | assert.isTrue(errorResponse.success); 142 | assert.strictEqual(errorResponse.body.length, 0); 143 | }); 144 | }); 145 | 146 | it('should not return an error for a placeholder used as selector part (#39)', async () => { 147 | const errorResponse = await getSemanticDiagnosticsForFile('function css(strings, ...) { return ""; }; const Content = "button"; const q = css`& > ${Content} { margin-left: 1px; }`'); 148 | assert.isTrue(errorResponse.success); 149 | assert.strictEqual(errorResponse.body.length, 0); 150 | }); 151 | 152 | it('should not return an error for a placeholder in multiple properties (#39)', () => { 153 | return getSemanticDiagnosticsForFile( 154 | `function css(strings, ...) { return ""; }; const Content = "button"; const q = css\` 155 | & > $\{'content'} { 156 | color: 1px; 157 | } 158 | 159 | & > $\{'styledNavBar'} { 160 | margin-left: $\{1}; 161 | } 162 | \`` 163 | ).then(errorResponse => { 164 | assert.isTrue(errorResponse.success); 165 | assert.strictEqual(errorResponse.body.length, 0); 166 | }); 167 | }); 168 | 169 | it('should not return an error for a placeholder that spans multiple lines aaa (#44)', () => { 170 | return getSemanticDiagnosticsForFile( 171 | `let css: any = {}; const q = css.a\` 172 | color: 173 | $\{'transparent'}; 174 | border-bottom: 1px; 175 | &:hover { 176 | color: inherit; 177 | text-decoration: none; 178 | } 179 | \`` 180 | ).then(errorResponse => { 181 | assert.isTrue(errorResponse.success); 182 | assert.strictEqual(errorResponse.body.length, 0); 183 | }); 184 | }); 185 | 186 | it('should not return an error for complicated style (#44)', () => { 187 | return getSemanticDiagnosticsForFile( 188 | `let css: any = {}; const q = css.a\` 189 | display: flex; 190 | width: 6rem; 191 | height: 5rem; 192 | margin-right: -3px; 193 | border-right: 3px solid 194 | $\{({ active, theme: { colors } }) => 195 | active ? colors.yellow : 'transparent'}; 196 | border-bottom: 1px solid rgba(255, 255, 255, 0.5); 197 | font-weight: bold; 198 | font-size: 0.875rem; 199 | color: white; 200 | cursor: pointer; 201 | &:not([href]):not([tabindex]) { 202 | color: white; 203 | } 204 | &:hover { 205 | color: inherit; 206 | text-decoration: none; 207 | } 208 | \`` 209 | ).then(errorResponse => { 210 | assert.isTrue(errorResponse.success); 211 | assert.strictEqual(errorResponse.body.length, 0); 212 | }); 213 | }); 214 | 215 | it('should not return an error for a placeholder value followed by unit (#48)', () => { 216 | return getSemanticDiagnosticsForFile( 217 | `function css(strings, ...) { return ""; }; const value = 1; const q = css\` 218 | width: $\{value}%; 219 | \`` 220 | ).then(errorResponse => { 221 | assert.isTrue(errorResponse.success); 222 | assert.strictEqual(errorResponse.body.length, 0); 223 | }); 224 | }); 225 | 226 | it('should not return an error for a placeholder as the declaration name (#52)', () => { 227 | return getSemanticDiagnosticsForFile( 228 | `function css(strings, ...) { return ""; }; const q = css\` 229 | $\{'width'}: 1px; 230 | \`` 231 | ).then(errorResponse => { 232 | assert.isTrue(errorResponse.success); 233 | assert.strictEqual(errorResponse.body.length, 0); 234 | }); 235 | }); 236 | 237 | it('should not return an error for a placeholder as part of a rule (#59)', () => { 238 | return getSemanticDiagnosticsForFile( 239 | `function css(strings, ...) { return ""; }; const q = css\` 240 | $\{'a'}, \${'button'} { 241 | width: 1px; 242 | } 243 | \`` 244 | ).then(errorResponse => { 245 | assert.isTrue(errorResponse.success); 246 | assert.strictEqual(errorResponse.body.length, 0); 247 | }); 248 | }); 249 | 250 | it('should not return an error placeholder used as entire property within nested (#54)', () => { 251 | return getSemanticDiagnosticsForFile( 252 | `function css(strings, ...) { return ""; }; const q = css\` 253 | &.buu-foo { 254 | \${'baseShape'}; 255 | &.active { 256 | font-size: 2rem; 257 | } 258 | } 259 | \`` 260 | ).then(errorResponse => { 261 | assert.isTrue(errorResponse.success); 262 | assert.strictEqual(errorResponse.body.length, 0); 263 | }); 264 | }); 265 | 266 | it('should not return an error on adjacent variables (#62)', () => { 267 | return getSemanticDiagnosticsForFile( 268 | `let css: any = {}; const margin1 = "3px"; const margin2 = "3px"; const q = css.a\` 269 | margin: $\{margin1} $\{margin2}; 270 | \`` 271 | ).then(errorResponse => { 272 | assert.isTrue(errorResponse.success); 273 | assert.strictEqual(errorResponse.body.length, 0); 274 | }); 275 | }); 276 | 277 | it('should not return an error for contextual selector (#71)', () => { 278 | return getSemanticDiagnosticsForFile( 279 | `let css: any = {}; const q = css.a\` 280 | html.test & { 281 | display: none; 282 | } 283 | \`` 284 | ).then(errorResponse => { 285 | assert.isTrue(errorResponse.success); 286 | assert.strictEqual(errorResponse.body.length, 0); 287 | }); 288 | }); 289 | 290 | it('should not return an error for placeholder used in contextual selector (#71)', async () => { 291 | { 292 | const errorResponse = await getSemanticDiagnosticsForFile( 293 | `let css: any = {}; let FlipContainer = 'button'; const q = css.a\` 294 | position: relative; 295 | 296 | $\{FlipContainer}:hover & { 297 | transform: rotateY(180deg); 298 | } 299 | \``); 300 | assert.isTrue(errorResponse.success); 301 | assert.strictEqual(errorResponse.body.length, 0); 302 | } 303 | { 304 | // #67 part 1 305 | const errorResponse = await getSemanticDiagnosticsForFile( 306 | `let css: any = {}; let OtherStyledElm = 'button'; const q = css.a\` 307 | \${OtherStyledElm}:not([value=""]) + & { 308 | transform: rotateY(180deg); 309 | } 310 | \``); 311 | assert.isTrue(errorResponse.success); 312 | assert.strictEqual(errorResponse.body.length, 0); 313 | } 314 | { 315 | // #67 part 2 316 | const errorResponse = await getSemanticDiagnosticsForFile( 317 | `let css: any = {}; let OtherStyledElm = 'button'; const q = css.a\` 318 | \${OtherStyledElm} + &, 319 | \${OtherStyledElm}:not([value=""]) + & { 320 | transform: rotateY(180deg); 321 | } 322 | \``); 323 | assert.isTrue(errorResponse.success); 324 | assert.strictEqual(errorResponse.body.length, 0); 325 | } 326 | }); 327 | 328 | it('should not return an error for custom function (#21)', async () => { 329 | const errorResponse = await getSemanticDiagnosticsForFile( 330 | `function css(): any {}; const q = css<{}>()(window.blur)\` 331 | display: none; 332 | \``); 333 | assert.isTrue(errorResponse.success); 334 | assert.strictEqual(errorResponse.body.length, 0); 335 | }); 336 | 337 | it('should not return an error for empty sub-rulesets (#50)', async () => { 338 | const errorResponse = await getSemanticDiagnosticsForFile( 339 | `let css: any = {}; const q = css.a\` 340 | :nth-of-type(1) { 341 | \${true ? "display: initial" : "display: hidden"} 342 | } 343 | \``); 344 | assert.isTrue(errorResponse.success); 345 | assert.strictEqual(errorResponse.body.length, 0); 346 | }); 347 | 348 | it('should not return an error (#74)', async () => { 349 | const errorResponse = await getSemanticDiagnosticsForFile( 350 | `let css: any = {}; 351 | const ListNoteItem = 'bla'; 352 | const ListNoteTitle = css.span\` 353 | font-weight: bold; 354 | color: \${props => props.theme.primaryColor}; 355 | \${ListNoteItem}:hover & { 356 | text-decoration: underline; 357 | } 358 | \`;`); 359 | assert.isTrue(errorResponse.success); 360 | assert.strictEqual(errorResponse.body.length, 0); 361 | }); 362 | 363 | it('should not return an error for child selector (#75)', async () => { 364 | const errorResponse = await getSemanticDiagnosticsForFile( 365 | `let css: any = {}; 366 | const ListNoteItem = 'bla'; 367 | const ListNoteTitle = css.span\` 368 | width: 100%; 369 | > \${ListNoteItem}:hover { 370 | color: red; 371 | } 372 | \`;`); 373 | assert.isTrue(errorResponse.success); 374 | assert.strictEqual(errorResponse.body.length, 0); 375 | }); 376 | 377 | it('should include error for unknown property in selector', async () => { 378 | const errorResponse = await getSemanticDiagnosticsForFile( 379 | `let css: any = {}; 380 | const ListNoteItem = 'bla'; 381 | const ListNoteTitle = css.span\` 382 | width: 100%; 383 | &:hover { 384 | noSuch: red; 385 | } 386 | \`;`); 387 | assert.isTrue(errorResponse.success); 388 | assert.strictEqual(errorResponse.body.length, 1); 389 | }); 390 | 391 | it('should not error for newer properties(#95, #53)', async () => { 392 | const errorResponse = await getSemanticDiagnosticsForFile( 393 | `let css: any = {}; 394 | const ListNoteTitle = css.span\` 395 | scrollbar-width: 10px; 396 | scrollbar-color: red; 397 | scroll-snap-align: initial; 398 | \`;`); 399 | assert.isTrue(errorResponse.success); 400 | assert.strictEqual(errorResponse.body.length, 0); 401 | }); 402 | }); 403 | -------------------------------------------------------------------------------- /e2e/tests/outliningSpans.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | const path = require('path'); 3 | const assert = require('chai').assert; 4 | const createServer = require('../server-fixture'); 5 | const { openMockFile, getFirstResponseOfType } = require('./_helpers'); 6 | 7 | const mockFileName = path.join(__dirname, '..', 'project-fixture', 'main.ts'); 8 | 9 | describe('OutliningSpans', () => { 10 | it('should return basic css outlining spans', async () => { 11 | const spans = await getOutlingSpansForMockFile([ 12 | 'const q = css`', 13 | 'a {', 14 | 'color: red;', 15 | '}', 16 | 'div {', 17 | '', 18 | '}', 19 | '`' 20 | ].join('\n')); 21 | 22 | assert.strictEqual(spans.length, 3); 23 | 24 | // The first span represents the root 25 | const [, span2, span3] = spans; 26 | assertPosition(span2.textSpan.start, 2, 1); 27 | assertPosition(span2.textSpan.end, 3, 1); 28 | 29 | assertPosition(span3.textSpan.start, 5, 1); 30 | assertPosition(span3.textSpan.end, 6, 1); 31 | }); 32 | }); 33 | 34 | function getOutlingSpansForMockFile(contents) { 35 | const server = createServer(); 36 | openMockFile(server, mockFileName, contents); 37 | server.sendCommand('getOutliningSpans', { file: mockFileName }); 38 | 39 | return server.close().then(() => getFirstResponseOfType('getOutliningSpans', server).body); 40 | } 41 | 42 | function assertPosition(pos, line, offset) { 43 | assert.strictEqual(pos.line, line); 44 | assert.strictEqual(pos.offset, offset); 45 | } -------------------------------------------------------------------------------- /e2e/tests/quickFix.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const assert = require('chai').assert; 3 | const path = require('path'); 4 | const createServer = require('../server-fixture'); 5 | const { openMockFile, getFirstResponseOfType, getResponsesOfType } = require('./_helpers'); 6 | 7 | const mockFileName = path.join(__dirname, '..', 'project-fixture', 'main.ts'); 8 | 9 | describe('QuickFix', () => { 10 | it('should return quickFix for misspelled properties fooa', () => { 11 | const server = createServer(); 12 | openMockFile(server, mockFileName, 'const q = css`boarder: 1px solid black;`'); 13 | server.sendCommand('getCodeFixes', { 14 | file: mockFileName, 15 | startLine: 1, 16 | startOffset: 16, 17 | endLine: 1, 18 | endOffset: 16, 19 | errorCodes: [9999] 20 | }); 21 | 22 | return server.close().then(() => { 23 | const response = getFirstResponseOfType('getCodeFixes', server); 24 | assert.isTrue(response.success); 25 | assert.strictEqual(response.body.length, 3); 26 | assert.isOk(response.body.find(fix => fix.description === 'Rename to \'border\'')); 27 | }); 28 | }); 29 | 30 | it('should not return quickFixes for correctly spelled properties', () => { 31 | const server = createServer(); 32 | openMockFile(server, mockFileName, 'const q = css`border: 1px solid black;`'); 33 | server.sendCommand('getCodeFixes', { 34 | file: mockFileName, 35 | startLine: 1, 36 | startOffset: 16, 37 | endLine: 1, 38 | endOffset: 16, 39 | errorCodes: [9999] 40 | }); 41 | 42 | return server.close().then(() => { 43 | const response = getFirstResponseOfType('getCodeFixes', server); 44 | assert.isTrue(response.success); 45 | assert.strictEqual(response.body.length, 0); 46 | }); 47 | }); 48 | 49 | it('should only return spelling quickFix when range includes misspelled property', () => { 50 | const server = createServer(); 51 | openMockFile(server, mockFileName, 'const q = css`boarder: 1px solid black;`'); 52 | server.sendCommand('getCodeFixes', { 53 | file: mockFileName, 54 | startLine: 1, 55 | startOffset: 14, 56 | endLine: 1, 57 | endOffset: 14, 58 | errorCodes: [9999] 59 | }); 60 | 61 | server.sendCommand('getCodeFixes', { 62 | file: mockFileName, 63 | startLine: 1, 64 | startOffset: 22, 65 | endLine: 1, 66 | endOffset: 22, 67 | errorCodes: [9999] 68 | }); 69 | 70 | return server.close().then(() => { 71 | const responses = getResponsesOfType('getCodeFixes', server); 72 | assert.strictEqual(responses.length, 2); 73 | { 74 | const response = responses[0] 75 | assert.isTrue(response.success); 76 | assert.strictEqual(response.body.length, 0); 77 | } 78 | { 79 | const response = responses[1] 80 | assert.isTrue(response.success); 81 | assert.strictEqual(response.body.length, 0); 82 | } 83 | }); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-styled-plugin", 3 | "version": "0.18.3", 4 | "description": "TypeScript language service plugin that adds IntelliSense for styled components", 5 | "keywords": [ 6 | "TypeScript", 7 | "styled", 8 | "styled-components", 9 | "styled components", 10 | "css" 11 | ], 12 | "main": "lib/index.js", 13 | "author": "Microsoft", 14 | "license": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/Microsoft/typescript-styled-plugin.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/Microsoft/typescript-styled-plugin/issues" 21 | }, 22 | "dependencies": { 23 | "typescript-template-language-service-decorator": "^2.3.2", 24 | "vscode-css-languageservice": "^5.1.4", 25 | "vscode-emmet-helper": "^2.6.4", 26 | "vscode-languageserver-textdocument": "^1.0.1", 27 | "vscode-languageserver-types": "^3.16.0" 28 | }, 29 | "files": [ 30 | "lib" 31 | ], 32 | "devDependencies": { 33 | "@types/chai": "^4.1.4", 34 | "@types/mocha": "^5.2.4", 35 | "@types/node": "^16.4.2", 36 | "@typescript-eslint/eslint-plugin": "^5.6.0", 37 | "@typescript-eslint/parser": "^5.6.0", 38 | "chai": "^4.1.2", 39 | "eslint": "^8.4.0", 40 | "eslint-plugin-prettier": "^4.0.0", 41 | "glob": "^7.1.2", 42 | "mocha": "^10.2.0", 43 | "prettier": "^2.2.1", 44 | "typescript": "^4.5.2" 45 | }, 46 | "scripts": { 47 | "compile": "tsc -p .", 48 | "watch:compile": "tsc --watch -p .", 49 | "e2e": "mocha e2e/tests --slow 2000 --timeout 10000", 50 | "lint": "eslint src", 51 | "unit": "mocha ./lib/test" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/_config.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | export const pluginName = 'ts-styled-plugin'; -------------------------------------------------------------------------------- /src/_configuration.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | export interface StyledPluginConfiguration { 5 | readonly tags: ReadonlyArray; 6 | readonly validate: boolean; 7 | readonly lint: { [key: string]: any }; 8 | readonly emmet: { [key: string]: any }; 9 | } 10 | 11 | export class ConfigurationManager { 12 | 13 | private static readonly defaultConfiguration: StyledPluginConfiguration = { 14 | tags: ['styled', 'css', 'extend', 'injectGlobal', 'createGlobalStyle', 'keyframes'], 15 | validate: true, 16 | lint: { 17 | emptyRules: 'ignore', 18 | }, 19 | emmet: {}, 20 | }; 21 | 22 | private readonly _configUpdatedListeners = new Set<() => void>(); 23 | 24 | public get config(): StyledPluginConfiguration { return this._configuration; } 25 | private _configuration: StyledPluginConfiguration = ConfigurationManager.defaultConfiguration; 26 | 27 | public updateFromPluginConfig(config: StyledPluginConfiguration) { 28 | const lint = Object.assign({}, ConfigurationManager.defaultConfiguration.lint, config.lint || {}); 29 | 30 | this._configuration = { 31 | tags: config.tags || ConfigurationManager.defaultConfiguration.tags, 32 | validate: typeof config.validate !== 'undefined' ? config.validate : ConfigurationManager.defaultConfiguration.validate, 33 | lint, 34 | emmet: config.emmet || ConfigurationManager.defaultConfiguration.emmet, 35 | }; 36 | 37 | for (const listener of this._configUpdatedListeners) { 38 | listener(); 39 | } 40 | } 41 | 42 | public onUpdatedConfig(listener: () => void) { 43 | this._configUpdatedListeners.add(listener); 44 | } 45 | } -------------------------------------------------------------------------------- /src/_language-service.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | // 4 | // Original code forked from https://github.com/Quramy/ts-graphql-plugin 5 | 6 | import { Logger, TemplateContext, TemplateLanguageService } from 'typescript-template-language-service-decorator'; 7 | import * as ts from 'typescript/lib/tsserverlibrary'; 8 | import { getCSSLanguageService, getSCSSLanguageService, LanguageService, FoldingRange } from 'vscode-css-languageservice'; 9 | import { doComplete as emmetDoComplete } from 'vscode-emmet-helper'; 10 | import * as vscode from 'vscode-languageserver-types'; 11 | import * as config from './_config'; 12 | import { ConfigurationManager } from './_configuration'; 13 | import { VirtualDocumentProvider } from './_virtual-document-provider'; 14 | import { TextDocument } from 'vscode-languageserver-textdocument'; 15 | 16 | 17 | const cssErrorCode = 9999; 18 | 19 | function arePositionsEqual( 20 | left: ts.LineAndCharacter, 21 | right: ts.LineAndCharacter 22 | ): boolean { 23 | return left.line === right.line && left.character === right.character; 24 | } 25 | 26 | function isAfter( 27 | left: vscode.Position, 28 | right: vscode.Position 29 | ): boolean { 30 | return right.line > left.line || (right.line === left.line && right.character >= left.character); 31 | } 32 | 33 | function overlaps( 34 | a: vscode.Range, 35 | b: vscode.Range 36 | ): boolean { 37 | return !isAfter(a.end, b.start) && !isAfter(b.end, a.start); 38 | } 39 | 40 | const emptyCompletionList: vscode.CompletionList = { 41 | items: [], 42 | isIncomplete: false, 43 | }; 44 | 45 | class CompletionsCache { 46 | private _cachedCompletionsFile?: string; 47 | private _cachedCompletionsPosition?: ts.LineAndCharacter; 48 | private _cachedCompletionsContent?: string; 49 | private _completions?: vscode.CompletionList; 50 | 51 | public getCached( 52 | context: TemplateContext, 53 | position: ts.LineAndCharacter 54 | ): vscode.CompletionList | undefined { 55 | if (this._completions 56 | && context.fileName === this._cachedCompletionsFile 57 | && this._cachedCompletionsPosition && arePositionsEqual(position, this._cachedCompletionsPosition) 58 | && context.text === this._cachedCompletionsContent 59 | ) { 60 | return this._completions; 61 | } 62 | 63 | return undefined; 64 | } 65 | 66 | public updateCached( 67 | context: TemplateContext, 68 | position: ts.LineAndCharacter, 69 | completions: vscode.CompletionList 70 | ) { 71 | this._cachedCompletionsFile = context.fileName; 72 | this._cachedCompletionsPosition = position; 73 | this._cachedCompletionsContent = context.text; 74 | this._completions = completions; 75 | } 76 | } 77 | 78 | export class StyledTemplateLanguageService implements TemplateLanguageService { 79 | private _cssLanguageService?: LanguageService; 80 | private _scssLanguageService?: LanguageService; 81 | private _completionsCache = new CompletionsCache(); 82 | 83 | constructor( 84 | private readonly typescript: typeof ts, 85 | private readonly configurationManager: ConfigurationManager, 86 | private readonly virtualDocumentFactory: VirtualDocumentProvider, 87 | private readonly logger: Logger // tslint:disable-line 88 | ) { 89 | configurationManager.onUpdatedConfig(() => { 90 | if (this._cssLanguageService) { 91 | this._cssLanguageService.configure(this.configurationManager.config); 92 | } 93 | if (this._scssLanguageService) { 94 | this._scssLanguageService.configure(this.configurationManager.config); 95 | } 96 | }); 97 | } 98 | 99 | private get cssLanguageService(): LanguageService { 100 | if (!this._cssLanguageService) { 101 | this._cssLanguageService = getCSSLanguageService(); 102 | this._cssLanguageService.configure(this.configurationManager.config); 103 | } 104 | return this._cssLanguageService; 105 | } 106 | 107 | private get scssLanguageService(): LanguageService { 108 | if (!this._scssLanguageService) { 109 | this._scssLanguageService = getSCSSLanguageService(); 110 | this._scssLanguageService.configure(this.configurationManager.config); 111 | } 112 | return this._scssLanguageService; 113 | } 114 | 115 | public getCompletionsAtPosition( 116 | context: TemplateContext, 117 | position: ts.LineAndCharacter 118 | ): ts.WithMetadata { 119 | const items = this.getCompletionItems(context, position); 120 | const doc = this.virtualDocumentFactory.createVirtualDocument(context); 121 | const wrapper = this.virtualDocumentFactory.getVirtualDocumentWrapper(context); 122 | return translateCompletionItemsToCompletionInfo(this.typescript, items, doc, wrapper); 123 | } 124 | 125 | public getCompletionEntryDetails( 126 | context: TemplateContext, 127 | position: ts.LineAndCharacter, 128 | name: string 129 | ): ts.CompletionEntryDetails { 130 | const item = this.getCompletionItems(context, position).items.find(x => x.label === name); 131 | if (!item) { 132 | return { 133 | name, 134 | kind: this.typescript.ScriptElementKind.unknown, 135 | kindModifiers: '', 136 | tags: [], 137 | displayParts: toDisplayParts(name), 138 | documentation: [], 139 | }; 140 | } 141 | return translateCompletionItemsToCompletionEntryDetails(this.typescript, item); 142 | } 143 | 144 | public getQuickInfoAtPosition( 145 | context: TemplateContext, 146 | position: ts.LineAndCharacter 147 | ): ts.QuickInfo | undefined { 148 | const doc = this.virtualDocumentFactory.createVirtualDocument(context); 149 | const stylesheet = this.scssLanguageService.parseStylesheet(doc); 150 | const hover = this.scssLanguageService.doHover(doc, this.virtualDocumentFactory.toVirtualDocPosition(position), stylesheet); 151 | if (hover) { 152 | return this.translateHover(hover, this.virtualDocumentFactory.toVirtualDocPosition(position), context); 153 | } 154 | return undefined; 155 | } 156 | 157 | public getSemanticDiagnostics( 158 | context: TemplateContext 159 | ): ts.Diagnostic[] { 160 | const doc = this.virtualDocumentFactory.createVirtualDocument(context); 161 | const stylesheet = this.scssLanguageService.parseStylesheet(doc); 162 | return this.translateDiagnostics( 163 | this.scssLanguageService.doValidation(doc, stylesheet), 164 | doc, 165 | context, 166 | context.text).filter(x => !!x) as ts.Diagnostic[]; 167 | } 168 | 169 | public getSupportedCodeFixes(): number[] { 170 | return [cssErrorCode]; 171 | } 172 | 173 | public getCodeFixesAtPosition( 174 | context: TemplateContext, 175 | start: number, 176 | end: number, 177 | // _errorCodes: number[], 178 | // _format: ts.FormatCodeSettings 179 | ): ts.CodeAction[] { 180 | const doc = this.virtualDocumentFactory.createVirtualDocument(context); 181 | const stylesheet = this.scssLanguageService.parseStylesheet(doc); 182 | const range = this.toVsRange(context, start, end); 183 | const diagnostics = this.scssLanguageService.doValidation(doc, stylesheet) 184 | .filter(diagnostic => overlaps(diagnostic.range, range)); 185 | 186 | return this.translateCodeActions( 187 | context, 188 | this.scssLanguageService.doCodeActions(doc, range, { diagnostics }, stylesheet)); 189 | } 190 | 191 | public getOutliningSpans( 192 | context: TemplateContext 193 | ): ts.OutliningSpan[] { 194 | const doc = this.virtualDocumentFactory.createVirtualDocument(context); 195 | const ranges = this.scssLanguageService.getFoldingRanges(doc); 196 | return ranges 197 | .filter(range => { 198 | // Filter out ranges outside on last line 199 | const end = context.toOffset({ 200 | line: range.endLine, 201 | character: range.endCharacter || 0, 202 | }); 203 | return end < context.text.length; 204 | }) 205 | .map(range => this.translateOutliningSpan(context, range)); 206 | } 207 | 208 | private toVsRange( 209 | context: TemplateContext, 210 | start: number, 211 | end: number 212 | ): vscode.Range { 213 | return { 214 | start: this.virtualDocumentFactory.toVirtualDocPosition(context.toPosition(start)), 215 | end: this.virtualDocumentFactory.toVirtualDocPosition(context.toPosition(end)), 216 | }; 217 | } 218 | 219 | private getCompletionItems( 220 | context: TemplateContext, 221 | position: ts.LineAndCharacter 222 | ): vscode.CompletionList { 223 | const cached = this._completionsCache.getCached(context, position); 224 | const completions: vscode.CompletionList = { 225 | isIncomplete: false, 226 | items: [], 227 | }; 228 | 229 | if (cached) { 230 | return cached; 231 | } 232 | 233 | /** 234 | * This would happen if a ` is triggered causing VSCode to open up two ``. At this stage completions aren't needed 235 | * but they are still requested. 236 | * Due to the fact there's nothing to complete (empty template) the language servers below end up requesting everything, 237 | * causing a 3-4 second delay. When a template string is opened up we should do nothing and return an empty list. 238 | * 239 | * Also fixes: https://github.com/styled-components/vscode-styled-components/issues/276 240 | **/ 241 | if (context.node.getText() === '``') { 242 | return completions; 243 | } 244 | 245 | const doc = this.virtualDocumentFactory.createVirtualDocument(context); 246 | const virtualPosition = this.virtualDocumentFactory.toVirtualDocPosition(position); 247 | const stylesheet = this.scssLanguageService.parseStylesheet(doc); 248 | this.cssLanguageService.setCompletionParticipants([]); 249 | const emmetResults = emmetDoComplete(doc, virtualPosition, 'css', this.configurationManager.config.emmet) || emptyCompletionList; 250 | const completionsCss = this.cssLanguageService.doComplete(doc, virtualPosition, stylesheet) || emptyCompletionList; 251 | const completionsScss = this.scssLanguageService.doComplete(doc, virtualPosition, stylesheet) || emptyCompletionList; 252 | completionsScss.items = filterScssCompletionItems(completionsScss.items); 253 | 254 | completions.items = [...completionsCss.items, ...completionsScss.items]; 255 | if (emmetResults.items.length) { 256 | completions.items.push(...emmetResults.items); 257 | completions.isIncomplete = true; 258 | } 259 | this._completionsCache.updateCached(context, position, completions); 260 | return completions; 261 | } 262 | 263 | private translateDiagnostics( 264 | diagnostics: vscode.Diagnostic[], 265 | doc: TextDocument, 266 | context: TemplateContext, 267 | content: string 268 | ) { 269 | const sourceFile = context.node.getSourceFile(); 270 | return diagnostics.map(diag => 271 | this.translateDiagnostic(diag, sourceFile, doc, context, content)); 272 | } 273 | 274 | private translateDiagnostic( 275 | diagnostic: vscode.Diagnostic, 276 | file: ts.SourceFile, 277 | doc: TextDocument, 278 | context: TemplateContext, 279 | content: string 280 | ): ts.Diagnostic | undefined { 281 | // Make sure returned error is within the real document 282 | if (diagnostic.range.start.line === 0 283 | || diagnostic.range.start.line > doc.lineCount 284 | || diagnostic.range.start.character >= content.length 285 | ) { 286 | return undefined; 287 | } 288 | 289 | const start = context.toOffset(this.virtualDocumentFactory.fromVirtualDocPosition(diagnostic.range.start)); 290 | const length = context.toOffset(this.virtualDocumentFactory.fromVirtualDocPosition(diagnostic.range.end)) - start; 291 | const code = typeof diagnostic.code === 'number' ? diagnostic.code : cssErrorCode; 292 | return { 293 | code, 294 | messageText: diagnostic.message, 295 | category: translateSeverity(this.typescript, diagnostic.severity), 296 | file, 297 | start, 298 | length, 299 | source: config.pluginName, 300 | }; 301 | } 302 | 303 | private translateHover( 304 | hover: vscode.Hover, 305 | position: ts.LineAndCharacter, 306 | context: TemplateContext 307 | ): ts.QuickInfo { 308 | const contents: ts.SymbolDisplayPart[] = []; 309 | const convertPart = (hoverContents: typeof hover.contents) => { 310 | if (typeof hoverContents === 'string') { 311 | contents.push({ kind: 'unknown', text: hoverContents }); 312 | } else if (Array.isArray(hoverContents)) { 313 | hoverContents.forEach(convertPart); 314 | } else { 315 | contents.push({ kind: 'unknown', text: hoverContents.value }); 316 | } 317 | }; 318 | convertPart(hover.contents); 319 | const start = context.toOffset(this.virtualDocumentFactory.fromVirtualDocPosition(hover.range ? hover.range.start : position)); 320 | return { 321 | kind: this.typescript.ScriptElementKind.unknown, 322 | kindModifiers: '', 323 | textSpan: { 324 | start, 325 | length: hover.range ? context.toOffset(this.virtualDocumentFactory.fromVirtualDocPosition(hover.range.end)) - start : 1, 326 | }, 327 | displayParts: [], 328 | documentation: contents, 329 | tags: [], 330 | }; 331 | } 332 | 333 | private translateCodeActions( 334 | context: TemplateContext, 335 | codeActions: vscode.Command[] 336 | ): ts.CodeAction[] { 337 | const actions: ts.CodeAction[] = []; 338 | for (const vsAction of codeActions) { 339 | if (vsAction.command !== '_css.applyCodeAction') { 340 | continue; 341 | } 342 | 343 | const edits = vsAction.arguments && vsAction.arguments[2] as vscode.TextEdit[]; 344 | if (edits) { 345 | actions.push({ 346 | description: vsAction.title, 347 | changes: edits.map(edit => this.translateTextEditToFileTextChange(context, edit)), 348 | }); 349 | } 350 | } 351 | return actions; 352 | } 353 | 354 | private translateTextEditToFileTextChange( 355 | context: TemplateContext, 356 | textEdit: vscode.TextEdit 357 | ): ts.FileTextChanges { 358 | const start = context.toOffset(this.virtualDocumentFactory.fromVirtualDocPosition(textEdit.range.start)); 359 | const end = context.toOffset(this.virtualDocumentFactory.fromVirtualDocPosition(textEdit.range.end)); 360 | return { 361 | fileName: context.fileName, 362 | textChanges: [{ 363 | newText: textEdit.newText, 364 | span: { 365 | start, 366 | length: end - start, 367 | }, 368 | }], 369 | }; 370 | } 371 | 372 | private translateOutliningSpan( 373 | context: TemplateContext, 374 | range: FoldingRange 375 | ): ts.OutliningSpan { 376 | const startOffset = context.toOffset(this.virtualDocumentFactory.fromVirtualDocPosition({ line: range.startLine, character: range.startCharacter || 0 })); 377 | const endOffset = context.toOffset(this.virtualDocumentFactory.fromVirtualDocPosition({ line: range.endLine, character: range.endCharacter || 0 })); 378 | const span = { 379 | start: startOffset, 380 | length: endOffset - startOffset, 381 | }; 382 | 383 | return { 384 | autoCollapse: false, 385 | kind: this.typescript.OutliningSpanKind.Code, 386 | bannerText: '', 387 | textSpan: span, 388 | hintSpan: span, 389 | }; 390 | } 391 | } 392 | 393 | function filterScssCompletionItems( 394 | items: vscode.CompletionItem[] 395 | ): vscode.CompletionItem[] { 396 | return items.filter(item => (item.kind === vscode.CompletionItemKind.Function && item.label.substr(0, 1) === ':')); 397 | } 398 | 399 | function translateCompletionItemsToCompletionInfo( 400 | typescript: typeof ts, 401 | items: vscode.CompletionList, 402 | doc: TextDocument, 403 | wrapper: string 404 | ): ts.WithMetadata { 405 | return { 406 | metadata: { 407 | isIncomplete: items.isIncomplete, 408 | }, 409 | isGlobalCompletion: false, 410 | isMemberCompletion: false, 411 | isNewIdentifierLocation: false, 412 | entries: items.items.map(x => translateCompetionEntry(typescript, x, doc, wrapper)), 413 | }; 414 | } 415 | 416 | function translateCompletionItemsToCompletionEntryDetails( 417 | typescript: typeof ts, 418 | item: vscode.CompletionItem 419 | ): ts.CompletionEntryDetails { 420 | return { 421 | name: item.label, 422 | kind: item.kind ? translateCompletionItemKind(typescript, item.kind) : typescript.ScriptElementKind.unknown, 423 | kindModifiers: getKindModifiers(item), 424 | displayParts: toDisplayParts(item.detail), 425 | documentation: toDisplayParts(item.documentation), 426 | tags: [], 427 | }; 428 | } 429 | 430 | function translateCompetionEntry( 431 | typescript: typeof ts, 432 | item: vscode.CompletionItem, 433 | doc: TextDocument, 434 | wrapper: string 435 | ): ts.CompletionEntry { 436 | return { 437 | name: item.label, 438 | kind: item.kind ? translateCompletionItemKind(typescript, item.kind) : typescript.ScriptElementKind.unknown, 439 | kindModifiers: getKindModifiers(item), 440 | sortText: item.sortText || item.label, 441 | replacementSpan: { 442 | // The correct offset for start seems to be the range.start minus the wrapper 443 | start: doc.offsetAt((item as any).textEdit.range.start) - wrapper.length, 444 | length: doc.offsetAt((item as any).textEdit.range.end) - doc.offsetAt((item as any).textEdit.range.start), 445 | }, 446 | }; 447 | } 448 | 449 | function translateCompletionItemKind( 450 | typescript: typeof ts, 451 | kind: vscode.CompletionItemKind 452 | ): ts.ScriptElementKind { 453 | switch (kind) { 454 | case vscode.CompletionItemKind.Method: 455 | return typescript.ScriptElementKind.memberFunctionElement; 456 | case vscode.CompletionItemKind.Function: 457 | return typescript.ScriptElementKind.functionElement; 458 | case vscode.CompletionItemKind.Constructor: 459 | return typescript.ScriptElementKind.constructorImplementationElement; 460 | case vscode.CompletionItemKind.Field: 461 | case vscode.CompletionItemKind.Variable: 462 | return typescript.ScriptElementKind.variableElement; 463 | case vscode.CompletionItemKind.Class: 464 | return typescript.ScriptElementKind.classElement; 465 | case vscode.CompletionItemKind.Interface: 466 | return typescript.ScriptElementKind.interfaceElement; 467 | case vscode.CompletionItemKind.Module: 468 | return typescript.ScriptElementKind.moduleElement; 469 | case vscode.CompletionItemKind.Property: 470 | return typescript.ScriptElementKind.memberVariableElement; 471 | case vscode.CompletionItemKind.Unit: 472 | case vscode.CompletionItemKind.Value: 473 | return typescript.ScriptElementKind.constElement; 474 | case vscode.CompletionItemKind.Enum: 475 | return typescript.ScriptElementKind.enumElement; 476 | case vscode.CompletionItemKind.Keyword: 477 | return typescript.ScriptElementKind.keyword; 478 | case vscode.CompletionItemKind.Color: 479 | return typescript.ScriptElementKind.constElement; 480 | case vscode.CompletionItemKind.Reference: 481 | return typescript.ScriptElementKind.alias; 482 | case vscode.CompletionItemKind.File: 483 | return typescript.ScriptElementKind.moduleElement; 484 | case vscode.CompletionItemKind.Snippet: 485 | case vscode.CompletionItemKind.Text: 486 | default: 487 | return typescript.ScriptElementKind.unknown; 488 | } 489 | } 490 | 491 | function getKindModifiers(item: vscode.CompletionItem): string { 492 | if (item.kind === vscode.CompletionItemKind.Color) { 493 | return 'color'; 494 | } 495 | return ''; 496 | } 497 | 498 | function translateSeverity( 499 | typescript: typeof ts, 500 | severity: vscode.DiagnosticSeverity | undefined 501 | ): ts.DiagnosticCategory { 502 | switch (severity) { 503 | case vscode.DiagnosticSeverity.Information: 504 | case vscode.DiagnosticSeverity.Hint: 505 | return typescript.DiagnosticCategory.Message; 506 | 507 | case vscode.DiagnosticSeverity.Warning: 508 | return typescript.DiagnosticCategory.Warning; 509 | 510 | case vscode.DiagnosticSeverity.Error: 511 | default: 512 | return typescript.DiagnosticCategory.Error; 513 | } 514 | } 515 | 516 | function toDisplayParts( 517 | text: string | vscode.MarkupContent | undefined 518 | ): ts.SymbolDisplayPart[] { 519 | if (!text) { 520 | return []; 521 | } 522 | return [{ 523 | kind: 'text', 524 | text: typeof text === 'string' ? text : text.value, 525 | }]; 526 | } -------------------------------------------------------------------------------- /src/_logger.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from 'typescript-template-language-service-decorator'; 2 | import { pluginName } from './_config'; 3 | 4 | export class LanguageServiceLogger implements Logger { 5 | constructor( 6 | private readonly info: ts.server.PluginCreateInfo 7 | ) { } 8 | 9 | public log(msg: string) { 10 | this.info.project.projectService.logger.info(`[${pluginName}] ${msg}`); 11 | } 12 | } -------------------------------------------------------------------------------- /src/_plugin.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | import { decorateWithTemplateLanguageService, TemplateSettings } from 'typescript-template-language-service-decorator'; 4 | import * as ts from 'typescript/lib/tsserverlibrary'; 5 | import { ConfigurationManager } from './_configuration'; 6 | import { StyledTemplateLanguageService } from './_language-service'; 7 | import { LanguageServiceLogger } from './_logger'; 8 | import { getSubstitutions } from './_substituter'; 9 | import { StyledVirtualDocumentFactory } from './_virtual-document-provider'; 10 | 11 | export class StyledPlugin { 12 | 13 | private _logger?: LanguageServiceLogger; 14 | private readonly _configManager = new ConfigurationManager(); 15 | 16 | public constructor( 17 | private readonly typescript: typeof ts 18 | ) { } 19 | 20 | public create(info: ts.server.PluginCreateInfo): ts.LanguageService { 21 | this._logger = new LanguageServiceLogger(info); 22 | this._configManager.updateFromPluginConfig(info.config); 23 | 24 | this._logger.log('config: ' + JSON.stringify(this._configManager.config)); 25 | 26 | if (!isValidTypeScriptVersion(this.typescript)) { 27 | this._logger.log('Invalid typescript version detected. TypeScript 3.x required.'); 28 | return info.languageService; 29 | } 30 | 31 | return decorateWithTemplateLanguageService( 32 | this.typescript, 33 | info.languageService, 34 | info.project, 35 | new StyledTemplateLanguageService(this.typescript, this._configManager, new StyledVirtualDocumentFactory(), this._logger), 36 | getTemplateSettings(this._configManager), 37 | { logger: this._logger }); 38 | } 39 | 40 | public onConfigurationChanged(config: any) { 41 | if (this._logger) { 42 | this._logger.log('onConfigurationChanged'); 43 | } 44 | this._configManager.updateFromPluginConfig(config); 45 | } 46 | } 47 | 48 | export function getTemplateSettings(configManager: ConfigurationManager): TemplateSettings { 49 | return { 50 | get tags() { return configManager.config.tags; }, 51 | enableForStringWithSubstitutions: true, 52 | getSubstitutions(templateString, spans): string { 53 | return getSubstitutions(templateString, spans); 54 | }, 55 | }; 56 | } 57 | 58 | function isValidTypeScriptVersion(typescript: typeof ts): boolean { 59 | const [major] = typescript.version.split('.'); 60 | return +major >= 3; 61 | } 62 | -------------------------------------------------------------------------------- /src/_substituter.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | export function getSubstitutions( 5 | contents: string, 6 | spans: ReadonlyArray<{ start: number, end: number }> 7 | ): string { 8 | const parts: string[] = []; 9 | let lastIndex = 0; 10 | const lineStarts = contents 11 | .split('\n') 12 | .map(line => line.length) 13 | .reduce((previousValue, currentValue, currentIndex) => [...previousValue, currentValue + previousValue[currentIndex] + 1], [0]); 14 | let lineStartIndex = 0; 15 | for (const span of spans) { 16 | while (lineStarts[lineStartIndex] <= span.start) { 17 | lineStartIndex++; 18 | } 19 | const preTillLineStart = contents.slice(lineStarts[lineStartIndex - 1], span.start); 20 | const preTillLastIndex = contents.slice(lastIndex, span.start); 21 | const post = contents.slice(span.end); 22 | const placeholder = contents.slice(span.start, span.end); 23 | 24 | parts.push(preTillLastIndex); 25 | parts.push(getSubstitution({ preTillLineStart, preTillLastIndex, placeholder, post })); 26 | lastIndex = span.end; 27 | } 28 | parts.push(contents.slice(lastIndex)); 29 | return parts.join(''); 30 | } 31 | 32 | function getSubstitution( 33 | context: { 34 | placeholder: string, 35 | preTillLineStart: string, 36 | preTillLastIndex: string, 37 | post: string 38 | } 39 | ): string { 40 | // Check to see if it's an in-property interplation, or a mixin, 41 | // and determine which character to use in either case 42 | // if in-property, replace with "xxxxxx" 43 | // if a mixin, replace with " " 44 | const replacementChar = getReplacementCharacter(context.preTillLineStart, context.preTillLastIndex, context.post); 45 | const result = context.placeholder.replace(/./gm, c => c === '\n' ? '\n' : replacementChar); 46 | 47 | // If followed by a semicolon, we may have to eat the semi colon using a false property 48 | if (replacementChar === ' ' && context.post.match(/^\s*;/)) { 49 | // Handle case where we need to eat the semi colon: 50 | // 51 | // styled.x` 52 | // ${'color: red'}; 53 | // ` 54 | // 55 | // vs. the other case where we do not: 56 | // 57 | // styled.x` 58 | // color: ${'red'}; 59 | // ` 60 | if (context.preTillLastIndex.match(/(;|^|\}|\{)[\s|\n]*$/)) { 61 | // Mixin, replace with a dummy variable declaration, so scss server doesn't complain about rogue semicolon 62 | return '$a:0' + result.slice(4); 63 | } 64 | return context.placeholder.replace(/./gm, c => c === '\n' ? '\n' : 'x'); 65 | } 66 | 67 | // Placeholder used as property name: 68 | // 69 | // styled.x` 70 | // ${'color'}: red; 71 | // ` 72 | // 73 | // But note that this shouldn't be included: 74 | // 75 | // styled.x` 76 | // ${'button'}:hover & { 77 | // color: red 78 | // } 79 | // ` 80 | // 81 | // Replace with fake property name 82 | if (context.post.match(/^\s*[:]/) && !context.post.match(/^\s*[:].+?[\{&]/)) { 83 | return '$a' + result.slice(2); 84 | } 85 | 86 | // Placeholder for component 87 | // 88 | // styled.x` 89 | // ${'button'}:hover & { 90 | // color: red 91 | // } 92 | // ` 93 | // Replace with fake selector 94 | if (context.post.match(/^\s*[:].+?[\{&]/)) { 95 | return '&' + ' '.repeat(result.length - 1); 96 | } 97 | 98 | // Placeholder used as hex value: 99 | // 100 | // styled.x` 101 | // color: #${'000'}; 102 | // ` 103 | if (context.preTillLastIndex.match(/#\s*$/)) { 104 | return '000' + ' '.repeat(Math.max(context.placeholder.length - 3, 0)); 105 | } 106 | 107 | return result; 108 | } 109 | 110 | function getReplacementCharacter( 111 | preTillLineStart: string, 112 | preTillLastIndex: string, 113 | post: string 114 | ) { 115 | const emptySpacesRegExp = /(^|\n)\s*$/g; 116 | if (preTillLineStart.match(emptySpacesRegExp) && preTillLastIndex.match(emptySpacesRegExp)) { 117 | if (!post.match(/^\s*[{:,]/)) { // ${'button'} { 118 | return ' '; 119 | } 120 | } 121 | 122 | // If the placeholder looks like a unit that would not work when replaced with an identifier, 123 | // try replaceing with a number. 124 | if (post.match(/^%/)) { 125 | return '0'; 126 | } 127 | 128 | // Otherwise replace with an identifier 129 | return 'x'; 130 | } 131 | -------------------------------------------------------------------------------- /src/_virtual-document-provider.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | import { TemplateContext } from 'typescript-template-language-service-decorator'; 4 | import {TextDocument, Position} from 'vscode-languageserver-textdocument'; 5 | 6 | /** 7 | * Handles mapping between template contents to virtual documents. 8 | */ 9 | export interface VirtualDocumentProvider { 10 | createVirtualDocument(context: TemplateContext): TextDocument; 11 | toVirtualDocPosition(position: ts.LineAndCharacter): ts.LineAndCharacter; 12 | fromVirtualDocPosition(position: ts.LineAndCharacter): ts.LineAndCharacter; 13 | toVirtualDocOffset(offset: number, context: TemplateContext): number; 14 | fromVirtualDocOffset(offset: number, context: TemplateContext): number; 15 | getVirtualDocumentWrapper(context: TemplateContext): string; 16 | } 17 | 18 | /** 19 | * Standard virtual document provider for styled content. 20 | * 21 | * Wraps content in a top level `:root { }` rule to make css language service happy 22 | * since styled allows properties to be top level elements. 23 | */ 24 | export class StyledVirtualDocumentFactory implements VirtualDocumentProvider { 25 | private static readonly wrapperPreRoot = ':root{\n'; 26 | private static readonly wrapperPreKeyframes = '@keyframes custom {\n'; 27 | 28 | public createVirtualDocument( 29 | context: TemplateContext 30 | ): TextDocument { 31 | const contents = `${this.getVirtualDocumentWrapper(context)}${context.text}\n}`; 32 | return { 33 | uri: 'untitled://embedded.scss', 34 | languageId: 'scss', 35 | version: 1, 36 | getText: () => contents, 37 | positionAt: (offset: number) => { 38 | const pos = context.toPosition(this.fromVirtualDocOffset(offset, context)); 39 | return this.toVirtualDocPosition(pos); 40 | }, 41 | offsetAt: (p: Position) => { 42 | const offset = context.toOffset(this.fromVirtualDocPosition(p)); 43 | return this.toVirtualDocOffset(offset, context); 44 | }, 45 | lineCount: contents.split(/\n/g).length + 1, 46 | }; 47 | } 48 | 49 | public toVirtualDocPosition(position: ts.LineAndCharacter): ts.LineAndCharacter { 50 | return { 51 | line: position.line + 1, 52 | character: position.character, 53 | }; 54 | } 55 | 56 | public fromVirtualDocPosition(position: ts.LineAndCharacter): ts.LineAndCharacter { 57 | return { 58 | line: position.line - 1, 59 | character: position.character, 60 | }; 61 | } 62 | 63 | public toVirtualDocOffset(offset: number, context: TemplateContext): number { 64 | return offset + this.getVirtualDocumentWrapper(context).length; 65 | } 66 | 67 | public fromVirtualDocOffset(offset: number, context: TemplateContext): number { 68 | return offset - this.getVirtualDocumentWrapper(context).length; 69 | } 70 | 71 | public getVirtualDocumentWrapper(context: TemplateContext): string { 72 | const tag = (context.node.parent as ts.Node & { tag: any })?.tag?.escapedText; 73 | return tag === 'keyframes' ? StyledVirtualDocumentFactory.wrapperPreKeyframes : StyledVirtualDocumentFactory.wrapperPreRoot; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/api.ts: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT License. 4 | 5 | // Public api that allows the language service to consumed by other libraries 6 | export { StyledTemplateLanguageService } from './_language-service'; 7 | export { StyledPluginConfiguration } from './_configuration'; 8 | export { VirtualDocumentProvider } from './_virtual-document-provider'; 9 | export { getTemplateSettings } from './_plugin'; -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | import * as ts from 'typescript/lib/tsserverlibrary'; 4 | import { StyledPlugin } from './_plugin'; 5 | 6 | export = (mod: { typescript: typeof ts }) => 7 | new StyledPlugin(mod.typescript); 8 | -------------------------------------------------------------------------------- /src/test/substituter.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { assert } from 'chai'; 3 | import 'mocha'; 4 | import { getSubstitutions } from '../_substituter'; 5 | 6 | describe('substituter', () => { 7 | it('should replace property value with x', () => { 8 | assert.deepEqual( 9 | performSubstitutions([ 10 | 'width: 1px;', 11 | `color: \${'red'};`, 12 | 'color: red;', 13 | ].join('\n')), 14 | [ 15 | 'width: 1px;', 16 | `color: xxxxxxxx;`, 17 | 'color: red;', 18 | ].join('\n') 19 | ); 20 | }); 21 | 22 | it('should insert whitespace when placeholder is used a entire property', () => { 23 | assert.deepEqual( 24 | performSubstitutions([ 25 | 'width: 1px;', 26 | `\${'color: red;'}`, 27 | 'color: red;', 28 | ].join('\n')), 29 | [ 30 | 'width: 1px;', 31 | ` `, 32 | 'color: red;', 33 | ].join('\n') 34 | ); 35 | }); 36 | 37 | it('should insert a false property when placeholder is used a entire property with trailing semi-colon', () => { 38 | assert.deepEqual( 39 | performSubstitutions([ 40 | 'width: 1px;', 41 | `\${'color: red'};`, 42 | 'color: red;', 43 | ].join('\n')), 44 | [ 45 | 'width: 1px;', 46 | `$a:0 ;`, 47 | 'color: red;', 48 | ].join('\n') 49 | ); 50 | }); 51 | 52 | it('should add a zero for percent units', () => { 53 | assert.deepEqual( 54 | performSubstitutions( 55 | 'width: ${10}%;' 56 | ), 57 | 'width: 00000%;' 58 | ); 59 | }); 60 | 61 | it('should replace property with fake proeprty when placeholder is used in name (#52)', () => { 62 | assert.deepEqual( 63 | performSubstitutions([ 64 | 'width: 1px;', 65 | `\${123}: 1px;`, 66 | 'color: red;', 67 | ].join('\n')), 68 | [ 69 | 'width: 1px;', 70 | `$axxxx: 1px;`, 71 | 'color: red;', 72 | ].join('\n') 73 | ); 74 | }); 75 | 76 | it('should insert x for placeholder used as rule', () => { 77 | assert.deepEqual( 78 | performSubstitutions([ 79 | '${"button"} {', 80 | 'color: ${"red"};', 81 | '}', 82 | ].join('\n')), 83 | [ 84 | 'xxxxxxxxxxx {', 85 | 'color: xxxxxxxx;', 86 | '}', 87 | ].join('\n') 88 | ); 89 | }); 90 | 91 | it('should insert x for placeholder used as part of a rule (#59)', () => { 92 | assert.deepEqual( 93 | performSubstitutions([ 94 | '${"button"}, ${"a"} {', 95 | 'color: ${"red"};', 96 | '}', 97 | ].join('\n')), 98 | [ 99 | 'xxxxxxxxxxx, xxxxxx {', 100 | 'color: xxxxxxxx;', 101 | '}', 102 | ].join('\n') 103 | ); 104 | }); 105 | 106 | it('should fake out property name when inside nested rule (#54)', () => { 107 | assert.deepEqual( 108 | performSubstitutions([ 109 | '&.buu-foo {', 110 | ' \${"baseShape"};', 111 | ' &.active {', 112 | ' font-size: 2rem;', 113 | ' }', 114 | '}', 115 | ].join('\n')), 116 | [ 117 | '&.buu-foo {', 118 | ' $a:0 ;', 119 | ' &.active {', 120 | ' font-size: 2rem;', 121 | ' }', 122 | '}', 123 | ].join('\n') 124 | ); 125 | }); 126 | 127 | it('should add zeros for color units (#60)', () => { 128 | assert.deepEqual( 129 | performSubstitutions( 130 | 'color: #${1};' 131 | ), 132 | 'color: #000 ;' 133 | ); 134 | }); 135 | 136 | it('should replace adjacent variables with x (#62)', () => { 137 | assert.deepEqual( 138 | performSubstitutions([ 139 | `margin: \${'1px'}\${'1px'};`, 140 | `padding: \${'1px'} \${'1px'};`, 141 | ].join('\n')), 142 | [ 143 | `margin: xxxxxxxxxxxxxxxx;`, 144 | `padding: xxxxxxxx xxxxxxxx;`, 145 | ].join('\n') 146 | ); 147 | }); 148 | 149 | it('should replace placeholder that spans multiple lines with x (#44)', () => { 150 | assert.deepEqual( 151 | performSubstitutions([ 152 | 'background:', 153 | ` $\{'transparent'};`, 154 | ].join('\n')), 155 | [ 156 | 'background:', 157 | ' xxxxxxxxxxxxxxxx;', 158 | ].join('\n') 159 | ); 160 | }); 161 | 162 | it('should replace placeholder used in contextual selector (#71)', () => { 163 | assert.deepEqual( 164 | performSubstitutions([ 165 | 'position: relative;', 166 | '', 167 | '${FlipContainer}:hover & {', 168 | ' transform: rotateY(180deg);', 169 | '}', 170 | ].join('\n')), 171 | [ 172 | 'position: relative;', 173 | '', 174 | '& :hover & {', 175 | ' transform: rotateY(180deg);', 176 | '}', 177 | ].join('\n') 178 | ); 179 | }); 180 | 181 | it('should replace placeholder used in child selector (#75)', () => { 182 | assert.deepEqual( 183 | performSubstitutions([ 184 | 'position: relative;', 185 | '> ${FlipContainer}:hover {', 186 | ' color: red;', 187 | '}', 188 | ].join('\n')), 189 | [ 190 | 'position: relative;', 191 | '> & :hover {', 192 | ' color: red;', 193 | '}', 194 | ].join('\n') 195 | ); 196 | }); 197 | }); 198 | 199 | function performSubstitutions(value: string) { 200 | return getSubstitutions(value, getSpans(value)); 201 | } 202 | 203 | function getSpans(value: string) { 204 | const spans: Array<{ start: number, end: number }> = []; 205 | const re = /(\$\{[^}]*\})/g; 206 | let match: RegExpExecArray | null = re.exec(value); 207 | while (match) { 208 | spans.push({ start: match.index, end: match.index + match[0].length }); 209 | match = re.exec(value); 210 | } 211 | return spans; 212 | } 213 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2019", 5 | "outDir": "lib", 6 | "rootDir": "src", 7 | "strict": true, 8 | "declaration": true, 9 | "sourceMap": true, 10 | "skipLibCheck": true 11 | }, 12 | "exclude": ["lib", "e2e", "node_modules", "test-workspace"] 13 | } 14 | --------------------------------------------------------------------------------