| import { |
| findJSDocComment, |
| } from '@es-joy/jsdoccomment'; |
| import debugModule from 'debug'; |
| |
| const debug = debugModule('requireExportJsdoc'); |
| |
| /** |
| * @typedef {{ |
| * value: string |
| * }} ValueObject |
| */ |
| |
| /** |
| * @typedef {{ |
| * type?: string, |
| * value?: ValueObject|import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node, |
| * props: { |
| * [key: string]: CreatedNode|null, |
| * }, |
| * special?: true, |
| * globalVars?: CreatedNode, |
| * exported?: boolean, |
| * ANONYMOUS_DEFAULT?: import('eslint').Rule.Node |
| * }} CreatedNode |
| */ |
| |
| /** |
| * @returns {CreatedNode} |
| */ |
| const createNode = function () { |
| return { |
| props: {}, |
| }; |
| }; |
| |
| /** |
| * @param {CreatedNode|null} symbol |
| * @returns {string|null} |
| */ |
| const getSymbolValue = function (symbol) { |
| /* c8 ignore next 3 */ |
| if (!symbol) { |
| return null; |
| } |
| |
| /* c8 ignore else */ |
| if (symbol.type === 'literal') { |
| return /** @type {ValueObject} */ (symbol.value).value; |
| } |
| /* c8 ignore next 2 */ |
| // eslint-disable-next-line @stylistic/padding-line-between-statements -- c8 |
| return null; |
| }; |
| |
| /** |
| * |
| * @param {import('estree').Identifier} node |
| * @param {CreatedNode} globals |
| * @param {CreatedNode} scope |
| * @param {SymbolOptions} opts |
| * @returns {CreatedNode|null} |
| */ |
| const getIdentifier = function (node, globals, scope, opts) { |
| if (opts.simpleIdentifier) { |
| // Type is Identier for noncomputed properties |
| const identifierLiteral = createNode(); |
| identifierLiteral.type = 'literal'; |
| identifierLiteral.value = { |
| value: node.name, |
| }; |
| |
| return identifierLiteral; |
| } |
| |
| /* c8 ignore next */ |
| const block = scope || globals; |
| |
| // As scopes are not currently supported, they are not traversed upwards recursively |
| if (block.props[node.name]) { |
| return block.props[node.name]; |
| } |
| |
| // Seems this will only be entered once scopes added and entered |
| /* c8 ignore next 3 */ |
| if (globals.props[node.name]) { |
| return globals.props[node.name]; |
| } |
| |
| return null; |
| }; |
| |
| /** |
| * @callback CreateSymbol |
| * @param {import('eslint').Rule.Node|null} node |
| * @param {CreatedNode} globals |
| * @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node|null} value |
| * @param {CreatedNode} [scope] |
| * @param {boolean|SymbolOptions} [isGlobal] |
| * @returns {CreatedNode|null} |
| */ |
| |
| /** @type {CreateSymbol} */ |
| let createSymbol; // eslint-disable-line prefer-const |
| |
| /* eslint-disable complexity -- Temporary */ |
| |
| /** |
| * @typedef {{ |
| * simpleIdentifier?: boolean |
| * }} SymbolOptions |
| */ |
| |
| /** |
| * |
| * @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} node |
| * @param {CreatedNode} globals |
| * @param {CreatedNode} scope |
| * @param {SymbolOptions} [opt] |
| * @returns {CreatedNode|null} |
| */ |
| const getSymbol = function (node, globals, scope, opt) { |
| /* eslint-enable complexity -- Temporary */ |
| const opts = opt || {}; |
| /* c8 ignore next */ |
| switch (node.type) { |
| /* c8 ignore next 4 -- No longer needed? */ |
| case 'ArrowFunctionExpression': |
| |
| // Fallthrough |
| case 'ClassDeclaration': |
| |
| case 'FunctionDeclaration': |
| |
| case 'FunctionExpression': |
| case 'TSEnumDeclaration': |
| case 'TSInterfaceDeclaration': |
| case 'TSTypeAliasDeclaration': { |
| const val = createNode(); |
| val.props.prototype = createNode(); |
| val.props.prototype.type = 'object'; |
| val.type = 'object'; |
| val.value = node; |
| |
| return val; |
| } |
| |
| case 'AssignmentExpression': { |
| return createSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.left), |
| globals, |
| /** @type {import('eslint').Rule.Node} */ |
| (node.right), |
| scope, |
| opts, |
| ); |
| } |
| |
| case 'ClassBody': { |
| const val = createNode(); |
| for (const method of node.body) { |
| // StaticBlock |
| if (!('key' in method)) { |
| continue; |
| } |
| |
| val.props[ |
| /** @type {import('estree').Identifier} */ ( |
| /** @type {import('estree').MethodDefinition} */ ( |
| method |
| ).key |
| ).name |
| ] = createNode(); |
| /** @type {{[key: string]: CreatedNode}} */ (val.props)[ |
| /** @type {import('estree').Identifier} */ ( |
| /** @type {import('estree').MethodDefinition} */ ( |
| method |
| ).key |
| ).name |
| ].type = 'object'; |
| /** @type {{[key: string]: CreatedNode}} */ (val.props)[ |
| /** @type {import('estree').Identifier} */ ( |
| /** @type {import('estree').MethodDefinition} */ ( |
| method |
| ).key |
| ).name |
| ].value = /** @type {import('eslint').Rule.Node} */ ( |
| /** @type {import('estree').MethodDefinition} */ (method).value |
| ); |
| } |
| |
| val.type = 'object'; |
| val.value = node.parent; |
| |
| return val; |
| } |
| |
| case 'ClassExpression': { |
| return getSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.body), |
| globals, |
| scope, |
| opts, |
| ); |
| } |
| |
| case 'Identifier': { |
| return getIdentifier(node, globals, scope, opts); |
| } |
| |
| case 'Literal': { |
| const val = createNode(); |
| val.type = 'literal'; |
| val.value = node; |
| |
| return val; |
| } |
| |
| case 'MemberExpression': { |
| const obj = getSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.object), |
| globals, |
| scope, |
| opts, |
| ); |
| const propertySymbol = getSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.property), |
| globals, |
| scope, |
| { |
| simpleIdentifier: !node.computed, |
| }, |
| ); |
| const propertyValue = getSymbolValue(propertySymbol); |
| |
| /* c8 ignore else */ |
| if (obj && propertyValue && obj.props[propertyValue]) { |
| const block = obj.props[propertyValue]; |
| |
| return block; |
| } |
| /* c8 ignore next 11 */ |
| /* |
| if (opts.createMissingProps && propertyValue) { |
| obj.props[propertyValue] = createNode(); |
| |
| return obj.props[propertyValue]; |
| } |
| */ |
| // eslint-disable-next-line @stylistic/padding-line-between-statements -- c8 |
| debug(`MemberExpression: Missing property ${ |
| /** @type {import('estree').PrivateIdentifier} */ (node.property).name |
| }`); |
| /* c8 ignore next 2 */ |
| return null; |
| } |
| |
| case 'ObjectExpression': { |
| const val = createNode(); |
| val.type = 'object'; |
| for (const prop of node.properties) { |
| if ([ |
| // @babel/eslint-parser |
| 'ExperimentalSpreadProperty', |
| |
| // typescript-eslint, espree, acorn, etc. |
| 'SpreadElement', |
| ].includes(prop.type)) { |
| continue; |
| } |
| |
| const propVal = getSymbol( |
| /** @type {import('eslint').Rule.Node} */ ( |
| /** @type {import('estree').Property} */ |
| (prop).value |
| ), |
| globals, |
| scope, |
| opts, |
| ); |
| /* c8 ignore next 8 */ |
| if (propVal) { |
| val.props[ |
| /** @type {import('estree').PrivateIdentifier} */ |
| ( |
| /** @type {import('estree').Property} */ (prop).key |
| ).name |
| ] = propVal; |
| } |
| } |
| |
| return val; |
| } |
| } |
| /* c8 ignore next 2 */ |
| // eslint-disable-next-line @stylistic/padding-line-between-statements -- c8 |
| return null; |
| }; |
| |
| /** |
| * |
| * @param {CreatedNode} block |
| * @param {string} name |
| * @param {CreatedNode|null} value |
| * @param {CreatedNode} globals |
| * @param {boolean|SymbolOptions|undefined} isGlobal |
| * @returns {void} |
| */ |
| const createBlockSymbol = function (block, name, value, globals, isGlobal) { |
| block.props[name] = value; |
| if (isGlobal && globals.props.window && globals.props.window.special) { |
| globals.props.window.props[name] = value; |
| } |
| }; |
| |
| createSymbol = function (node, globals, value, scope, isGlobal) { |
| const block = scope || globals; |
| /* c8 ignore next 3 */ |
| if (!node) { |
| return null; |
| } |
| |
| let symbol; |
| switch (node.type) { |
| case 'ClassDeclaration': |
| /* c8 ignore next */ |
| // @ts-expect-error TS OK |
| // Fall through |
| case 'FunctionDeclaration': case 'TSEnumDeclaration': |
| /* c8 ignore next */ |
| // @ts-expect-error TS OK |
| // Fall through |
| case 'TSInterfaceDeclaration': case 'TSTypeAliasDeclaration': { |
| const nde = /** @type {import('estree').ClassDeclaration} */ (node); |
| /* c8 ignore else */ |
| if (nde.id && nde.id.type === 'Identifier') { |
| return createSymbol( |
| /** @type {import('eslint').Rule.Node} */ (nde.id), |
| globals, |
| node, |
| globals, |
| ); |
| } |
| /* c8 ignore next 3 */ |
| // eslint-disable-next-line @stylistic/padding-line-between-statements -- c8 |
| break; |
| } |
| |
| case 'Identifier': { |
| const nde = /** @type {import('estree').Identifier} */ (node); |
| if (value) { |
| const valueSymbol = getSymbol(value, globals, block); |
| /* c8 ignore else */ |
| if (valueSymbol) { |
| createBlockSymbol(block, nde.name, valueSymbol, globals, isGlobal); |
| |
| return block.props[nde.name]; |
| } |
| /* c8 ignore next 2 */ |
| // eslint-disable-next-line @stylistic/padding-line-between-statements -- c8 |
| debug('Identifier: Missing value symbol for %s', nde.name); |
| } else { |
| createBlockSymbol(block, nde.name, createNode(), globals, isGlobal); |
| |
| return block.props[nde.name]; |
| } |
| /* c8 ignore next 3 */ |
| // eslint-disable-next-line @stylistic/padding-line-between-statements -- c8 |
| break; |
| } |
| |
| case 'MemberExpression': { |
| const nde = /** @type {import('estree').MemberExpression} */ (node); |
| symbol = getSymbol( |
| /** @type {import('eslint').Rule.Node} */ (nde.object), globals, block, |
| ); |
| |
| const propertySymbol = getSymbol( |
| /** @type {import('eslint').Rule.Node} */ (nde.property), |
| globals, |
| block, |
| { |
| simpleIdentifier: !nde.computed, |
| }, |
| ); |
| const propertyValue = getSymbolValue(propertySymbol); |
| if (symbol && propertyValue) { |
| createBlockSymbol(symbol, propertyValue, getSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (value), globals, block, |
| ), globals, isGlobal); |
| return symbol.props[propertyValue]; |
| } |
| |
| debug( |
| 'MemberExpression: Missing symbol: %s', |
| /** @type {import('estree').Identifier} */ ( |
| nde.property |
| ).name, |
| ); |
| break; |
| } |
| } |
| |
| return null; |
| }; |
| |
| /** |
| * Creates variables from variable definitions |
| * @param {import('eslint').Rule.Node} node |
| * @param {CreatedNode} globals |
| * @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opts |
| * @returns {void} |
| */ |
| const initVariables = function (node, globals, opts) { |
| switch (node.type) { |
| case 'ExportNamedDeclaration': { |
| if (node.declaration) { |
| initVariables( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.declaration), |
| globals, |
| opts, |
| ); |
| } |
| |
| break; |
| } |
| |
| case 'ExpressionStatement': { |
| initVariables( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.expression), |
| globals, |
| opts, |
| ); |
| break; |
| } |
| |
| case 'Program': { |
| for (const childNode of node.body) { |
| initVariables( |
| /** @type {import('eslint').Rule.Node} */ |
| (childNode), |
| globals, |
| opts, |
| ); |
| } |
| |
| break; |
| } |
| |
| case 'VariableDeclaration': { |
| for (const declaration of node.declarations) { |
| // let and const |
| const symbol = createSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (declaration.id), |
| globals, |
| null, |
| globals, |
| ); |
| if (opts.initWindow && node.kind === 'var' && globals.props.window) { |
| // If var, also add to window |
| globals.props.window.props[ |
| /** @type {import('estree').Identifier} */ |
| (declaration.id).name |
| ] = symbol; |
| } |
| } |
| |
| break; |
| } |
| } |
| }; |
| |
| /* eslint-disable complexity -- Temporary */ |
| |
| /** |
| * Populates variable maps using AST |
| * @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} node |
| * @param {CreatedNode} globals |
| * @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt |
| * @param {true} [isExport] |
| * @returns {boolean} |
| */ |
| const mapVariables = function (node, globals, opt, isExport) { |
| /* eslint-enable complexity -- Temporary */ |
| /* c8 ignore next */ |
| const opts = opt || {}; |
| /* c8 ignore next */ |
| switch (node.type) { |
| case 'AssignmentExpression': { |
| createSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.left), |
| globals, |
| /** @type {import('eslint').Rule.Node} */ |
| (node.right), |
| ); |
| break; |
| } |
| |
| case 'ClassDeclaration': { |
| createSymbol( |
| /** @type {import('eslint').Rule.Node|null} */ (node.id), |
| globals, |
| /** @type {import('eslint').Rule.Node} */ (node.body), |
| globals, |
| ); |
| break; |
| } |
| |
| case 'ExportDefaultDeclaration': { |
| const symbol = createSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.declaration), |
| globals, |
| /** @type {import('eslint').Rule.Node} */ |
| (node.declaration), |
| ); |
| if (symbol) { |
| symbol.exported = true; |
| /* c8 ignore next 6 */ |
| } else { |
| // if (!node.id) { |
| globals.ANONYMOUS_DEFAULT = /** @type {import('eslint').Rule.Node} */ ( |
| node.declaration |
| ); |
| } |
| |
| break; |
| } |
| |
| case 'ExportNamedDeclaration': { |
| if (node.declaration) { |
| if (node.declaration.type === 'VariableDeclaration') { |
| mapVariables( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.declaration), |
| globals, |
| opts, |
| true, |
| ); |
| } else { |
| const symbol = createSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.declaration), |
| globals, |
| /** @type {import('eslint').Rule.Node} */ |
| (node.declaration), |
| ); |
| /* c8 ignore next 3 */ |
| if (symbol) { |
| symbol.exported = true; |
| } |
| } |
| } |
| |
| for (const specifier of node.specifiers) { |
| mapVariables( |
| /** @type {import('eslint').Rule.Node} */ |
| (specifier), |
| globals, |
| opts, |
| ); |
| } |
| |
| break; |
| } |
| |
| case 'ExportSpecifier': { |
| const symbol = getSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.local), |
| globals, |
| globals, |
| ); |
| /* c8 ignore next 3 */ |
| if (symbol) { |
| symbol.exported = true; |
| } |
| |
| break; |
| } |
| |
| case 'ExpressionStatement': { |
| mapVariables( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.expression), |
| globals, |
| opts, |
| ); |
| break; |
| } |
| |
| case 'FunctionDeclaration': |
| |
| case 'TSTypeAliasDeclaration': { |
| /* c8 ignore next 10 */ |
| if (/** @type {import('estree').Identifier} */ (node.id).type === 'Identifier') { |
| createSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (node.id), |
| globals, |
| node, |
| globals, |
| true, |
| ); |
| } |
| |
| break; |
| } |
| |
| case 'Program': { |
| if (opts.ancestorsOnly) { |
| return false; |
| } |
| |
| for (const childNode of node.body) { |
| mapVariables( |
| /** @type {import('eslint').Rule.Node} */ |
| (childNode), |
| globals, |
| opts, |
| ); |
| } |
| |
| break; |
| } |
| |
| case 'VariableDeclaration': { |
| for (const declaration of node.declarations) { |
| const isGlobal = Boolean(opts.initWindow && node.kind === 'var' && globals.props.window); |
| const symbol = createSymbol( |
| /** @type {import('eslint').Rule.Node} */ |
| (declaration.id), |
| globals, |
| /** @type {import('eslint').Rule.Node} */ |
| (declaration.init), |
| globals, |
| isGlobal, |
| ); |
| if (symbol && isExport) { |
| symbol.exported = true; |
| } |
| } |
| |
| break; |
| } |
| |
| default: { |
| /* c8 ignore next */ |
| return false; |
| } |
| } |
| |
| return true; |
| }; |
| |
| /** |
| * |
| * @param {import('eslint').Rule.Node} node |
| * @param {CreatedNode|ValueObject|string|undefined| |
| * import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node} block |
| * @param {(CreatedNode|ValueObject|string| |
| * import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node)[]} [cache] |
| * @returns {boolean} |
| */ |
| const findNode = function (node, block, cache) { |
| let blockCache = cache || []; |
| if (!block || blockCache.includes(block)) { |
| return false; |
| } |
| |
| blockCache = blockCache.slice(); |
| blockCache.push(block); |
| |
| if ( |
| typeof block === 'object' && |
| 'type' in block && |
| (block.type === 'object' || block.type === 'MethodDefinition') && |
| block.value === node |
| ) { |
| return true; |
| } |
| |
| if (typeof block !== 'object') { |
| return false; |
| } |
| |
| const props = ('props' in block && block.props) || ('body' in block && block.body); |
| for (const propval of Object.values(props || {})) { |
| if (Array.isArray(propval)) { |
| /* c8 ignore next 5 */ |
| if (propval.some((val) => { |
| return findNode(node, val, blockCache); |
| })) { |
| return true; |
| } |
| } else if (findNode(node, propval, blockCache)) { |
| return true; |
| } |
| } |
| |
| return false; |
| }; |
| |
| const exportTypes = new Set([ |
| 'ExportDefaultDeclaration', 'ExportNamedDeclaration', |
| ]); |
| const ignorableNestedTypes = new Set([ |
| 'ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression', |
| ]); |
| |
| /** |
| * @param {import('eslint').Rule.Node} nde |
| * @returns {import('eslint').Rule.Node|false} |
| */ |
| const getExportAncestor = function (nde) { |
| let node = nde; |
| let idx = 0; |
| const ignorableIfDeep = ignorableNestedTypes.has(nde?.type); |
| while (node) { |
| // Ignore functions nested more deeply than say `export default function () {}` |
| if (idx >= 2 && ignorableIfDeep) { |
| break; |
| } |
| |
| if (exportTypes.has(node.type)) { |
| return node; |
| } |
| |
| node = node.parent; |
| idx++; |
| } |
| |
| return false; |
| }; |
| |
| const canBeExportedByAncestorType = new Set([ |
| 'ClassProperty', |
| 'Method', |
| 'PropertyDefinition', |
| 'TSMethodSignature', |
| 'TSPropertySignature', |
| ]); |
| |
| const canExportChildrenType = new Set([ |
| 'ClassBody', |
| 'ClassDeclaration', |
| 'ClassDefinition', |
| 'ClassExpression', |
| 'Program', |
| 'TSInterfaceBody', |
| 'TSInterfaceDeclaration', |
| 'TSTypeAliasDeclaration', |
| 'TSTypeLiteral', |
| 'TSTypeParameterInstantiation', |
| 'TSTypeReference', |
| ]); |
| |
| /** |
| * @param {import('eslint').Rule.Node} nde |
| * @returns {false|import('eslint').Rule.Node} |
| */ |
| const isExportByAncestor = function (nde) { |
| if (!canBeExportedByAncestorType.has(nde.type)) { |
| return false; |
| } |
| |
| let node = nde.parent; |
| while (node) { |
| if (exportTypes.has(node.type)) { |
| return node; |
| } |
| |
| if (!canExportChildrenType.has(node.type)) { |
| return false; |
| } |
| |
| node = node.parent; |
| } |
| |
| return false; |
| }; |
| |
| /** |
| * |
| * @param {CreatedNode} block |
| * @param {import('eslint').Rule.Node} node |
| * @param {CreatedNode[]} [cache] Currently unused |
| * @returns {boolean} |
| */ |
| const findExportedNode = function (block, node, cache) { |
| /* c8 ignore next 3 */ |
| if (block === null) { |
| return false; |
| } |
| |
| const blockCache = cache || []; |
| const { |
| props, |
| } = block; |
| for (const propval of Object.values(props)) { |
| const pval = /** @type {CreatedNode} */ (propval); |
| blockCache.push(pval); |
| if (pval.exported && (node === pval.value || findNode(node, pval.value))) { |
| return true; |
| } |
| |
| // No need to check `propval` for exported nodes as ESM |
| // exports are only global |
| } |
| |
| return false; |
| }; |
| |
| /** |
| * |
| * @param {import('eslint').Rule.Node} node |
| * @param {CreatedNode} globals |
| * @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt |
| * @returns {boolean} |
| */ |
| const isNodeExported = function (node, globals, opt) { |
| const moduleExports = globals.props.module?.props?.exports; |
| if ( |
| opt.initModuleExports && moduleExports && findNode(node, moduleExports) |
| ) { |
| return true; |
| } |
| |
| if (opt.initWindow && globals.props.window && findNode(node, globals.props.window)) { |
| return true; |
| } |
| |
| if (opt.esm && findExportedNode(globals, node)) { |
| return true; |
| } |
| |
| return false; |
| }; |
| |
| /** |
| * |
| * @param {import('eslint').Rule.Node} node |
| * @param {CreatedNode} globalVars |
| * @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opts |
| * @returns {boolean} |
| */ |
| const parseRecursive = function (node, globalVars, opts) { |
| // Iterate from top using recursion - stop at first processed node from top |
| if (node.parent && parseRecursive(node.parent, globalVars, opts)) { |
| return true; |
| } |
| |
| return mapVariables(node, globalVars, opts); |
| }; |
| |
| /** |
| * |
| * @param {import('eslint').Rule.Node} ast |
| * @param {import('eslint').Rule.Node} node |
| * @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt |
| * @returns {CreatedNode} |
| */ |
| const parse = function (ast, node, opt) { |
| /* c8 ignore next 6 */ |
| const opts = opt || { |
| ancestorsOnly: false, |
| esm: true, |
| initModuleExports: true, |
| initWindow: true, |
| }; |
| |
| const globalVars = createNode(); |
| if (opts.initModuleExports) { |
| globalVars.props.module = createNode(); |
| globalVars.props.module.props.exports = createNode(); |
| globalVars.props.exports = globalVars.props.module.props.exports; |
| } |
| |
| if (opts.initWindow) { |
| globalVars.props.window = createNode(); |
| globalVars.props.window.special = true; |
| } |
| |
| if (opts.ancestorsOnly) { |
| parseRecursive(node, globalVars, opts); |
| } else { |
| initVariables(ast, globalVars, opts); |
| mapVariables(ast, globalVars, opts); |
| } |
| |
| return { |
| globalVars, |
| props: {}, |
| }; |
| }; |
| |
| const accessibilityNodes = new Set([ |
| 'MethodDefinition', |
| 'PropertyDefinition', |
| ]); |
| |
| /** |
| * |
| * @param {import('eslint').Rule.Node} node |
| * @returns {boolean} |
| */ |
| const isPrivate = (node) => { |
| return accessibilityNodes.has(node.type) && |
| ( |
| 'accessibility' in node && |
| node.accessibility !== 'public' && node.accessibility !== undefined |
| ) || |
| 'key' in node && |
| node.key.type === 'PrivateIdentifier'; |
| }; |
| |
| /** |
| * |
| * @param {import('eslint').Rule.Node} node |
| * @param {import('eslint').SourceCode} sourceCode |
| * @param {import('./rules/requireJsdoc.js').RequireJsdocOpts} opt |
| * @param {import('./iterateJsdoc.js').Settings} settings |
| * @returns {boolean} |
| */ |
| const isUncommentedExport = function (node, sourceCode, opt, settings) { |
| // console.log({node}); |
| // Optimize with ancestor check for esm |
| if (opt.esm) { |
| if (isPrivate(node) || |
| node.parent && isPrivate(node.parent)) { |
| return false; |
| } |
| |
| const exportNode = getExportAncestor(node); |
| |
| // Is export node comment |
| if (exportNode && !findJSDocComment(exportNode, sourceCode, settings)) { |
| return true; |
| } |
| |
| /** |
| * Some typescript types are not in variable map, but inherit exported (interface property and method) |
| */ |
| if ( |
| isExportByAncestor(node) && |
| !findJSDocComment(node, sourceCode, settings) |
| ) { |
| return true; |
| } |
| } |
| |
| const ast = /** @type {unknown} */ (sourceCode.ast); |
| |
| const parseResult = parse( |
| /** @type {import('eslint').Rule.Node} */ |
| (ast), |
| node, |
| opt, |
| ); |
| |
| return isNodeExported( |
| node, /** @type {CreatedNode} */ (parseResult.globalVars), opt, |
| ); |
| }; |
| |
| export default { |
| isUncommentedExport, |
| parse, |
| }; |