├── .gitattributes ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── jest.config.js ├── package.json ├── prisma └── schema.prisma ├── src ├── cli.ts ├── generator │ ├── apitypes.ts │ ├── helpers.ts │ ├── model.ts │ ├── toc.ts │ └── transformDMMF.ts ├── index.ts ├── printer │ └── index.ts ├── styles │ └── .gitkeep └── tests │ ├── __snapshots__ │ ├── model.test.ts.snap │ └── toc.test.ts.snap │ ├── model.test.ts │ ├── toc.test.ts │ └── transformDMMF.test.ts ├── styles_generator ├── index.html ├── package.json ├── postcss.config.js ├── styles │ └── index.css ├── tailwind.config.js └── yarn.lock ├── tsconfig.json └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | styles_generator/* linguist-vendored 2 | 3 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Setup Node 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 18 23 | cache: yarn 24 | 25 | - name: Install & Test 26 | run: | 27 | yarn install --frozen-lockfile 28 | yarn test 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .env 4 | dmmf.json 5 | *.log 6 | .DS_Store 7 | src/styles/main.css 8 | prisma/docs 9 | 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | styles_generator 2 | prisma/docs 3 | dmmf.json 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Harshit Pant 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 | ## Prisma Documentation Generator 2 | 3 | Automatically generate a reference from your Prisma Schema. This package contains a prisma generator so reference will automatically update everytime you will run `prisma generate` 4 | 5 | ![screenshot](https://user-images.githubusercontent.com/22195362/89097596-edeadc00-d3fd-11ea-91ea-86d5d8076da0.png) 6 | 7 | ## Getting Started 8 | 9 | 1. Install this package using: 10 | 11 | ```shell 12 | npm install -D prisma-docs-generator 13 | ``` 14 | 15 | 2. Add the generator to the schema 16 | 17 | ```prisma 18 | generator docs { 19 | provider = "node node_modules/prisma-docs-generator" 20 | } 21 | ``` 22 | 23 | 3. Run `npx prisma generate` to trigger the generator. This will create a `docs` folder in `prisma/docs` 24 | 4. Serve the docs using `npx prisma-docs-generator serve` 25 | 26 | ## Options 27 | 28 | ### Specifying Output 29 | 30 | You can specify the out of the docs using the output property 31 | 32 | ```prisma 33 | generator docs { 34 | provider = "node node_modules/prisma-docs-generator" 35 | output = "../../docs" 36 | } 37 | ``` 38 | 39 | ### includeRelationFields 40 | 41 | You can specify whether relation fields are shown or not. 42 | Default value is `true`. 43 | 44 | ```prisma 45 | generator docs { 46 | provider = "node node_modules/prisma-docs-generator" 47 | includeRelationFields = false 48 | } 49 | ``` 50 | 51 | ## CLI 52 | 53 | This package also ships with a CLI which is used to serve the docs right now. It has the following subcommands: 54 | 55 | ### `serve` 56 | 57 | Serves the static html which the generator generated. It reads the output path from the prisma schema or it will use the default. 58 | Use `--port` or `-p` to change the port the express server uses. 59 | 60 | --- 61 | 62 | ### License 63 | 64 | MIT Harshit Pant 65 | 66 | (This is not an official Prisma project. It is personally maintained by [me](https://github.com/pantharshit00) ) 67 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prisma-docs-generator", 3 | "version": "0.8.0", 4 | "main": "dist/index.js", 5 | "license": "MIT", 6 | "bin": { 7 | "prisma-docs-generator": "dist/cli.js" 8 | }, 9 | "description": "Documentation reference generator for Prisma Schema", 10 | "author": { 11 | "name": "Harshit Pant", 12 | "email": "pantharshit00@gmail.com", 13 | "url": "https://harshitpant.com" 14 | }, 15 | "keywords": [ 16 | "prisma2", 17 | "prisma", 18 | "prisma-generator", 19 | "prisma-schema", 20 | "code-generation", 21 | "docs", 22 | "reference" 23 | ], 24 | "homepage": "https://github.com/pantharshit00/prisma-docs-generator", 25 | "repository": { 26 | "url": "git@github.com:pantharshit00/prisma-docs-generator.git" 27 | }, 28 | "bugs": { 29 | "email": "pantharshit00@gmail.com", 30 | "url": "https://github.com/pantharshit00/prisma-docs-generator/issues" 31 | }, 32 | "dependencies": { 33 | "@prisma/generator-helper": "^4.14.0", 34 | "@prisma/internals": "^4.14.0", 35 | "express": "^4.18.2", 36 | "indent-string": "^5.0.0", 37 | "kleur": "^4.1.5", 38 | "meow": "9", 39 | "pluralize": "^8.0.0", 40 | "prismjs": "^1.29.0", 41 | "ts-toolbelt": "^9.6.0" 42 | }, 43 | "devDependencies": { 44 | "@prisma/client": "^4.14.0", 45 | "@types/express": "4.17.17", 46 | "@types/jest": "^29.5.1", 47 | "@types/node": "^20.1.7", 48 | "@types/prismjs": "^1.26.0", 49 | "cpy-cli": "^4.2.0", 50 | "jest": "^29.5.0", 51 | "prettier": "^2.8.8", 52 | "prisma": "^4.14.0", 53 | "rimraf": "^5.0.0", 54 | "ts-jest": "^29.1.0", 55 | "ts-node": "^10.9.1", 56 | "typescript": "^5.0.4" 57 | }, 58 | "scripts": { 59 | "start": "ts-node main.ts", 60 | "generate": "prisma generate", 61 | "clean": "rimraf dist && rimraf styles_generator/dist && rimraf src/styles/main.css", 62 | "build:gen": "tsc -d && cpy --flat src/styles dist/styles", 63 | "build:styles": "cd styles_generator && yarn build && cpy --flat ./dist/main.css ../src/styles/", 64 | "build": "yarn build:styles && yarn build:gen", 65 | "test": "jest", 66 | "format": "prettier --ignore-path .gitignore src/**/*.ts --write" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | } 4 | 5 | generator docs { 6 | provider = "node ./dist/index.js" 7 | } 8 | 9 | datasource db { 10 | provider = "postgresql" 11 | url = env("DATABASE_URL") 12 | } 13 | 14 | /// this is 15 | model Post { 16 | /// this is id for the post 17 | id Int @default(autoincrement()) 18 | createdAt DateTime @default(now()) 19 | title String 20 | content String? 21 | published Boolean @default(false) 22 | author User @relation(fields: [authorId], references: [id]) 23 | authorId Int @default(1) 24 | 25 | @@id([id, authorId]) 26 | @@unique([authorId]) 27 | @@unique(name: "test", fields: [content, title]) 28 | @@index([id, authorId]) 29 | @@index([authorId, title]) 30 | @@map("posts") 31 | } 32 | 33 | model Profile { 34 | id Int @id @default(autoincrement()) 35 | bio String? 36 | user User @relation(fields: [userId], references: [id]) 37 | userId Int @unique 38 | } 39 | 40 | model User { 41 | id Int @id @default(autoincrement()) 42 | email String @unique 43 | name String? 44 | posts Post[] 45 | profile Profile? 46 | arrayField String[] @default([]) 47 | } 48 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import meow from 'meow'; 4 | import kleur from 'kleur'; 5 | import express from 'express'; 6 | import { getSchemaPath, getGenerators } from '@prisma/internals'; 7 | import { Server } from 'http'; 8 | 9 | const cli = meow( 10 | ` 11 | Usage 12 | $ prisma-docs-generator [command] [flags] 13 | 14 | Options 15 | -v Prints out the version number 16 | 17 | ${kleur.bold('serve')} 18 | --port -p Specify the port from which this cli should serve the docs 19 | 20 | `, 21 | { 22 | flags: { 23 | port: { 24 | type: 'number', 25 | alias: 'p', 26 | default: 5858, 27 | }, 28 | version: { 29 | alias: 'v', 30 | }, 31 | }, 32 | } 33 | ); 34 | 35 | class ExpressService { 36 | exp: express.Express; 37 | appInstance: Server | null; 38 | port: number; 39 | servePath: string; 40 | 41 | constructor(port: number, path: string) { 42 | this.port = port; 43 | this.servePath = path; 44 | this.exp = express(); 45 | this.appInstance = null; 46 | } 47 | 48 | start() { 49 | this.exp.use('/', express.static(this.servePath)); 50 | this.appInstance = this.exp.listen(this.port, () => { 51 | console.log( 52 | `Prisma Docs Generator started at http://localhost:${this.port}` 53 | ); 54 | }); 55 | } 56 | 57 | exit() { 58 | if (this.appInstance) { 59 | this.appInstance.close(); 60 | } 61 | } 62 | } 63 | 64 | async function execute(cli: meow.Result) { 65 | const { 66 | flags: { port }, 67 | input, 68 | } = cli; 69 | if (input.length < 1) { 70 | console.error(kleur.red('No sub command was specified')); 71 | cli.showHelp(); 72 | } 73 | 74 | const mainSubcommand = input[0]; 75 | 76 | switch (mainSubcommand) { 77 | case 'serve': { 78 | //@ts-ignore 79 | const schemaPath = await getSchemaPath(); 80 | if (!schemaPath) { 81 | console.error(kleur.red('Unable to find schema.prisma file')); 82 | process.exit(1); 83 | } 84 | const gens = await getGenerators({ 85 | schemaPath: schemaPath, 86 | dataProxy: false, 87 | }); 88 | const docsGen = gens.find( 89 | (gen) => gen.manifest?.prettyName === 'Prisma Docs Generator' 90 | ); 91 | if (!docsGen) { 92 | console.error( 93 | kleur.red('Prisma Docs Generator was not specified in the schema') 94 | ); 95 | process.exit(1); 96 | } 97 | 98 | const servePath = docsGen.options?.generator.output?.value; 99 | if (!servePath) { 100 | console.error( 101 | kleur.red('Unable to resolve output path for the generator') 102 | ); 103 | process.exit(1); 104 | } 105 | 106 | const server = new ExpressService(port as number, servePath); 107 | server.start(); 108 | 109 | process.on('SIGTERM', () => { 110 | server.exit(); 111 | }); 112 | 113 | break; 114 | } 115 | default: { 116 | console.error(kleur.red(`Unknown command ${kleur.bold(mainSubcommand)}`)); 117 | cli.showHelp(); 118 | } 119 | } 120 | } 121 | 122 | execute(cli); 123 | -------------------------------------------------------------------------------- /src/generator/apitypes.ts: -------------------------------------------------------------------------------- 1 | import { Generatable, isScalarType } from './helpers'; 2 | import { DMMFDocument } from './transformDMMF'; 3 | import { DMMF } from '@prisma/generator-helper'; 4 | 5 | type TypesGeneratorStructure = { 6 | inputTypes: TGType[]; 7 | outputTypes: TGType[]; 8 | // TODO: evaluate calling them enums as that can be ambigious 9 | //enums: any; 10 | }; 11 | 12 | type TGType = { 13 | name: string; 14 | fields: TGTypeField[]; 15 | }; 16 | 17 | type TGTypeField = { 18 | name: string; 19 | type: DMMF.SchemaArgInputType[]; 20 | nullable: boolean; 21 | }; 22 | 23 | class TypesGenerator implements Generatable { 24 | data: TypesGeneratorStructure; 25 | 26 | constructor(d: DMMFDocument) { 27 | this.data = this.getData(d); 28 | } 29 | 30 | getTypeFieldHTML( 31 | field: TGTypeField, 32 | kind: 'inputType' | 'outputType' 33 | ): string { 34 | return ` 35 | 36 | 37 | ${field.name} 38 | 39 | ${field.type 40 | .map((f) => 41 | isScalarType(f.type as string) 42 | ? f.type 43 | : `${f.type}${ 44 | f.isList ? '[]' : '' 45 | }` 46 | ) 47 | .join(' | ')} 48 | 49 | 50 | 51 | ${field.nullable ? 'Yes' : 'No'} 52 | 53 | 54 | `; 55 | } 56 | 57 | getTypeHTML(type: TGType, kind: 'inputType' | 'outputType'): string { 58 | return ` 59 |
60 |

${ 61 | type.name 62 | }

63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ${type.fields 73 | .map((field) => this.getTypeFieldHTML(field, kind)) 74 | .join('')} 75 | 76 |
NameTypeNullable
77 |
78 | `; 79 | } 80 | 81 | toHTML() { 82 | return `
83 |

Types

84 |
85 |
86 |

Input Types

87 |
88 | ${this.data.inputTypes 89 | .map((inputType) => this.getTypeHTML(inputType, 'inputType')) 90 | .join(`
`)} 91 |
92 |
93 |
94 |

Output Types

