diff --git a/.README/rules/check-values.md b/.README/rules/check-values.md index 8dd5fada0..3d08d7412 100644 --- a/.README/rules/check-values.md +++ b/.README/rules/check-values.md @@ -18,6 +18,7 @@ This rule checks the values for a handful of tags: 6. `@kind` - Insists that it be one of the allowed values: 'class', 'constant', 'event', 'external', 'file', 'function', 'member', 'mixin', 'module', 'namespace', 'typedef', +7. `@import` - For TypeScript `mode` only. Enforces valid ES import syntax. ## Options diff --git a/docs/rules/check-values.md b/docs/rules/check-values.md index 17d3b4dca..be6adf927 100644 --- a/docs/rules/check-values.md +++ b/docs/rules/check-values.md @@ -28,6 +28,7 @@ This rule checks the values for a handful of tags: 6. `@kind` - Insists that it be one of the allowed values: 'class', 'constant', 'event', 'external', 'file', 'function', 'member', 'mixin', 'module', 'namespace', 'typedef', +7. `@import` - For TypeScript `mode` only. Enforces valid ES import syntax. @@ -251,6 +252,14 @@ function quux (foo) { } // "jsdoc/check-values": ["error"|"warn", {"licensePattern":"^([^\n]+)\nCopyright"}] // Message: Invalid JSDoc @license: "Oops"; expected SPDX expression: https://spdx.org/licenses/. + +/** + * @import BadImportIgnoredByThisRule + */ +/** + * @import {AnotherBadImportIgnoredByThisRule} from + */ +// Message: Bad @import tag ```` @@ -405,5 +414,21 @@ function quux (foo) { } // "jsdoc/check-values": ["error"|"warn", {"licensePattern":"^([^\n]+)\nCopyright"}] + +/** + * @import LinterDef, { Sth as Something, Another as Another2 } from "eslint" + */ +/** + * @import { Linter } from "eslint" + */ +/** + * @import LinterDefault from "eslint" + */ +/** + * @import {Linter as Lintee} from "eslint" + */ +/** + * @import * as Linters from "eslint" + */ ```` diff --git a/src/rules/checkValues.js b/src/rules/checkValues.js index b3741c8ca..97a8ce142 100644 --- a/src/rules/checkValues.js +++ b/src/rules/checkValues.js @@ -1,6 +1,7 @@ -import iterateJsdoc from '../iterateJsdoc.js'; +import { parseImportsSync } from 'parse-imports'; import semver from 'semver'; import spdxExpressionParse from 'spdx-expression-parse'; +import iterateJsdoc from '../iterateJsdoc.js'; const allowedKinds = new Set([ 'class', @@ -20,6 +21,7 @@ export default iterateJsdoc(({ utils, report, context, + settings, }) => { const options = context.options[0] || {}; const { @@ -157,6 +159,30 @@ export default iterateJsdoc(({ } }); + if (settings.mode === 'typescript') { + utils.forEachPreferredTag('import', (tag) => { + const { + type, name, description + } = tag; + const typePart = type ? `{${type}} `: ''; + const imprt = 'import ' + (description + ? `${typePart}${name} ${description}` + : `${typePart}${name}`); + + try { + // Should technically await non-sync, but ESLint doesn't support async rules; + // thankfully, the Wasm load time is safely fast + parseImportsSync(imprt); + } catch (err) { + report( + `Bad @import tag`, + null, + tag, + ); + } + }); + } + utils.forEachPreferredTag('author', (jsdocParameter, targetTagName) => { const author = /** @type {string} */ ( utils.getTagDescription(jsdocParameter) diff --git a/test/rules/assertions/checkValues.js b/test/rules/assertions/checkValues.js index 5138ba9ea..bb2bb0f79 100644 --- a/test/rules/assertions/checkValues.js +++ b/test/rules/assertions/checkValues.js @@ -353,6 +353,26 @@ export default { }, ], }, + { + code: ` + /** + * @import BadImportIgnoredByThisRule + */ + /** + * @import {AnotherBadImportIgnoredByThisRule} from + */ + `, + errors: [ + { + line: 3, + message: 'Bad @import tag', + }, + { + line: 6, + message: 'Bad @import tag', + }, + ], + }, ], valid: [ { @@ -592,5 +612,24 @@ export default { }, ], }, + { + code: ` + /** + * @import LinterDef, { Sth as Something, Another as Another2 } from "eslint" + */ + /** + * @import { Linter } from "eslint" + */ + /** + * @import LinterDefault from "eslint" + */ + /** + * @import {Linter as Lintee} from "eslint" + */ + /** + * @import * as Linters from "eslint" + */ + `, + }, ], };