Skip to content

Commit

Permalink
feat(check-types, no-undefined-types, valid-types): use mode-aw…
Browse files Browse the repository at this point in the history
…are type parsing; fixes part of #356; fixes #495 (#586)

BREAKING CHANGE:

Requires Node 10+

Also:
1. Adds "permissive" mode
2. Checks "param" for valid namepaths
  • Loading branch information
brettz9 committed Jun 21, 2020
2 parents 916088a + 6341e27 commit f98a8e4
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 86 deletions.
5 changes: 5 additions & 0 deletions .README/README.md
Expand Up @@ -131,13 +131,18 @@ 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
"typescript" modes
- 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
Expand Down
2 changes: 1 addition & 1 deletion .babelrc.json
Expand Up @@ -8,7 +8,7 @@
"@babel/preset-env",
{
"targets": {
"node": 8
"node": 10
}
}
]
Expand Down
6 changes: 1 addition & 5 deletions .ncurc.js
Expand Up @@ -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'
};
1 change: 0 additions & 1 deletion .travis.yml
Expand Up @@ -3,7 +3,6 @@ node_js:
- 14
- 12
- 10
- 8
before_install:
- npm config set depth 0
before_script: >
Expand Down
38 changes: 38 additions & 0 deletions README.md
Expand Up @@ -189,13 +189,18 @@ 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
"typescript" modes
- 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

<a name="eslint-plugin-jsdoc-settings-alias-preference"></a>
Expand Down Expand Up @@ -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~<
*/
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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"}}
````
24 changes: 12 additions & 12 deletions package.json
Expand Up @@ -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": {
Expand Down
4 changes: 1 addition & 3 deletions src/bin/generateReadme.js
Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions src/jsdocUtils.js
Expand Up @@ -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')) {
Expand Down Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions src/rules/checkTypes.js
Expand Up @@ -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';
}
}
}
Expand All @@ -64,7 +69,7 @@ export default iterateJsdoc(({
return utils.tagMightHaveTypePosition(tag.tag);
});

const {preferredTypes} = settings;
const {preferredTypes, mode} = settings;
const {
noDefaults,
unifyParentAndChildTypeChecks,
Expand Down Expand Up @@ -126,7 +131,7 @@ export default iterateJsdoc(({
let typeAst;

try {
typeAst = parse(jsdocTag.type);
typeAst = parse(jsdocTag.type, {mode});
} catch {
return;
}
Expand Down
4 changes: 2 additions & 2 deletions src/rules/noUndefinedTypes.js
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 1 addition & 5 deletions src/rules/requireDescriptionCompleteSentence.js
Expand Up @@ -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(/(?<![;:])\n\n/u);
return [...text].reverse().join('').split(/\n\n(?![;:])/u).map((par) => {
return [...par].reverse().join('');
}).reverse();
return text.split(/(?<![;:])\n\n/u);
};

const extractSentences = (text, abbreviationsRegex) => {
Expand Down
92 changes: 44 additions & 48 deletions src/rules/requireJsdoc.js
Expand Up @@ -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: {
Expand Down

0 comments on commit f98a8e4

Please sign in to comment.