'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: ["local", "exported"], 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;