From 079485f875bf2c17555f7a927948c3b64721ae94 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Tue, 9 Sep 2025 20:33:58 +0800 Subject: [PATCH] fix(typescript): attempt to fix types export --- package.json | 23 +- pnpm-lock.yaml | 97 ++++++ src/bin/buildEntryFileForTS.js | 16 + src/index-esm.js | 75 +++++ src/index.js | 531 ++++++++++++++++++++++++++++++++- 5 files changed, 732 insertions(+), 10 deletions(-) create mode 100644 src/bin/buildEntryFileForTS.js create mode 100644 src/index-esm.js diff --git a/package.json b/package.json index 7d8f8ff7c..0a1e0aa64 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "description": "JSDoc linting rules for ESLint.", "devDependencies": { + "@arethetypeswrong/cli": "^0.18.2", "@babel/cli": "^7.28.3", "@babel/core": "^7.28.4", "@babel/eslint-parser": "^7.28.4", @@ -83,19 +84,24 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./src/index.js", - "require": "./dist/index-cjs.cjs" + "import": { + "types": "./dist/index.d.ts", + "default": "./src/index.js" + }, + "require": { + "types": "./dist/index-cjs.d.ts", + "default": "./dist/index-cjs.cjs" + } }, "./getJsdocProcessorPlugin.js": { "types": "./dist/getJsdocProcessorPlugin.d.ts", - "import": "./dist/getJsdocProcessorPlugin.cjs", - "require": "./src/getJsdocProcessorPlugin.js" + "import": "./src/getJsdocProcessorPlugin.js", + "require": "./dist/getJsdocProcessorPlugin.cjs" }, "./iterateJsdoc.js": { "types": "./dist/iterateJsdoc.d.ts", - "import": "./dist/iterateJsdoc.cjs", - "require": "./src/iterateJsdoc.js" + "import": "./src/iterateJsdoc.js", + "require": "./dist/iterateJsdoc.cjs" } }, "name": "eslint-plugin-jsdoc", @@ -137,7 +143,8 @@ "scripts": { "tsc": "tsc", "tsc-build": "tsc -p tsconfig-prod.json", - "build": "rimraf ./dist && NODE_ENV=production babel ./src --out-file-extension .cjs --out-dir ./dist --copy-files --source-maps --ignore ./src/bin/*.js --no-copy-ignored && replace 'require\\(\"\\.(.*?)\\.[^.]*?\"\\)' 'require(\".$1.cjs\")' 'dist' -r --include=\"*.cjs\" && pnpm tsc-build", + "build": "node ./src/bin/buildEntryFileForTS.js && rimraf ./dist && NODE_ENV=production babel ./src --out-file-extension .cjs --out-dir ./dist --copy-files --source-maps --ignore ./src/bin/*.js --no-copy-ignored && replace 'require\\(\"\\.(.*?)\\.[^.]*?\"\\)' 'require(\".$1.cjs\")' 'dist' -r --include=\"*.cjs\" && pnpm tsc-build", + "attw": "attw --pack .", "check-docs": "node ./src/bin/generateDocs.js --check", "create-docs": "pnpm run create-options && node ./src/bin/generateDocs.js", "create-rule": "node ./src/bin/generateRule.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b3fdbcd49..95a2183ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,9 @@ importers: specifier: ^4.0.0 version: 4.0.0 devDependencies: + '@arethetypeswrong/cli': + specifier: ^0.18.2 + version: 0.18.2 '@babel/cli': specifier: ^7.28.3 version: 7.28.3(@babel/core@7.28.4) @@ -183,6 +186,18 @@ importers: packages: + '@andrewbranch/untar.js@1.0.3': + resolution: {integrity: sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==} + + '@arethetypeswrong/cli@0.18.2': + resolution: {integrity: sha512-PcFM20JNlevEDKBg4Re29Rtv2xvjvQZzg7ENnrWFSS0PHgdP2njibVFw+dRUhNkPgNfac9iUqO0ohAXqQL4hbw==} + engines: {node: '>=20'} + hasBin: true + + '@arethetypeswrong/core@0.18.2': + resolution: {integrity: sha512-GiwTmBFOU1/+UVNqqCGzFJYfBXEytUkiI+iRZ6Qx7KmUVtLm00sYySkfe203C9QtPG11yOz1ZaMek8dT/xnlgg==} + engines: {node: '>=20'} + '@babel/cli@7.28.3': resolution: {integrity: sha512-n1RU5vuCX0CsaqaXm9I0KUCNKNQMy5epmzl/xdSSm70bSqhg9GWhgeosypyQLc0bK24+Xpk1WGzZlI9pJtkZdg==} engines: {node: '>=6.9.0'} @@ -722,6 +737,9 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} + '@braidai/lang@1.1.2': + resolution: {integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -1013,6 +1031,9 @@ packages: '@jridgewell/trace-mapping@0.3.30': resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@loaderkit/resolve@1.0.4': + resolution: {integrity: sha512-rJzYKVcV4dxJv+vW6jlvagF8zvGxHJ2+HTr1e2qOejfmGhAApgJHl8Aog4mMszxceTRiKTTbnpgmTO1bEZHV/A==} + '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} @@ -1844,6 +1865,9 @@ packages: resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} engines: {node: '>=8'} + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + clean-regexp@1.0.0: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} @@ -1907,6 +1931,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + commander@14.0.0: resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} engines: {node: '>=20'} @@ -2661,6 +2689,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + figures@2.0.0: resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} engines: {node: '>=4'} @@ -3617,6 +3648,11 @@ packages: engines: {node: '>= 18'} hasBin: true + marked@9.1.6: + resolution: {integrity: sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==} + engines: {node: '>= 16'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -4845,6 +4881,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' + typescript@5.6.1-rc: + resolution: {integrity: sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} @@ -4962,6 +5003,10 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} @@ -5122,6 +5167,29 @@ packages: snapshots: + '@andrewbranch/untar.js@1.0.3': {} + + '@arethetypeswrong/cli@0.18.2': + dependencies: + '@arethetypeswrong/core': 0.18.2 + chalk: 4.1.2 + cli-table3: 0.6.5 + commander: 10.0.1 + marked: 9.1.6 + marked-terminal: 7.3.0(marked@9.1.6) + semver: 7.7.2 + + '@arethetypeswrong/core@0.18.2': + dependencies: + '@andrewbranch/untar.js': 1.0.3 + '@loaderkit/resolve': 1.0.4 + cjs-module-lexer: 1.4.3 + fflate: 0.8.2 + lru-cache: 11.1.0 + semver: 7.7.2 + typescript: 5.6.1-rc + validate-npm-package-name: 5.0.1 + '@babel/cli@7.28.3(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 @@ -5835,6 +5903,8 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} + '@braidai/lang@1.1.2': {} + '@colors/colors@1.5.0': optional: true @@ -6260,6 +6330,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@loaderkit/resolve@1.0.4': + dependencies: + '@braidai/lang': 1.1.2 + '@napi-rs/wasm-runtime@0.2.12': dependencies: '@emnapi/core': 1.4.5 @@ -7235,6 +7309,8 @@ snapshots: ci-info@4.3.0: {} + cjs-module-lexer@1.4.3: {} + clean-regexp@1.0.0: dependencies: escape-string-regexp: 1.0.5 @@ -7310,6 +7386,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@10.0.1: {} + commander@14.0.0: {} commander@6.2.1: {} @@ -8304,6 +8382,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fflate@0.8.2: {} + figures@2.0.0: dependencies: escape-string-regexp: 1.0.5 @@ -9309,10 +9389,23 @@ snapshots: node-emoji: 2.2.0 supports-hyperlinks: 3.2.0 + marked-terminal@7.3.0(marked@9.1.6): + dependencies: + ansi-escapes: 7.0.0 + ansi-regex: 6.1.0 + chalk: 5.5.0 + cli-highlight: 2.1.11 + cli-table3: 0.6.5 + marked: 9.1.6 + node-emoji: 2.2.0 + supports-hyperlinks: 3.2.0 + marked@13.0.3: {} marked@15.0.12: {} + marked@9.1.6: {} + math-intrinsics@1.1.0: {} meow@13.2.0: {} @@ -10588,6 +10681,8 @@ snapshots: transitivePeerDependencies: - supports-color + typescript@5.6.1-rc: {} + typescript@5.9.2: {} uglify-js@3.19.3: @@ -10707,6 +10802,8 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 + validate-npm-package-name@5.0.1: {} + verror@1.10.0: dependencies: assert-plus: 1.0.0 diff --git a/src/bin/buildEntryFileForTS.js b/src/bin/buildEntryFileForTS.js new file mode 100644 index 000000000..a139c1c1b --- /dev/null +++ b/src/bin/buildEntryFileForTS.js @@ -0,0 +1,16 @@ +import { + readFile, + writeFile, +} from 'node:fs/promises'; + +const { + dirname, +} = import.meta; +const dataToEmbed = await readFile(dirname + '/../index-cjs.js', 'utf8'); +const templateContent = await readFile(dirname + '/../index-esm.js', 'utf8'); + +const finalContent = '/* AUTO-GENERATED BY build SCRIPT */\n' + templateContent.replace( + /\/\/ BEGIN REPLACE[\s\S]*\/\/ END REPLACE\n/v, + `${dataToEmbed}`, +); +await writeFile(dirname + '/../index.js', finalContent); diff --git a/src/index-esm.js b/src/index-esm.js new file mode 100644 index 000000000..6099369d8 --- /dev/null +++ b/src/index-esm.js @@ -0,0 +1,75 @@ +/* eslint-disable perfectionist/sort-imports -- For auto-generate; Do not remove */ +import { + merge, +} from 'object-deep-merge'; + +// BEGIN REPLACE +import index from './index-cjs.js'; + +// eslint-disable-next-line unicorn/prefer-export-from --- Reusing `index` +export default index; +// END REPLACE + +/* eslint-disable jsdoc/valid-types -- Bug */ +/** + * @type {(( + * cfg?: { + * mergeSettings?: boolean, + * config?: `flat/${import('./index-cjs.js').ConfigGroups}${import('./index-cjs.js').ConfigVariants}${import('./index-cjs.js').ErrorLevelVariants}`, + * settings?: Partial + * } + * ) => import('eslint').Linter.Config)} + */ +/* eslint-enable jsdoc/valid-types -- Bug */ +export const jsdoc = function (cfg) { + /** @type {import('eslint').Linter.Config} */ + let outputConfig = { + plugins: { + jsdoc: index, + }, + }; + if ( + cfg?.config + ) { + // @ts-expect-error Security check + if (cfg.config === '__proto__') { + throw new TypeError('Disallowed config value'); + } + + outputConfig = index.configs[cfg.config]; + } + + outputConfig.settings = { + jsdoc: cfg?.mergeSettings === false ? + cfg.settings : + merge( + {}, + cfg?.settings ?? {}, + cfg?.config?.includes('recommended') ? + { + // We may need to drop these for "typescript" (non-"flavor") configs, + // if support is later added: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html + structuredTags: { + next: { + required: [ + 'type', + ], + }, + throws: { + required: [ + 'type', + ], + }, + yields: { + required: [ + 'type', + ], + }, + }, + } : + {}, + ), + }; + + return outputConfig; +}; diff --git a/src/index.js b/src/index.js index f7a897143..eba24c658 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,536 @@ -import index from './index-cjs.js'; +/* AUTO-GENERATED BY build SCRIPT */ +/* eslint-disable perfectionist/sort-imports -- For auto-generate; Do not remove */ import { merge, } from 'object-deep-merge'; -// eslint-disable-next-line unicorn/prefer-export-from --- Reusing `index` +import { + getJsdocProcessorPlugin, +} from './getJsdocProcessorPlugin.js'; +import checkAccess from './rules/checkAccess.js'; +import checkAlignment from './rules/checkAlignment.js'; +import checkExamples from './rules/checkExamples.js'; +import checkIndentation from './rules/checkIndentation.js'; +import checkLineAlignment from './rules/checkLineAlignment.js'; +import checkParamNames from './rules/checkParamNames.js'; +import checkPropertyNames from './rules/checkPropertyNames.js'; +import checkSyntax from './rules/checkSyntax.js'; +import checkTagNames from './rules/checkTagNames.js'; +import checkTemplateNames from './rules/checkTemplateNames.js'; +import checkTypes from './rules/checkTypes.js'; +import checkValues from './rules/checkValues.js'; +import convertToJsdocComments from './rules/convertToJsdocComments.js'; +import emptyTags from './rules/emptyTags.js'; +import implementsOnClasses from './rules/implementsOnClasses.js'; +import importsAsDependencies from './rules/importsAsDependencies.js'; +import informativeDocs from './rules/informativeDocs.js'; +import linesBeforeBlock from './rules/linesBeforeBlock.js'; +import matchDescription from './rules/matchDescription.js'; +import matchName from './rules/matchName.js'; +import multilineBlocks from './rules/multilineBlocks.js'; +import noBadBlocks from './rules/noBadBlocks.js'; +import noBlankBlockDescriptions from './rules/noBlankBlockDescriptions.js'; +import noBlankBlocks from './rules/noBlankBlocks.js'; +import noDefaults from './rules/noDefaults.js'; +import noMissingSyntax from './rules/noMissingSyntax.js'; +import noMultiAsterisks from './rules/noMultiAsterisks.js'; +import noRestrictedSyntax from './rules/noRestrictedSyntax.js'; +import noTypes from './rules/noTypes.js'; +import noUndefinedTypes from './rules/noUndefinedTypes.js'; +import requireAsteriskPrefix from './rules/requireAsteriskPrefix.js'; +import requireDescription from './rules/requireDescription.js'; +import requireDescriptionCompleteSentence from './rules/requireDescriptionCompleteSentence.js'; +import requireExample from './rules/requireExample.js'; +import requireFileOverview from './rules/requireFileOverview.js'; +import requireHyphenBeforeParamDescription from './rules/requireHyphenBeforeParamDescription.js'; +import requireJsdoc from './rules/requireJsdoc.js'; +import requireParam from './rules/requireParam.js'; +import requireParamDescription from './rules/requireParamDescription.js'; +import requireParamName from './rules/requireParamName.js'; +import requireParamType from './rules/requireParamType.js'; +import requireProperty from './rules/requireProperty.js'; +import requirePropertyDescription from './rules/requirePropertyDescription.js'; +import requirePropertyName from './rules/requirePropertyName.js'; +import requirePropertyType from './rules/requirePropertyType.js'; +import requireReturns from './rules/requireReturns.js'; +import requireReturnsCheck from './rules/requireReturnsCheck.js'; +import requireReturnsDescription from './rules/requireReturnsDescription.js'; +import requireReturnsType from './rules/requireReturnsType.js'; +import requireTemplate from './rules/requireTemplate.js'; +import requireThrows from './rules/requireThrows.js'; +import requireYields from './rules/requireYields.js'; +import requireYieldsCheck from './rules/requireYieldsCheck.js'; +import sortTags from './rules/sortTags.js'; +import tagLines from './rules/tagLines.js'; +import textEscaping from './rules/textEscaping.js'; +import validTypes from './rules/validTypes.js'; + +/* eslint-disable jsdoc/valid-types -- Bug */ +/** + * @typedef {"recommended" | "stylistic" | "contents" | "logical" | "requirements"} ConfigGroups + * @typedef {"" | "-typescript" | "-typescript-flavor"} ConfigVariants + * @typedef {"" | "-error"} ErrorLevelVariants + * @type {import('eslint').ESLint.Plugin & { + * configs: Record<`flat/${ConfigGroups}${ConfigVariants}${ErrorLevelVariants}`, + * import('eslint').Linter.Config> + * }} + */ +const index = {}; +/* eslint-enable jsdoc/valid-types -- Bug */ +index.configs = {}; +index.rules = { + 'check-access': checkAccess, + 'check-alignment': checkAlignment, + 'check-examples': checkExamples, + 'check-indentation': checkIndentation, + 'check-line-alignment': checkLineAlignment, + 'check-param-names': checkParamNames, + 'check-property-names': checkPropertyNames, + 'check-syntax': checkSyntax, + 'check-tag-names': checkTagNames, + 'check-template-names': checkTemplateNames, + 'check-types': checkTypes, + 'check-values': checkValues, + 'convert-to-jsdoc-comments': convertToJsdocComments, + 'empty-tags': emptyTags, + 'implements-on-classes': implementsOnClasses, + 'imports-as-dependencies': importsAsDependencies, + 'informative-docs': informativeDocs, + 'lines-before-block': linesBeforeBlock, + 'match-description': matchDescription, + 'match-name': matchName, + 'multiline-blocks': multilineBlocks, + 'no-bad-blocks': noBadBlocks, + 'no-blank-block-descriptions': noBlankBlockDescriptions, + 'no-blank-blocks': noBlankBlocks, + 'no-defaults': noDefaults, + 'no-missing-syntax': noMissingSyntax, + 'no-multi-asterisks': noMultiAsterisks, + 'no-restricted-syntax': noRestrictedSyntax, + 'no-types': noTypes, + 'no-undefined-types': noUndefinedTypes, + 'require-asterisk-prefix': requireAsteriskPrefix, + 'require-description': requireDescription, + 'require-description-complete-sentence': requireDescriptionCompleteSentence, + 'require-example': requireExample, + 'require-file-overview': requireFileOverview, + 'require-hyphen-before-param-description': requireHyphenBeforeParamDescription, + 'require-jsdoc': requireJsdoc, + 'require-param': requireParam, + 'require-param-description': requireParamDescription, + 'require-param-name': requireParamName, + 'require-param-type': requireParamType, + 'require-property': requireProperty, + 'require-property-description': requirePropertyDescription, + 'require-property-name': requirePropertyName, + 'require-property-type': requirePropertyType, + 'require-returns': requireReturns, + 'require-returns-check': requireReturnsCheck, + 'require-returns-description': requireReturnsDescription, + 'require-returns-type': requireReturnsType, + 'require-template': requireTemplate, + 'require-throws': requireThrows, + 'require-yields': requireYields, + 'require-yields-check': requireYieldsCheck, + 'sort-tags': sortTags, + 'tag-lines': tagLines, + 'text-escaping': textEscaping, + 'valid-types': validTypes, +}; + +/** + * @param {"warn"|"error"} warnOrError + * @param {string} [flatName] + * @returns {import('eslint').Linter.Config} + */ +const createRecommendedRuleset = (warnOrError, flatName) => { + return { + ...(flatName ? { + name: 'jsdoc/' + flatName, + } : {}), + // @ts-expect-error ESLint 8 plugins + plugins: + flatName ? { + jsdoc: index, + } : [ + 'jsdoc', + ], + rules: { + 'jsdoc/check-access': warnOrError, + 'jsdoc/check-alignment': warnOrError, + 'jsdoc/check-examples': 'off', + 'jsdoc/check-indentation': 'off', + 'jsdoc/check-line-alignment': 'off', + 'jsdoc/check-param-names': warnOrError, + 'jsdoc/check-property-names': warnOrError, + 'jsdoc/check-syntax': 'off', + 'jsdoc/check-tag-names': warnOrError, + 'jsdoc/check-template-names': 'off', + 'jsdoc/check-types': warnOrError, + 'jsdoc/check-values': warnOrError, + 'jsdoc/convert-to-jsdoc-comments': 'off', + 'jsdoc/empty-tags': warnOrError, + 'jsdoc/implements-on-classes': warnOrError, + 'jsdoc/imports-as-dependencies': 'off', + 'jsdoc/informative-docs': 'off', + 'jsdoc/lines-before-block': 'off', + 'jsdoc/match-description': 'off', + 'jsdoc/match-name': 'off', + 'jsdoc/multiline-blocks': warnOrError, + 'jsdoc/no-bad-blocks': 'off', + 'jsdoc/no-blank-block-descriptions': 'off', + 'jsdoc/no-blank-blocks': 'off', + 'jsdoc/no-defaults': warnOrError, + 'jsdoc/no-missing-syntax': 'off', + 'jsdoc/no-multi-asterisks': warnOrError, + 'jsdoc/no-restricted-syntax': 'off', + 'jsdoc/no-types': 'off', + 'jsdoc/no-undefined-types': warnOrError, + 'jsdoc/require-asterisk-prefix': 'off', + 'jsdoc/require-description': 'off', + 'jsdoc/require-description-complete-sentence': 'off', + 'jsdoc/require-example': 'off', + 'jsdoc/require-file-overview': 'off', + 'jsdoc/require-hyphen-before-param-description': 'off', + 'jsdoc/require-jsdoc': warnOrError, + 'jsdoc/require-param': warnOrError, + 'jsdoc/require-param-description': warnOrError, + 'jsdoc/require-param-name': warnOrError, + 'jsdoc/require-param-type': warnOrError, + 'jsdoc/require-property': warnOrError, + 'jsdoc/require-property-description': warnOrError, + 'jsdoc/require-property-name': warnOrError, + 'jsdoc/require-property-type': warnOrError, + 'jsdoc/require-returns': warnOrError, + 'jsdoc/require-returns-check': warnOrError, + 'jsdoc/require-returns-description': warnOrError, + 'jsdoc/require-returns-type': warnOrError, + 'jsdoc/require-template': 'off', + 'jsdoc/require-throws': 'off', + 'jsdoc/require-yields': warnOrError, + 'jsdoc/require-yields-check': warnOrError, + 'jsdoc/sort-tags': 'off', + 'jsdoc/tag-lines': warnOrError, + 'jsdoc/text-escaping': 'off', + 'jsdoc/valid-types': warnOrError, + }, + }; +}; + +/** + * @param {"warn"|"error"} warnOrError + * @param {string} [flatName] + * @returns {import('eslint').Linter.Config} + */ +const createRecommendedTypeScriptRuleset = (warnOrError, flatName) => { + const ruleset = createRecommendedRuleset(warnOrError, flatName); + + return { + ...ruleset, + rules: { + ...ruleset.rules, + /* eslint-disable @stylistic/indent -- Extra indent to avoid use by auto-rule-editing */ + 'jsdoc/check-tag-names': [ + warnOrError, { + typed: true, + }, + ], + 'jsdoc/no-types': warnOrError, + 'jsdoc/no-undefined-types': 'off', + 'jsdoc/require-param-type': 'off', + 'jsdoc/require-property-type': 'off', + 'jsdoc/require-returns-type': 'off', + /* eslint-enable @stylistic/indent */ + }, + }; +}; + +/** + * @param {"warn"|"error"} warnOrError + * @param {string} [flatName] + * @returns {import('eslint').Linter.Config} + */ +const createRecommendedTypeScriptFlavorRuleset = (warnOrError, flatName) => { + const ruleset = createRecommendedRuleset(warnOrError, flatName); + + return { + ...ruleset, + rules: { + ...ruleset.rules, + /* eslint-disable @stylistic/indent -- Extra indent to avoid use by auto-rule-editing */ + 'jsdoc/no-undefined-types': 'off', + /* eslint-enable @stylistic/indent */ + }, + }; +}; + +/** + * @param {(string | unknown[])[]} ruleNames + */ +const createStandaloneRulesetFactory = (ruleNames) => { + /** + * @param {"warn"|"error"} warnOrError + * @param {string} [flatName] + * @returns {import('eslint').Linter.Config} + */ + return (warnOrError, flatName) => { + return { + name: 'jsdoc/' + flatName, + plugins: { + jsdoc: index, + }, + rules: Object.fromEntries( + ruleNames.map( + (ruleName) => { + return (typeof ruleName === 'string' ? + [ + ruleName, warnOrError, + ] : + [ + ruleName[0], [ + warnOrError, ...ruleName.slice(1), + ], + ]); + }, + ), + ), + }; + }; +}; + +const contentsRules = [ + 'jsdoc/informative-docs', + 'jsdoc/match-description', + 'jsdoc/no-blank-block-descriptions', + 'jsdoc/no-blank-blocks', + [ + 'jsdoc/text-escaping', { + escapeHTML: true, + }, + ], +]; + +const createContentsTypescriptRuleset = createStandaloneRulesetFactory(contentsRules); + +const createContentsTypescriptFlavorRuleset = createStandaloneRulesetFactory(contentsRules); + +const logicalRules = [ + 'jsdoc/check-access', + 'jsdoc/check-param-names', + 'jsdoc/check-property-names', + 'jsdoc/check-syntax', + 'jsdoc/check-tag-names', + 'jsdoc/check-template-names', + 'jsdoc/check-types', + 'jsdoc/check-values', + 'jsdoc/empty-tags', + 'jsdoc/implements-on-classes', + 'jsdoc/require-returns-check', + 'jsdoc/require-yields-check', + 'jsdoc/no-bad-blocks', + 'jsdoc/no-defaults', + 'jsdoc/no-types', + 'jsdoc/no-undefined-types', + 'jsdoc/valid-types', +]; + +const createLogicalTypescriptRuleset = createStandaloneRulesetFactory(logicalRules); + +const createLogicalTypescriptFlavorRuleset = createStandaloneRulesetFactory(logicalRules); + +const requirementsRules = [ + 'jsdoc/require-example', + 'jsdoc/require-jsdoc', + 'jsdoc/require-param', + 'jsdoc/require-param-description', + 'jsdoc/require-param-name', + 'jsdoc/require-property', + 'jsdoc/require-property-description', + 'jsdoc/require-property-name', + 'jsdoc/require-returns', + 'jsdoc/require-returns-description', + 'jsdoc/require-yields', +]; + +const createRequirementsTypeScriptRuleset = createStandaloneRulesetFactory(requirementsRules); + +const createRequirementsTypeScriptFlavorRuleset = createStandaloneRulesetFactory([ + ...requirementsRules, + 'jsdoc/require-param-type', + 'jsdoc/require-property-type', + 'jsdoc/require-returns-type', + 'jsdoc/require-template', +]); + +const stylisticRules = [ + 'jsdoc/check-alignment', + 'jsdoc/check-line-alignment', + 'jsdoc/lines-before-block', + 'jsdoc/multiline-blocks', + 'jsdoc/no-multi-asterisks', + 'jsdoc/require-asterisk-prefix', + [ + 'jsdoc/require-hyphen-before-param-description', 'never', + ], + 'jsdoc/tag-lines', +]; + +const createStylisticTypeScriptRuleset = createStandaloneRulesetFactory(stylisticRules); + +const createStylisticTypeScriptFlavorRuleset = createStandaloneRulesetFactory(stylisticRules); + +/* c8 ignore next 3 -- TS */ +if (!index.configs) { + throw new Error('TypeScript guard'); +} + +index.configs.recommended = createRecommendedRuleset('warn'); +index.configs['recommended-error'] = createRecommendedRuleset('error'); +index.configs['recommended-typescript'] = createRecommendedTypeScriptRuleset('warn'); +index.configs['recommended-typescript-error'] = createRecommendedTypeScriptRuleset('error'); +index.configs['recommended-typescript-flavor'] = createRecommendedTypeScriptFlavorRuleset('warn'); +index.configs['recommended-typescript-flavor-error'] = createRecommendedTypeScriptFlavorRuleset('error'); + +index.configs['flat/recommended'] = createRecommendedRuleset('warn', 'flat/recommended'); +index.configs['flat/recommended-error'] = createRecommendedRuleset('error', 'flat/recommended-error'); +index.configs['flat/recommended-typescript'] = createRecommendedTypeScriptRuleset('warn', 'flat/recommended-typescript'); +index.configs['flat/recommended-typescript-error'] = createRecommendedTypeScriptRuleset('error', 'flat/recommended-typescript-error'); +index.configs['flat/recommended-typescript-flavor'] = createRecommendedTypeScriptFlavorRuleset('warn', 'flat/recommended-typescript-flavor'); +index.configs['flat/recommended-typescript-flavor-error'] = createRecommendedTypeScriptFlavorRuleset('error', 'flat/recommended-typescript-flavor-error'); + +index.configs['flat/contents-typescript'] = createContentsTypescriptRuleset('warn', 'flat/contents-typescript'); +index.configs['flat/contents-typescript-error'] = createContentsTypescriptRuleset('error', 'flat/contents-typescript-error'); +index.configs['flat/contents-typescript-flavor'] = createContentsTypescriptFlavorRuleset('warn', 'flat/contents-typescript-flavor'); +index.configs['flat/contents-typescript-flavor-error'] = createContentsTypescriptFlavorRuleset('error', 'flat/contents-typescript-error-flavor'); +index.configs['flat/logical-typescript'] = createLogicalTypescriptRuleset('warn', 'flat/logical-typescript'); +index.configs['flat/logical-typescript-error'] = createLogicalTypescriptRuleset('error', 'flat/logical-typescript-error'); +index.configs['flat/logical-typescript-flavor'] = createLogicalTypescriptFlavorRuleset('warn', 'flat/logical-typescript-flavor'); +index.configs['flat/logical-typescript-flavor-error'] = createLogicalTypescriptFlavorRuleset('error', 'flat/logical-typescript-error-flavor'); +index.configs['flat/requirements-typescript'] = createRequirementsTypeScriptRuleset('warn', 'flat/requirements-typescript'); +index.configs['flat/requirements-typescript-error'] = createRequirementsTypeScriptRuleset('error', 'flat/requirements-typescript-error'); +index.configs['flat/requirements-typescript-flavor'] = createRequirementsTypeScriptFlavorRuleset('warn', 'flat/requirements-typescript-flavor'); +index.configs['flat/requirements-typescript-flavor-error'] = createRequirementsTypeScriptFlavorRuleset('error', 'flat/requirements-typescript-error-flavor'); +index.configs['flat/stylistic-typescript'] = createStylisticTypeScriptRuleset('warn', 'flat/stylistic-typescript'); +index.configs['flat/stylistic-typescript-error'] = createStylisticTypeScriptRuleset('error', 'flat/stylistic-typescript-error'); +index.configs['flat/stylistic-typescript-flavor'] = createStylisticTypeScriptFlavorRuleset('warn', 'flat/stylistic-typescript-flavor'); +index.configs['flat/stylistic-typescript-flavor-error'] = createStylisticTypeScriptFlavorRuleset('error', 'flat/stylistic-typescript-error-flavor'); + +index.configs.examples = /** @type {import('eslint').Linter.Config[]} */ ([ + { + files: [ + '**/*.js', + ], + name: 'jsdoc/examples/processor', + plugins: { + examples: getJsdocProcessorPlugin(), + }, + processor: 'examples/examples', + }, + { + files: [ + '**/*.md/*.js', + ], + name: 'jsdoc/examples/rules', + rules: { + // "always" newline rule at end unlikely in sample code + 'eol-last': 0, + + // Wouldn't generally expect example paths to resolve relative to JS file + 'import/no-unresolved': 0, + + // Snippets likely too short to always include import/export info + 'import/unambiguous': 0, + + 'jsdoc/require-file-overview': 0, + + // The end of a multiline comment would end the comment the example is in. + 'jsdoc/require-jsdoc': 0, + + // Unlikely to have inadvertent debugging within examples + 'no-console': 0, + + // Often wish to start `@example` code after newline; also may use + // empty lines for spacing + 'no-multiple-empty-lines': 0, + + // Many variables in examples will be `undefined` + 'no-undef': 0, + + // Common to define variables for clarity without always using them + 'no-unused-vars': 0, + + // See import/no-unresolved + 'node/no-missing-import': 0, + 'node/no-missing-require': 0, + + // Can generally look nicer to pad a little even if code imposes more stringency + 'padded-blocks': 0, + }, + }, +]); + +index.configs['default-expressions'] = /** @type {import('eslint').Linter.Config[]} */ ([ + { + files: [ + '**/*.js', + ], + name: 'jsdoc/default-expressions/processor', + plugins: { + examples: getJsdocProcessorPlugin({ + checkDefaults: true, + checkParams: true, + checkProperties: true, + }), + }, + processor: 'examples/examples', + }, + { + files: [ + '**/*.jsdoc-defaults', '**/*.jsdoc-params', '**/*.jsdoc-properties', + ], + name: 'jsdoc/default-expressions/rules', + rules: { + ...index.configs.examples[1].rules, + 'chai-friendly/no-unused-expressions': 0, + 'no-empty-function': 0, + 'no-new': 0, + 'no-unused-expressions': 0, + quotes: [ + 'error', 'double', + ], + semi: [ + 'error', 'never', + ], + strict: 0, + }, + }, +]); + +index.configs['examples-and-default-expressions'] = /** @type {import('eslint').Linter.Config[]} */ ([ + { + name: 'jsdoc/examples-and-default-expressions', + plugins: { + examples: getJsdocProcessorPlugin({ + checkDefaults: true, + checkParams: true, + checkProperties: true, + }), + }, + }, + ...index.configs.examples.map((config) => { + return { + ...config, + plugins: {}, + }; + }), + ...index.configs['default-expressions'].map((config) => { + return { + ...config, + plugins: {}, + }; + }), +]); + export default index; /* eslint-disable jsdoc/valid-types -- Bug */