diff --git a/.README/README.md b/.README/README.md index c7903f48c..98e325076 100644 --- a/.README/README.md +++ b/.README/README.md @@ -131,6 +131,9 @@ how many line breaks to add when a block is missing. ### Mode - `settings.jsdoc.mode` - Set to `jsdoc` (the default), `typescript`, or `closure`. + 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. Currently is used for the following: - Determine valid tags for `check-tag-names` - Only check `@template` in `no-undefined-types` for types in "closure" and @@ -138,6 +141,8 @@ how many line breaks to add when a block is missing. - For type-checking rules, determine which tags will be checked for types (Closure allows types on some tags which the others do not, so these tags will additionally be checked in "closure" mode) + - For type-checking rules, impacts parsing of types (through + [jsdoctypeparser](https://github.com/jsdoctypeparser/jsdoctypeparser) dependency) - Check preferred tag names ### Alias Preference diff --git a/.babelrc.json b/.babelrc.json index d48cd86cc..1b0f7930a 100644 --- a/.babelrc.json +++ b/.babelrc.json @@ -8,7 +8,7 @@ "@babel/preset-env", { "targets": { - "node": 8 + "node": 10 } } ] diff --git a/.ncurc.js b/.ncurc.js index 8c926d090..89cb16a78 100644 --- a/.ncurc.js +++ b/.ncurc.js @@ -4,9 +4,5 @@ module.exports = { // Whitelist all for checking besides `peer` which indicates // somewhat older versions of `eslint` we still support even // while our devDeps point to a more recent version - dep: 'prod,dev,optional,bundle', - reject: [ - // Todo[engine:node@>=10.0.0]: 7.0.0 of semver has minimum of Node 10 - 'semver' - ] + dep: 'prod,dev,optional,bundle' }; diff --git a/.travis.yml b/.travis.yml index f35ca83fc..ce0f650d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ node_js: - 14 - 12 - 10 - - 8 before_install: - npm config set depth 0 before_script: > diff --git a/README.md b/README.md index ce86ee82a..4a7902a8f 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,9 @@ how many line breaks to add when a block is missing. ### Mode - `settings.jsdoc.mode` - Set to `jsdoc` (the default), `typescript`, or `closure`. + 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. Currently is used for the following: - Determine valid tags for `check-tag-names` - Only check `@template` in `no-undefined-types` for types in "closure" and @@ -196,6 +199,8 @@ how many line breaks to add when a block is missing. - For type-checking rules, determine which tags will be checked for types (Closure allows types on some tags which the others do not, so these tags will additionally be checked in "closure" mode) + - For type-checking rules, impacts parsing of types (through + [jsdoctypeparser](https://github.com/jsdoctypeparser/jsdoctypeparser) dependency) - Check preferred tag names @@ -13273,6 +13278,14 @@ function quux() { } // Message: Syntax error in namepath: module:namespace.SomeClass<~ +/** + * @param someParam<~ + */ +function quux() { + +} +// Message: Syntax error in namepath: someParam<~ + /** * @memberof module:namespace.SomeClass~< */ @@ -13408,6 +13421,15 @@ function quux () {} let foo; // Settings: {"jsdoc":{"mode":"closure"}} // Message: Tag @this must have a type + +/** + * Foo function. + * + * @param {[number, string]} bar - The bar array. + */ +function foo(bar) {} +// Settings: {"jsdoc":{"mode":"jsdoc"}} +// Message: Syntax error in type: [number, string] ```` The following patterns are not considered problems: @@ -13582,6 +13604,22 @@ function quux () {} * @define */ function quux () {} + +/** + * Foo function. + * + * @param {[number, string]} bar - The bar array. + */ +function foo(bar) {} +// Settings: {"jsdoc":{"mode":"typescript"}} + +/** + * Foo function. + * + * @param {[number, string]} bar - The bar array. + */ +function foo(bar) {} +// Settings: {"jsdoc":{"mode":"permissive"}} ```` diff --git a/package.json b/package.json index cc7cd7afd..b3ebcd429 100644 --- a/package.json +++ b/package.json @@ -7,39 +7,39 @@ "dependencies": { "comment-parser": "^0.7.5", "debug": "^4.1.1", - "jsdoctypeparser": "^6.1.0", + "jsdoctypeparser": "^7.0.0", "lodash": "^4.17.15", "regextras": "^0.7.1", - "semver": "^6.3.0", + "semver": "^7.3.2", "spdx-expression-parse": "^3.0.1" }, "description": "JSDoc linting rules for ESLint.", "devDependencies": { - "@babel/cli": "^7.10.1", - "@babel/core": "^7.10.2", - "@babel/node": "^7.10.1", + "@babel/cli": "^7.10.3", + "@babel/core": "^7.10.3", + "@babel/node": "^7.10.3", "@babel/plugin-transform-flow-strip-types": "^7.10.1", - "@babel/preset-env": "^7.10.2", - "@babel/register": "^7.10.1", - "@typescript-eslint/parser": "^3.2.0", + "@babel/preset-env": "^7.10.3", + "@babel/register": "^7.10.3", + "@typescript-eslint/parser": "^3.3.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.2.0", - "eslint-config-canonical": "^20.0.5", + "eslint": "7.3.0", + "eslint-config-canonical": "^20.0.6", "gitdown": "^3.1.3", "glob": "^7.1.6", "husky": "^4.2.5", - "mocha": "^7.2.0", + "mocha": "^8.0.1", "nyc": "^15.1.0", "rimraf": "^3.0.2", "semantic-release": "^17.0.8", "typescript": "^3.9.5" }, "engines": { - "node": ">=8" + "node": ">=10" }, "husky": { "hooks": { diff --git a/src/bin/generateReadme.js b/src/bin/generateReadme.js index 96b10d7ab..372f7ffb0 100644 --- a/src/bin/generateReadme.js +++ b/src/bin/generateReadme.js @@ -8,9 +8,7 @@ import glob from 'glob'; import Gitdown from 'gitdown'; const trimCode = (code) => { - // todo[engine:node@>10]: Change to `trimEnd` - // eslint-disable-next-line unicorn/prefer-trim-start-end - let lines = code.replace(/^\n/u, '').trimRight().split('\n'); + let lines = code.replace(/^\n/u, '').trimEnd().split('\n'); const firsLineIndentation = lines[0].match(/^\s+/u); const lastLineIndentation = lines[lines.length - 1].match(/^\s+/u); diff --git a/src/jsdocUtils.js b/src/jsdocUtils.js index 98d451264..6428a3a0a 100644 --- a/src/jsdocUtils.js +++ b/src/jsdocUtils.js @@ -195,7 +195,7 @@ const getTagNamesForMode = (mode, context) => { return jsdocTags; case 'typescript': return typeScriptTags; - case 'closure': + case 'closure': case 'permissive': return closureTags; default: if (!modeWarnSettings.hasBeenWarned(context, 'mode')) { @@ -402,9 +402,10 @@ const namepathDefiningTags = new Set([ ]); // The following do not seem to allow curly brackets in their doc -// signature or examples (besides `modifies`) +// signature or examples (besides `modifies` and `param`) const tagsWithOptionalNamePosition = new Set([ ...namepathDefiningTags, + 'param', // `borrows` has a different format, however, so needs special parsing; // seems to require both, and as "namepath"'s diff --git a/src/rules/checkTypes.js b/src/rules/checkTypes.js index bc4be9b9c..3105a3e2a 100644 --- a/src/rules/checkTypes.js +++ b/src/rules/checkTypes.js @@ -38,6 +38,11 @@ const adjustNames = (type, preferred, isGenericMatch, nodeName, node, parentNode if (bracketEnd) { parentNode.meta.syntax = 'ANGLE_BRACKET'; ret = preferred.slice(0, -2); + } else if ( + parentNode.meta.syntax === 'SQUARE_BRACKET' && + (nodeName === '[]' || nodeName === 'Array') + ) { + parentNode.meta.syntax = 'ANGLE_BRACKET'; } } } @@ -64,7 +69,7 @@ export default iterateJsdoc(({ return utils.tagMightHaveTypePosition(tag.tag); }); - const {preferredTypes} = settings; + const {preferredTypes, mode} = settings; const { noDefaults, unifyParentAndChildTypeChecks, @@ -126,7 +131,7 @@ export default iterateJsdoc(({ let typeAst; try { - typeAst = parse(jsdocTag.type); + typeAst = parse(jsdocTag.type, {mode}); } catch { return; } diff --git a/src/rules/noUndefinedTypes.js b/src/rules/noUndefinedTypes.js index 54bdfe90b..f24d9d075 100644 --- a/src/rules/noUndefinedTypes.js +++ b/src/rules/noUndefinedTypes.js @@ -30,7 +30,7 @@ export default iterateJsdoc(({ const {definedTypes = []} = context.options[0] || {}; let definedPreferredTypes = []; - const {preferredTypes} = settings; + const {preferredTypes, mode} = settings; if (Object.keys(preferredTypes).length) { // Replace `_.values` with `Object.values` when we may start requiring Node 7+ definedPreferredTypes = _.values(preferredTypes).map((preferredType) => { @@ -139,7 +139,7 @@ export default iterateJsdoc(({ let parsedType; try { - parsedType = parseType(tag.type); + parsedType = parseType(tag.type, {mode}); } catch { // On syntax error, will be handled by valid-types. return; diff --git a/src/rules/requireDescriptionCompleteSentence.js b/src/rules/requireDescriptionCompleteSentence.js index 071e70a04..395b8661a 100644 --- a/src/rules/requireDescriptionCompleteSentence.js +++ b/src/rules/requireDescriptionCompleteSentence.js @@ -11,11 +11,7 @@ const otherDescriptiveTags = new Set([ ]); const extractParagraphs = (text) => { - // Todo [engine:node@>8.11.0]: Uncomment following line with neg. lookbehind instead - // return text.split(/(? { - return [...par].reverse().join(''); - }).reverse(); + return text.split(/(? { diff --git a/src/rules/requireJsdoc.js b/src/rules/requireJsdoc.js index 36ef55cda..f07b99191 100644 --- a/src/rules/requireJsdoc.js +++ b/src/rules/requireJsdoc.js @@ -225,67 +225,63 @@ export default { } }; - // todo[engine:node@>=8.3.0]: Change to object spread - // eslint-disable-next-line fp/no-mutating-assign - return Object.assign( - jsdocUtils.getContextObject(jsdocUtils.enforcedContexts(context, []), checkJsDoc), - { - ArrowFunctionExpression (node) { - if (!requireOption.ArrowFunctionExpression) { - return; - } + return { + ...jsdocUtils.getContextObject(jsdocUtils.enforcedContexts(context, []), checkJsDoc), + ArrowFunctionExpression (node) { + if (!requireOption.ArrowFunctionExpression) { + return; + } - if (!['VariableDeclarator', 'ExportDefaultDeclaration', 'AssignmentExpression'].includes(node.parent.type)) { - return; - } + if (!['VariableDeclarator', 'ExportDefaultDeclaration', 'AssignmentExpression'].includes(node.parent.type)) { + return; + } - checkJsDoc(node, true); - }, + checkJsDoc(node, true); + }, - ClassDeclaration (node) { - if (!requireOption.ClassDeclaration) { - return; - } + ClassDeclaration (node) { + if (!requireOption.ClassDeclaration) { + return; + } - checkJsDoc(node); - }, + checkJsDoc(node); + }, - ClassExpression (node) { - if (!requireOption.ClassExpression) { - return; - } + ClassExpression (node) { + if (!requireOption.ClassExpression) { + return; + } - checkJsDoc(node); - }, + checkJsDoc(node); + }, - FunctionDeclaration (node) { - if (!requireOption.FunctionDeclaration) { - return; - } + FunctionDeclaration (node) { + if (!requireOption.FunctionDeclaration) { + return; + } - checkJsDoc(node, true); - }, + checkJsDoc(node, true); + }, - FunctionExpression (node) { - if (requireOption.MethodDefinition && node.parent.type === 'MethodDefinition') { - checkJsDoc(node, true); + FunctionExpression (node) { + if (requireOption.MethodDefinition && node.parent.type === 'MethodDefinition') { + checkJsDoc(node, true); - return; - } + return; + } - if (!requireOption.FunctionExpression) { - return; - } + if (!requireOption.FunctionExpression) { + return; + } - if ( - ['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type) || - node.parent.type === 'Property' && node === node.parent.value - ) { - checkJsDoc(node, true); - } - }, + if ( + ['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type) || + node.parent.type === 'Property' && node === node.parent.value + ) { + checkJsDoc(node, true); + } }, - ); + }; }, meta: { docs: { diff --git a/src/rules/validTypes.js b/src/rules/validTypes.js index 1a8e9e23c..30320d1f4 100644 --- a/src/rules/validTypes.js +++ b/src/rules/validTypes.js @@ -8,19 +8,20 @@ export default iterateJsdoc(({ report, utils, context, + settings, }) => { const { allowEmptyNamepaths = true, checkSeesForNamepaths = false, } = context.options[0] || {}; - + const {mode} = settings; if (!jsdoc.tags) { return; } jsdoc.tags.forEach((tag) => { const validNamepathParsing = function (namepath, tagName) { try { - parse(namepath); + parse(namepath, {mode}); } catch { let handled = false; @@ -29,7 +30,7 @@ export default iterateJsdoc(({ const endChar = namepath.slice(-1); if (['#', '.', '~'].includes(endChar)) { try { - parse(namepath.slice(0, -1)); + parse(namepath.slice(0, -1), {mode}); handled = true; } catch { // Use the original error for including the whole type @@ -39,7 +40,7 @@ export default iterateJsdoc(({ const startChar = namepath.charAt(); if (['#', '.', '~'].includes(startChar)) { try { - parse(namepath.slice(1)); + parse(namepath.slice(1), {mode}); handled = true; } catch { // Use the original error for including the whole type @@ -60,7 +61,7 @@ export default iterateJsdoc(({ const validTypeParsing = function (type) { try { - parse(type); + parse(type, {mode}); } catch { report(`Syntax error in type: ${type}`, null, tag); diff --git a/test/rules/assertions/validTypes.js b/test/rules/assertions/validTypes.js index 5db72b898..1c0a823b1 100644 --- a/test/rules/assertions/validTypes.js +++ b/test/rules/assertions/validTypes.js @@ -32,6 +32,22 @@ export default { }, ], }, + { + code: ` + /** + * @param someParam<~ + */ + function quux() { + + } + `, + errors: [ + { + line: 3, + message: 'Syntax error in namepath: someParam<~', + }, + ], + }, { code: ` /** @@ -322,6 +338,27 @@ export default { }, }, }, + { + code: ` + /** + * Foo function. + * + * @param {[number, string]} bar - The bar array. + */ + function foo(bar) {} + `, + errors: [ + { + line: 5, + message: 'Syntax error in type: [number, string]', + }, + ], + settings: { + jsdoc: { + mode: 'jsdoc', + }, + }, + }, ], valid: [ { @@ -592,5 +629,35 @@ export default { function quux () {} `, }, + { + code: ` + /** + * Foo function. + * + * @param {[number, string]} bar - The bar array. + */ + function foo(bar) {} + `, + settings: { + jsdoc: { + mode: 'typescript', + }, + }, + }, + { + code: ` + /** + * Foo function. + * + * @param {[number, string]} bar - The bar array. + */ + function foo(bar) {} + `, + settings: { + jsdoc: { + mode: 'permissive', + }, + }, + }, ], };