'use strict'; /* eslint-disable jsdoc/valid-types -- doesn't allow `readonly`. TODO: remove eslint-disable when https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/164 is fixed */ /** * @typedef {{ readonly [type: string]: ReadonlyArray }} VisitorKeys */ /* eslint-enable jsdoc/valid-types -- doesn't allow `readonly string[]`. TODO: check why */ /** * @type {VisitorKeys} */ const KEYS = { ArrayExpression: ['elements'], ArrayPattern: ['elements'], ArrowFunctionExpression: ['params', 'body'], AssignmentExpression: ['left', 'right'], AssignmentPattern: ['left', 'right'], AwaitExpression: ['argument'], BinaryExpression: ['left', 'right'], BlockStatement: ['body'], BreakStatement: ['label'], CallExpression: ['callee', 'arguments'], CatchClause: ['param', 'body'], ChainExpression: ['expression'], ClassBody: ['body'], ClassDeclaration: ['id', 'superClass', 'body'], ClassExpression: ['id', 'superClass', 'body'], ConditionalExpression: ['test', 'consequent', 'alternate'], ContinueStatement: ['label'], DebuggerStatement: [], DoWhileStatement: ['body', 'test'], EmptyStatement: [], ExperimentalRestProperty: ['argument'], ExperimentalSpreadProperty: ['argument'], ExportAllDeclaration: ['exported', 'source', 'attributes'], ExportDefaultDeclaration: ['declaration'], ExportNamedDeclaration: ['declaration', 'specifiers', 'source', 'attributes'], ExportSpecifier: ['exported', 'local'], ExpressionStatement: ['expression'], ForInStatement: ['left', 'right', 'body'], ForOfStatement: ['left', 'right', 'body'], ForStatement: ['init', 'test', 'update', 'body'], FunctionDeclaration: ['id', 'params', 'body'], FunctionExpression: ['id', 'params', 'body'], Identifier: [], IfStatement: ['test', 'consequent', 'alternate'], ImportAttribute: ['key', 'value'], ImportDeclaration: ['specifiers', 'source', 'attributes'], ImportDefaultSpecifier: ['local'], ImportExpression: ['source', 'options'], ImportNamespaceSpecifier: ['local'], ImportSpecifier: ['imported', 'local'], JSXAttribute: ['name', 'value'], JSXClosingElement: ['name'], JSXClosingFragment: [], JSXElement: ['openingElement', 'children', 'closingElement'], JSXEmptyExpression: [], JSXExpressionContainer: ['expression'], JSXFragment: ['openingFragment', 'children', 'closingFragment'], JSXIdentifier: [], JSXMemberExpression: ['object', 'property'], JSXNamespacedName: ['namespace', 'name'], JSXOpeningElement: ['name', 'attributes'], JSXOpeningFragment: [], JSXSpreadAttribute: ['argument'], JSXSpreadChild: ['expression'], JSXText: [], LabeledStatement: ['label', 'body'], Literal: [], LogicalExpression: ['left', 'right'], MemberExpression: ['object', 'property'], MetaProperty: ['meta', 'property'], MethodDefinition: ['key', 'value'], NewExpression: ['callee', 'arguments'], ObjectExpression: ['properties'], ObjectPattern: ['properties'], PrivateIdentifier: [], Program: ['body'], Property: ['key', 'value'], PropertyDefinition: ['key', 'value'], RestElement: ['argument'], ReturnStatement: ['argument'], SequenceExpression: ['expressions'], SpreadElement: ['argument'], StaticBlock: ['body'], Super: [], SwitchCase: ['test', 'consequent'], SwitchStatement: ['discriminant', 'cases'], TaggedTemplateExpression: ['tag', 'quasi'], TemplateElement: [], TemplateLiteral: ['quasis', 'expressions'], ThisExpression: [], ThrowStatement: ['argument'], TryStatement: ['block', 'handler', 'finalizer'], UnaryExpression: ['argument'], UpdateExpression: ['argument'], VariableDeclaration: ['declarations'], VariableDeclarator: ['id', 'init'], WhileStatement: ['test', 'body'], WithStatement: ['object', 'body'], YieldExpression: ['argument'], }; // Types. const NODE_TYPES = Object.keys(KEYS); // Freeze the keys. for (const type of NODE_TYPES) { Object.freeze(KEYS[type]); } Object.freeze(KEYS); /** * @author Toru Nagashima * See LICENSE file in root directory for full license. */ /** * @typedef {import('./visitor-keys.js').VisitorKeys} VisitorKeys */ // List to ignore keys. const KEY_BLACKLIST = new Set([ 'parent', 'leadingComments', 'trailingComments', ]); /** * Check whether a given key should be used or not. * @param {string} key The key to check. * @returns {boolean} `true` if the key should be used. */ function filterKey(key) { return !KEY_BLACKLIST.has(key) && key[0] !== '_'; } /* eslint-disable jsdoc/valid-types -- doesn't allow `readonly`. TODO: remove eslint-disable when https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/164 is fixed */ /** * Get visitor keys of a given node. * @param {Object} node The AST node to get keys. * @returns {readonly string[]} Visitor keys of the node. */ function getKeys(node) { return Object.keys(node).filter(filterKey); } /* eslint-enable jsdoc/valid-types -- doesn't allow `readonly` */ /** * Make the union set with `KEYS` and given keys. * @param {VisitorKeys} additionalKeys The additional keys. * @returns {VisitorKeys} The union set. */ function unionWith(additionalKeys) { const retv = /** @type {{ [type: string]: ReadonlyArray }} */ (Object.assign({}, KEYS)); for (const type of Object.keys(additionalKeys)) { if (Object.hasOwn(retv, type)) { const keys = new Set(additionalKeys[type]); for (const key of retv[type]) { keys.add(key); } retv[type] = Object.freeze(Array.from(keys)); } else { retv[type] = Object.freeze(Array.from(additionalKeys[type])); } } return Object.freeze(retv); } exports.KEYS = KEYS; exports.getKeys = getKeys; exports.unionWith = unionWith;