From 647d47e5f6b392a60287b39f8799fa92b229190e Mon Sep 17 00:00:00 2001 From: Simon Seyock Date: Sun, 16 May 2021 13:20:02 +0200 Subject: [PATCH] feat: use `jsdoc-type-pratt-parser` --- package.json | 2 +- src/rules/checkTypes.js | 97 ++++++++++++++++------------------- src/rules/noUndefinedTypes.js | 22 ++++---- src/rules/validTypes.js | 10 ++-- 4 files changed, 63 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index 79657da86..991f851ac 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/rules/checkTypes.js b/src/rules/checkTypes.js index 61300b16d..f021b8ab2 100644 --- a/src/rules/checkTypes.js +++ b/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 = [ @@ -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; } }; @@ -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]; @@ -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; @@ -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) => { diff --git a/src/rules/noUndefinedTypes.js b/src/rules/noUndefinedTypes.js index 62f1c8d3d..f2965032a 100644 --- a/src/rules/noUndefinedTypes.js +++ b/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, @@ -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); } } }); diff --git a/src/rules/validTypes.js b/src/rules/validTypes.js index b4daf942e..44d34f79b 100644 --- a/src/rules/validTypes.js +++ b/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; @@ -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 { @@ -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);