From f53bb42ed5b776ee75a6b8e14c1c0b9c5545786d Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Thu, 23 Jan 2025 10:32:07 +0100 Subject: [PATCH] feat: ajoute une validation JSDoc --- front/.eslintrc.cjs | 9 +- front/package-lock.json | 177 +++++++++++++++++++++++++ front/package.json | 5 +- front/src/components/SelectCombobox.js | 2 +- front/src/helpers/bibtex.js | 2 +- front/src/helpers/forms.js | 2 +- front/src/helpers/graphQL.js | 2 +- front/src/helpers/isidore.js | 4 +- front/src/helpers/zotero.js | 44 +++--- front/src/hooks/graphql.js | 2 + 10 files changed, 220 insertions(+), 29 deletions(-) diff --git a/front/.eslintrc.cjs b/front/.eslintrc.cjs index cefe9ce4b..48f374c64 100644 --- a/front/.eslintrc.cjs +++ b/front/.eslintrc.cjs @@ -8,6 +8,7 @@ module.exports = { 'eslint:recommended', 'plugin:react/recommended', 'plugin:jsonc/recommended-with-json', + 'plugin:jsdoc/recommended-typescript-flavor', ], parserOptions: { ecmaFeatures: { @@ -22,7 +23,7 @@ module.exports = { parser: 'jsonc-eslint-parser', }, ], - plugins: ['react', 'vitest'], + plugins: ['react', 'vitest', 'jsdoc'], settings: { react: { version: '16.13', @@ -39,6 +40,12 @@ module.exports = { __HUMANID_REGISTER_ENDPOINT__: true, }, rules: { + 'jsdoc/require-description': ['off'], + 'jsdoc/require-example': ['off'], + 'jsdoc/require-jsdoc': ['off'], + 'jsdoc/require-param-description': ['off'], + 'jsdoc/require-returns-description': ['off'], + 'jsdoc/require-property-description': ['off'], 'react/prop-types': ['warn'], 'no-unused-vars': ['warn'], 'jsonc/indent': ['error', 2], diff --git a/front/package-lock.json b/front/package-lock.json index b92786084..3d7fcce14 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -65,6 +65,7 @@ "@vitest/coverage-v8": "^3.0.3", "@welldone-software/why-did-you-render": "~8.0", "eslint": "^8.2.0", + "eslint-plugin-jsdoc": "^50.6.2", "eslint-plugin-jsonc": "^2.5.0", "eslint-plugin-react": "^7.27.0", "eslint-plugin-vitest": "^0.5.4", @@ -1743,6 +1744,20 @@ "node": ">=18" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz", + "integrity": "sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==", + "dev": true, + "dependencies": { + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~4.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -2489,6 +2504,18 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@react-dnd/asap": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", @@ -3570,6 +3597,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4120,6 +4156,15 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/compute-gcd": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz", @@ -5261,6 +5306,88 @@ } } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "50.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.6.2.tgz", + "integrity": "sha512-n7GNZ4czMAAbDg7DsDA7PvHo1IPIUwAXYmxTx6j/hTlXbt5V0x5q/kGkiJ7s4wA9SpB/yaiK8jF7CO237lOLew==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.49.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.6", + "escape-string-regexp": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.6.0", + "parse-imports": "^2.1.1", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/eslint-plugin-jsonc": { "version": "2.18.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.18.2.tgz", @@ -6822,6 +6949,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsdom": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", @@ -7791,6 +7927,19 @@ "node": ">=6" } }, + "node_modules/parse-imports": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", + "integrity": "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==", + "dev": true, + "dependencies": { + "es-module-lexer": "^1.5.3", + "slashes": "^3.0.12" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/parse5": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", @@ -8874,6 +9023,12 @@ "node": ">=8" } }, + "node_modules/slashes": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", + "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==", + "dev": true + }, "node_modules/slugify": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", @@ -8907,6 +9062,28 @@ "source-map": "^0.6.0" } }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", diff --git a/front/package.json b/front/package.json index bddaa177b..fbd22c097 100644 --- a/front/package.json +++ b/front/package.json @@ -77,8 +77,8 @@ "yjs": "^13.6.6" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "^4.31.0", - "@esbuild/linux-x64": "^0.24.2" + "@esbuild/linux-x64": "^0.24.2", + "@rollup/rollup-linux-x64-gnu": "^4.31.0" }, "overrides": { "@geist-ui/core": { @@ -100,6 +100,7 @@ "@vitest/coverage-v8": "^3.0.3", "@welldone-software/why-did-you-render": "~8.0", "eslint": "^8.2.0", + "eslint-plugin-jsdoc": "^50.6.2", "eslint-plugin-jsonc": "^2.5.0", "eslint-plugin-react": "^7.27.0", "eslint-plugin-vitest": "^0.5.4", diff --git a/front/src/components/SelectCombobox.js b/front/src/components/SelectCombobox.js index d8469ee58..1f1b9201b 100644 --- a/front/src/components/SelectCombobox.js +++ b/front/src/components/SelectCombobox.js @@ -4,7 +4,7 @@ /** * @param {ComboboxItem[]} items - * @returns {Record} + * @returns {Record} */ export function groupItems(items) { return Array.from( diff --git a/front/src/helpers/bibtex.js b/front/src/helpers/bibtex.js index 0bf655c64..42b269c4f 100644 --- a/front/src/helpers/bibtex.js +++ b/front/src/helpers/bibtex.js @@ -19,7 +19,6 @@ export function toBibtex(entries) { /** * We tolerate `unexpected_field` warnings as it's user provided, it does not have any side effect * @see https://github.com/EcrituresNumeriques/stylo/issues/187 - * * @param {string} bibtex * @returns {Promise<{success: number,empty: boolean,warnings: Array.,error: Array.}>} */ @@ -88,6 +87,7 @@ export function deriveAuthorNameAndDate(entry) { /** * @param {string} Bibtex bibliography + * @param input * @returns {Array.<{ title: string, key: string, type: string }} */ export function toEntries(input) { diff --git a/front/src/helpers/forms.js b/front/src/helpers/forms.js index edc9f7378..8efe49be9 100644 --- a/front/src/helpers/forms.js +++ b/front/src/helpers/forms.js @@ -2,8 +2,8 @@ * Transforms a form into a plain object * * formData.entries() does not handle multiple values (sic) and formData.getAll() only works at a field level - * * @param {React.ReactHTMLElement|FormData} formElement + * @param input * @returns {Record} */ export function fromFormData(input) { diff --git a/front/src/helpers/graphQL.js b/front/src/helpers/graphQL.js index 75e9978d0..c39b29380 100644 --- a/front/src/helpers/graphQL.js +++ b/front/src/helpers/graphQL.js @@ -69,7 +69,7 @@ export function useGraphQL() { * @param {string} sessionToken * @param {DocumentNode|string} queryOrAST * @param {{[string: key]: value}} variables - * @return {Promise} + * @returns {Promise} */ export function runQuery({ sessionToken }, { query: queryOrAST, variables }) { const query = typeof queryOrAST === 'string' ? queryOrAST : print(queryOrAST) diff --git a/front/src/helpers/isidore.js b/front/src/helpers/isidore.js index c7f3f1369..253403698 100644 --- a/front/src/helpers/isidore.js +++ b/front/src/helpers/isidore.js @@ -4,7 +4,7 @@ * Search a vocabulary from the Isidore API. * Eg: https://api.isidore.science/vocabulary/suggest?q=quebe&output=json * @param {string} searchValue - * @returns {Promise} + * @returns {Promise} */ export async function searchKeyword(searchValue) { if (searchValue && searchValue.length > 0) { @@ -51,7 +51,7 @@ export async function searchKeyword(searchValue) { * Search an author from the Isidore API. * Eg: https://api.isidore.science/resource/suggest?q=marcell&feed=feed-creator&output=json * @param {string} searchValue - * @returns {Promise} + * @returns {Promise} */ export async function searchAuthor(searchValue) { if (searchValue && searchValue.length > 0) { diff --git a/front/src/helpers/zotero.js b/front/src/helpers/zotero.js index 0d332b924..53dc6251b 100644 --- a/front/src/helpers/zotero.js +++ b/front/src/helpers/zotero.js @@ -4,39 +4,39 @@ import { filter } from './bibtex' /** * @typedef ZoteroCollection * @property {ZoteroCollectionData} data - * @property {String} key + * @property {string} key * @property {ZoteroLibrary} library * @property {Object.} links * @property {ZoteroMeta} meta - * @property {Number} version + * @property {number} version */ /** * @typedef ZoteroCollectionData - * @property {String} key - * @property {String} name Name of the collection - * @property {String} parentCollection Parent collection key - * @property {Number} version + * @property {string} key + * @property {string} name Name of the collection + * @property {string} parentCollection Parent collection key + * @property {number} version */ /** * @typedef ZoteroLibrary - * @property {Number} id + * @property {number} id * @property {Object.} links - * @property {String} name Name of the library - * @property {String} type Enum of 'user' or 'group' (whom it belongs) + * @property {string} name Name of the library + * @property {string} type Enum of 'user' or 'group' (whom it belongs) */ /** * @typedef ZoteroMeta - * @property {Number} numCollections - * @property {Number} numItems + * @property {number} numCollections + * @property {number} numItems */ /** * @typedef HTMLLink - * @property {String} href - * @property {String} type Mime-Type of the link + * @property {string} href + * @property {string} type Mime-Type of the link */ /** @@ -58,8 +58,8 @@ function getNextLink(headers) { /** * * @param {URL} url - * @param {String} key Zotero API key - * @param {Object[]} agg + * @param {string} key Zotero API key + * @param {object[]} agg * @returns {Promise} a list of JSON responses */ async function fetchAllJSON(url, key, agg = []) { @@ -122,7 +122,7 @@ async function fetchAllBibTeX(url, key, agg = []) { } /** - * @param {String} url + * @param {string} url * @returns {Promise} - a JSON response */ function fetchJSON(url) { @@ -130,7 +130,7 @@ function fetchJSON(url) { } /** - * @param {String} token Zotero API token + * @param {string} token Zotero API token * @returns {Promise} - a JSON response (contains userID and key) */ function fetchUserFromToken(token) { @@ -138,8 +138,8 @@ function fetchUserFromToken(token) { } /** - * @param {String} userID - * @param {String} key Zotero API key + * @param {string} userID + * @param {string} key Zotero API key * @returns {Promise} - a list of Zotero collections */ async function fetchAllCollections({ userID, key }) { @@ -168,7 +168,7 @@ async function fetchAllCollections({ userID, key }) { } /** - * @param {String} token + * @param {string} token * @returns {Promise} */ export async function fetchAllCollectionsPerLibrary({ token }) { @@ -180,6 +180,8 @@ export async function fetchAllCollectionsPerLibrary({ token }) { /** * * @param token + * @param token.userID + * @param token.key * @returns {Promise} */ export async function fetchUserCollections({ userID, key }) { @@ -190,8 +192,10 @@ export async function fetchUserCollections({ userID, key }) { } /** + * @param collectionHref.collectionHref * @param collectionHref * @param key Zotero API key + * @param collectionHref.token * @returns {Promise} */ export const fetchBibliographyFromCollectionHref = async ({ diff --git a/front/src/hooks/graphql.js b/front/src/hooks/graphql.js index 2bc87d84b..1030005e5 100644 --- a/front/src/hooks/graphql.js +++ b/front/src/hooks/graphql.js @@ -43,7 +43,9 @@ async function request({ query, variables, sessionToken, type = 'fetch' }) { /** * @param queryOrAST GraphQL query + * @param queryOrAST.query * @param variables query arguments + * @param queryOrAST.variables * @param {SWRConfiguration} [options] - optional SWR options * @returns {SWRResponse} */