From c2b39fb0bfc4897a5aa3e030482a76cfc142d44d Mon Sep 17 00:00:00 2001 From: Guz013 <43732358+Guz013@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:39:24 -0300 Subject: [PATCH] feat: jsdoc eslint --- packages/core/package.json | 3 +- .../core/src/@types/eslint-plugin-jsdoc.d.ts | 26 +++++++ .../core/src/@types/typescript-eslint.d.ts | 2 + packages/core/src/configs/common.js | 1 + packages/core/src/configs/environments.js | 7 +- packages/core/src/configs/formatting.js | 1 + packages/core/src/configs/index.js | 1 + packages/core/src/configs/jsdoc.js | 20 +++++ packages/core/src/configs/typescript.js | 12 ++- packages/core/src/index.js | 31 ++++++-- packages/core/src/tsconfigs.js | 8 +- packages/core/src/types.d.ts | 1 - pnpm-lock.yaml | 73 +++++++++++++++++-- 13 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 packages/core/src/@types/eslint-plugin-jsdoc.d.ts create mode 100644 packages/core/src/configs/jsdoc.js diff --git a/packages/core/package.json b/packages/core/package.json index 3acafe6..1ccb737 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -20,7 +20,6 @@ "author": "", "license": "ISC", "devDependencies": { - "@eslint/js": "^8.44.0", "@types/eslint__js": "^8.42.0", "@types/node": "^20.4.1", "eslint": "^8.44.0", @@ -28,8 +27,10 @@ }, "dependencies": { "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "^8.45.0", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", + "eslint-plugin-jsdoc": "^46.4.4", "globals": "^13.20.0", "yaml": "^2.3.1" } diff --git a/packages/core/src/@types/eslint-plugin-jsdoc.d.ts b/packages/core/src/@types/eslint-plugin-jsdoc.d.ts new file mode 100644 index 0000000..15e6190 --- /dev/null +++ b/packages/core/src/@types/eslint-plugin-jsdoc.d.ts @@ -0,0 +1,26 @@ +import type { ESLint } from 'eslint'; + +/** + * @see {@link https://www.npmjs.org/package/eslint-plugin-jsdoc npm package} + * + * @summary JSDoc specific linting rules for ESLint. + * + * --- + * **Note:** Types in this project where overridden to be compatible with ESLint new flat + * config types. ESlint already has backwards compatibility for plugins not created in the + * new flat config. + */ +declare module 'eslint-plugin-jsdoc' { + interface jsDocESlintPlugin extends ESLint.Plugin { + configs: ESLint.Plugin['configs'] & { + recommended: ESLint.ConfigData + 'recommended-error': ESLint.ConfigData + 'recommended-typescript': ESLint.ConfigData + 'recommended-typescript-error': ESLint.ConfigData + 'recommended-typescript-flavor': ESLint.ConfigData + 'recommended-typescript-flavor-error': ESLint.ConfigData + } + } + declare const plugin: jsDocESlintPlugin; + export default plugin; +} diff --git a/packages/core/src/@types/typescript-eslint.d.ts b/packages/core/src/@types/typescript-eslint.d.ts index b01e94e..212ed15 100644 --- a/packages/core/src/@types/typescript-eslint.d.ts +++ b/packages/core/src/@types/typescript-eslint.d.ts @@ -2,6 +2,7 @@ import type { ESLint, Linter } from 'eslint'; /** * @see {@link https://www.npmjs.com/package/@typescript-eslint/eslint-plugin npm package} + * * @summary An ESLint plugin which provides lint rules for TypeScript codebases. * * --- @@ -32,6 +33,7 @@ declare module '@typescript-eslint/eslint-plugin' { /** * @see {@link https://www.npmjs.com/package/@typescript-eslint/parser npm package} + * * @summary An ESLint parser which leverages TypeScript ESTree to allow for ESLint * to lint TypeScript source code. * diff --git a/packages/core/src/configs/common.js b/packages/core/src/configs/common.js index cd6918b..3dda1cb 100644 --- a/packages/core/src/configs/common.js +++ b/packages/core/src/configs/common.js @@ -1,6 +1,7 @@ /** * Common configuration related to language features of Javascript and Typescript + * * @type {import('../types').ESConfig} */ const config = { diff --git a/packages/core/src/configs/environments.js b/packages/core/src/configs/environments.js index 737179f..c9ff16e 100644 --- a/packages/core/src/configs/environments.js +++ b/packages/core/src/configs/environments.js @@ -2,12 +2,13 @@ import globals from 'globals'; /** * @param {import('../types').Config['environment']} environment - * + * Manual configuration of environments, if undefined, + * the function tries to detect the environment automatically * @returns {import('../types').ESConfig[]} -*/ + * ESLint configuration with global variables and environment + */ export function environments(environment) { - /** @type {Record>} */ environment ||= { node: typeof window === 'undefined' && diff --git a/packages/core/src/configs/formatting.js b/packages/core/src/configs/formatting.js index 1028345..5cda1cb 100644 --- a/packages/core/src/configs/formatting.js +++ b/packages/core/src/configs/formatting.js @@ -1,5 +1,6 @@ /** * Formatting rules/configuration for Javascript and Typescript + * * @type {import('../types').ESConfig} */ const config = { diff --git a/packages/core/src/configs/index.js b/packages/core/src/configs/index.js index c8822fa..fed89bb 100644 --- a/packages/core/src/configs/index.js +++ b/packages/core/src/configs/index.js @@ -1,4 +1,5 @@ export { default as common } from './common.js'; export { default as formatting } from './formatting.js'; +export { default as jsdoc } from './jsdoc.js'; export { default as typescript } from './typescript.js'; export * from './environments.js'; diff --git a/packages/core/src/configs/jsdoc.js b/packages/core/src/configs/jsdoc.js new file mode 100644 index 0000000..b364525 --- /dev/null +++ b/packages/core/src/configs/jsdoc.js @@ -0,0 +1,20 @@ +/** + * JSDoc rules overrides + * + * @type {import('../types').ESConfig} + */ +const config = { + files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'], + rules: { + 'jsdoc/tag-lines': ['error', 'always', { + count: 1, + applyToEndTag: false, + startLines: 1, + endLines: 0, + tags: { + param: { lines: 'never' }, + }, + }], + }, +}; +export default config; diff --git a/packages/core/src/configs/typescript.js b/packages/core/src/configs/typescript.js index 311693f..50226e1 100644 --- a/packages/core/src/configs/typescript.js +++ b/packages/core/src/configs/typescript.js @@ -1,10 +1,20 @@ +import jsdoc from 'eslint-plugin-jsdoc'; + /** - * Typescript syntax-specific configuration + * Typescript specific configuration + * * @type {import('../types').ESConfig} */ const config = { files: ['**/*.ts', '**/*.cts', '**/*.mts'], + // See plugins['jsdoc'] on index.js for more info on this error + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment rules: { + + // See plugins['jsdoc'] on index.js for more info on this error + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ...jsdoc.configs['recommended-typescript-error'].rules, + '@typescript-eslint/adjacent-overload-signatures': 'error', '@typescript-eslint/array-type': 'error', '@typescript-eslint/class-literal-property-style': 'error', diff --git a/packages/core/src/index.js b/packages/core/src/index.js index 00ad2c7..4d4cb97 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -1,15 +1,17 @@ import { eslintrc } from './eslintrc-compact.js'; -import tsEslint from '@typescript-eslint/eslint-plugin'; +import tsESlint from '@typescript-eslint/eslint-plugin'; import tsParser from '@typescript-eslint/parser'; +import jsdoc from 'eslint-plugin-jsdoc'; import js from '@eslint/js'; import * as configs from './configs/index.js'; import { getTsConfigs } from './tsconfigs.js'; /** * @param {import('./types').Config} userConfig - * + * User configuration * @returns {Promise} -*/ + * The complete list of configs for ESLint + */ export async function defineConfig(userConfig) { userConfig.strict ??= true; @@ -38,7 +40,14 @@ export async function defineConfig(userConfig) { { files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'], plugins: { - '@typescript-eslint': tsEslint, + '@typescript-eslint': tsESlint, + /** + * @todo + * Fix eslint-plugin-jsdoc type definitions. + * _Typescript should have detected [eslint-plugin-jsdoc.d.ts](./@types/eslint-plugin-jsdoc.d.ts)._ + */ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + 'jsdoc': jsdoc, }, languageOptions: { sourceType: 'module', @@ -48,15 +57,21 @@ export async function defineConfig(userConfig) { tsconfigRootDir: userConfig.rootDir, }, }, + // See plugins['jsdoc'] for more info on this error + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment rules: { - ...tsEslint.configs.recommended.rules, - ...tsEslint.configs['recommended-requiring-type-checking'].rules, - ...tsEslint.configs['eslint-recommended'].rules, - ...(userConfig.strict ? tsEslint.configs.strict.rules : null), + ...tsESlint.configs.recommended.rules, + ...tsESlint.configs['recommended-requiring-type-checking'].rules, + ...tsESlint.configs['eslint-recommended'].rules, + ...(userConfig.strict ? tsESlint.configs.strict.rules : null), + // See plugins['jsdoc'] for more info on this error + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ...jsdoc.configs['recommended-typescript-flavor-error'].rules, }, }, configs.common, configs.formatting, + configs.jsdoc, configs.typescript, ...configs.environments(userConfig.environment), ...userOverrides, diff --git a/packages/core/src/tsconfigs.js b/packages/core/src/tsconfigs.js index 4df55bf..2811c05 100644 --- a/packages/core/src/tsconfigs.js +++ b/packages/core/src/tsconfigs.js @@ -13,8 +13,8 @@ function exists(...path) { } /** - * @param {string} directory - * @returns {Promise} + * @param {string} directory what the root directory to detect an workspace/monorepo configuration file + * @returns {Promise} list of possible paths of packages' tsconfig.json and jsconfig.json files */ async function getMonorepoConfigs(directory) { @@ -60,8 +60,8 @@ async function getMonorepoConfigs(directory) { } /** - * @param {string} directory - * @returns {Promise} + * @param {string} directory what the root directory to work on + * @returns {Promise} list of tsconfig.json and jsconfig.json file paths */ export async function getTsConfigs(directory) { diff --git a/packages/core/src/types.d.ts b/packages/core/src/types.d.ts index 502fc2d..9bf8c16 100644 --- a/packages/core/src/types.d.ts +++ b/packages/core/src/types.d.ts @@ -22,7 +22,6 @@ export interface Config { * **Note:** this does not enables CommonJS globals, if you are using * CommonJS, use a file ending in `.cjs` or `.cts` * - * * @example // Detects if * typeof window === 'undefined' && * typeof process !== 'undefined' && diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f97bc9a..405ad76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,12 +78,18 @@ importers: '@eslint/eslintrc': specifier: ^2.1.0 version: 2.1.0 + '@eslint/js': + specifier: ^8.45.0 + version: 8.45.0 '@typescript-eslint/eslint-plugin': specifier: ^5.45.0 version: 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.44.0)(typescript@5.1.6) '@typescript-eslint/parser': specifier: ^5.45.0 version: 5.45.0(eslint@8.44.0)(typescript@5.1.6) + eslint-plugin-jsdoc: + specifier: ^46.4.4 + version: 46.4.4(eslint@8.44.0) globals: specifier: ^13.20.0 version: 13.20.0 @@ -91,9 +97,6 @@ importers: specifier: ^2.3.1 version: 2.3.1 devDependencies: - '@eslint/js': - specifier: ^8.44.0 - version: 8.44.0 '@types/eslint__js': specifier: ^8.42.0 version: 8.42.0 @@ -347,6 +350,15 @@ packages: chalk: 4.1.2 dev: true + /@es-joy/jsdoccomment@0.39.4: + resolution: {integrity: sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==} + engines: {node: '>=16'} + dependencies: + comment-parser: 1.3.1 + esquery: 1.5.0 + jsdoc-type-pratt-parser: 4.0.0 + dev: false + /@esbuild/android-arm64@0.18.11: resolution: {integrity: sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw==} engines: {node: '>=12'} @@ -578,6 +590,11 @@ packages: resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@eslint/js@8.45.0: + resolution: {integrity: sha512-p/4a2uaWVHCbAPLxPcv9rkhooU1FVfUUiLoZq09v1dOgtLym/Qlsyw2L3JWr9inVRt1XaLKPP5PBcDtTxRUJPg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: false + /@fontsource/fira-mono@4.5.10: resolution: {integrity: sha512-bxUnRP8xptGRo8YXeY073DSpfK74XpSb0ZyRNpHV9WvLnJ7TwPOjZll8hTMin7zLC6iOp59pDZ8EQDj1gzgAQQ==} dev: true @@ -1088,6 +1105,11 @@ packages: picomatch: 2.3.1 dev: true + /are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + dev: false + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -1181,6 +1203,11 @@ packages: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} dev: true + /builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: false + /busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -1304,6 +1331,11 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + /comment-parser@1.3.1: + resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} + engines: {node: '>= 12.0.0'} + dev: false + /compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} dependencies: @@ -1582,6 +1614,26 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + /eslint-plugin-jsdoc@46.4.4(eslint@8.44.0): + resolution: {integrity: sha512-D8TGPOkq3bnzmYmA7Q6jdsW+Slx7CunhJk1tlouVq6wJjlP1p6eigZPvxFn7aufud/D66xBsNVMhkDQEuqumMg==} + engines: {node: '>=16'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@es-joy/jsdoccomment': 0.39.4 + are-docs-informative: 0.0.2 + comment-parser: 1.3.1 + debug: 4.3.4 + escape-string-regexp: 4.0.0 + eslint: 8.44.0 + esquery: 1.5.0 + is-builtin-module: 3.2.1 + semver: 7.5.4 + spdx-expression-parse: 3.0.1 + transitivePeerDependencies: + - supports-color + dev: false + /eslint-plugin-svelte@2.30.0(eslint@8.44.0)(svelte@4.0.5): resolution: {integrity: sha512-2/qj0BJsfM0U2j4EjGb7iC/0nbUvXx1Gn78CdtyuXpi/rSomLPCPwnsZsloXMzlt6Xwe8LBlpRvZObSKEHLP5A==} engines: {node: ^14.17.0 || >=16.0.0} @@ -2097,6 +2149,13 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: false + /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -2251,6 +2310,11 @@ packages: dependencies: argparse: 2.0.1 + /jsdoc-type-pratt-parser@4.0.0: + resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} + engines: {node: '>=12.0.0'} + dev: false + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -2968,18 +3032,15 @@ packages: /spdx-exceptions@2.3.0: resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} - dev: true /spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} dependencies: spdx-exceptions: 2.3.0 spdx-license-ids: 3.0.13 - dev: true /spdx-license-ids@3.0.13: resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} - dev: true /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}