UNPKG

eslint-plugin-jsdoc

Version:
269 lines (230 loc) 8.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _jsdoctypeparser = require("jsdoctypeparser"); var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const strictNativeTypes = ['undefined', 'null', 'boolean', 'number', 'bigint', 'string', 'symbol', 'object', 'Array', 'Function', 'Date', 'RegExp']; 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'; ret = 'Array'; } else { const dotBracketEnd = preferred.match(/\.(?:<>)?$/u); if (dotBracketEnd) { parentNode.meta.syntax = 'ANGLE_BRACKET_WITH_DOT'; ret = preferred.slice(0, -dotBracketEnd[0].length); } else { const bracketEnd = preferred.endsWith('<>'); 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'; } } } } else if (type === 'ANY') { node.type = 'NAME'; } node.name = ret.replace(/(?:\.|<>|\.<>|\[\])$/u, ''); // For bare pseudo-types like `<>` if (!ret) { node.name = nodeName; } }; var _default = (0, _iterateJsdoc.default)(({ jsdocNode, sourceCode, report, utils, settings, context }) => { const jsdocTagsWithPossibleType = utils.filterTags(tag => { return utils.tagMightHaveTypePosition(tag.tag); }); const { preferredTypes, structuredTags, mode } = settings; const { noDefaults, unifyParentAndChildTypeChecks, exemptTagContexts = [] } = context.options[0] || {}; const getPreferredTypeInfo = (_type, nodeName, parentName, parentNode) => { let hasMatchingPreferredType; let isGenericMatch; let typeName = nodeName; if (Object.keys(preferredTypes).length) { const parentType = parentName === 'subject'; if (unifyParentAndChildTypeChecks || parentType) { var _parentNode$meta; const syntax = parentNode === null || parentNode === void 0 ? void 0 : (_parentNode$meta = parentNode.meta) === null || _parentNode$meta === void 0 ? void 0 : _parentNode$meta.syntax; [['.', 'ANGLE_BRACKET_WITH_DOT'], ['.<>', 'ANGLE_BRACKET_WITH_DOT'], ['<>', 'ANGLE_BRACKET']].some(([checkPostFix, syn]) => { isGenericMatch = (preferredTypes === null || preferredTypes === void 0 ? void 0 : preferredTypes[nodeName + checkPostFix]) !== undefined && syntax === syn; if (isGenericMatch) { typeName += checkPostFix; } return isGenericMatch; }); if (!isGenericMatch && parentType) { [['[]', 'SQUARE_BRACKET'], ['.', 'ANGLE_BRACKET_WITH_DOT'], ['.<>', 'ANGLE_BRACKET_WITH_DOT'], ['<>', 'ANGLE_BRACKET']].some(([checkPostFix, syn]) => { isGenericMatch = (preferredTypes === null || preferredTypes === void 0 ? void 0 : preferredTypes[checkPostFix]) !== undefined && syntax === syn; if (isGenericMatch) { typeName = checkPostFix; } return isGenericMatch; }); } } const directNameMatch = (preferredTypes === null || preferredTypes === void 0 ? void 0 : preferredTypes[nodeName]) !== undefined && !Object.values(preferredTypes).includes(nodeName); const unifiedSyntaxParentMatch = parentType && directNameMatch && unifyParentAndChildTypeChecks; isGenericMatch = isGenericMatch || unifiedSyntaxParentMatch; hasMatchingPreferredType = isGenericMatch || directNameMatch && !parentType; } return [hasMatchingPreferredType, typeName, isGenericMatch]; }; jsdocTagsWithPossibleType.forEach(jsdocTag => { const invalidTypes = []; let typeAst; try { typeAst = (0, _jsdoctypeparser.parse)(jsdocTag.type, { mode }); } catch { return; } const tagName = jsdocTag.tag; (0, _jsdoctypeparser.traverse)(typeAst, (node, parentName, parentNode) => { const { type, name } = node; if (!['NAME', 'ANY'].includes(type)) { return; } let nodeName = type === 'ANY' ? '*' : name; const [hasMatchingPreferredType, typeName, isGenericMatch] = getPreferredTypeInfo(type, nodeName, parentName, parentNode); let preferred; let types; if (hasMatchingPreferredType) { const preferredSetting = preferredTypes[typeName]; nodeName = typeName === '[]' ? typeName : nodeName; if (!preferredSetting) { invalidTypes.push([nodeName]); } else if (typeof preferredSetting === 'string') { preferred = preferredSetting; invalidTypes.push([nodeName, preferred]); } else if (typeof preferredSetting === 'object') { preferred = preferredSetting === null || preferredSetting === void 0 ? void 0 : preferredSetting.replacement; invalidTypes.push([nodeName, preferred, preferredSetting === null || preferredSetting === void 0 ? void 0 : preferredSetting.message]); } else { utils.reportSettings('Invalid `settings.jsdoc.preferredTypes`. Values must be falsy, a string, or an object.'); return; } } else if (Object.entries(structuredTags).some(([tag, { type: typs }]) => { types = typs; return tag === tagName && Array.isArray(types) && !types.includes(nodeName); })) { invalidTypes.push([nodeName, types]); } else if (!noDefaults && type === 'NAME') { for (const strictNativeType of strictNativeTypes) { if (strictNativeType === 'object' && mode === 'typescript') { continue; } if (strictNativeType.toLowerCase() === nodeName.toLowerCase() && strictNativeType !== nodeName && ( // Don't report if user has own map for a strict native type !preferredTypes || (preferredTypes === null || preferredTypes === void 0 ? void 0 : preferredTypes[strictNativeType]) === undefined)) { preferred = strictNativeType; invalidTypes.push([nodeName, preferred]); break; } } } // For fixer if (preferred) { adjustNames(type, preferred, isGenericMatch, nodeName, node, parentNode); } }); if (invalidTypes.length) { const fixedType = (0, _jsdoctypeparser.publish)(typeAst); invalidTypes.forEach(([badType, preferredType = '', message]) => { const fix = fixer => { return fixer.replaceText(jsdocNode, sourceCode.getText(jsdocNode).replace(`{${jsdocTag.type}}`, `{${fixedType}}`)); }; const tagValue = jsdocTag.name ? ` "${jsdocTag.name}"` : ''; if (exemptTagContexts.some(({ tag, types }) => { return tag === tagName && (types === true || types.includes(jsdocTag.type)); })) { return; } report(message || `Invalid JSDoc @${tagName}${tagValue} type "${badType}"` + (preferredType ? '; ' : '.') + (preferredType ? `prefer: ${JSON.stringify(preferredType)}.` : ''), preferredType ? fix : null, jsdocTag, message ? { tagName, tagValue } : null); }); } }); }, { iterateAllJsdocs: true, meta: { docs: { description: 'Reports invalid types.', url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-check-types' }, fixable: 'code', schema: [{ additionalProperties: false, properties: { exemptTagContexts: { items: { additionalProperties: false, properties: { tag: { type: 'string' }, types: { oneOf: [{ type: 'boolean' }, { items: { type: 'string' }, type: 'array' }] } }, type: 'object' }, type: 'array' }, noDefaults: { type: 'boolean' }, unifyParentAndChildTypeChecks: { type: 'boolean' } }, type: 'object' }], type: 'suggestion' } }); exports.default = _default; module.exports = exports.default; //# sourceMappingURL=checkTypes.js.map