diff --git a/.eslintrc.json b/.eslintrc.json index f78f3250020cf..2b6afe437b0f5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,7 +10,7 @@ "es6": true }, "plugins": [ - "@typescript-eslint", "jsdoc", "no-null", "import", "eslint-plugin-local" + "@typescript-eslint", "no-null", "import", "eslint-plugin-local" ], "rules": { "@typescript-eslint/adjacent-overload-signatures": "error", @@ -95,9 +95,6 @@ // eslint-plugin-no-null "no-null/no-null": "error", - // eslint-plugin-jsdoc - "jsdoc/check-alignment": "error", - // eslint "constructor-super": "error", "curly": ["error", "multi-line"], diff --git a/package-lock.json b/package-lock.json index 9fa34faf23d81..a093d15787f94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,6 @@ "eslint": "^8.22.0", "eslint-formatter-autolinkable-stylish": "^1.2.0", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsdoc": "^39.3.6", "eslint-plugin-local": "^1.0.0", "eslint-plugin-no-null": "^1.0.2", "fast-xml-parser": "^4.0.11", @@ -57,20 +56,6 @@ "node": ">=4.2.0" } }, - "node_modules/@es-joy/jsdoccomment": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.0.tgz", - "integrity": "sha512-u0XZyvUF6Urb2cSivSXA8qXIpT/CxkHcdtZKoWusAzgzmsTWpg0F2FpWXsolHmMUyVY3dLWaoy+0ccJ5uf2QjA==", - "dev": true, - "dependencies": { - "comment-parser": "1.3.1", - "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "~3.1.0" - }, - "engines": { - "node": "^14 || ^16 || ^17 || ^18 || ^19" - } - }, "node_modules/@esbuild/android-arm": { "version": "0.15.13", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz", @@ -1167,15 +1152,6 @@ "node": ">=8" } }, - "node_modules/comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", - "dev": true, - "engines": { - "node": ">= 12.0.0" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1963,27 +1939,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/eslint-plugin-jsdoc": { - "version": "39.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.2.tgz", - "integrity": "sha512-dvgY/W7eUFoAIIiaWHERIMI61ZWqcz9YFjEeyTzdPlrZc3TY/3aZm5aB91NUoTLWYZmO/vFlYSuQi15tF7uE5A==", - "dev": true, - "dependencies": { - "@es-joy/jsdoccomment": "~0.36.0", - "comment-parser": "1.3.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "esquery": "^1.4.0", - "semver": "^7.3.8", - "spdx-expression-parse": "^3.0.1" - }, - "engines": { - "node": "^14 || ^16 || ^17 || ^18 || ^19" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, "node_modules/eslint-plugin-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-local/-/eslint-plugin-local-1.0.0.tgz", @@ -3051,15 +3006,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsdoc-type-pratt-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", - "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", - "dev": true, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4010,28 +3956,6 @@ "source-map": "^0.6.0" } }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", - "dev": true - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4534,17 +4458,6 @@ } }, "dependencies": { - "@es-joy/jsdoccomment": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.0.tgz", - "integrity": "sha512-u0XZyvUF6Urb2cSivSXA8qXIpT/CxkHcdtZKoWusAzgzmsTWpg0F2FpWXsolHmMUyVY3dLWaoy+0ccJ5uf2QjA==", - "dev": true, - "requires": { - "comment-parser": "1.3.1", - "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "~3.1.0" - } - }, "@esbuild/android-arm": { "version": "0.15.13", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz", @@ -5338,12 +5251,6 @@ } } }, - "comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5870,21 +5777,6 @@ } } }, - "eslint-plugin-jsdoc": { - "version": "39.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.2.tgz", - "integrity": "sha512-dvgY/W7eUFoAIIiaWHERIMI61ZWqcz9YFjEeyTzdPlrZc3TY/3aZm5aB91NUoTLWYZmO/vFlYSuQi15tF7uE5A==", - "dev": true, - "requires": { - "@es-joy/jsdoccomment": "~0.36.0", - "comment-parser": "1.3.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "esquery": "^1.4.0", - "semver": "^7.3.8", - "spdx-expression-parse": "^3.0.1" - } - }, "eslint-plugin-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-local/-/eslint-plugin-local-1.0.0.tgz", @@ -6625,12 +6517,6 @@ "argparse": "^2.0.1" } }, - "jsdoc-type-pratt-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", - "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7288,28 +7174,6 @@ "source-map": "^0.6.0" } }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", - "dev": true - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/package.json b/package.json index 559555ba47917..40a5b90c61222 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,6 @@ "eslint": "^8.22.0", "eslint-formatter-autolinkable-stylish": "^1.2.0", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsdoc": "^39.3.6", "eslint-plugin-local": "^1.0.0", "eslint-plugin-no-null": "^1.0.2", "fast-xml-parser": "^4.0.11", diff --git a/scripts/eslint/rules/jsdoc-format.cjs b/scripts/eslint/rules/jsdoc-format.cjs index 86783b3547a76..fe4bf64fb9699 100644 --- a/scripts/eslint/rules/jsdoc-format.cjs +++ b/scripts/eslint/rules/jsdoc-format.cjs @@ -13,9 +13,11 @@ module.exports = createRule({ internalCommentNotLastError: `@internal should only appear in final JSDoc comment for declaration.`, multipleJSDocError: `Declaration has multiple JSDoc comments.`, internalCommentOnParameterProperty: `@internal cannot appear on a JSDoc comment; use a declared property and an assignment in the constructor instead.`, + misalignedJSDocComment: `This JSDoc comment is misaligned.`, }, schema: [], type: "problem", + fixable: "whitespace", }, defaultOptions: [], @@ -24,6 +26,11 @@ module.exports = createRule({ const atInternal = "@internal"; const jsdocStart = "/**"; + /** @type {(text: string) => boolean} */ + function isJSDocText(text) { + return text.startsWith(jsdocStart); + } + /** @type {(c: TSESTree.Comment, indexInComment: number) => TSESTree.SourceLocation} */ const getAtInternalLoc = (c, indexInComment) => { const line = c.loc.start.line; @@ -51,7 +58,7 @@ module.exports = createRule({ }; /** @type {(node: TSESTree.Node) => void} */ - const checkJSDocFormat = (node) => { + const checkDeclaration = (node) => { const blockComments = sourceCode.getCommentsBefore(node).filter(c => c.type === "Block"); if (blockComments.length === 0) { return; @@ -63,7 +70,7 @@ module.exports = createRule({ const c = blockComments[i]; const rawComment = sourceCode.getText(c); - const isJSDoc = rawComment.startsWith(jsdocStart); + const isJSDoc = isJSDocText(rawComment); if (isJSDoc && seenJSDoc) { context.report({ messageId: "multipleJSDocError", node: c, loc: getJSDocStartLoc(c) }); } @@ -86,25 +93,85 @@ module.exports = createRule({ } }; + /** @type {(node: TSESTree.Node) => void} */ + const checkProgram = () => { + const comments = sourceCode.getAllComments(); + + for (const c of comments) { + if (c.type !== "Block") { + continue; + } + + const rawComment = sourceCode.getText(c); + if (!isJSDocText(rawComment)) { + continue; + } + + const expected = c.loc.start.column + 2; + const split = rawComment.split(/\r?\n/g); + for (let i = 1; i < split.length; i++) { + const line = split[i]; + const match = /^ *\*/.exec(line); + if (!match) { + continue; + } + + const actual = match[0].length; + const diff = actual - expected; + if (diff !== 0) { + const line = c.loc.start.line + i; + context.report({ + messageId: "misalignedJSDocComment", + node: c, + loc: { + start: { + line, + column: 0, + }, + end: { + line, + column: actual - 1, + } + }, + fix: (fixer) => { + if (diff > 0) { + // Too many + const start = sourceCode.getIndexFromLoc({ line, column: expected - 1 }); + return fixer.removeRange([start, start + diff]); + } + else { + // Too few + const start = sourceCode.getIndexFromLoc({ line, column: 0 }); + return fixer.insertTextAfterRange([start, start], " ".repeat(-diff)); + } + }, + }); + break; + } + } + } + }; + return { - ClassDeclaration: checkJSDocFormat, - FunctionDeclaration: checkJSDocFormat, - TSEnumDeclaration: checkJSDocFormat, - TSModuleDeclaration: checkJSDocFormat, - VariableDeclaration: checkJSDocFormat, - TSInterfaceDeclaration: checkJSDocFormat, - TSTypeAliasDeclaration: checkJSDocFormat, - TSCallSignatureDeclaration: checkJSDocFormat, - ExportAllDeclaration: checkJSDocFormat, - ExportNamedDeclaration: checkJSDocFormat, - TSImportEqualsDeclaration: checkJSDocFormat, - TSNamespaceExportDeclaration: checkJSDocFormat, - TSConstructSignatureDeclaration: checkJSDocFormat, - ExportDefaultDeclaration: checkJSDocFormat, - TSPropertySignature: checkJSDocFormat, - TSIndexSignature: checkJSDocFormat, - TSMethodSignature: checkJSDocFormat, - TSParameterProperty: checkJSDocFormat, + Program: checkProgram, + ClassDeclaration: checkDeclaration, + FunctionDeclaration: checkDeclaration, + TSEnumDeclaration: checkDeclaration, + TSModuleDeclaration: checkDeclaration, + VariableDeclaration: checkDeclaration, + TSInterfaceDeclaration: checkDeclaration, + TSTypeAliasDeclaration: checkDeclaration, + TSCallSignatureDeclaration: checkDeclaration, + ExportAllDeclaration: checkDeclaration, + ExportNamedDeclaration: checkDeclaration, + TSImportEqualsDeclaration: checkDeclaration, + TSNamespaceExportDeclaration: checkDeclaration, + TSConstructSignatureDeclaration: checkDeclaration, + ExportDefaultDeclaration: checkDeclaration, + TSPropertySignature: checkDeclaration, + TSIndexSignature: checkDeclaration, + TSMethodSignature: checkDeclaration, + TSParameterProperty: checkDeclaration, }; }, });