95 |
96 | ${this.data.outputTypes 97 | .map((outputType) => this.getTypeHTML(outputType, 'outputType')) 98 | .join(`
`)} 99 |
100 |
101 |
102 |
103 | `; 104 | } 105 | 106 | getInputTypes(dmmfInputType: DMMF.InputType[]): TGType[] { 107 | return dmmfInputType.map((inputType) => ({ 108 | name: inputType.name, 109 | fields: inputType.fields.map((ip) => ({ 110 | name: ip.name, 111 | nullable: ip.isNullable, 112 | type: ip.inputTypes, 113 | })), 114 | })); 115 | } 116 | 117 | getOutputTypes(dmmfOutputTypes: DMMF.OutputType[]): TGType[] { 118 | return dmmfOutputTypes.map((outputType) => ({ 119 | name: outputType.name, 120 | fields: outputType.fields.map((op) => ({ 121 | name: op.name, 122 | nullable: !op.isNullable, 123 | list: (op.outputType as any).isList, 124 | type: [ 125 | { 126 | isList: op.outputType.isList, 127 | type: op.outputType.type as string, 128 | location: op.outputType.location, 129 | }, 130 | ], 131 | })), 132 | })); 133 | } 134 | 135 | getData(d: DMMFDocument) { 136 | return { 137 | inputTypes: this.getInputTypes(d.schema.inputObjectTypes.prisma), 138 | outputTypes: this.getOutputTypes([ 139 | ...d.schema.outputObjectTypes.model, 140 | ...d.schema.outputObjectTypes.prisma.filter( 141 | (op) => op.name !== 'Query' && op.name !== 'Mutation' 142 | ), 143 | ]), 144 | }; 145 | } 146 | } 147 | 148 | export default TypesGenerator; 149 | -------------------------------------------------------------------------------- /src/generator/helpers.ts: -------------------------------------------------------------------------------- 1 | import { DMMFDocument } from './transformDMMF'; 2 | 3 | export interface Generatable { 4 | data: T; 5 | toHTML(): string; 6 | getData(d: DMMFDocument): T; 7 | } 8 | 9 | export function capitalize(str: string): string { 10 | return str[0].toUpperCase() + str.slice(1); 11 | } 12 | 13 | export function lowerCase(name: string): string { 14 | return name.substring(0, 1).toLowerCase() + name.substring(1); 15 | } 16 | 17 | const primitiveTypes = [ 18 | 'String', 19 | 'Boolean', 20 | 'Int', 21 | 'Float', 22 | 'Json', 23 | 'DateTime', 24 | 'Null', 25 | ]; 26 | 27 | export function isScalarType(type: string): boolean { 28 | return primitiveTypes.includes(type); 29 | } 30 | -------------------------------------------------------------------------------- /src/generator/model.ts: -------------------------------------------------------------------------------- 1 | import { Generatable, capitalize } from './helpers'; 2 | import { DMMF } from '@prisma/generator-helper'; 3 | import { DMMFDocument, DMMFMapping } from './transformDMMF'; 4 | import * as Prism from 'prismjs'; 5 | import { lowerCase, isScalarType } from './helpers'; 6 | 7 | type ModelGeneratorStructure = { 8 | models: MGModel[]; 9 | }; 10 | 11 | type MGModel = { 12 | documentation?: string; 13 | name: string; 14 | directives: MGModelDirective[]; 15 | fields: MGModelField[]; 16 | operations: MGModelOperation[]; 17 | }; 18 | 19 | type MGModelDirective = { 20 | name: string; 21 | values: any[]; 22 | }; 23 | 24 | type MGModelField = { 25 | name: string; 26 | type: string; 27 | bareTypeName: string; 28 | directives: string[]; 29 | documentation?: string; 30 | required: boolean; 31 | }; 32 | 33 | type MGModelOperationKeys = { 34 | name: string; 35 | types: DMMF.SchemaArgInputType[]; 36 | required: boolean; 37 | }; 38 | 39 | type MGModelOperationOutput = { 40 | type: string; 41 | required: boolean; 42 | list: boolean; 43 | }; 44 | 45 | type MakeOptionallyUndefined = { 46 | [k in keyof T]: T[k] | undefined; 47 | }; 48 | // TODO: Select and include keys are generated by the Prisma Client 49 | // It might need some transformation 50 | type MGModelOperation = { 51 | name: string; 52 | description: string; 53 | opKeys: MGModelOperationKeys[] | undefined; 54 | usage: string; 55 | output: MakeOptionallyUndefined; 56 | }; 57 | 58 | interface FieldDefault { 59 | name: string; 60 | args: any[]; 61 | } 62 | 63 | let fieldDirectiveMap = new Map([ 64 | ['isUnique', '@unique'], 65 | ['isId', '@id'], 66 | ['hasDefaultValue', '@default'], 67 | ['isUpdatedAt', '@updatedAt'], 68 | ['hasDefaultValue', '@default'], 69 | ]); 70 | 71 | export default class ModelGenerator 72 | implements Generatable 73 | { 74 | data: ModelGeneratorStructure; 75 | 76 | constructor(d: DMMFDocument) { 77 | this.data = this.getData(d); 78 | } 79 | 80 | getModelDiretiveHTML(directive: MGModelDirective): string { 81 | return ` 82 | 83 | 84 | ${directive.name} 85 | 86 | 87 |
    88 | ${directive.values.map((val) => `
  • ${val}
  • `).join('')} 89 |
