diff --git a/.README/README.md b/.README/README.md index 782c4d1b1..727e5f0cb 100644 --- a/.README/README.md +++ b/.README/README.md @@ -130,7 +130,9 @@ how many line breaks to add when a block is missing. ### Mode -- `settings.jsdoc.mode` - Set to `jsdoc` (the default), `typescript`, or `closure`. +- `settings.jsdoc.mode` - Set to `typescript`, `closure`, or `jsdoc` (the + default unless the `@typescript-eslint` parser is in use in which case + `typescript` will be the default). Note that if you do not wish to use separate `.eslintrc.*` files for a project containing both JavaScript and TypeScript, you can also use [`overrides`](https://eslint.org/docs/user-guide/configuring). You may also set to `"permissive"` to try to be as accommodating to any of the styles, but this is not recommended. @@ -139,6 +141,9 @@ how many line breaks to add when a block is missing. - `no-undefined-types`: Only check `@template` for types in "closure" and "typescript" modes - `check-syntax`: determines aspects that may be enforced + - `valid-types`: in non-Closure mode, `@extends`, `@package` and access tags + (e.g., `@private`) with a bracketed type are reported as are missing + names with `@typedef` - For type/namepath-checking rules, determine which tags will be checked for types/namepaths (Closure allows types on some tags which the others do not, so these tags will additionally be checked in "closure" mode) diff --git a/.travis.yml b/.travis.yml index ce0f650d9..e74a6b7a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,6 @@ before_script: > fi notifications: email: false -sudo: false script: - npm run test - 'if [ -n "${LINT-}" ]; then npm run lint; fi' diff --git a/README.md b/README.md index 1183ee80a..9fd4a27cb 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,9 @@ how many line breaks to add when a block is missing. ### Mode -- `settings.jsdoc.mode` - Set to `jsdoc` (the default), `typescript`, or `closure`. +- `settings.jsdoc.mode` - Set to `typescript`, `closure`, or `jsdoc` (the + default unless the `@typescript-eslint` parser is in use in which case + `typescript` will be the default). Note that if you do not wish to use separate `.eslintrc.*` files for a project containing both JavaScript and TypeScript, you can also use [`overrides`](https://eslint.org/docs/user-guide/configuring). You may also set to `"permissive"` to try to be as accommodating to any of the styles, but this is not recommended. @@ -197,6 +199,9 @@ how many line breaks to add when a block is missing. - `no-undefined-types`: Only check `@template` for types in "closure" and "typescript" modes - `check-syntax`: determines aspects that may be enforced + - `valid-types`: in non-Closure mode, `@extends`, `@package` and access tags + (e.g., `@private`) with a bracketed type are reported as are missing + names with `@typedef` - For type/namepath-checking rules, determine which tags will be checked for types/namepaths (Closure allows types on some tags which the others do not, so these tags will additionally be checked in "closure" mode) @@ -13489,6 +13494,20 @@ function foo(bar) {} */ // Settings: {"jsdoc":{"mode":"closure"}} // Message: @interface should not have a name in "closure" mode. + +/** + * @typedef {SomeType} + */ +function quux () {} +// Settings: {"jsdoc":{"mode":"jsdoc"}} +// Message: @typedef must have a name in "jsdoc" mode. + +/** + * @private {SomeType} + */ +function quux () {} +// Settings: {"jsdoc":{"mode":"jsdoc"}} +// Message: @private should not have a bracketed type in "jsdoc" mode. ```` The following patterns are not considered problems: @@ -13617,10 +13636,16 @@ function quux() { */ class Bar {}; +/** + * @extends Foo + */ + class Bar {}; + /** * @extends {Foo} */ class Bar {}; +// Settings: {"jsdoc":{"mode":"closure"}} /** * @typedef {number|string} UserDefinedType @@ -13630,17 +13655,13 @@ function quux() { * @typedef {number|string} */ let UserDefinedGCCType; +// Settings: {"jsdoc":{"mode":"closure"}} /** * @modifies {foo|bar} */ function quux (foo, bar, baz) {} -/** - * @private {BadTypeNotCheckedInJsdoc<} - */ -function quux () {} - /** * @this {Navigator} */ @@ -13672,6 +13693,13 @@ function quux () {} function foo(bar) {} // Settings: {"jsdoc":{"mode":"typescript"}} +/** + * Foo function. + * + * @param {[number, string]} bar - The bar array. + */ +function foo(bar) {} + /** * Foo function. * @@ -13679,6 +13707,18 @@ function foo(bar) {} */ function foo(bar) {} // Settings: {"jsdoc":{"mode":"permissive"}} + +/** + * @typedef {SomeType} + */ +function quux () {} +// Settings: {"jsdoc":{"mode":"closure"}} + +/** + * @private {SomeType} + */ +function quux () {} +// Settings: {"jsdoc":{"mode":"closure"}} ```` diff --git a/package.json b/package.json index b3ebcd429..7cde7fd54 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,13 @@ "@babel/plugin-transform-flow-strip-types": "^7.10.1", "@babel/preset-env": "^7.10.3", "@babel/register": "^7.10.3", - "@typescript-eslint/parser": "^3.3.0", + "@typescript-eslint/parser": "^3.4.0", "babel-eslint": "^10.1.0", "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-istanbul": "^6.0.0", "chai": "^4.2.0", "cross-env": "^7.0.2", - "eslint": "7.3.0", + "eslint": "7.3.1", "eslint-config-canonical": "^20.0.6", "gitdown": "^3.1.3", "glob": "^7.1.6", @@ -35,7 +35,7 @@ "mocha": "^8.0.1", "nyc": "^15.1.0", "rimraf": "^3.0.2", - "semantic-release": "^17.0.8", + "semantic-release": "^17.1.0", "typescript": "^3.9.5" }, "engines": { diff --git a/src/iterateJsdoc.js b/src/iterateJsdoc.js index f01430e2c..6311a7dc4 100644 --- a/src/iterateJsdoc.js +++ b/src/iterateJsdoc.js @@ -434,7 +434,8 @@ const getSettings = (context) => { augmentsExtendsReplacesDocs: context.settings.jsdoc?.augmentsExtendsReplacesDocs, // Many rules, e.g., `check-tag-names` - mode: context.settings.jsdoc?.mode ?? 'jsdoc', + mode: context.settings.jsdoc?.mode ?? + (context.parserPath.includes('@typescript-eslint') ? 'typescript' : 'jsdoc'), }; /* eslint-enable sort-keys-fix/sort-keys-fix */ diff --git a/src/rules/validTypes.js b/src/rules/validTypes.js index 8c894e0e3..cc20b793a 100644 --- a/src/rules/validTypes.js +++ b/src/rules/validTypes.js @@ -81,6 +81,7 @@ export default iterateJsdoc(({ const hasEither = utils.tagMightHaveEitherTypeOrNamePosition(tag.tag) && (hasTypePosition || hasNameOrNamepathPosition); const mustHaveEither = utils.tagMustHaveEitherTypeOrNamePosition(tag.tag); + let skip; switch (tag.tag) { case 'borrows': { const thisNamepath = tag.description.replace(asExpression, ''); @@ -98,8 +99,27 @@ export default iterateJsdoc(({ } break; } + case 'extends': + case 'package': case 'private': case 'protected': case 'public': case 'static': { + if (mode !== 'closure' && mode !== 'permissive' && tag.type) { + report(`@${tag.tag} should not have a bracketed type in "${mode}" mode.`, null, tag); + break; + } + skip = true; + } + + // Fallthrough + case 'typedef': { + if (!skip && mode !== 'closure' && mode !== 'permissive' && !tag.name) { + report(`@typedef must have a name in "${mode}" mode.`, null, tag); + break; + } + skip = true; + } + + // Fallthrough case 'interface': { - if (mode === 'closure' && tag.name) { + if (!skip && mode === 'closure' && tag.name) { report('@interface should not have a name in "closure" mode.', null, tag); break; } diff --git a/test/rules/assertions/validTypes.js b/test/rules/assertions/validTypes.js index 8cbc9693c..003c6b429 100644 --- a/test/rules/assertions/validTypes.js +++ b/test/rules/assertions/validTypes.js @@ -410,6 +410,44 @@ export default { }, }, }, + { + code: ` + /** + * @typedef {SomeType} + */ + function quux () {} + + `, + errors: [ + { + message: '@typedef must have a name in "jsdoc" mode.', + }, + ], + settings: { + jsdoc: { + mode: 'jsdoc', + }, + }, + }, + { + code: ` + /** + * @private {SomeType} + */ + function quux () {} + + `, + errors: [ + { + message: '@private should not have a bracketed type in "jsdoc" mode.', + }, + ], + settings: { + jsdoc: { + mode: 'jsdoc', + }, + }, + }, ], valid: [ { @@ -593,6 +631,14 @@ export default { class Bar {}; `, }, + { + code: ` + /** + * @extends Foo + */ + class Bar {}; + `, + }, { code: ` /** @@ -600,6 +646,11 @@ export default { */ class Bar {}; `, + settings: { + jsdoc: { + mode: 'closure', + }, + }, }, { code: ` @@ -615,6 +666,11 @@ export default { */ let UserDefinedGCCType; `, + settings: { + jsdoc: { + mode: 'closure', + }, + }, }, { code: ` @@ -624,15 +680,6 @@ export default { function quux (foo, bar, baz) {} `, }, - { - code: ` - /** - * @private {BadTypeNotCheckedInJsdoc<} - */ - function quux () {} - - `, - }, { code: ` /** @@ -695,6 +742,17 @@ export default { }, }, }, + { + code: ` + /** + * Foo function. + * + * @param {[number, string]} bar - The bar array. + */ + function foo(bar) {} + `, + parser: require.resolve('@typescript-eslint/parser'), + }, { code: ` /** @@ -710,5 +768,33 @@ export default { }, }, }, + { + code: ` + /** + * @typedef {SomeType} + */ + function quux () {} + + `, + settings: { + jsdoc: { + mode: 'closure', + }, + }, + }, + { + code: ` + /** + * @private {SomeType} + */ + function quux () {} + + `, + settings: { + jsdoc: { + mode: 'closure', + }, + }, + }, ], };