Skip to content
This repository has been archived by the owner on Aug 20, 2024. It is now read-only.

chore: Derive keys from AST definitions #35

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
"sourceType": "module",
"ecmaVersion": 2020
},
"settings": {
"jsdoc": {
"mode": "typescript",
"preferredTypes": {
"Object": "object",
"object<>": "Object"
}
}
},
"overrides": [
{
"files": ["*.cjs"],
Expand Down
15 changes: 10 additions & 5 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
*/
import KEYS from "./visitor-keys.js";

/**
* @typedef {{ [type: string]: ReadonlyArray<string> }} KeysStrict
* @typedef {{ readonly [type: string]: ReadonlyArray<string> }} KeysStrictReadonly
*/

// List to ignore keys.
const KEY_BLACKLIST = new Set([
"parent",
Expand All @@ -22,8 +27,8 @@ function filterKey(key) {

/**
* Get visitor keys of a given node.
* @param {Object} node The AST node to get keys.
* @returns {string[]} Visitor keys of the node.
* @param {object} node The AST node to get keys.
* @returns {readonly string[]} Visitor keys of the node.
*/
export function getKeys(node) {
return Object.keys(node).filter(filterKey);
Expand All @@ -33,11 +38,11 @@ export function getKeys(node) {
// eslint-disable-next-line valid-jsdoc
/**
* Make the union set with `KEYS` and given keys.
* @param {Object} additionalKeys The additional keys.
* @returns {{ [type: string]: string[] | undefined }} The union set.
* @param {KeysStrictReadonly} additionalKeys The additional keys.
* @returns {KeysStrictReadonly} The union set.
*/
export function unionWith(additionalKeys) {
const retv = Object.assign({}, KEYS);
const retv = /** @type {KeysStrict} */ (Object.assign({}, KEYS));

for (const type of Object.keys(additionalKeys)) {
if (Object.prototype.hasOwnProperty.call(retv, type)) {
Expand Down
172 changes: 94 additions & 78 deletions lib/visitor-keys.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
/**
* @typedef {import('./index.js').KeysStrictReadonly} KeysStrictReadonly
*/

/**
* @type {KeysStrictReadonly}
*/
const KEYS = {
ArrayExpression: [
"elements"
],
ArrayPattern: [
"elements"
],
ArrowFunctionExpression: [
"body",
"params"
],
AssignmentExpression: [
"left",
"right"
Expand All @@ -7,36 +24,27 @@ const KEYS = {
"left",
"right"
],
ArrayExpression: [
"elements"
],
ArrayPattern: [
"elements"
],
ArrowFunctionExpression: [
"params",
"body"
AssignmentProperty: [
"key",
"value"
],
AwaitExpression: [
"argument"
],
BlockStatement: [
"body"
],
BigIntLiteral: [],
BinaryExpression: [
"left",
"right"
],
BlockStatement: [
"body"
],
BreakStatement: [
"label"
],
CallExpression: [
"callee",
"arguments"
],
CatchClause: [
"param",
"body"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order is important here as it indicates which property should be traversed first. As such, we can change the order to alphabetical. (Same comment throughout this file.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, sure, understood. But if the AST definers can't be convinced to define the properties in the order of iteration (taking into account nested interfaces, etc.), we'd have to add our own logic on how to sort these (and it might not be correct once new properties are added).

"body",
"param"
],
brettz9 marked this conversation as resolved.
Show resolved Hide resolved
ChainExpression: [
"expression"
Expand All @@ -45,24 +53,27 @@ const KEYS = {
"body"
],
ClassDeclaration: [
"body",
"id",
"superClass",
"body"
"superClass"
],
ClassExpression: [
"body",
"id",
"superClass",
"body"
"superClass"
],
ConditionalExpression: [
"test",
"alternate",
"consequent",
"alternate"
"test"
],
ContinueStatement: [
"label"
],
DebuggerStatement: [],
Directive: [
"expression"
],
DoWhileStatement: [
"body",
"test"
Expand All @@ -87,43 +98,37 @@ const KEYS = {
ExpressionStatement: [
"expression"
],
ExperimentalRestProperty: [
"argument"
],
ExperimentalSpreadProperty: [
"argument"
],
ForStatement: [
"init",
"test",
"update",
"body"
],
ForInStatement: [
"body",
"left",
"right",
"body"
"right"
],
ForOfStatement: [
"body",
"left",
"right",
"body"
"right"
],
ForStatement: [
"body",
"init",
"test",
"update"
],
FunctionDeclaration: [
"body",
"id",
"params",
"body"
"params"
],
FunctionExpression: [
"body",
"id",
"params",
"body"
"params"
],
Identifier: [],
IfStatement: [
"test",
"alternate",
"consequent",
"alternate"
"test"
],
ImportDeclaration: [
"specifiers",
Expand All @@ -146,18 +151,27 @@ const KEYS = {
"name",
"value"
],
JSXBoundaryElement: [
"name"
],
JSXClosingElement: [
"name"
],
JSXClosingFragment: [],
JSXElement: [
"openingElement",
"children",
"closingElement"
"closingElement",
"openingElement"
],
JSXEmptyExpression: [],
JSXExpressionContainer: [
"expression"
],
JSXFragment: [
"children",
"closingFragment",
"openingFragment"
],
JSXIdentifier: [],
JSXMemberExpression: [
"object",
Expand All @@ -168,24 +182,20 @@ const KEYS = {
"name"
],
JSXOpeningElement: [
"name",
"attributes"
"attributes",
"name"
],
JSXOpeningFragment: [],
JSXSpreadAttribute: [
"argument"
],
JSXText: [],
JSXFragment: [
"openingFragment",
"children",
"closingFragment"
JSXSpreadChild: [
"expression"
],
JSXClosingFragment: [],
JSXOpeningFragment: [],
Literal: [],
JSXText: [],
LabeledStatement: [
"label",
"body"
"body",
"label"
],
LogicalExpression: [
"left",
Expand All @@ -204,8 +214,8 @@ const KEYS = {
"value"
],
NewExpression: [
"callee",
"arguments"
"arguments",
"callee"
],
ObjectExpression: [
"properties"
Expand All @@ -225,6 +235,7 @@ const KEYS = {
"key",
"value"
],
RegExpLiteral: [],
RestElement: [
"argument"
],
Expand All @@ -234,38 +245,43 @@ const KEYS = {
SequenceExpression: [
"expressions"
],
SimpleCallExpression: [
"arguments",
"callee"
],
Comment on lines +248 to +251
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nodes with type: "SimpleCallExpression" don't exist, but nodes with type: "CallExpression" do, and we need those here. When generating this object, we should probably use values of type properties instead of names of interfaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this approach:

  1. This should cause SimpleLiteral, BigIntLiteral, and RegExpLiteral to be dropped from the new output, as apparently was the case earlier and I imagine still expected now (and merging isn't a problem for this script as they don't have any non-primitives to traverse).
  2. Directive and ExpressionStatement present a similar situation, causing Directive to now be dropped as I think was expected, and though both have their own traversable property, it is the same name, expression (as makes sense).
  3. Comment has two types Line and Block though I can make two types out of that (or ignore both).

I imagine there may be not too much work to adapt this to use type, and can then see if can get the expected results (minus the ordering of the keys which seems may not be as easy to do programmatically based solely on the AST).

SimpleLiteral: [],
SpreadElement: [
"argument"
],
StaticBlock: [
"body"
],
Super: [],
SwitchStatement: [
"discriminant",
"cases"
],
SwitchCase: [
"test",
"consequent"
"consequent",
"test"
],
SwitchStatement: [
"cases",
"discriminant"
],
TaggedTemplateExpression: [
"tag",
"quasi"
"quasi",
"tag"
],
TemplateElement: [],
TemplateLiteral: [
"quasis",
"expressions"
"expressions",
"quasis"
],
ThisExpression: [],
ThrowStatement: [
"argument"
],
TryStatement: [
"block",
"handler",
"finalizer"
"finalizer",
"handler"
],
UnaryExpression: [
"argument"
Expand All @@ -281,12 +297,12 @@ const KEYS = {
"init"
],
WhileStatement: [
"test",
"body"
"body",
"test"
],
WithStatement: [
"object",
"body"
"body",
"object"
],
YieldExpression: [
"argument"
Expand Down
Loading