90 | 91 | 92 | `; 93 | } 94 | 95 | getModelFieldTableRow(field: MGModelField, modelName: string): string { 96 | return ` 97 | 98 | 99 | ${field.name} 100 | 101 | 102 | ${ 103 | isScalarType(field.bareTypeName) 104 | ? field.type 105 | : `${field.type}` 106 | } 107 | 108 | 109 |
    110 | ${ 111 | field.directives.length > 0 112 | ? field.directives 113 | .map((directive) => `
  • ${directive}
  • `) 114 | .join('') 115 | : '
  • -
  • ' 116 | } 117 |
118 | 119 | 120 | ${field.required ? `Yes` : 'No'} 121 | 122 | 123 | ${field.documentation ?? '-'} 124 | 125 | 126 | `; 127 | } 128 | 129 | getModelOperationMarkup( 130 | operation: MGModelOperation, 131 | modelName: string 132 | ): string { 133 | return ` 134 |
135 |

${ 136 | operation.name 137 | }

138 |

${operation.description}

139 |
140 |
${
143 |                       operation.usage
144 |                     }
145 |
146 |

Input

147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | ${operation.opKeys 157 | ?.map( 158 | (opK) => ` 159 | 160 | 163 | 174 | 177 | 178 | ` 179 | ) 180 | .join('')} 181 | 182 |
NameTypeRequired
161 | ${opK.name} 162 | 164 | ${opK.types 165 | .map((t) => 166 | isScalarType(t.type as string) 167 | ? t.type 168 | : `${t.type}${ 169 | t.isList ? '[]' : '' 170 | }` 171 | ) 172 | .join(' | ')} 173 | 175 | ${opK.required ? 'Yes' : 'No'} 176 |
183 |

Output

184 | 187 |
Required: 188 | ${operation.output.required ? `Yes` : `No`}
189 |
List: 190 | ${operation.output.list ? `Yes` : `No`}
191 |
192 | `; 193 | } 194 | 195 | toHTML() { 196 | 197 | 198 | return ` 199 |
200 |

Models

201 | ${this.data.models 202 | .map( 203 | (model) => ` 204 |
205 |

${model.name}

206 | ${ 207 | model.documentation 208 | ? `
Description: ${model.documentation}
` 209 | : '' 210 | } 211 | ${ 212 | model.directives.length > 0 213 | ? ` 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | ${model.directives 223 | .map((directive) => this.getModelDiretiveHTML(directive)) 224 | .join('')} 225 | 226 |
NameValue
227 | ` 228 | : '' 229 | } 230 |
231 |

Fields

234 |
235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | ${model.fields 247 | .map((field) => 248 | this.getModelFieldTableRow(field, model.name) 249 | ) 250 | .join('')} 251 | 252 |
NameTypeAttributesRequiredComment
253 |
254 |
255 |
256 |
257 |

Operations

260 |
261 | ${model.operations 262 | .map((op) => this.getModelOperationMarkup(op, model.name)) 263 | .join(`
`)} 264 |
265 |
266 |
267 | ` 268 | ) 269 | .join(`
`)} 270 |
271 | `; 272 | } 273 | 274 | getModelDirective(model: DMMF.Model): MGModelDirective[] { 275 | let directiveValue: MGModelDirective[] = []; 276 | 277 | if (model.primaryKey) 278 | directiveValue.push({ name: '@@id', values: model.primaryKey.fields }); 279 | 280 | if (model.uniqueFields.length > 0) { 281 | model.uniqueFields.forEach((uniqueField) => { 282 | directiveValue.push({ 283 | name: '@@unique', 284 | values: uniqueField, 285 | }); 286 | }); 287 | } 288 | 289 | if (model.uniqueIndexes.length > 0) { 290 | model.uniqueIndexes.forEach((uniqueIndex) => { 291 | directiveValue.push({ name: '@@index', values: uniqueIndex.fields }); 292 | }); 293 | } 294 | return directiveValue; 295 | } 296 | 297 | getModelFields(model: DMMF.Model): MGModelField[] { 298 | return model.fields.map((field) => { 299 | return { 300 | name: field.name, 301 | type: this.getFieldType(field), 302 | bareTypeName: field.type, 303 | documentation: (field as any).documentation, 304 | directives: this.getFieldDirectives(field), 305 | required: field.isRequired, 306 | }; 307 | }); 308 | } 309 | 310 | getFieldType(field: DMMF.Field): string { 311 | let name = field.type; 312 | if (!field.isRequired && !field.isList) { 313 | name += '?'; 314 | } 315 | if (field.isList) { 316 | name += '[]'; 317 | } 318 | return name; 319 | } 320 | 321 | getFieldDirectives(field: DMMF.Field): string[] { 322 | const filteredEntries = Object.entries(field).filter(([_, v]) => 323 | Boolean(v) 324 | ); 325 | 326 | let directives: string[] = []; 327 | 328 | filteredEntries.forEach(([k]) => { 329 | const mappedDirectiveValue = fieldDirectiveMap.get(k); 330 | if (mappedDirectiveValue) { 331 | // default needs separate treatment right now as it can be a fn or any other type really 332 | if (k === 'hasDefaultValue' && field.default !== undefined) { 333 | if ( 334 | typeof field.default === 'string' || 335 | typeof field.default === 'number' || 336 | typeof field.default === 'boolean' 337 | ) { 338 | directives.push(`${mappedDirectiveValue}(${field.default})`); 339 | } 340 | 341 | if(Array.isArray(field.default)) { 342 | directives.push(`${mappedDirectiveValue}([${field.default.toString()}])`) 343 | } 344 | 345 | else if (typeof field.default === 'object') { 346 | // Output of this template is, for example, @default(now()) 347 | directives.push( 348 | `${mappedDirectiveValue}(${ 349 | (field.default as FieldDefault).name 350 | }(${(field.default as FieldDefault).args.join(',')}))` 351 | ); 352 | } 353 | 354 | } else { 355 | directives.push(mappedDirectiveValue); 356 | } 357 | } 358 | }); 359 | 360 | return directives; 361 | } 362 | 363 | getModelOperations( 364 | model: DMMF.Model, 365 | mappings: DMMFMapping | undefined, 366 | schema: DMMF.Schema 367 | ): MGModelOperation[] { 368 | if (!mappings) { 369 | throw new Error(`No operation mapping found for model: ${model.name}`); 370 | } 371 | const modelOps = Object.entries(mappings).filter( 372 | ([map, _val]) => map !== 'model' 373 | ); 374 | let ops: MGModelOperation[] = []; 375 | modelOps.forEach(([op, val]) => { 376 | const singular = capitalize(model.name); 377 | const plural = capitalize(singular); 378 | const method = `prisma.${lowerCase(model.name)}.${op}`; 379 | switch (op) { 380 | case DMMF.ModelAction.create: { 381 | const field = schema.outputObjectTypes.prisma 382 | .find((t) => t.name === 'Mutation') 383 | ?.fields.find((f) => f.name === val); 384 | ops.push({ 385 | name: op, 386 | description: `Create one ${singular}`, 387 | usage: Prism.highlight( 388 | `// Create one ${singular} 389 | const ${singular} = await ${method}({ 390 | data: { 391 | // ... data to create a ${singular} 392 | } 393 | }) 394 | `, 395 | Prism.languages.javascript, 396 | 'javascript' 397 | ), 398 | opKeys: field?.args.map((a) => ({ 399 | name: a.name, 400 | types: a.inputTypes, 401 | required: a.isRequired, 402 | })), 403 | output: { 404 | type: field?.outputType.type as string, 405 | required: !field?.isNullable, 406 | list: field?.outputType.isList, 407 | }, 408 | }); 409 | break; 410 | } 411 | case DMMF.ModelAction.deleteMany: { 412 | const field = schema.outputObjectTypes.prisma 413 | .find((t) => t.name === 'Mutation') 414 | ?.fields.find((f) => f.name === val); 415 | ops.push({ 416 | name: op, 417 | description: `Delete zero or more ${singular}`, 418 | usage: Prism.highlight( 419 | `// Delete a few ${plural} 420 | const { count } = await ${method}({ 421 | where: { 422 | // ... provide filter here 423 | } 424 | }) 425 | `, 426 | Prism.languages.javascript, 427 | 'javascript' 428 | ), 429 | opKeys: field?.args.map((a) => ({ 430 | name: a.name, 431 | types: a.inputTypes, 432 | required: a.isRequired, 433 | })), 434 | output: { 435 | type: field?.outputType.type as string, 436 | required: !field?.isNullable, 437 | list: field?.outputType.isList, 438 | }, 439 | }); 440 | break; 441 | } 442 | case DMMF.ModelAction.delete: { 443 | const field = schema.outputObjectTypes.prisma 444 | .find((t) => t.name === 'Mutation') 445 | ?.fields.find((f) => f.name === val); 446 | ops.push({ 447 | name: op, 448 | description: `Delete one ${singular}`, 449 | usage: Prism.highlight( 450 | `// Delete one ${singular} 451 | const ${singular} = await ${method}({ 452 | where: { 453 | // ... filter to delete one ${singular} 454 | } 455 | })`, 456 | Prism.languages.javascript, 457 | 'javascript' 458 | ), 459 | opKeys: field?.args.map((a) => ({ 460 | name: a.name, 461 | types: a.inputTypes, 462 | required: a.isRequired, 463 | })), 464 | output: { 465 | type: field?.outputType.type as string, 466 | required: !field?.isNullable, 467 | list: field?.outputType.isList, 468 | }, 469 | }); 470 | break; 471 | } 472 | case DMMF.ModelAction.findMany: { 473 | const field = schema.outputObjectTypes.prisma 474 | .find((t) => t.name === 'Query') 475 | ?.fields.find((f) => f.name === val); 476 | ops.push({ 477 | name: op, 478 | description: `Find zero or more ${plural}`, 479 | usage: Prism.highlight( 480 | `// Get all ${plural} 481 | const ${plural} = await ${method}() 482 | // Get first 10 ${plural} 483 | const ${plural} = await ${method}({ take: 10 }) 484 | `, 485 | Prism.languages.javascript, 486 | 'javascript' 487 | ), 488 | opKeys: field?.args.map((a) => ({ 489 | name: a.name, 490 | types: a.inputTypes, 491 | required: a.isRequired, 492 | })), 493 | output: { 494 | type: field?.outputType.type as string, 495 | required: !field?.isNullable, 496 | list: field?.outputType.isList, 497 | }, 498 | }); 499 | break; 500 | } 501 | case DMMF.ModelAction.findUnique: { 502 | const field = schema.outputObjectTypes.prisma 503 | .find((t) => t.name === 'Query') 504 | ?.fields.find((f) => f.name === val); 505 | ops.push({ 506 | name: op, 507 | description: `Find zero or one ${plural}`, 508 | usage: Prism.highlight( 509 | `// Get one ${singular} 510 | const ${lowerCase(singular)} = await ${method}({ 511 | where: { 512 | // ... provide filter here 513 | } 514 | }) 515 | `, 516 | Prism.languages.javascript, 517 | 'javascript' 518 | ), 519 | opKeys: field?.args.map((a) => ({ 520 | name: a.name, 521 | types: a.inputTypes, 522 | required: a.isRequired, 523 | })), 524 | output: { 525 | type: field?.outputType.type as string, 526 | required: !field?.isNullable, 527 | list: field?.outputType.isList, 528 | }, 529 | }); 530 | break; 531 | } 532 | 533 | case DMMF.ModelAction.findFirst: { 534 | const field = schema.outputObjectTypes.prisma 535 | .find((t) => t.name === 'Query') 536 | ?.fields.find((f) => f.name === val); 537 | ops.push({ 538 | name: op, 539 | description: `Find first ${plural}`, 540 | usage: Prism.highlight( 541 | `// Get one ${singular} 542 | const ${lowerCase(singular)} = await ${method}({ 543 | where: { 544 | // ... provide filter here 545 | } 546 | }) 547 | `, 548 | Prism.languages.javascript, 549 | 'javascript' 550 | ), 551 | opKeys: field?.args.map((a) => ({ 552 | name: a.name, 553 | types: a.inputTypes, 554 | required: a.isRequired, 555 | })), 556 | output: { 557 | type: field?.outputType.type as string, 558 | required: !field?.isNullable, 559 | list: field?.outputType.isList, 560 | }, 561 | }); 562 | break; 563 | } 564 | 565 | case DMMF.ModelAction.update: { 566 | const field = schema.outputObjectTypes.prisma 567 | .find((t) => t.name === 'Mutation') 568 | ?.fields.find((f) => f.name === val); 569 | ops.push({ 570 | name: op, 571 | description: `Update one ${singular}`, 572 | usage: Prism.highlight( 573 | `// Update one ${singular} 574 | const ${lowerCase(singular)} = await ${method}({ 575 | where: { 576 | // ... provide filter here 577 | }, 578 | data: { 579 | // ... provide data here 580 | } 581 | }) 582 | `, 583 | Prism.languages.javascript, 584 | 'javascript' 585 | ), 586 | opKeys: field?.args.map((a) => ({ 587 | name: a.name, 588 | types: a.inputTypes, 589 | required: a.isRequired, 590 | })), 591 | output: { 592 | type: field?.outputType.type as string, 593 | required: !field?.isNullable, 594 | list: field?.outputType.isList, 595 | }, 596 | }); 597 | break; 598 | } 599 | 600 | case DMMF.ModelAction.updateMany: { 601 | const field = schema.outputObjectTypes.prisma 602 | .find((t) => t.name === 'Mutation') 603 | ?.fields.find((f) => f.name === val); 604 | ops.push({ 605 | name: op, 606 | description: `Update zero or one ${plural}`, 607 | usage: Prism.highlight( 608 | `const { count } = await ${method}({ 609 | where: { 610 | // ... provide filter here 611 | }, 612 | data: { 613 | // ... provide data here 614 | } 615 | })`, 616 | Prism.languages.javascript, 617 | 'javascript' 618 | ), 619 | opKeys: field?.args.map((a) => ({ 620 | name: a.name, 621 | types: a.inputTypes, 622 | required: a.isRequired, 623 | })), 624 | output: { 625 | type: field?.outputType.type as string, 626 | required: !field?.isNullable, 627 | list: field?.outputType.isList, 628 | }, 629 | }); 630 | break; 631 | } 632 | case DMMF.ModelAction.upsert: { 633 | const field = schema.outputObjectTypes.prisma 634 | .find((t) => t.name === 'Mutation') 635 | ?.fields.find((f) => f.name === val); 636 | ops.push({ 637 | name: op, 638 | description: `Create or update one ${plural}`, 639 | usage: Prism.highlight( 640 | `// Update or create a ${singular} 641 | const ${lowerCase(singular)} = await ${method}({ 642 | create: { 643 | // ... data to create a ${singular} 644 | }, 645 | update: { 646 | // ... in case it already exists, update 647 | }, 648 | where: { 649 | // ... the filter for the ${singular} we want to update 650 | } 651 | })`, 652 | Prism.languages.javascript, 653 | 'javascript' 654 | ), 655 | opKeys: field?.args.map((a) => ({ 656 | name: a.name, 657 | types: a.inputTypes, 658 | required: a.isRequired, 659 | })), 660 | output: { 661 | type: field?.outputType.type as string, 662 | required: !field?.isNullable, 663 | list: field?.outputType.isList, 664 | }, 665 | }); 666 | break; 667 | } 668 | } 669 | }); 670 | return ops; 671 | } 672 | 673 | getModels(dmmf: DMMFDocument): MGModel[] { 674 | return dmmf.datamodel.models.map((model) => { 675 | return { 676 | name: model.name, 677 | documentation: (model as any).documentation as string, // TODO: Open issue for generator helper 678 | directives: this.getModelDirective(model), 679 | fields: this.getModelFields(model), 680 | operations: this.getModelOperations( 681 | model, 682 | dmmf.mappings.find((map) => map.model === model.name), 683 | dmmf.schema 684 | ), 685 | }; 686 | }); 687 | } 688 | 689 | getData(d: DMMFDocument) { 690 | return { 691 | models: this.getModels(d), 692 | }; 693 | } 694 | } 695 | -------------------------------------------------------------------------------- /src/generator/toc.ts: -------------------------------------------------------------------------------- 1 | import { Generatable } from './helpers'; 2 | import { DMMF } from '@prisma/generator-helper'; 3 | import { DMMFDocument, DMMFMapping } from './transformDMMF'; 4 | 5 | type TOCStructure = { 6 | models: TOCModel[]; 7 | types: TOCTypes; 8 | }; 9 | 10 | type TOCModel = { 11 | name: string; 12 | fields: string[]; 13 | operations: string[]; 14 | }; 15 | 16 | type TOCTypes = { 17 | inputTypes: string[]; 18 | outputTypes: string[]; 19 | }; 20 | 21 | export default class TOCGenerator implements Generatable { 22 | data: TOCStructure; 23 | 24 | constructor(d: DMMFDocument) { 25 | this.data = this.getData(d); 26 | } 27 | 28 | getTOCSubHeaderHTML(name: string): string { 29 | return ` 30 |
31 | ${name} 32 |
33 | `; 34 | } 35 | 36 | getSubFieldHTML(identifier: string, root: string, field: string): string { 37 | return `
  • ${field}
  • `; 38 | } 39 | 40 | toHTML() { 41 | return ` 42 |
    43 |
    Models
    44 |
      45 | ${this.data.models 46 | .map( 47 | (model) => ` 48 |
    • 49 | ${this.getTOCSubHeaderHTML(model.name)} 50 |
      51 | 54 |
        55 | ${model.fields 56 | .map((field) => 57 | this.getSubFieldHTML('model', model.name, field) 58 | ) 59 | .join('')} 60 |
      61 |
      62 |
      63 | 66 |
        67 | ${model.operations 68 | .map((op) => 69 | this.getSubFieldHTML('model', model.name, op) 70 | ) 71 | .join('')} 72 |
      73 |
      74 |
    • 75 | ` 76 | ) 77 | .join('')} 78 |
    79 |
    Types
    80 |
      81 |
    • 82 |
      83 | Input Types 84 |
      85 |
        86 | ${this.data.types.inputTypes 87 | .map((inputType) => 88 | this.getSubFieldHTML('type', 'inputType', inputType) 89 | ) 90 | .join('')} 91 |
      92 |
    • 93 |
    • 94 |
      95 | Output Types 96 |
      97 |
        98 | ${this.data.types.outputTypes 99 | .map((outputType) => 100 | this.getSubFieldHTML('type', 'outputType', outputType) 101 | ) 102 | .join('')} 103 |
      104 |
    • 105 |
    106 |
    107 | `; 108 | } 109 | 110 | getModels(dmmfModel: DMMF.Model[], mappings: DMMFMapping[]): TOCModel[] { 111 | return dmmfModel.map((model) => { 112 | return { 113 | name: model.name, 114 | fields: model.fields.map((field) => field.name), 115 | operations: Object.keys( 116 | mappings.find((x) => x.model === model.name) ?? {} 117 | ).filter((op) => op !== 'model'), 118 | }; 119 | }); 120 | } 121 | 122 | getTypes(dmmfSchema: DMMF.Schema): TOCTypes { 123 | return { 124 | inputTypes: dmmfSchema.inputObjectTypes.prisma.map( 125 | (inputType) => inputType.name 126 | ), 127 | outputTypes: [ 128 | ...dmmfSchema.outputObjectTypes.model.map((ot) => ot.name), 129 | ...dmmfSchema.outputObjectTypes.prisma 130 | .map((outputType) => outputType.name) 131 | .filter((ot) => ot !== 'Query' && ot !== 'Mutation'), 132 | ], 133 | }; 134 | } 135 | 136 | getData(d: DMMFDocument) { 137 | return { 138 | models: this.getModels(d.datamodel.models, d.mappings), 139 | types: this.getTypes(d.schema), 140 | }; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/generator/transformDMMF.ts: -------------------------------------------------------------------------------- 1 | import { DMMF as ExternalDMMF } from '@prisma/generator-helper'; 2 | 3 | export function lowerCase(name: string): string { 4 | return name.substring(0, 1).toLowerCase() + name.substring(1); 5 | } 6 | 7 | export interface DMMFMapping { 8 | model: string; 9 | findOne?: string | null; 10 | findFirst?: string | null; 11 | findMany?: string | null; 12 | create?: string | null; 13 | update?: string | null; 14 | updateMany?: string | null; 15 | upsert?: string | null; 16 | delete?: string | null; 17 | deleteMany?: string | null; 18 | } 19 | 20 | export type DMMFDocument = Omit & { 21 | mappings: DMMFMapping[]; 22 | }; 23 | 24 | type OptionsForTransformDMMF = { 25 | includeRelationFields: boolean 26 | } 27 | 28 | export default function transformDMMF( 29 | dmmf: ExternalDMMF.Document, 30 | { includeRelationFields }: OptionsForTransformDMMF 31 | ): DMMFDocument { 32 | if (!includeRelationFields) { 33 | dmmf.datamodel.models = dmmf.datamodel.models.map(model => { 34 | model.fields = model.fields.filter( 35 | field => !field.relationName 36 | ); 37 | return model; 38 | }); 39 | } 40 | 41 | return { 42 | ...dmmf, 43 | mappings: getMappings(dmmf.mappings, dmmf.datamodel), 44 | }; 45 | } 46 | 47 | function getMappings( 48 | mappings: ExternalDMMF.Mappings, 49 | datamodel: ExternalDMMF.Datamodel 50 | ): DMMFMapping[] { 51 | const modelOperations = mappings.modelOperations 52 | .filter((mapping) => { 53 | const model = datamodel.models.find((m) => m.name === mapping.model); 54 | if (!model) { 55 | throw new Error(`Mapping without model ${mapping.model}`); 56 | } 57 | return model.fields.some((f) => f.kind !== 'object'); 58 | }) 59 | .map((mapping: any) => ({ 60 | model: mapping.model, 61 | findUnique: mapping.findSingle || mapping.findOne || mapping.findUnique, 62 | findFirst: mapping.findFirst, 63 | findMany: mapping.findMany, 64 | create: mapping.createOne || mapping.createSingle || mapping.create, 65 | delete: mapping.deleteOne || mapping.deleteSingle || mapping.delete, 66 | update: mapping.updateOne || mapping.updateSingle || mapping.update, 67 | deleteMany: mapping.deleteMany, 68 | updateMany: mapping.updateMany, 69 | upsert: mapping.upsertOne || mapping.upsertSingle || mapping.upsert, 70 | })); 71 | 72 | return modelOperations; 73 | } 74 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { generatorHandler } from '@prisma/generator-helper'; 2 | import HTMLPrinter from './printer'; 3 | import transformDMMF from './generator/transformDMMF'; 4 | import * as fs from 'fs'; 5 | import * as path from 'path'; 6 | 7 | generatorHandler({ 8 | onManifest() { 9 | return { 10 | defaultOutput: './docs', 11 | prettyName: 'Prisma Docs Generator', 12 | }; 13 | }, 14 | async onGenerate(options) { 15 | const { config } = options.generator; 16 | const includeRelationFields = config.includeRelationFields === 'false' ? false : true; 17 | 18 | const dmmf = transformDMMF(options.dmmf, { 19 | includeRelationFields, 20 | }); 21 | const html = new HTMLPrinter(dmmf); 22 | 23 | const output = options.generator.output?.value; 24 | 25 | if (output) { 26 | const styleFile = await fs.promises.readFile( 27 | path.join(__dirname, 'styles', 'main.css') 28 | ); 29 | try { 30 | await fs.promises.mkdir(output, { 31 | recursive: true, 32 | }); 33 | await fs.promises.mkdir(path.join(output, 'styles'), { 34 | recursive: true, 35 | }); 36 | await fs.promises.writeFile( 37 | path.join(output, 'index.html'), 38 | html.toHTML() 39 | ); 40 | 41 | await fs.promises.writeFile( 42 | path.join(output, 'styles', 'main.css'), 43 | styleFile 44 | ); 45 | } catch (e) { 46 | console.error('Error: unable to write files for Prisma Docs Generator'); 47 | throw e; 48 | } 49 | } else { 50 | throw new Error('No output was specified for Prisma Docs Generator'); 51 | } 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /src/printer/index.ts: -------------------------------------------------------------------------------- 1 | // TODO: separate out printers from transformers 2 | import { Generatable } from '../generator/helpers'; 3 | import TOCGenerator from '../generator/toc'; 4 | import ModelGenerator from '../generator/model'; 5 | import TypesGenerator from '../generator/apitypes'; 6 | import { DMMFDocument } from '../generator/transformDMMF'; 7 | 8 | export default class HTMLPrinter implements Generatable { 9 | data: DMMFDocument; 10 | 11 | constructor(d: DMMFDocument) { 12 | this.data = this.getData(d); 13 | } 14 | 15 | getPrismaSvg(): string { 16 | return ` 17 | 26 | 27 | 31 | 32 | 33 | 34 | `; 35 | } 36 | 37 | getDarkModeToggle(): string { 38 | return ` 39 |
    40 | 41 | 42 |
    43 | 63 | ` 64 | } 65 | 66 | getHead(csspath: string): string { 67 | return ` 68 | 69 | 70 | Prisma Generated Docs 71 | 72 | `; 73 | } 74 | 75 | getData(d: DMMFDocument) { 76 | // this can seem redundant but we can have a transform here in the future 77 | return d; 78 | } 79 | 80 | toHTML() { 81 | // all the printers 82 | const tocGen = new TOCGenerator(this.data); 83 | const modelGen = new ModelGenerator(this.data); 84 | const typeGen = new TypesGenerator(this.data); 85 | 86 | return ` 87 | 88 | 89 | 90 | ${this.getHead('styles/main.css')} 91 | 92 | 93 |
    94 |
    97 |
    98 | ${this.getPrismaSvg()} 99 | ${this.getDarkModeToggle()} 100 |
    101 | ${tocGen.toHTML()} 102 |
    103 |
    104 | ${modelGen.toHTML()} 105 | ${typeGen.toHTML()} 106 |
    107 |
    108 |
    109 |
    110 | 111 | 112 | `; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/styles/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pantharshit00/prisma-docs-generator/90c10a0e346e1b32c9199b29c851771c6fffd1a5/src/styles/.gitkeep -------------------------------------------------------------------------------- /src/tests/__snapshots__/model.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`model generator renders model directive html correctly 1`] = ` 4 | " 5 | 6 | 7 | @@id 8 | 9 | 10 |
      11 |
    • name
    • id
    • 12 |
    13 | 14 | 15 | " 16 | `; 17 | 18 | exports[`model generator renders model field table row correctly 1`] = ` 19 | " 20 | 21 | 22 | title 23 | 24 | 25 | String? 26 | 27 | 28 |
      29 |
    • -
    • 30 |
    31 | 32 | 33 | No 34 | 35 | 36 | - 37 | 38 | 39 | " 40 | `; 41 | 42 | exports[`model generator renders model field table row correctly 2`] = ` 43 | " 44 | 45 | 46 | email 47 | 48 | 49 | String 50 | 51 | 52 |
      53 |
    • -
    • 54 |
    55 | 56 | 57 | Yes 58 | 59 | 60 | This is some docs 61 | 62 | 63 | " 64 | `; 65 | 66 | exports[`model generator renders model operation html correctly 1`] = ` 67 | " 68 |
    69 |

    findOne

    70 |

    Find zero or one user

    71 |
    72 |
    Example usage
    75 |
    76 |

    Input

    77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 91 | 94 | 97 | 98 | 99 | 100 |
    NameTypeRequired
    89 | where 90 | 92 | UserWhereInput 93 | 95 | No 96 |
    101 |

    Output

    102 |
    Type: User
    103 |
    Required: 104 | Yes
    105 |
    List: 106 | No
    107 |
    108 | " 109 | `; 110 | -------------------------------------------------------------------------------- /src/tests/__snapshots__/toc.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`TOC renders TOC Subheader correctly 1`] = ` 4 | " 5 |
    6 | Post 7 |
    8 | " 9 | `; 10 | 11 | exports[`TOC renders TOC subfield correctly 1`] = `"
  • userId
  • "`; 12 | 13 | exports[`TOC renders on toHTML 1`] = ` 14 | " 15 |
    16 |
    Models
    17 | 60 |
    Types
    61 | 79 |
    80 | " 81 | `; 82 | -------------------------------------------------------------------------------- /src/tests/model.test.ts: -------------------------------------------------------------------------------- 1 | import ModelGenerator from '../generator/model'; 2 | import transformDMMF from '../generator/transformDMMF'; 3 | //@ts-ignore 4 | import { getDMMF } from '@prisma/internals'; 5 | 6 | describe('model generator', () => { 7 | it('renders model directive html correctly', async () => { 8 | const datamodelString = /* Prisma */ ` 9 | datasource postgres { 10 | provider = "postgresql" 11 | url = env("DATABASE_URL") 12 | } 13 | model User { 14 | id String @default(cuid()) 15 | name String 16 | arrayField String[] @default([]) 17 | @@index([name]) 18 | @@id([name, id]) 19 | @@unique([name]) 20 | } 21 | `; 22 | 23 | const dmmf = await getDMMF({ datamodel: datamodelString }); 24 | const transformedDmmf = transformDMMF(dmmf, { 25 | includeRelationFields: true, 26 | }); 27 | 28 | const modelGen = new ModelGenerator(transformedDmmf); 29 | const spy = jest.spyOn(modelGen, 'getModelDiretiveHTML'); 30 | 31 | // Run the generator against the model 32 | modelGen.toHTML(); 33 | 34 | expect(spy).toHaveBeenCalledTimes(3); 35 | expect(spy).toHaveBeenCalledWith({ 36 | name: '@@index', 37 | values: ['name'], 38 | }); 39 | expect(spy).toHaveBeenCalledWith({ 40 | name: '@@id', 41 | values: ['name', 'id'], 42 | }); 43 | expect(spy).toHaveBeenCalledWith({ 44 | name: '@@unique', 45 | values: ['name'], 46 | }); 47 | 48 | // snapshot for record 49 | expect( 50 | modelGen.getModelDiretiveHTML({ name: '@@id', values: ['name', 'id'] }) 51 | ).toMatchSnapshot(); 52 | }); 53 | 54 | it('renders model field table row correctly', async () => { 55 | const datamodelString = /* Prisma */ ` 56 | model User { 57 | id String @id @default(cuid()) 58 | name String 59 | /// this the email of the user 60 | email String @unique 61 | password String 62 | } 63 | 64 | model Post { 65 | id String @id @default(cuid()) 66 | title String? 67 | } 68 | `; 69 | 70 | const dmmf = await getDMMF({ datamodel: datamodelString }); 71 | const transformedDmmf = transformDMMF(dmmf, { 72 | includeRelationFields: true, 73 | }); 74 | 75 | const modelGen = new ModelGenerator(transformedDmmf); 76 | const spy = jest.spyOn(modelGen, 'getModelFieldTableRow'); 77 | 78 | // Run the generator against the model 79 | modelGen.toHTML(); 80 | 81 | expect(spy).toHaveBeenCalledTimes(6); 82 | expect(spy).toHaveBeenCalledWith( 83 | { 84 | name: 'id', 85 | type: 'String', 86 | bareTypeName: 'String', 87 | directives: ['@id', '@default(cuid())'], 88 | required: true, 89 | }, 90 | 'User' 91 | ); 92 | 93 | expect(spy).toHaveBeenCalledWith( 94 | { 95 | name: 'email', 96 | type: 'String', 97 | bareTypeName: 'String', 98 | directives: ['@unique'], 99 | documentation: 'this the email of the user', 100 | required: true, 101 | }, 102 | 'User' 103 | ); 104 | 105 | expect(spy).toHaveBeenCalledWith( 106 | { 107 | name: 'title', 108 | type: 'String?', 109 | bareTypeName: 'String', 110 | directives: [], 111 | required: false, 112 | }, 113 | 'Post' 114 | ); 115 | 116 | expect( 117 | modelGen.getModelFieldTableRow( 118 | { 119 | name: 'title', 120 | type: 'String?', 121 | bareTypeName: 'String', 122 | directives: [], 123 | required: false, 124 | }, 125 | 'Post' 126 | ) 127 | ).toMatchSnapshot(); 128 | 129 | expect( 130 | modelGen.getModelFieldTableRow( 131 | { 132 | name: 'email', 133 | type: 'String', 134 | bareTypeName: 'String', 135 | directives: [], 136 | documentation: 'This is some docs', 137 | required: true, 138 | }, 139 | 'User' 140 | ) 141 | ).toMatchSnapshot(); 142 | }); 143 | 144 | it('renders model operation html correctly', async () => { 145 | const datamodelString = /* Prisma */ ` 146 | model User { 147 | id String @default(cuid()) @id 148 | name String 149 | otherField Int 150 | posts Post[] 151 | } 152 | 153 | model Post { 154 | id String @id @default(cuid()) 155 | title String? 156 | userId String 157 | user User @relation(fields:[userId], references:[id]) 158 | } 159 | `; 160 | 161 | const dmmf = await getDMMF({ datamodel: datamodelString }); 162 | const transformedDmmf = transformDMMF(dmmf, { 163 | includeRelationFields: true, 164 | }); 165 | 166 | const modelGen = new ModelGenerator(transformedDmmf); 167 | const spy = jest.spyOn(modelGen, 'getModelOperationMarkup'); 168 | modelGen.toHTML(); 169 | 170 | expect(spy).toHaveBeenCalled(); 171 | 172 | expect( 173 | modelGen.getModelOperationMarkup( 174 | { 175 | name: 'findOne', 176 | description: 'Find zero or one user', 177 | usage: 'Example usage', 178 | opKeys: [ 179 | { 180 | name: 'where', 181 | required: false, 182 | types: [ 183 | { 184 | type: 'UserWhereInput', 185 | isList: false, 186 | location: 'inputObjectTypes', 187 | }, 188 | ], 189 | }, 190 | ], 191 | output: { type: 'User', required: true, list: false }, 192 | }, 193 | 'User' 194 | ) 195 | ).toMatchSnapshot(); 196 | }); 197 | 198 | // TODO: add more tests for operations transform 199 | }); 200 | -------------------------------------------------------------------------------- /src/tests/toc.test.ts: -------------------------------------------------------------------------------- 1 | import { getDMMF } from '@prisma/internals'; 2 | import TOCGenerator from '../generator/toc'; 3 | import transformDMMF from '../generator/transformDMMF'; 4 | 5 | const datamodel = /* Prisma */ ` 6 | model Post { 7 | id String @id @default(cuid()) 8 | name String 9 | @@index([name]) 10 | } 11 | 12 | model User { 13 | userId String @id @default(cuid()) 14 | something String 15 | } 16 | `; 17 | 18 | describe('TOC', () => { 19 | it('renders TOC Subheader correctly', async () => { 20 | const dmmf = await getDMMF({ datamodel }); 21 | const toc = new TOCGenerator( 22 | transformDMMF(dmmf, { 23 | includeRelationFields: true, 24 | }) 25 | ); 26 | const spy = jest.spyOn(toc, 'getTOCSubHeaderHTML'); 27 | // trigger the function 28 | toc.toHTML(); 29 | expect(spy).toHaveBeenCalledTimes(2); 30 | expect(spy).toHaveBeenCalledWith('Post'); 31 | expect(spy).toHaveBeenCalledWith('User'); 32 | expect(toc.getTOCSubHeaderHTML('Post')).toMatchSnapshot(); 33 | }); 34 | 35 | it('renders TOC subfield correctly', async () => { 36 | const dmmf = await getDMMF({ datamodel }); 37 | const toc = new TOCGenerator( 38 | transformDMMF(dmmf, { 39 | includeRelationFields: true, 40 | }) 41 | ); 42 | 43 | const spy = jest.spyOn(toc, 'getSubFieldHTML'); 44 | toc.toHTML(); 45 | 46 | expect(spy).toHaveBeenCalled(); 47 | // every case of model name and one case of others 48 | expect(spy).toHaveBeenCalledWith('model', 'Post', 'id'); 49 | expect(spy).toHaveBeenCalledWith('model', 'Post', 'name'); 50 | expect(spy).toHaveBeenCalledWith('model', 'User', 'userId'); 51 | expect(spy).toHaveBeenCalledWith('model', 'User', 'something'); 52 | expect(spy).toHaveBeenCalledWith('model', 'User', 'findUnique'); 53 | expect(spy).toHaveBeenCalledWith('type', 'inputType', 'UserWhereInput'); 54 | expect(spy).toHaveBeenCalledWith('type', 'outputType', 'User'); 55 | 56 | expect(toc.getSubFieldHTML('model', 'Post', 'userId')).toMatchSnapshot(); 57 | }); 58 | 59 | it('renders on toHTML', async () => { 60 | const dmmf = await getDMMF({ datamodel }); 61 | const toc = new TOCGenerator( 62 | transformDMMF(dmmf, { 63 | includeRelationFields: true, 64 | }) 65 | ); 66 | 67 | const subheaderSpy = jest.spyOn(toc, 'getTOCSubHeaderHTML'); 68 | const subfieldSpy = jest.spyOn(toc, 'getSubFieldHTML'); 69 | const result = toc.toHTML(); 70 | 71 | // one case of each just to make sure code calls them 72 | expect(subheaderSpy).toHaveBeenCalledWith('Post'); 73 | expect(subfieldSpy).toHaveBeenCalledWith('model', 'Post', 'id'); 74 | 75 | expect(result).toMatchSnapshot(); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/tests/transformDMMF.test.ts: -------------------------------------------------------------------------------- 1 | import transformDMMF from '../generator/transformDMMF'; 2 | //@ts-ignore 3 | import { getDMMF } from '@prisma/internals'; 4 | 5 | describe('transformDMMF', () => { 6 | it('show relation fields when includeRelationFields = true', async () => { 7 | const datamodelString = /* Prisma */ ` 8 | model User { 9 | id String @default(cuid()) @id 10 | name String 11 | otherField Int 12 | posts Post[] 13 | } 14 | 15 | model Post { 16 | id String @id @default(cuid()) 17 | title String? 18 | userId String 19 | user User @relation(fields:[userId], references:[id]) 20 | } 21 | `; 22 | 23 | const dmmf = await getDMMF({ datamodel: datamodelString }); 24 | const transformedDmmf = transformDMMF(dmmf, { 25 | includeRelationFields: true, 26 | }); 27 | 28 | expect(transformedDmmf).toMatchObject({ 29 | datamodel: { 30 | models: [ 31 | { 32 | name: 'User', 33 | fields: [ 34 | { name: 'id' }, 35 | { name: 'name' }, 36 | { name: 'otherField' }, 37 | { name: 'posts' }, 38 | ], 39 | }, 40 | { 41 | name: 'Post', 42 | fields: [ 43 | { name: 'id' }, 44 | { name: 'title' }, 45 | { name: 'userId' }, 46 | { name: 'user' }, 47 | ], 48 | }, 49 | ], 50 | }, 51 | }); 52 | }); 53 | 54 | it('hide relation fields when includeRelationFields = false', async () => { 55 | const datamodelString = /* Prisma */ ` 56 | model User { 57 | id String @default(cuid()) @id 58 | name String 59 | otherField Int 60 | posts Post[] 61 | } 62 | 63 | model Post { 64 | id String @id @default(cuid()) 65 | title String? 66 | userId String 67 | user User @relation(fields:[userId], references:[id]) 68 | } 69 | `; 70 | 71 | const dmmf = await getDMMF({ datamodel: datamodelString }); 72 | const transformedDmmf = transformDMMF(dmmf, { 73 | includeRelationFields: false, 74 | }); 75 | 76 | expect(transformedDmmf).toMatchObject({ 77 | datamodel: { 78 | models: [ 79 | { 80 | name: 'User', 81 | fields: [{ name: 'id' }, { name: 'name' }, { name: 'otherField' }], 82 | }, 83 | { 84 | name: 'Post', 85 | fields: [{ name: 'id' }, { name: 'title' }, { name: 'userId' }], 86 | }, 87 | ], 88 | }, 89 | }); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /styles_generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prisma-docs-generator-theme", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "autoprefixer": "^10.4.13", 8 | "cross-env": "^7.0.3", 9 | "cssnano": "^5.1.15", 10 | "postcss": "^8.4.21", 11 | "postcss-cli": "^10.1.0", 12 | "tailwindcss": "^3.2.6" 13 | }, 14 | "scripts": { 15 | "build": "cross-env NODE_ENV=production postcss -o dist/main.css ./styles/**/**.css", 16 | "watch": "postcss -w -o dist/main.css ./styles/**/**.css" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /styles_generator/postcss.config.js: -------------------------------------------------------------------------------- 1 | let plugins = [require("tailwindcss"), require("autoprefixer")]; 2 | 3 | if (process.env.NODE_ENV === "production") { 4 | plugins.push( 5 | require("cssnano")({ 6 | preset: "default", 7 | }) 8 | ); 9 | } 10 | 11 | module.exports = { 12 | plugins, 13 | }; 14 | -------------------------------------------------------------------------------- /styles_generator/styles/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; 6 | 7 | body { 8 | /* Code blocks */ 9 | --code-inner-color: #718096; 10 | --code-token1-color: #d5408c; 11 | --code-token2-color: #805ad5; 12 | --code-token3-color: #319795; 13 | --code-token4-color: #dd6b21; 14 | --code-token5-color: #690; 15 | --code-token6-color: #9a6e3a; 16 | --code-token7-color: #e90; 17 | --code-linenum-color: #cbd5e0; 18 | --code-added-color: #47bb78; 19 | --code-added-bg-color: #d9f4e6; 20 | --code-deleted-color: #e53e3e; 21 | --code-deleted-bg-color: #f5e4e7; 22 | --code-highlight-color: #a0aec0; 23 | --code-highlight-bg-color: #e2e8f0; 24 | --code-result-bg-color: #e7edf3; 25 | --main-font-color: #1a202c; 26 | --border-color: #e2e8f0; 27 | --code-bgd-color: #f6f8fa; 28 | } 29 | 30 | /* Prism */ 31 | 32 | code[class*="language-"], 33 | pre[class*="language-"], 34 | pre[class*="language-"] code { 35 | color: var(--main-font-color) !important; 36 | font-family: "Roboto Mono", SFMono-Regular, Menlo, Monaco, Consolas, 37 | "Liberation Mono", "Courier New", monospace; 38 | font-variant: no-common-ligatures no-discretionary-ligatures 39 | no-historical-ligatures no-contextual; 40 | font-size: 14px; 41 | text-align: left; 42 | white-space: pre; 43 | word-spacing: normal; 44 | word-break: normal; 45 | word-wrap: normal; 46 | line-height: 1.5; 47 | 48 | -moz-tab-size: 4; 49 | -o-tab-size: 4; 50 | tab-size: 4; 51 | 52 | -webkit-hyphens: none; 53 | -moz-hyphens: none; 54 | -ms-hyphens: none; 55 | hyphens: none; 56 | 57 | border-radius: 8px; 58 | display: block; 59 | grid-template-rows: max-content; 60 | width: 100%; 61 | overflow: auto; 62 | } 63 | 64 | /* Code blocks */ 65 | pre[class*="language-"] { 66 | padding: 1em; 67 | border-radius: 1em; 68 | margin: 0; 69 | overflow: auto; 70 | } 71 | 72 | :not(pre) > code[class*="language-"], 73 | pre[class*="language-"] { 74 | background: var(--code-bgd-color) !important; 75 | } 76 | 77 | /* Inline code */ 78 | :not(pre) > code[class*="language-"] { 79 | padding: 0.1em; 80 | border-radius: 0.3em; 81 | white-space: normal; 82 | } 83 | 84 | code.inline-code, 85 | .inline-code { 86 | display: inline; 87 | vertical-align: baseline; 88 | padding: 0.05em 0.3em 0.2em 0.3em; 89 | background: var(--code-inline-bgd-color); 90 | font-size: 14px; 91 | font-feature-settings: "clig" 0, "calt" 0; 92 | font-variant: no-common-ligatures no-discretionary-ligatures 93 | no-historical-ligatures no-contextual; 94 | font-family: "Roboto Mono"; 95 | font-style: normal; 96 | line-height: 24px; 97 | border-radius: 5px; 98 | color: var(--main-font-color); 99 | font-weight: 500; 100 | } 101 | 102 | .inline-code { 103 | background-color: var(--border-color); 104 | } 105 | 106 | .top-section h1 inlinecode { 107 | font-size: 2rem; 108 | } 109 | 110 | .token.comment, 111 | .token.prolog, 112 | .token.doctype, 113 | .token.cdata { 114 | color: var(--code-inner-color) !important; 115 | font-style: normal !important; 116 | } 117 | 118 | .token.namespace { 119 | opacity: 0.7; 120 | } 121 | 122 | .token.property, 123 | .token.tag, 124 | .token.type-args, 125 | .token.number, 126 | .token.constant, 127 | .token.symbol, 128 | .token.deleted { 129 | color: var(--code-token4-color) !important; 130 | } 131 | 132 | .token.selector, 133 | .token.attr-name, 134 | .token.string, 135 | .token.char, 136 | .token.builtin, 137 | .token.inserted { 138 | color: var(--code-token5-color) !important; 139 | } 140 | 141 | .token.operator, 142 | .token.entity, 143 | .token.url, 144 | .language-css .token.string, 145 | .style .token.string { 146 | color: var(--code-token6-color) !important; 147 | } 148 | 149 | .token.atrule, 150 | .token.attr-value, 151 | .token.keyword { 152 | color: var(--code-token1-color) !important; 153 | } 154 | 155 | .token.function, 156 | .token.class-name, 157 | .token[class*="class-name"], 158 | .token.boolean { 159 | color: var(--code-token2-color) !important; 160 | } 161 | 162 | .token.regex, 163 | .token.important, 164 | .token.variable { 165 | color: var(--code-token7-color) !important; 166 | } 167 | 168 | .token.important, 169 | .token.bold { 170 | font-weight: bold; 171 | } 172 | .token.italic { 173 | font-style: italic; 174 | } 175 | 176 | .token.entity { 177 | cursor: help; 178 | } 179 | 180 | .token.annotation { 181 | color: var(--code-token3-color) !important; 182 | } 183 | 184 | /* PRISM END */ 185 | -------------------------------------------------------------------------------- /styles_generator/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | module.exports = { 4 | darkMode: 'class', 5 | content: ['./**/*.html'], 6 | theme: { 7 | extend: {}, 8 | }, 9 | variants: {}, 10 | plugins: [], 11 | }; 12 | -------------------------------------------------------------------------------- /styles_generator/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@nodelib/fs.scandir@2.1.4": 6 | version "2.1.4" 7 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" 8 | integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== 9 | dependencies: 10 | "@nodelib/fs.stat" "2.0.4" 11 | run-parallel "^1.1.9" 12 | 13 | "@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": 14 | version "2.0.4" 15 | resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" 16 | integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== 17 | 18 | "@nodelib/fs.walk@^1.2.3": 19 | version "1.2.6" 20 | resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" 21 | integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== 22 | dependencies: 23 | "@nodelib/fs.scandir" "2.1.4" 24 | fastq "^1.6.0" 25 | 26 | "@trysound/sax@0.2.0": 27 | version "0.2.0" 28 | resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" 29 | integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== 30 | 31 | acorn-node@^1.8.2: 32 | version "1.8.2" 33 | resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" 34 | integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== 35 | dependencies: 36 | acorn "^7.0.0" 37 | acorn-walk "^7.0.0" 38 | xtend "^4.0.2" 39 | 40 | acorn-walk@^7.0.0: 41 | version "7.2.0" 42 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" 43 | integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== 44 | 45 | acorn@^7.0.0: 46 | version "7.4.1" 47 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" 48 | integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== 49 | 50 | ansi-regex@^5.0.0: 51 | version "5.0.0" 52 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" 53 | integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== 54 | 55 | ansi-regex@^5.0.1: 56 | version "5.0.1" 57 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 58 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 59 | 60 | ansi-styles@^4.0.0: 61 | version "4.3.0" 62 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 63 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 64 | dependencies: 65 | color-convert "^2.0.1" 66 | 67 | anymatch@~3.1.1: 68 | version "3.1.1" 69 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" 70 | integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== 71 | dependencies: 72 | normalize-path "^3.0.0" 73 | picomatch "^2.0.4" 74 | 75 | anymatch@~3.1.2: 76 | version "3.1.3" 77 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" 78 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 79 | dependencies: 80 | normalize-path "^3.0.0" 81 | picomatch "^2.0.4" 82 | 83 | arg@^5.0.2: 84 | version "5.0.2" 85 | resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" 86 | integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== 87 | 88 | autoprefixer@^10.4.13: 89 | version "10.4.13" 90 | resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" 91 | integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg== 92 | dependencies: 93 | browserslist "^4.21.4" 94 | caniuse-lite "^1.0.30001426" 95 | fraction.js "^4.2.0" 96 | normalize-range "^0.1.2" 97 | picocolors "^1.0.0" 98 | postcss-value-parser "^4.2.0" 99 | 100 | binary-extensions@^2.0.0: 101 | version "2.2.0" 102 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 103 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 104 | 105 | boolbase@^1.0.0: 106 | version "1.0.0" 107 | resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" 108 | integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= 109 | 110 | braces@^3.0.2, braces@~3.0.2: 111 | version "3.0.2" 112 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 113 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 114 | dependencies: 115 | fill-range "^7.0.1" 116 | 117 | browserslist@^4.0.0: 118 | version "4.16.3" 119 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" 120 | integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== 121 | dependencies: 122 | caniuse-lite "^1.0.30001181" 123 | colorette "^1.2.1" 124 | electron-to-chromium "^1.3.649" 125 | escalade "^3.1.1" 126 | node-releases "^1.1.70" 127 | 128 | browserslist@^4.21.4: 129 | version "4.21.5" 130 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" 131 | integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== 132 | dependencies: 133 | caniuse-lite "^1.0.30001449" 134 | electron-to-chromium "^1.4.284" 135 | node-releases "^2.0.8" 136 | update-browserslist-db "^1.0.10" 137 | 138 | camelcase-css@^2.0.1: 139 | version "2.0.1" 140 | resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" 141 | integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== 142 | 143 | caniuse-api@^3.0.0: 144 | version "3.0.0" 145 | resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" 146 | integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== 147 | dependencies: 148 | browserslist "^4.0.0" 149 | caniuse-lite "^1.0.0" 150 | lodash.memoize "^4.1.2" 151 | lodash.uniq "^4.5.0" 152 | 153 | caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001449: 154 | version "1.0.30001453" 155 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001453.tgz#6d3a1501622bf424a3cee5ad9550e640b0de3de8" 156 | integrity sha512-R9o/uySW38VViaTrOtwfbFEiBFUh7ST3uIG4OEymIG3/uKdHDO4xk/FaqfUw0d+irSUyFPy3dZszf9VvSTPnsA== 157 | 158 | chokidar@^3.3.0: 159 | version "3.5.1" 160 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" 161 | integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== 162 | dependencies: 163 | anymatch "~3.1.1" 164 | braces "~3.0.2" 165 | glob-parent "~5.1.0" 166 | is-binary-path "~2.1.0" 167 | is-glob "~4.0.1" 168 | normalize-path "~3.0.0" 169 | readdirp "~3.5.0" 170 | optionalDependencies: 171 | fsevents "~2.3.1" 172 | 173 | chokidar@^3.5.3: 174 | version "3.5.3" 175 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 176 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 177 | dependencies: 178 | anymatch "~3.1.2" 179 | braces "~3.0.2" 180 | glob-parent "~5.1.2" 181 | is-binary-path "~2.1.0" 182 | is-glob "~4.0.1" 183 | normalize-path "~3.0.0" 184 | readdirp "~3.6.0" 185 | optionalDependencies: 186 | fsevents "~2.3.2" 187 | 188 | cliui@^8.0.1: 189 | version "8.0.1" 190 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" 191 | integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== 192 | dependencies: 193 | string-width "^4.2.0" 194 | strip-ansi "^6.0.1" 195 | wrap-ansi "^7.0.0" 196 | 197 | color-convert@^2.0.1: 198 | version "2.0.1" 199 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 200 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 201 | dependencies: 202 | color-name "~1.1.4" 203 | 204 | color-name@^1.1.4, color-name@~1.1.4: 205 | version "1.1.4" 206 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 207 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 208 | 209 | colord@^2.9.1: 210 | version "2.9.3" 211 | resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" 212 | integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== 213 | 214 | colorette@^1.2.1: 215 | version "1.2.2" 216 | resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" 217 | integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== 218 | 219 | commander@^7.2.0: 220 | version "7.2.0" 221 | resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" 222 | integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== 223 | 224 | cross-env@^7.0.3: 225 | version "7.0.3" 226 | resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" 227 | integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== 228 | dependencies: 229 | cross-spawn "^7.0.1" 230 | 231 | cross-spawn@^7.0.1: 232 | version "7.0.3" 233 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 234 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 235 | dependencies: 236 | path-key "^3.1.0" 237 | shebang-command "^2.0.0" 238 | which "^2.0.1" 239 | 240 | css-declaration-sorter@^6.3.1: 241 | version "6.3.1" 242 | resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz#be5e1d71b7a992433fb1c542c7a1b835e45682ec" 243 | integrity sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w== 244 | 245 | css-select@^4.1.3: 246 | version "4.3.0" 247 | resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" 248 | integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== 249 | dependencies: 250 | boolbase "^1.0.0" 251 | css-what "^6.0.1" 252 | domhandler "^4.3.1" 253 | domutils "^2.8.0" 254 | nth-check "^2.0.1" 255 | 256 | css-tree@^1.1.2: 257 | version "1.1.2" 258 | resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.2.tgz#9ae393b5dafd7dae8a622475caec78d3d8fbd7b5" 259 | integrity sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ== 260 | dependencies: 261 | mdn-data "2.0.14" 262 | source-map "^0.6.1" 263 | 264 | css-tree@^1.1.3: 265 | version "1.1.3" 266 | resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" 267 | integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== 268 | dependencies: 269 | mdn-data "2.0.14" 270 | source-map "^0.6.1" 271 | 272 | css-what@^6.0.1: 273 | version "6.1.0" 274 | resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" 275 | integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== 276 | 277 | cssesc@^3.0.0: 278 | version "3.0.0" 279 | resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" 280 | integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== 281 | 282 | cssnano-preset-default@^5.2.14: 283 | version "5.2.14" 284 | resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz#309def4f7b7e16d71ab2438052093330d9ab45d8" 285 | integrity sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A== 286 | dependencies: 287 | css-declaration-sorter "^6.3.1" 288 | cssnano-utils "^3.1.0" 289 | postcss-calc "^8.2.3" 290 | postcss-colormin "^5.3.1" 291 | postcss-convert-values "^5.1.3" 292 | postcss-discard-comments "^5.1.2" 293 | postcss-discard-duplicates "^5.1.0" 294 | postcss-discard-empty "^5.1.1" 295 | postcss-discard-overridden "^5.1.0" 296 | postcss-merge-longhand "^5.1.7" 297 | postcss-merge-rules "^5.1.4" 298 | postcss-minify-font-values "^5.1.0" 299 | postcss-minify-gradients "^5.1.1" 300 | postcss-minify-params "^5.1.4" 301 | postcss-minify-selectors "^5.2.1" 302 | postcss-normalize-charset "^5.1.0" 303 | postcss-normalize-display-values "^5.1.0" 304 | postcss-normalize-positions "^5.1.1" 305 | postcss-normalize-repeat-style "^5.1.1" 306 | postcss-normalize-string "^5.1.0" 307 | postcss-normalize-timing-functions "^5.1.0" 308 | postcss-normalize-unicode "^5.1.1" 309 | postcss-normalize-url "^5.1.0" 310 | postcss-normalize-whitespace "^5.1.1" 311 | postcss-ordered-values "^5.1.3" 312 | postcss-reduce-initial "^5.1.2" 313 | postcss-reduce-transforms "^5.1.0" 314 | postcss-svgo "^5.1.0" 315 | postcss-unique-selectors "^5.1.1" 316 | 317 | cssnano-utils@^3.1.0: 318 | version "3.1.0" 319 | resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" 320 | integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== 321 | 322 | cssnano@^5.1.15: 323 | version "5.1.15" 324 | resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" 325 | integrity sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw== 326 | dependencies: 327 | cssnano-preset-default "^5.2.14" 328 | lilconfig "^2.0.3" 329 | yaml "^1.10.2" 330 | 331 | csso@^4.2.0: 332 | version "4.2.0" 333 | resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" 334 | integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== 335 | dependencies: 336 | css-tree "^1.1.2" 337 | 338 | defined@^1.0.0: 339 | version "1.0.0" 340 | resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" 341 | integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= 342 | 343 | dependency-graph@^0.11.0: 344 | version "0.11.0" 345 | resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" 346 | integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== 347 | 348 | detective@^5.2.1: 349 | version "5.2.1" 350 | resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" 351 | integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== 352 | dependencies: 353 | acorn-node "^1.8.2" 354 | defined "^1.0.0" 355 | minimist "^1.2.6" 356 | 357 | didyoumean@^1.2.2: 358 | version "1.2.2" 359 | resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" 360 | integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== 361 | 362 | dir-glob@^3.0.1: 363 | version "3.0.1" 364 | resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" 365 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== 366 | dependencies: 367 | path-type "^4.0.0" 368 | 369 | dlv@^1.1.3: 370 | version "1.1.3" 371 | resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" 372 | integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== 373 | 374 | dom-serializer@^1.0.1: 375 | version "1.4.1" 376 | resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" 377 | integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== 378 | dependencies: 379 | domelementtype "^2.0.1" 380 | domhandler "^4.2.0" 381 | entities "^2.0.0" 382 | 383 | domelementtype@^2.0.1: 384 | version "2.1.0" 385 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" 386 | integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== 387 | 388 | domelementtype@^2.2.0: 389 | version "2.3.0" 390 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" 391 | integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== 392 | 393 | domhandler@^4.2.0, domhandler@^4.3.1: 394 | version "4.3.1" 395 | resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" 396 | integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== 397 | dependencies: 398 | domelementtype "^2.2.0" 399 | 400 | domutils@^2.8.0: 401 | version "2.8.0" 402 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" 403 | integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== 404 | dependencies: 405 | dom-serializer "^1.0.1" 406 | domelementtype "^2.2.0" 407 | domhandler "^4.2.0" 408 | 409 | electron-to-chromium@^1.3.649: 410 | version "1.3.685" 411 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.685.tgz#f636d17c9c232c925f8bbfbff4f86303da091ff9" 412 | integrity sha512-C3oFZNkJ8lz85ADqr3hzpjBc2ciejMRN2SCd/D0hwcqpr6MGxfdN/j89VN6l+ERTuCUvhg0VYsf40Q4qTz4bhQ== 413 | 414 | electron-to-chromium@^1.4.284: 415 | version "1.4.299" 416 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.299.tgz#faa2069cd4879a73e540e533178db5c618768d41" 417 | integrity sha512-lQ7ijJghH6pCGbfWXr6EY+KYCMaRSjgsY925r1p/TlpSfVM1VjHTcn1gAc15VM4uwti283X6QtjPTXdpoSGiZQ== 418 | 419 | emoji-regex@^8.0.0: 420 | version "8.0.0" 421 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 422 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 423 | 424 | entities@^2.0.0: 425 | version "2.2.0" 426 | resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" 427 | integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== 428 | 429 | escalade@^3.1.1: 430 | version "3.1.1" 431 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 432 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 433 | 434 | fast-glob@^3.2.11, fast-glob@^3.2.12: 435 | version "3.2.12" 436 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" 437 | integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== 438 | dependencies: 439 | "@nodelib/fs.stat" "^2.0.2" 440 | "@nodelib/fs.walk" "^1.2.3" 441 | glob-parent "^5.1.2" 442 | merge2 "^1.3.0" 443 | micromatch "^4.0.4" 444 | 445 | fastq@^1.6.0: 446 | version "1.11.0" 447 | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" 448 | integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== 449 | dependencies: 450 | reusify "^1.0.4" 451 | 452 | fill-range@^7.0.1: 453 | version "7.0.1" 454 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 455 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 456 | dependencies: 457 | to-regex-range "^5.0.1" 458 | 459 | fraction.js@^4.2.0: 460 | version "4.2.0" 461 | resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" 462 | integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== 463 | 464 | fs-extra@^11.0.0: 465 | version "11.1.0" 466 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" 467 | integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== 468 | dependencies: 469 | graceful-fs "^4.2.0" 470 | jsonfile "^6.0.1" 471 | universalify "^2.0.0" 472 | 473 | fsevents@~2.3.1, fsevents@~2.3.2: 474 | version "2.3.2" 475 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 476 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 477 | 478 | function-bind@^1.1.1: 479 | version "1.1.1" 480 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 481 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 482 | 483 | get-caller-file@^2.0.5: 484 | version "2.0.5" 485 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 486 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 487 | 488 | get-stdin@^9.0.0: 489 | version "9.0.0" 490 | resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575" 491 | integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== 492 | 493 | glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: 494 | version "5.1.2" 495 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 496 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 497 | dependencies: 498 | is-glob "^4.0.1" 499 | 500 | glob-parent@^6.0.2: 501 | version "6.0.2" 502 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" 503 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 504 | dependencies: 505 | is-glob "^4.0.3" 506 | 507 | globby@^13.0.0: 508 | version "13.1.3" 509 | resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" 510 | integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== 511 | dependencies: 512 | dir-glob "^3.0.1" 513 | fast-glob "^3.2.11" 514 | ignore "^5.2.0" 515 | merge2 "^1.4.1" 516 | slash "^4.0.0" 517 | 518 | graceful-fs@^4.1.6, graceful-fs@^4.2.0: 519 | version "4.2.6" 520 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" 521 | integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== 522 | 523 | has@^1.0.3: 524 | version "1.0.3" 525 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 526 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 527 | dependencies: 528 | function-bind "^1.1.1" 529 | 530 | ignore@^5.2.0: 531 | version "5.2.4" 532 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" 533 | integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== 534 | 535 | indexes-of@^1.0.1: 536 | version "1.0.1" 537 | resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" 538 | integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= 539 | 540 | is-binary-path@~2.1.0: 541 | version "2.1.0" 542 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 543 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 544 | dependencies: 545 | binary-extensions "^2.0.0" 546 | 547 | is-core-module@^2.9.0: 548 | version "2.11.0" 549 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" 550 | integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== 551 | dependencies: 552 | has "^1.0.3" 553 | 554 | is-extglob@^2.1.1: 555 | version "2.1.1" 556 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 557 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 558 | 559 | is-fullwidth-code-point@^3.0.0: 560 | version "3.0.0" 561 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 562 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 563 | 564 | is-glob@^4.0.1, is-glob@~4.0.1: 565 | version "4.0.1" 566 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" 567 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 568 | dependencies: 569 | is-extglob "^2.1.1" 570 | 571 | is-glob@^4.0.3: 572 | version "4.0.3" 573 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 574 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 575 | dependencies: 576 | is-extglob "^2.1.1" 577 | 578 | is-number@^7.0.0: 579 | version "7.0.0" 580 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 581 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 582 | 583 | isexe@^2.0.0: 584 | version "2.0.0" 585 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 586 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 587 | 588 | jsonfile@^6.0.1: 589 | version "6.1.0" 590 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" 591 | integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== 592 | dependencies: 593 | universalify "^2.0.0" 594 | optionalDependencies: 595 | graceful-fs "^4.1.6" 596 | 597 | lilconfig@^2.0.3, lilconfig@^2.0.5, lilconfig@^2.0.6: 598 | version "2.0.6" 599 | resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" 600 | integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== 601 | 602 | lodash.difference@^4.5.0: 603 | version "4.5.0" 604 | resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" 605 | integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw= 606 | 607 | lodash.forown@^4.4.0: 608 | version "4.4.0" 609 | resolved "https://registry.yarnpkg.com/lodash.forown/-/lodash.forown-4.4.0.tgz#85115cf04f73ef966eced52511d3893cc46683af" 610 | integrity sha1-hRFc8E9z75ZuztUlEdOJPMRmg68= 611 | 612 | lodash.get@^4.4.2: 613 | version "4.4.2" 614 | resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" 615 | integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= 616 | 617 | lodash.groupby@^4.6.0: 618 | version "4.6.0" 619 | resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" 620 | integrity sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E= 621 | 622 | lodash.memoize@^4.1.2: 623 | version "4.1.2" 624 | resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" 625 | integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= 626 | 627 | lodash.sortby@^4.7.0: 628 | version "4.7.0" 629 | resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" 630 | integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= 631 | 632 | lodash.uniq@^4.5.0: 633 | version "4.5.0" 634 | resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" 635 | integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= 636 | 637 | mdn-data@2.0.14: 638 | version "2.0.14" 639 | resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" 640 | integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== 641 | 642 | merge2@^1.3.0, merge2@^1.4.1: 643 | version "1.4.1" 644 | resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" 645 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 646 | 647 | micromatch@^4.0.4, micromatch@^4.0.5: 648 | version "4.0.5" 649 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" 650 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== 651 | dependencies: 652 | braces "^3.0.2" 653 | picomatch "^2.3.1" 654 | 655 | minimist@^1.2.6: 656 | version "1.2.8" 657 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" 658 | integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== 659 | 660 | nanoid@^3.3.4: 661 | version "3.3.4" 662 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" 663 | integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== 664 | 665 | node-releases@^1.1.70: 666 | version "1.1.71" 667 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" 668 | integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== 669 | 670 | node-releases@^2.0.8: 671 | version "2.0.10" 672 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" 673 | integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== 674 | 675 | normalize-path@^3.0.0, normalize-path@~3.0.0: 676 | version "3.0.0" 677 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 678 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 679 | 680 | normalize-range@^0.1.2: 681 | version "0.1.2" 682 | resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" 683 | integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= 684 | 685 | normalize-url@^6.0.1: 686 | version "6.1.0" 687 | resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" 688 | integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== 689 | 690 | nth-check@^2.0.1: 691 | version "2.1.1" 692 | resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" 693 | integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== 694 | dependencies: 695 | boolbase "^1.0.0" 696 | 697 | object-hash@^3.0.0: 698 | version "3.0.0" 699 | resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" 700 | integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== 701 | 702 | path-key@^3.1.0: 703 | version "3.1.1" 704 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 705 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 706 | 707 | path-parse@^1.0.7: 708 | version "1.0.7" 709 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 710 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 711 | 712 | path-type@^4.0.0: 713 | version "4.0.0" 714 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" 715 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 716 | 717 | picocolors@^1.0.0: 718 | version "1.0.0" 719 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 720 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 721 | 722 | picomatch@^2.0.4, picomatch@^2.2.1: 723 | version "2.2.2" 724 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" 725 | integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== 726 | 727 | picomatch@^2.3.1: 728 | version "2.3.1" 729 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 730 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 731 | 732 | pify@^2.3.0: 733 | version "2.3.0" 734 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 735 | integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= 736 | 737 | postcss-calc@^8.2.3: 738 | version "8.2.4" 739 | resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" 740 | integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== 741 | dependencies: 742 | postcss-selector-parser "^6.0.9" 743 | postcss-value-parser "^4.2.0" 744 | 745 | postcss-cli@^10.1.0: 746 | version "10.1.0" 747 | resolved "https://registry.yarnpkg.com/postcss-cli/-/postcss-cli-10.1.0.tgz#f6658c4998a1c35bd66cd71009132b2d12e04f80" 748 | integrity sha512-Zu7PLORkE9YwNdvOeOVKPmWghprOtjFQU3srMUGbdz3pHJiFh7yZ4geiZFMkjMfB0mtTFR3h8RemR62rPkbOPA== 749 | dependencies: 750 | chokidar "^3.3.0" 751 | dependency-graph "^0.11.0" 752 | fs-extra "^11.0.0" 753 | get-stdin "^9.0.0" 754 | globby "^13.0.0" 755 | picocolors "^1.0.0" 756 | postcss-load-config "^4.0.0" 757 | postcss-reporter "^7.0.0" 758 | pretty-hrtime "^1.0.3" 759 | read-cache "^1.0.0" 760 | slash "^5.0.0" 761 | yargs "^17.0.0" 762 | 763 | postcss-colormin@^5.3.1: 764 | version "5.3.1" 765 | resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz#86c27c26ed6ba00d96c79e08f3ffb418d1d1988f" 766 | integrity sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ== 767 | dependencies: 768 | browserslist "^4.21.4" 769 | caniuse-api "^3.0.0" 770 | colord "^2.9.1" 771 | postcss-value-parser "^4.2.0" 772 | 773 | postcss-convert-values@^5.1.3: 774 | version "5.1.3" 775 | resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz#04998bb9ba6b65aa31035d669a6af342c5f9d393" 776 | integrity sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA== 777 | dependencies: 778 | browserslist "^4.21.4" 779 | postcss-value-parser "^4.2.0" 780 | 781 | postcss-discard-comments@^5.1.2: 782 | version "5.1.2" 783 | resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" 784 | integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== 785 | 786 | postcss-discard-duplicates@^5.1.0: 787 | version "5.1.0" 788 | resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" 789 | integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== 790 | 791 | postcss-discard-empty@^5.1.1: 792 | version "5.1.1" 793 | resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" 794 | integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== 795 | 796 | postcss-discard-overridden@^5.1.0: 797 | version "5.1.0" 798 | resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" 799 | integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== 800 | 801 | postcss-import@^14.1.0: 802 | version "14.1.0" 803 | resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" 804 | integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== 805 | dependencies: 806 | postcss-value-parser "^4.0.0" 807 | read-cache "^1.0.0" 808 | resolve "^1.1.7" 809 | 810 | postcss-js@^4.0.0: 811 | version "4.0.1" 812 | resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" 813 | integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== 814 | dependencies: 815 | camelcase-css "^2.0.1" 816 | 817 | postcss-load-config@^3.1.4: 818 | version "3.1.4" 819 | resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" 820 | integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== 821 | dependencies: 822 | lilconfig "^2.0.5" 823 | yaml "^1.10.2" 824 | 825 | postcss-load-config@^4.0.0: 826 | version "4.0.1" 827 | resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz#152383f481c2758274404e4962743191d73875bd" 828 | integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== 829 | dependencies: 830 | lilconfig "^2.0.5" 831 | yaml "^2.1.1" 832 | 833 | postcss-merge-longhand@^5.1.7: 834 | version "5.1.7" 835 | resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz#24a1bdf402d9ef0e70f568f39bdc0344d568fb16" 836 | integrity sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ== 837 | dependencies: 838 | postcss-value-parser "^4.2.0" 839 | stylehacks "^5.1.1" 840 | 841 | postcss-merge-rules@^5.1.4: 842 | version "5.1.4" 843 | resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz#2f26fa5cacb75b1402e213789f6766ae5e40313c" 844 | integrity sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g== 845 | dependencies: 846 | browserslist "^4.21.4" 847 | caniuse-api "^3.0.0" 848 | cssnano-utils "^3.1.0" 849 | postcss-selector-parser "^6.0.5" 850 | 851 | postcss-minify-font-values@^5.1.0: 852 | version "5.1.0" 853 | resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" 854 | integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== 855 | dependencies: 856 | postcss-value-parser "^4.2.0" 857 | 858 | postcss-minify-gradients@^5.1.1: 859 | version "5.1.1" 860 | resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" 861 | integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== 862 | dependencies: 863 | colord "^2.9.1" 864 | cssnano-utils "^3.1.0" 865 | postcss-value-parser "^4.2.0" 866 | 867 | postcss-minify-params@^5.1.4: 868 | version "5.1.4" 869 | resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz#c06a6c787128b3208b38c9364cfc40c8aa5d7352" 870 | integrity sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw== 871 | dependencies: 872 | browserslist "^4.21.4" 873 | cssnano-utils "^3.1.0" 874 | postcss-value-parser "^4.2.0" 875 | 876 | postcss-minify-selectors@^5.2.1: 877 | version "5.2.1" 878 | resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" 879 | integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== 880 | dependencies: 881 | postcss-selector-parser "^6.0.5" 882 | 883 | postcss-nested@6.0.0: 884 | version "6.0.0" 885 | resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.0.tgz#1572f1984736578f360cffc7eb7dca69e30d1735" 886 | integrity sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w== 887 | dependencies: 888 | postcss-selector-parser "^6.0.10" 889 | 890 | postcss-normalize-charset@^5.1.0: 891 | version "5.1.0" 892 | resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" 893 | integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== 894 | 895 | postcss-normalize-display-values@^5.1.0: 896 | version "5.1.0" 897 | resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" 898 | integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== 899 | dependencies: 900 | postcss-value-parser "^4.2.0" 901 | 902 | postcss-normalize-positions@^5.1.1: 903 | version "5.1.1" 904 | resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" 905 | integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== 906 | dependencies: 907 | postcss-value-parser "^4.2.0" 908 | 909 | postcss-normalize-repeat-style@^5.1.1: 910 | version "5.1.1" 911 | resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" 912 | integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== 913 | dependencies: 914 | postcss-value-parser "^4.2.0" 915 | 916 | postcss-normalize-string@^5.1.0: 917 | version "5.1.0" 918 | resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" 919 | integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== 920 | dependencies: 921 | postcss-value-parser "^4.2.0" 922 | 923 | postcss-normalize-timing-functions@^5.1.0: 924 | version "5.1.0" 925 | resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" 926 | integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== 927 | dependencies: 928 | postcss-value-parser "^4.2.0" 929 | 930 | postcss-normalize-unicode@^5.1.1: 931 | version "5.1.1" 932 | resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz#f67297fca3fea7f17e0d2caa40769afc487aa030" 933 | integrity sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA== 934 | dependencies: 935 | browserslist "^4.21.4" 936 | postcss-value-parser "^4.2.0" 937 | 938 | postcss-normalize-url@^5.1.0: 939 | version "5.1.0" 940 | resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" 941 | integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== 942 | dependencies: 943 | normalize-url "^6.0.1" 944 | postcss-value-parser "^4.2.0" 945 | 946 | postcss-normalize-whitespace@^5.1.1: 947 | version "5.1.1" 948 | resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" 949 | integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== 950 | dependencies: 951 | postcss-value-parser "^4.2.0" 952 | 953 | postcss-ordered-values@^5.1.3: 954 | version "5.1.3" 955 | resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" 956 | integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== 957 | dependencies: 958 | cssnano-utils "^3.1.0" 959 | postcss-value-parser "^4.2.0" 960 | 961 | postcss-reduce-initial@^5.1.2: 962 | version "5.1.2" 963 | resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz#798cd77b3e033eae7105c18c9d371d989e1382d6" 964 | integrity sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg== 965 | dependencies: 966 | browserslist "^4.21.4" 967 | caniuse-api "^3.0.0" 968 | 969 | postcss-reduce-transforms@^5.1.0: 970 | version "5.1.0" 971 | resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" 972 | integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== 973 | dependencies: 974 | postcss-value-parser "^4.2.0" 975 | 976 | postcss-reporter@^7.0.0: 977 | version "7.0.2" 978 | resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-7.0.2.tgz#03e9e7381c1afe40646f9c22e7aeeb860e051065" 979 | integrity sha512-JyQ96NTQQsso42y6L1H1RqHfWH1C3Jr0pt91mVv5IdYddZAE9DUZxuferNgk6q0o6vBVOrfVJb10X1FgDzjmDw== 980 | dependencies: 981 | colorette "^1.2.1" 982 | lodash.difference "^4.5.0" 983 | lodash.forown "^4.4.0" 984 | lodash.get "^4.4.2" 985 | lodash.groupby "^4.6.0" 986 | lodash.sortby "^4.7.0" 987 | 988 | postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: 989 | version "6.0.11" 990 | resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" 991 | integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== 992 | dependencies: 993 | cssesc "^3.0.0" 994 | util-deprecate "^1.0.2" 995 | 996 | postcss-selector-parser@^6.0.4: 997 | version "6.0.4" 998 | resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" 999 | integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== 1000 | dependencies: 1001 | cssesc "^3.0.0" 1002 | indexes-of "^1.0.1" 1003 | uniq "^1.0.1" 1004 | util-deprecate "^1.0.2" 1005 | 1006 | postcss-svgo@^5.1.0: 1007 | version "5.1.0" 1008 | resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" 1009 | integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== 1010 | dependencies: 1011 | postcss-value-parser "^4.2.0" 1012 | svgo "^2.7.0" 1013 | 1014 | postcss-unique-selectors@^5.1.1: 1015 | version "5.1.1" 1016 | resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" 1017 | integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== 1018 | dependencies: 1019 | postcss-selector-parser "^6.0.5" 1020 | 1021 | postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: 1022 | version "4.2.0" 1023 | resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" 1024 | integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== 1025 | 1026 | postcss@^8.0.9, postcss@^8.4.21: 1027 | version "8.4.21" 1028 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" 1029 | integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== 1030 | dependencies: 1031 | nanoid "^3.3.4" 1032 | picocolors "^1.0.0" 1033 | source-map-js "^1.0.2" 1034 | 1035 | pretty-hrtime@^1.0.3: 1036 | version "1.0.3" 1037 | resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" 1038 | integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= 1039 | 1040 | queue-microtask@^1.2.2: 1041 | version "1.2.2" 1042 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" 1043 | integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== 1044 | 1045 | quick-lru@^5.1.1: 1046 | version "5.1.1" 1047 | resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" 1048 | integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== 1049 | 1050 | read-cache@^1.0.0: 1051 | version "1.0.0" 1052 | resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" 1053 | integrity sha1-5mTvMRYRZsl1HNvo28+GtftY93Q= 1054 | dependencies: 1055 | pify "^2.3.0" 1056 | 1057 | readdirp@~3.5.0: 1058 | version "3.5.0" 1059 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" 1060 | integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== 1061 | dependencies: 1062 | picomatch "^2.2.1" 1063 | 1064 | readdirp@~3.6.0: 1065 | version "3.6.0" 1066 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 1067 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 1068 | dependencies: 1069 | picomatch "^2.2.1" 1070 | 1071 | require-directory@^2.1.1: 1072 | version "2.1.1" 1073 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 1074 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 1075 | 1076 | resolve@^1.1.7, resolve@^1.22.1: 1077 | version "1.22.1" 1078 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 1079 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 1080 | dependencies: 1081 | is-core-module "^2.9.0" 1082 | path-parse "^1.0.7" 1083 | supports-preserve-symlinks-flag "^1.0.0" 1084 | 1085 | reusify@^1.0.4: 1086 | version "1.0.4" 1087 | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" 1088 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 1089 | 1090 | run-parallel@^1.1.9: 1091 | version "1.2.0" 1092 | resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" 1093 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 1094 | dependencies: 1095 | queue-microtask "^1.2.2" 1096 | 1097 | shebang-command@^2.0.0: 1098 | version "2.0.0" 1099 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 1100 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 1101 | dependencies: 1102 | shebang-regex "^3.0.0" 1103 | 1104 | shebang-regex@^3.0.0: 1105 | version "3.0.0" 1106 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 1107 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 1108 | 1109 | slash@^4.0.0: 1110 | version "4.0.0" 1111 | resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" 1112 | integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== 1113 | 1114 | slash@^5.0.0: 1115 | version "5.0.0" 1116 | resolved "https://registry.yarnpkg.com/slash/-/slash-5.0.0.tgz#8c18a871096b71ee0e002976a4fe3374991c3074" 1117 | integrity sha512-n6KkmvKS0623igEVj3FF0OZs1gYYJ0o0Hj939yc1fyxl2xt+xYpLnzJB6xBSqOfV9ZFLEWodBBN/heZJahuIJQ== 1118 | 1119 | source-map-js@^1.0.2: 1120 | version "1.0.2" 1121 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 1122 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 1123 | 1124 | source-map@^0.6.1: 1125 | version "0.6.1" 1126 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 1127 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 1128 | 1129 | stable@^0.1.8: 1130 | version "0.1.8" 1131 | resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" 1132 | integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== 1133 | 1134 | string-width@^4.1.0, string-width@^4.2.0: 1135 | version "4.2.2" 1136 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" 1137 | integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== 1138 | dependencies: 1139 | emoji-regex "^8.0.0" 1140 | is-fullwidth-code-point "^3.0.0" 1141 | strip-ansi "^6.0.0" 1142 | 1143 | string-width@^4.2.3: 1144 | version "4.2.3" 1145 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 1146 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 1147 | dependencies: 1148 | emoji-regex "^8.0.0" 1149 | is-fullwidth-code-point "^3.0.0" 1150 | strip-ansi "^6.0.1" 1151 | 1152 | strip-ansi@^6.0.0: 1153 | version "6.0.0" 1154 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" 1155 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== 1156 | dependencies: 1157 | ansi-regex "^5.0.0" 1158 | 1159 | strip-ansi@^6.0.1: 1160 | version "6.0.1" 1161 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 1162 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1163 | dependencies: 1164 | ansi-regex "^5.0.1" 1165 | 1166 | stylehacks@^5.1.1: 1167 | version "5.1.1" 1168 | resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" 1169 | integrity sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw== 1170 | dependencies: 1171 | browserslist "^4.21.4" 1172 | postcss-selector-parser "^6.0.4" 1173 | 1174 | supports-preserve-symlinks-flag@^1.0.0: 1175 | version "1.0.0" 1176 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 1177 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1178 | 1179 | svgo@^2.7.0: 1180 | version "2.8.0" 1181 | resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" 1182 | integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== 1183 | dependencies: 1184 | "@trysound/sax" "0.2.0" 1185 | commander "^7.2.0" 1186 | css-select "^4.1.3" 1187 | css-tree "^1.1.3" 1188 | csso "^4.2.0" 1189 | picocolors "^1.0.0" 1190 | stable "^0.1.8" 1191 | 1192 | tailwindcss@^3.2.6: 1193 | version "3.2.6" 1194 | resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.6.tgz#9bedbc744a4a85d6120ce0cc3db024c551a5c733" 1195 | integrity sha512-BfgQWZrtqowOQMC2bwaSNe7xcIjdDEgixWGYOd6AL0CbKHJlvhfdbINeAW76l1sO+1ov/MJ93ODJ9yluRituIw== 1196 | dependencies: 1197 | arg "^5.0.2" 1198 | chokidar "^3.5.3" 1199 | color-name "^1.1.4" 1200 | detective "^5.2.1" 1201 | didyoumean "^1.2.2" 1202 | dlv "^1.1.3" 1203 | fast-glob "^3.2.12" 1204 | glob-parent "^6.0.2" 1205 | is-glob "^4.0.3" 1206 | lilconfig "^2.0.6" 1207 | micromatch "^4.0.5" 1208 | normalize-path "^3.0.0" 1209 | object-hash "^3.0.0" 1210 | picocolors "^1.0.0" 1211 | postcss "^8.0.9" 1212 | postcss-import "^14.1.0" 1213 | postcss-js "^4.0.0" 1214 | postcss-load-config "^3.1.4" 1215 | postcss-nested "6.0.0" 1216 | postcss-selector-parser "^6.0.11" 1217 | postcss-value-parser "^4.2.0" 1218 | quick-lru "^5.1.1" 1219 | resolve "^1.22.1" 1220 | 1221 | to-regex-range@^5.0.1: 1222 | version "5.0.1" 1223 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1224 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1225 | dependencies: 1226 | is-number "^7.0.0" 1227 | 1228 | uniq@^1.0.1: 1229 | version "1.0.1" 1230 | resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" 1231 | integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= 1232 | 1233 | universalify@^2.0.0: 1234 | version "2.0.0" 1235 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" 1236 | integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== 1237 | 1238 | update-browserslist-db@^1.0.10: 1239 | version "1.0.10" 1240 | resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" 1241 | integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== 1242 | dependencies: 1243 | escalade "^3.1.1" 1244 | picocolors "^1.0.0" 1245 | 1246 | util-deprecate@^1.0.2: 1247 | version "1.0.2" 1248 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1249 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1250 | 1251 | which@^2.0.1: 1252 | version "2.0.2" 1253 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 1254 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1255 | dependencies: 1256 | isexe "^2.0.0" 1257 | 1258 | wrap-ansi@^7.0.0: 1259 | version "7.0.0" 1260 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 1261 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 1262 | dependencies: 1263 | ansi-styles "^4.0.0" 1264 | string-width "^4.1.0" 1265 | strip-ansi "^6.0.0" 1266 | 1267 | xtend@^4.0.2: 1268 | version "4.0.2" 1269 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 1270 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 1271 | 1272 | y18n@^5.0.5: 1273 | version "5.0.5" 1274 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" 1275 | integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== 1276 | 1277 | yaml@^1.10.2: 1278 | version "1.10.2" 1279 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" 1280 | integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== 1281 | 1282 | yaml@^2.1.1: 1283 | version "2.2.1" 1284 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" 1285 | integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== 1286 | 1287 | yargs-parser@^21.1.1: 1288 | version "21.1.1" 1289 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" 1290 | integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== 1291 | 1292 | yargs@^17.0.0: 1293 | version "17.6.2" 1294 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" 1295 | integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== 1296 | dependencies: 1297 | cliui "^8.0.1" 1298 | escalade "^3.1.1" 1299 | get-caller-file "^2.0.5" 1300 | require-directory "^2.1.1" 1301 | string-width "^4.2.3" 1302 | y18n "^5.0.5" 1303 | yargs-parser "^21.1.1" 1304 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "baseUrl": "src", 5 | "outDir": "dist", 6 | "sourceMap": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "lib": ["es2019", "dom"], // dom is added for prismjs :( 10 | "strict": true, 11 | "esModuleInterop": true 12 | } 13 | } 14 | --------------------------------------------------------------------------------