730 lines
22 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _jsdoccomment = require("@es-joy/jsdoccomment");
var _debug = _interopRequireDefault(require("debug"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const debug = (0, _debug.default)('requireExportJsdoc');
/**
* @typedef {{
* value: string
* }} ValueObject
*/
/**
* @typedef {{
* type?: string,
* value?: ValueObject|import('eslint').Rule.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 */
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|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} 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) {
case 'Identifier':
{
return getIdentifier(node, globals, scope, opts);
}
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 10 */
/*
if (opts.createMissingProps && propertyValue) {
obj.props[propertyValue] = createNode();
return obj.props[propertyValue];
}
*/
debug(`MemberExpression: Missing property ${/** @type {import('estree').PrivateIdentifier} */node.property.name}`);
/* c8 ignore next 2 */
return null;
}
case 'ClassExpression':
{
return getSymbol( /** @type {import('eslint').Rule.Node} */
node.body, globals, scope, opts);
}
/* c8 ignore next 7 -- No longer needed? */
// @ts-expect-error TS OK
case 'TSTypeAliasDeclaration':
// @ts-expect-error TS OK
// Fallthrough
case 'TSEnumDeclaration':
// @ts-expect-error TS OK
case 'TSInterfaceDeclaration':
case 'ClassDeclaration':
case 'FunctionExpression':
case 'FunctionDeclaration':
case 'ArrowFunctionExpression':
{
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) {
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 'ObjectExpression':
{
const val = createNode();
val.type = 'object';
for (const prop of node.properties) {
if ([
// typescript-eslint, espree, acorn, etc.
'SpreadElement',
// @babel/eslint-parser
'ExperimentalSpreadProperty'].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;
}
case 'Literal':
{
const val = createNode();
val.type = 'literal';
val.value = node;
return val;
}
}
/* c8 ignore next */
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 'FunctionDeclaration':
/* c8 ignore next */
// @ts-expect-error TS OK
// Fall through
case 'TSEnumDeclaration':
case 'TSInterfaceDeclaration':
/* c8 ignore next */
// @ts-expect-error TS OK
// Fall through
case 'TSTypeAliasDeclaration':
case 'ClassDeclaration':
{
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 2 */
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 */
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 2 */
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 'Program':
{
for (const childNode of node.body) {
initVariables( /** @type {import('eslint').Rule.Node} */
childNode, globals, opts);
}
break;
}
case 'ExpressionStatement':
{
initVariables( /** @type {import('eslint').Rule.Node} */
node.expression, 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;
}
case 'ExportNamedDeclaration':
{
if (node.declaration) {
initVariables( /** @type {import('eslint').Rule.Node} */
node.declaration, globals, opts);
}
break;
}
}
};
/* eslint-disable complexity -- Temporary */
/**
* Populates variable maps using AST
* @param {import('eslint').Rule.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 'Program':
{
if (opts.ancestorsOnly) {
return false;
}
for (const childNode of node.body) {
mapVariables( /** @type {import('eslint').Rule.Node} */
childNode, globals, opts);
}
break;
}
case 'ExpressionStatement':
{
mapVariables( /** @type {import('eslint').Rule.Node} */
node.expression, globals, opts);
break;
}
case 'AssignmentExpression':
{
createSymbol( /** @type {import('eslint').Rule.Node} */
node.left, globals, /** @type {import('eslint').Rule.Node} */
node.right);
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;
}
case 'FunctionDeclaration':
{
/* 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 '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 'ClassDeclaration':
{
createSymbol( /** @type {import('eslint').Rule.Node|null} */node.id, globals, /** @type {import('eslint').Rule.Node} */node.body, globals);
break;
}
default:
{
/* c8 ignore next */
return false;
}
}
return true;
};
/**
*
* @param {import('eslint').Rule.Node} node
* @param {CreatedNode|ValueObject|string|undefined|
* import('eslint').Rule.Node} block
* @param {(CreatedNode|ValueObject|string|
* import('eslint').Rule.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(['ExportNamedDeclaration', 'ExportDefaultDeclaration']);
const ignorableNestedTypes = new Set(['FunctionDeclaration', 'ArrowFunctionExpression', '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 === null || nde === void 0 ? void 0 : 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(['TSPropertySignature', 'TSMethodSignature', 'ClassProperty', 'PropertyDefinition', 'Method']);
const canExportChildrenType = new Set(['TSInterfaceBody', 'TSInterfaceDeclaration', 'TSTypeLiteral', 'TSTypeAliasDeclaration', 'TSTypeParameterInstantiation', 'TSTypeReference', 'ClassDeclaration', 'ClassBody', 'ClassDefinition', 'ClassExpression', 'Program']);
/**
* @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) {
var _globals$props$module;
const moduleExports = (_globals$props$module = globals.props.module) === null || _globals$props$module === void 0 || (_globals$props$module = _globals$props$module.props) === null || _globals$props$module === void 0 ? void 0 : _globals$props$module.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(['PropertyDefinition', 'MethodDefinition']);
/**
*
* @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 && !(0, _jsdoccomment.findJSDocComment)(exportNode, sourceCode, settings)) {
return true;
}
/**
* Some typescript types are not in variable map, but inherit exported (interface property and method)
*/
if (isExportByAncestor(node) && !(0, _jsdoccomment.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);
};
var _default = exports.default = {
isUncommentedExport,
parse
};
module.exports = exports.default;
//# sourceMappingURL=exportParser.cjs.map