Skip to content

Commit

Permalink
feat: use jsdoc-type-pratt-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
simonseyock authored and brettz9 committed May 30, 2021
1 parent b81e25d commit 647d47e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 68 deletions.
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -9,7 +9,7 @@
"comment-parser": "1.1.5",
"debug": "^4.3.1",
"esquery": "^1.4.0",
"jsdoctypeparser": "^9.0.0",
"jsdoc-type-pratt-parser": "^1.0.0-alpha.10.0",
"lodash": "^4.17.21",
"regextras": "^0.7.1",
"semver": "^7.3.5",
Expand Down
97 changes: 45 additions & 52 deletions src/rules/checkTypes.js
@@ -1,6 +1,4 @@
import {
parse, traverse, publish,
} from 'jsdoctypeparser';
import { Parser, stringify, traverse } from 'jsdoc-type-pratt-parser';
import iterateJsdoc from '../iterateJsdoc';

const strictNativeTypes = [
Expand All @@ -22,40 +20,38 @@ const adjustNames = (type, preferred, isGenericMatch, nodeName, node, parentNode
let ret = preferred;
if (isGenericMatch) {
if (preferred === '[]') {
if (parentNode.objects[0].type === 'UNION') {
parentNode.objects[0] = {
type: 'PARENTHESIS',
value: parentNode.objects[0],
};
}
parentNode.meta.syntax = 'SQUARE_BRACKET';
parentNode.meta.brackets = '[]';
parentNode.meta.dot = false;
ret = 'Array';
} else {
const dotBracketEnd = preferred.match(/\.(?:<>)?$/u);
if (dotBracketEnd) {
parentNode.meta.syntax = 'ANGLE_BRACKET_WITH_DOT';
parentNode.meta.brackets = '<>';
parentNode.meta.dot = true;
ret = preferred.slice(0, -dotBracketEnd[0].length);
} else {
const bracketEnd = preferred.endsWith('<>');
if (bracketEnd) {
parentNode.meta.syntax = 'ANGLE_BRACKET';
parentNode.meta.brackets = '<>';
parentNode.meta.dot = false;
ret = preferred.slice(0, -2);
} else if (
parentNode.meta.syntax === 'SQUARE_BRACKET' &&
parentNode.meta.brackets === '[]' &&
(nodeName === '[]' || nodeName === 'Array')
) {
parentNode.meta.syntax = 'ANGLE_BRACKET';
parentNode.meta.brackets = '<>';
parentNode.meta.dot = false;
}
}
}
} else if (type === 'ANY') {
node.type = 'NAME';
}
node.name = ret.replace(/(?:\.|<>|\.<>|\[\])$/u, '');
node.value = ret.replace(/(?:\.|<>|\.<>|\[\])$/u, '');

// For bare pseudo-types like `<>`
if (!ret) {
node.name = nodeName;
node.value = nodeName;
}
};

Expand All @@ -78,52 +74,46 @@ export default iterateJsdoc(({
exemptTagContexts = [],
} = context.options[0] || {};

const getPreferredTypeInfo = (_type, nodeName, parentName, parentNode) => {
const getPreferredTypeInfo = (_type, nodeName, parentNode, property) => {
let hasMatchingPreferredType;
let isGenericMatch;
let typeName = nodeName;
if (Object.keys(preferredTypes).length) {
const parentType = parentName === 'subject';
if (unifyParentAndChildTypeChecks || parentType) {
const syntax = parentNode?.meta?.syntax;
const isNameOfGeneric = parentNode !== undefined && parentNode.type === 'GENERIC' && property === 'left';
if (unifyParentAndChildTypeChecks || isNameOfGeneric) {
const brackets = parentNode?.meta?.brackets;
const dot = parentNode?.meta?.dot;

[
['.', 'ANGLE_BRACKET_WITH_DOT'],
['.<>', 'ANGLE_BRACKET_WITH_DOT'],
['<>', 'ANGLE_BRACKET'],
].some(([checkPostFix, syn]) => {
isGenericMatch = preferredTypes?.[nodeName + checkPostFix] !== undefined &&
syntax === syn;
if (isGenericMatch) {
typeName += checkPostFix;
}
if (brackets === '<>') {
const checkPostFixes = dot ? ['.', '.<>'] : ['<>'];
isGenericMatch = checkPostFixes.some(checkPostFix => {
if (preferredTypes?.[nodeName + checkPostFix] !== undefined) {
typeName += checkPostFix;
return true;
}
return false;
});
}

if (!isGenericMatch && property) {
const checkPostFixes = dot ? ['.', '.<>'] : [brackets]

return isGenericMatch;
});
if (!isGenericMatch && parentType) {
[
['[]', 'SQUARE_BRACKET'],
['.', 'ANGLE_BRACKET_WITH_DOT'],
['.<>', 'ANGLE_BRACKET_WITH_DOT'],
['<>', 'ANGLE_BRACKET'],
].some(([checkPostFix, syn]) => {
isGenericMatch = preferredTypes?.[checkPostFix] !== undefined &&
syntax === syn;
if (isGenericMatch) {
isGenericMatch = checkPostFixes.some(checkPostFix => {
if (preferredTypes?.[checkPostFix] !== undefined) {
typeName = checkPostFix;
return true;
}

return isGenericMatch;
return false;
});
}
}
const directNameMatch = preferredTypes?.[nodeName] !== undefined &&
!Object.values(preferredTypes).includes(nodeName);
const unifiedSyntaxParentMatch = parentType && directNameMatch && unifyParentAndChildTypeChecks;
const unifiedSyntaxParentMatch = property && directNameMatch && unifyParentAndChildTypeChecks;
isGenericMatch = isGenericMatch || unifiedSyntaxParentMatch;

hasMatchingPreferredType = isGenericMatch ||
directNameMatch && !parentType;
directNameMatch && !property;
}

return [hasMatchingPreferredType, typeName, isGenericMatch];
Expand All @@ -134,20 +124,23 @@ export default iterateJsdoc(({
let typeAst;

try {
typeAst = parse(jsdocTag.type, {mode});
const parser = new Parser({
mode
});
typeAst = parser.parse(jsdocTag.type);
} catch {
return;
}
const tagName = jsdocTag.tag;

traverse(typeAst, (node, parentName, parentNode) => {
const {type, name} = node;
traverse(typeAst, (node, parentNode, property) => {
const {type, value} = node;
if (!['NAME', 'ANY'].includes(type)) {
return;
}
let nodeName = type === 'ANY' ? '*' : name;
let nodeName = type === 'ANY' ? '*' : value;

const [hasMatchingPreferredType, typeName, isGenericMatch] = getPreferredTypeInfo(type, nodeName, parentName, parentNode);
const [hasMatchingPreferredType, typeName, isGenericMatch] = getPreferredTypeInfo(type, nodeName, parentNode, property);

let preferred;
let types;
Expand Down Expand Up @@ -207,7 +200,7 @@ export default iterateJsdoc(({
});

if (invalidTypes.length) {
const fixedType = publish(typeAst);
const fixedType = stringify(typeAst);

invalidTypes.forEach(([badType, preferredType = '', message]) => {
const fix = (fixer) => {
Expand Down
22 changes: 12 additions & 10 deletions src/rules/noUndefinedTypes.js
@@ -1,9 +1,7 @@
import {
getJSDocComment,
} from '@es-joy/jsdoccomment';
import {
parse as parseType, traverse,
} from 'jsdoctypeparser';
import { Parser, traverse } from 'jsdoc-type-pratt-parser'
import _ from 'lodash';
import iterateJsdoc, {
parseComment,
Expand Down Expand Up @@ -146,21 +144,25 @@ export default iterateJsdoc(({
let parsedType;

try {
parsedType = parseType(tag.type, {mode});
const parser = new Parser({mode})
parsedType = parser.parse(tag.type);
} catch {
// On syntax error, will be handled by valid-types.
return;
}

traverse(parsedType, ({type, name}) => {
traverse(parsedType, ({type, value}, parentNode, property) => {
if (type === 'NAME') {
if (parentNode !== undefined && parentNode.type === 'KEY_VALUE' && property === 'left') {
return
}
const structuredTypes = structuredTags[tag.tag]?.type;
if (!allDefinedTypes.has(name) &&
(!Array.isArray(structuredTypes) || !structuredTypes.includes(name))
if (!allDefinedTypes.has(value) &&
(!Array.isArray(structuredTypes) || !structuredTypes.includes(value))
) {
report(`The type '${name}' is undefined.`, null, tag);
} else if (!extraTypes.includes(name)) {
context.markVariableAsUsed(name);
report(`The type '${value}' is undefined.`, null, tag);
} else if (!extraTypes.includes(value)) {
context.markVariableAsUsed(value);
}
}
});
Expand Down
10 changes: 5 additions & 5 deletions src/rules/validTypes.js
@@ -1,6 +1,4 @@
import {
parse,
} from 'jsdoctypeparser';
import { Parser } from 'jsdoc-type-pratt-parser';
import iterateJsdoc from '../iterateJsdoc';

const asExpression = /as\s+/u;
Expand All @@ -19,7 +17,8 @@ export default iterateJsdoc(({

const tryParseIgnoreError = (path) => {
try {
parse(path, {mode});
const parser = new Parser({mode})
parser.parse(path);

return true;
} catch {
Expand Down Expand Up @@ -72,7 +71,8 @@ export default iterateJsdoc(({

const validTypeParsing = function (type) {
try {
parse(type, {mode});
const parser = new Parser({mode});
parser.parse(type);
} catch {
report(`Syntax error in type: ${type}`, null, tag);

Expand Down

0 comments on commit 647d47e

Please sign in to comment.