├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── assets └── model-instance-type.png ├── jest.config.cjs ├── package-lock.json ├── package.json ├── patches └── ts-jest+29.1.2.patch ├── src ├── errors.ts ├── extensions.ts ├── index.ts ├── mongoose-helpers.ts ├── mz-types.ts ├── setup.ts ├── to-mongoose.ts ├── types.ts ├── utils.ts └── zod-helpers.ts ├── test ├── gen-timestamps.test.ts ├── mongoose-types.test.ts ├── plugins.test.ts ├── schema-options.test.ts ├── schema-shape.test.ts ├── setup.test.ts ├── setup.ts ├── shared.ts ├── type-options-mz.test.ts ├── type-options.test.ts ├── validation.test.ts └── virtuals.test.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | ; http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_style = space 9 | indent_size = 2 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | insert_final_newline = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .idea/ 4 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/no-useless-spread, no-unused-vars, @typescript-eslint/no-unused-vars, import/no-unresolved */ 2 | 3 | /** ******** 4 | * AUXILIARY 5 | *********** */ 6 | 7 | const safeRequire = (moduleId) => { 8 | try { 9 | // eslint-disable-next-line security/detect-non-literal-require, import/no-dynamic-require 10 | return require(moduleId); 11 | } catch (error) { 12 | if (error?.code === 'MODULE_NOT_FOUND') { 13 | return null; 14 | } 15 | throw error; 16 | } 17 | }; 18 | 19 | safeRequire('dotenv')?.config(); 20 | 21 | const __FALSE__ = false; 22 | const POSSIBLE_JS_TS_EXTENSIONS = ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts']; 23 | 24 | const arrayFlattenAndFilterOutFalsyValues = (arr) => arr.flat().filter(Boolean); 25 | 26 | /** ****** 27 | * OPTIONS 28 | ********* */ 29 | 30 | const ESLINT_ENV = { 31 | es2023: true, 32 | node: true, 33 | }; 34 | 35 | const ENV = { 36 | typescript: true, 37 | vue: __FALSE__, 38 | nuxt3: __FALSE__, 39 | import: true, 40 | node: Boolean(ESLINT_ENV.node), 41 | }; 42 | ENV.nuxt3 = ENV.nuxt3 && ENV.vue; 43 | 44 | const OPTIONS = { 45 | ecmaVersion: 'latest', 46 | customParser: undefined, 47 | parserOptions: {}, 48 | errorsInsteadOfWarnings: __FALSE__, 49 | nuxtOrVueProjectDir: '', 50 | }; 51 | 52 | const NODE = { 53 | pathsToCheck: [], 54 | }; 55 | 56 | const TYPESCRIPT = { 57 | // eslint-disable-next-line no-unneeded-ternary 58 | typeCheckedRules: process.env.ESLINT_CONFIG_DISABLE_TYPE_CHECKED_RULES ? false : true, 59 | disallowTypeAssertions: __FALSE__, 60 | project: arrayFlattenAndFilterOutFalsyValues(['tsconfig.json']), 61 | }; 62 | 63 | const VUE = { 64 | vue2: __FALSE__, 65 | '>=vue3.3': true, 66 | '>=vue3.4': true, 67 | a11y: true, 68 | extensionRules: true, 69 | nuxtDisableImportNoCycleRule: __FALSE__, 70 | enforceTypescriptInScript: ENV.typescript, 71 | noUndefComponentsIgnorePatterns: arrayFlattenAndFilterOutFalsyValues([ 72 | ENV.nuxt3 && [/^(lazy-)?nuxt-/, /^(lazy-)?client-only$/], 73 | ]), 74 | noPropertyAccessFromIndexSignatureSetInTsconfigForVueFiles: __FALSE__, 75 | }; 76 | 77 | const MISC = { 78 | configFiles: arrayFlattenAndFilterOutFalsyValues([ 79 | ...POSSIBLE_JS_TS_EXTENSIONS.map((ext) => [`*.config.${ext}`, `.*rc.${ext}`]), 80 | ]), 81 | }; 82 | 83 | const OFF = 0; 84 | const WARNING = OPTIONS.errorsInsteadOfWarnings ? 2 : 1; 85 | const ERROR = 2; 86 | 87 | // This will be put at the end of the `rules` object 88 | const GLOBAL_RULE_OVERRIDES = { 89 | // ... 90 | }; 91 | 92 | // Additional entries to eslint's "overrides" section 93 | const OVERRIDES = [ 94 | { 95 | files: ['test/**/*'], 96 | env: { 97 | 'jest/globals': true, 98 | }, 99 | plugins: ['jest'], 100 | extends: ['plugin:jest/recommended'], 101 | }, 102 | ]; 103 | 104 | /** **** 105 | * RULES 106 | ******* */ 107 | 108 | const TS_ESLINT_RULES_NOT_TYPE_CHECKED = { 109 | '@typescript-eslint/ban-types': [ERROR, {types: {object: false, '{}': false}}], 110 | '@typescript-eslint/consistent-type-imports': [ERROR, {fixStyle: 'inline-type-imports'}], 111 | '@typescript-eslint/method-signature-style': ERROR, 112 | '@typescript-eslint/no-import-type-side-effects': ERROR, 113 | '@typescript-eslint/no-dynamic-delete': WARNING, 114 | '@typescript-eslint/no-empty-interface': [ERROR, {allowSingleExtends: true}], 115 | '@typescript-eslint/no-explicit-any': [WARNING, {ignoreRestArgs: true}], 116 | '@typescript-eslint/prefer-literal-enum-member': [ERROR, {allowBitwiseExpressions: true}], 117 | '@typescript-eslint/no-unused-vars': [ERROR, {ignoreRestSiblings: true}], 118 | 119 | // Extension Rules 120 | 'default-param-last': OFF, 121 | '@typescript-eslint/default-param-last': ERROR, 122 | 'no-dupe-class-members': OFF, 123 | '@typescript-eslint/no-dupe-class-members': ERROR, 124 | 'no-loop-func': OFF, 125 | '@typescript-eslint/no-loop-func': ERROR, 126 | 'no-redeclare': OFF, 127 | '@typescript-eslint/no-redeclare': ERROR, 128 | 'no-shadow': OFF, 129 | '@typescript-eslint/no-shadow': ERROR, 130 | 'no-unused-expressions': OFF, 131 | '@typescript-eslint/no-unused-expressions': [ 132 | ERROR, 133 | { 134 | allowShortCircuit: true, 135 | allowTernary: true, 136 | allowTaggedTemplates: true, 137 | }, 138 | ], 139 | 'no-use-before-define': OFF, 140 | '@typescript-eslint/no-use-before-define': [ERROR, {ignoreTypeReferences: false}], 141 | 142 | // Conflicting with corresponding base rules 143 | 'no-useless-constructor': OFF, 144 | 'dot-notation': OFF, 145 | }; 146 | 147 | const TS_ESLINT_RULES_TYPE_CHECKED = { 148 | '@typescript-eslint/no-floating-promises': WARNING, 149 | '@typescript-eslint/no-unnecessary-type-assertion': OFF, 150 | '@typescript-eslint/no-unsafe-argument': WARNING, 151 | '@typescript-eslint/no-unsafe-assignment': WARNING, 152 | '@typescript-eslint/no-unsafe-call': WARNING, 153 | '@typescript-eslint/no-unsafe-enum-comparison': WARNING, 154 | '@typescript-eslint/no-unsafe-member-access': WARNING, 155 | '@typescript-eslint/no-unsafe-return': WARNING, 156 | '@typescript-eslint/prefer-nullish-coalescing': OFF, 157 | '@typescript-eslint/restrict-template-expressions': [ 158 | ERROR, 159 | {allowAny: false, allowRegExp: false}, 160 | ], 161 | '@typescript-eslint/switch-exhaustiveness-check': ERROR, 162 | 163 | // Disable auto-fix 164 | 165 | '@typescript-eslint/no-unnecessary-condition': OFF, 166 | 'disable-autofix/@typescript-eslint/no-unnecessary-condition': [ 167 | ERROR, 168 | {allowConstantLoopConditions: true}, 169 | ], 170 | // Could remove type aliases 171 | '@typescript-eslint/no-unnecessary-type-arguments': OFF, 172 | 'disable-autofix/@typescript-eslint/no-unnecessary-type-arguments': ERROR, 173 | 174 | // Extension Rules 175 | 176 | 'no-return-await': OFF, 177 | '@typescript-eslint/return-await': [ERROR, 'always'], 178 | }; 179 | 180 | const IMPORT_RULES = { 181 | ...(ENV.nuxt3 && 182 | VUE.nuxtDisableImportNoCycleRule && { 183 | 'import/no-cycle': OFF, 184 | }), 185 | 'import/no-extraneous-dependencies': [ERROR, {peerDependencies: false}], 186 | 'import/no-default-export': ERROR, 187 | 'import/prefer-default-export': OFF, 188 | 'import/extensions': [ 189 | ERROR, 190 | 'ignorePackages', 191 | { 192 | js: 'always', 193 | ts: 'never', 194 | }, 195 | ], 196 | 'import/no-unresolved': [ERROR, {}], 197 | 'import/order': [ 198 | ERROR, 199 | { 200 | groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], 201 | alphabetize: {order: 'asc'}, 202 | }, 203 | ], 204 | }; 205 | 206 | const VUE_RULES = { 207 | ...(VUE.enforceTypescriptInScript && { 208 | 'vue/block-lang': [ERROR, {script: {lang: 'ts'}}], 209 | }), 210 | 'vue/component-tags-order': OFF, // Deprecated - but still included in the recommended 211 | 'vue/block-order': [ 212 | ERROR, 213 | {order: ['template', 'script:not([setup])', 'script[setup]', 'style']}, 214 | ], 215 | 'vue/component-name-in-template-casing': [ 216 | ERROR, 217 | 'kebab-case', 218 | { 219 | registeredComponentsOnly: false, 220 | ignores: arrayFlattenAndFilterOutFalsyValues(['/^[A-Z][a-z]+$/']), 221 | }, 222 | ], 223 | 'vue/define-emits-declaration': ERROR, 224 | 'vue/define-props-declaration': [ERROR, 'runtime'], 225 | 'vue/define-macros-order': [ 226 | ERROR, 227 | { 228 | order: ['defineOptions', 'defineModel', 'defineProps', 'defineEmits', 'defineSlots'], 229 | ...(VUE['>=vue3.4'] && {defineExposeLast: true}), 230 | }, 231 | ], 232 | 'vue/html-button-has-type': ERROR, 233 | 'vue/html-self-closing': [ 234 | ERROR, 235 | { 236 | html: { 237 | void: 'any', // Setting other value here for `void` would conflict with Prettier 238 | normal: 'never', 239 | component: 'never', 240 | }, 241 | }, 242 | ], 243 | 'vue/no-deprecated-model-definition': ERROR, 244 | 'vue/no-empty-component-block': ERROR, 245 | 'vue/no-ref-object-reactivity-loss': ERROR, 246 | 'vue/no-required-prop-with-default': ERROR, 247 | 'vue/no-this-in-before-route-enter': ERROR, 248 | 'vue/no-undef-components': [ 249 | ERROR, 250 | { 251 | ignorePatterns: [...VUE.noUndefComponentsIgnorePatterns], 252 | }, 253 | ], 254 | 'vue/no-unused-refs': ERROR, 255 | 'vue/no-use-v-else-with-v-for': ERROR, 256 | 'vue/no-useless-mustaches': ERROR, 257 | 'vue/no-useless-v-bind': ERROR, 258 | 'vue/no-v-html': ERROR, 259 | 'vue/padding-line-between-blocks': ERROR, 260 | 'vue/prefer-separate-static-class': ERROR, 261 | 'vue/prefer-true-attribute-shorthand': ERROR, 262 | 'vue/require-default-prop': OFF, 263 | 'vue/require-explicit-emits': OFF, 264 | ...(!VUE.vue2 && {'vue/require-explicit-slots': ERROR}), 265 | 'vue/require-typed-object-prop': ERROR, 266 | 'vue/require-typed-ref': ERROR, 267 | 'vue/v-for-delimiter-style': ERROR, 268 | 'vue/v-on-handler-style': [ERROR, 'inline'], 269 | }; 270 | 271 | const VUE_3_3_RULES = { 272 | 'vue/prefer-define-options': ERROR, 273 | 'vue/valid-define-options': ERROR, 274 | }; 275 | 276 | const VUE_A11Y_RULES = { 277 | 'vuejs-accessibility/label-has-for': OFF, 278 | 'vuejs-accessibility/form-control-has-label': OFF, 279 | 'vuejs-accessibility/anchor-has-content': OFF, 280 | }; 281 | 282 | const VUE_EXTENSION_RULES = { 283 | 'vue/camelcase': [ERROR, {properties: 'never'}], 284 | ...(!VUE.noPropertyAccessFromIndexSignatureSetInTsconfigForVueFiles && { 285 | 'vue/dot-notation': ERROR, 286 | }), 287 | 'vue/eqeqeq': [ERROR, 'always', {null: 'ignore'}], 288 | 'vue/no-console': ERROR, 289 | 'vue/no-constant-condition': WARNING, 290 | 'vue/no-empty-pattern': ERROR, 291 | 'vue/no-irregular-whitespace': ERROR, 292 | 'vue/no-loss-of-precision': ERROR, 293 | 'vue/no-restricted-syntax': ERROR, 294 | 'vue/no-sparse-arrays': ERROR, 295 | 'vue/no-useless-concat': ERROR, 296 | 'vue/object-shorthand': ERROR, 297 | 'vue/prefer-template': ERROR, 298 | }; 299 | 300 | const NODE_RULES = { 301 | ...(ENV.import && { 302 | 'n/no-extraneous-import': OFF, 303 | 'n/no-missing-import': OFF, 304 | 'n/no-unpublished-import': OFF, 305 | }), 306 | }; 307 | 308 | const SECURITY_RULES = { 309 | 'security/detect-object-injection': OFF, 310 | }; 311 | 312 | const SONARJS_RULES = { 313 | 'sonarjs/cognitive-complexity': OFF, 314 | 'sonarjs/no-duplicate-string': OFF, 315 | 'sonarjs/no-nested-switch': OFF, 316 | 'sonarjs/no-nested-template-literals': OFF, 317 | 'sonarjs/prefer-immediate-return': OFF, 318 | }; 319 | 320 | const UNICORN_RULES = { 321 | 'unicorn/catch-error-name': OFF, 322 | 'unicorn/consistent-destructuring': OFF, 323 | 'unicorn/filename-case': OFF, 324 | 'unicorn/no-array-callback-reference': OFF, 325 | 'unicorn/no-array-for-each': OFF, 326 | 'unicorn/no-array-reduce': OFF, 327 | 'unicorn/no-await-expression-member': OFF, 328 | 'unicorn/no-for-loop': OFF, 329 | 'unicorn/no-nested-ternary': OFF, 330 | 'unicorn/no-null': OFF, 331 | 'unicorn/no-unreadable-array-destructuring': OFF, 332 | 'unicorn/numeric-separators-style': [ERROR, {onlyIfContainsSeparator: true}], 333 | 'unicorn/prefer-dom-node-text-content': OFF, 334 | 'unicorn/prefer-event-target': OFF, 335 | 'unicorn/prefer-export-from': [ERROR, {ignoreUsedVariables: true}], 336 | 'unicorn/prefer-module': OFF, 337 | 'unicorn/prefer-regexp-test': OFF, 338 | 'unicorn/prefer-switch': [ERROR, {minimumCases: 4, emptyDefaultCase: 'do-nothing-comment'}], 339 | 'unicorn/prefer-query-selector': OFF, 340 | 'unicorn/prevent-abbreviations': OFF, 341 | 'unicorn/relative-url-style': [ERROR, 'always'], 342 | 343 | // With disable autofixes 344 | 'unicorn/explicit-length-check': OFF, 345 | 'disable-autofix/unicorn/explicit-length-check': ERROR, 346 | 'unicorn/prefer-spread': OFF, 347 | 'disable-autofix/unicorn/prefer-spread': ERROR, 348 | 'unicorn/no-useless-undefined': OFF, 349 | 'disable-autofix/unicorn/no-useless-undefined': [ERROR, {checkArguments: false}], 350 | }; 351 | 352 | const PROMISE_RULES = { 353 | 'promise/always-return': [ERROR, {ignoreLastCallback: true}], 354 | 'promise/catch-or-return': [ 355 | ERROR, 356 | { 357 | allowThen: true, 358 | allowFinally: true, 359 | }, 360 | ], 361 | }; 362 | 363 | const VANILLA_ESLINT_RULES = { 364 | camelcase: ERROR, 365 | 'class-methods-use-this': OFF, 366 | 'func-names': OFF, 367 | 'lines-between-class-members': OFF, 368 | 'max-classes-per-file': OFF, 369 | 'no-await-in-loop': WARNING, 370 | 'no-bitwise': OFF, 371 | 'no-continue': OFF, 372 | 'no-empty': [ERROR, {allowEmptyCatch: true}], 373 | 'no-implicit-coercion': [ERROR, {boolean: true}], 374 | 'no-nested-ternary': OFF, 375 | 'no-new': WARNING, 376 | 'no-param-reassign': [ERROR, {props: false}], 377 | 'no-plusplus': OFF, 378 | 'no-restricted-syntax': [ERROR, 'ForInStatement', 'LabeledStatement', 'WithStatement'], 379 | 'no-return-await': OFF, 380 | 'no-underscore-dangle': OFF, 381 | ...(!ENV.typescript && { 382 | 'no-unused-expressions': [ 383 | ERROR, 384 | { 385 | allowShortCircuit: true, 386 | allowTernary: true, 387 | allowTaggedTemplates: true, 388 | }, 389 | ], 390 | }), 391 | 'no-useless-constructor': ERROR, 392 | 'no-void': [ERROR, {allowAsStatement: true}], 393 | 'prefer-const': [ERROR, {destructuring: 'all'}], 394 | 'prefer-destructuring': [ 395 | ERROR, 396 | { 397 | VariableDeclarator: { 398 | array: false, 399 | object: true, 400 | }, 401 | AssignmentExpression: { 402 | array: false, 403 | object: false, 404 | }, 405 | }, 406 | ], 407 | 'prefer-rest-params': OFF, 408 | 'sort-imports': [ERROR, {ignoreDeclarationSort: true}], 409 | 410 | // Deprecated: 411 | 'global-require': OFF, 412 | 'spaced-comment': OFF, 413 | 'lines-around-directive': OFF, 414 | quotes: OFF, 415 | }; 416 | 417 | const parser = OPTIONS.customParser || ENV.typescript ? '@typescript-eslint/parser' : undefined; 418 | 419 | /** ************************* 420 | * START OF THE ESLINT CONFIG 421 | * @see https://eslint.org/docs/latest/use/configure/configuration-files 422 | **************************** */ 423 | 424 | module.exports = { 425 | root: true, 426 | 427 | env: ESLINT_ENV, 428 | 429 | parserOptions: { 430 | parser, 431 | project: ENV.typescript && TYPESCRIPT.typeCheckedRules && TYPESCRIPT.project, 432 | ecmaVersion: OPTIONS.ecmaVersion, 433 | sourceType: 'module', 434 | ecmaFeatures: { 435 | jsx: true, 436 | }, 437 | extraFileExtensions: arrayFlattenAndFilterOutFalsyValues([ENV.vue && '.vue']), 438 | ...OPTIONS.parserOptions, 439 | }, 440 | 441 | plugins: arrayFlattenAndFilterOutFalsyValues([ 442 | // 🌐 https://github.com/chiefmikey/eslint-plugin-disable-autofix 443 | 'disable-autofix', 444 | 445 | // 🌐 https://github.com/typescript-eslint/typescript-eslint 446 | ENV.typescript && '@typescript-eslint', 447 | ENV.typescript && TYPESCRIPT.disallowTypeAssertions && 'no-type-assertion', 448 | 449 | // 🌐 https://github.com/BrainMaestro/eslint-plugin-optimize-regex 450 | 'optimize-regex', 451 | // 🌐 https://github.com/SonarSource/eslint-plugin-sonarjs 452 | 'sonarjs', 453 | // 🌐 https://github.com/sindresorhus/eslint-plugin-unicorn 454 | 'unicorn', 455 | ]), 456 | 457 | extends: arrayFlattenAndFilterOutFalsyValues([ 458 | // 🌐 https://www.npmjs.com/package/eslint-plugin-sonarjs 459 | 'plugin:sonarjs/recommended', 460 | // 🌐 https://www.npmjs.com/package/eslint-plugin-unicorn 461 | 'plugin:unicorn/recommended', 462 | 463 | 'eslint:recommended', 464 | // 🌐 https://www.npmjs.com/package/eslint-config-airbnb-base 465 | 'airbnb-base', 466 | 467 | // 🌐 https://github.com/import-js/eslint-plugin-import 468 | ENV.import && 'plugin:import/recommended', 469 | // 🌐 https://www.npmjs.com/package/eslint-import-resolver-typescript 470 | ENV.import && ENV.typescript && 'plugin:import/typescript', 471 | 472 | ...(ENV.typescript 473 | ? TYPESCRIPT.typeCheckedRules 474 | ? [ 475 | 'plugin:@typescript-eslint/strict-type-checked', 476 | 'plugin:@typescript-eslint/stylistic-type-checked', 477 | ] 478 | : ['plugin:@typescript-eslint/strict', 'plugin:@typescript-eslint/stylistic'] 479 | : []), 480 | 481 | // 🌐 https://github.com/eslint-community/eslint-plugin-promise 482 | 'plugin:promise/recommended', 483 | // 🌐 https://github.com/eslint-community/eslint-plugin-security 484 | 'plugin:security/recommended-legacy', 485 | 486 | // 🔴 Must be at the end of the list as it disables incompatible rules from the previous configs 487 | // 🌐 https://github.com/prettier/eslint-config-prettier 488 | 'prettier', 489 | 'prettier/prettier', // https://github.com/prettier/eslint-plugin-prettier?tab=readme-ov-file#arrow-body-style-and-prefer-arrow-callback-issue 490 | ]), 491 | 492 | settings: { 493 | ...(ENV.import && { 494 | 'import/resolver': { 495 | node: ENV.node, 496 | ...(ENV.typescript && { 497 | typescript: { 498 | project: TYPESCRIPT.project, 499 | }, 500 | }), 501 | }, 502 | 503 | ...(ENV.typescript && { 504 | 'import/parsers': { 505 | '@typescript-eslint/parser': ['.ts', '.tsx'], 506 | }, 507 | }), 508 | }), 509 | }, 510 | 511 | rules: { 512 | ...VANILLA_ESLINT_RULES, 513 | ...(ENV.typescript && { 514 | ...TS_ESLINT_RULES_NOT_TYPE_CHECKED, 515 | ...(TYPESCRIPT.typeCheckedRules && TS_ESLINT_RULES_TYPE_CHECKED), 516 | ...(TYPESCRIPT.disallowTypeAssertions && { 517 | 'no-type-assertion/no-type-assertion': ERROR, 518 | }), 519 | }), 520 | ...(ENV.import && IMPORT_RULES), 521 | 'optimize-regex/optimize-regex': WARNING, 522 | ...SECURITY_RULES, 523 | ...SONARJS_RULES, 524 | ...UNICORN_RULES, 525 | ...PROMISE_RULES, 526 | 527 | ...GLOBAL_RULE_OVERRIDES, 528 | }, 529 | 530 | overrides: arrayFlattenAndFilterOutFalsyValues([ 531 | ENV.node && 532 | NODE.pathsToCheck?.length && { 533 | files: NODE.pathsToCheck, 534 | extends: arrayFlattenAndFilterOutFalsyValues([ 535 | // 🌐 https://github.com/eslint-community/eslint-plugin-n 536 | 'plugin:n/recommended', 537 | 538 | 'prettier', 539 | 'prettier/prettier', 540 | ]), 541 | rules: { 542 | ...NODE_RULES, 543 | }, 544 | }, 545 | 546 | ENV.vue && { 547 | files: ['*.vue'], 548 | plugins: arrayFlattenAndFilterOutFalsyValues([ 549 | // 🌐 https://github.com/vue-a11y/eslint-plugin-vuejs-accessibility 550 | VUE.a11y && 'vuejs-accessibility', 551 | ]), 552 | extends: arrayFlattenAndFilterOutFalsyValues([ 553 | // 🌐 https://github.com/vuejs/eslint-plugin-vue 554 | !VUE.vue2 && 'plugin:vue/vue3-recommended', 555 | VUE.vue2 && 'plugin:vue/recommended', 556 | VUE.a11y && 'plugin:vuejs-accessibility/recommended', 557 | 558 | 'prettier', 559 | 'prettier/prettier', 560 | ]), 561 | rules: { 562 | ...VUE_RULES, 563 | ...(VUE['>=vue3.3'] && VUE_3_3_RULES), 564 | ...(VUE.a11y && VUE_A11Y_RULES), 565 | ...(VUE.extensionRules && VUE_EXTENSION_RULES), 566 | 567 | '@typescript-eslint/prefer-function-type': OFF, 568 | '@typescript-eslint/unified-signatures': OFF, 569 | 'import/first': OFF, // May be wrong if multiple