Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add alias docs for @babel/types #13151

Merged
merged 6 commits into from Apr 30, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
124 changes: 121 additions & 3 deletions packages/babel-types/scripts/generators/docs.js
Expand Up @@ -124,17 +124,18 @@ function printAliasKeys(key, readme) {
"Aliases: " +
t.ALIAS_KEYS[key]
.map(function (key) {
return "`" + key + "`";
return "[`" + key + "`](#" + key.toLowerCase() + ")";
})
.join(", ")
);
}
}

readme.push("### Node Builders");
readme.push("");
Object.keys(t.BUILDER_KEYS)
.sort()
.forEach(function (key) {
readme.push("### " + toFunctionName(key));
readme.push("#### " + toFunctionName(key));
readme.push("");
readme.push("```javascript");
readme.push(
Expand All @@ -159,4 +160,121 @@ Object.keys(t.BUILDER_KEYS)
readme.push("");
});

function generateMapAliasToNodeTypes() {
const result = new Map();
for (const nodeType of Object.keys(t.ALIAS_KEYS)) {
const aliases = t.ALIAS_KEYS[nodeType];
if (!aliases) continue;
for (const alias of aliases) {
if (!result.has(alias)) {
result.set(alias, []);
}
const nodeTypes = result.get(alias);
nodeTypes.push(nodeType);
}
}
return result;
}
const aliasDescriptions = {
Binary:
"A cover of BinaryExpression and LogicalExpression, which share the same AST shape.",
Block: "Deprecated. Will be removed in Babel 8.",
BlockParent:
"A cover of AST nodes that start an execution context with new [LexicalEnvironment](https://tc39.es/ecma262/#table-additional-state-components-for-ecmascript-code-execution-contexts). In other words, they define the scope of `let` and `const` declarations.",
Class:
"A cover of ClassExpression and ClassDeclaration, which share the same AST shape.",
CompletionStatement:
"A statement that indicates the [completion records](https://tc39.es/ecma262/#sec-completion-record-specification-type). In other words, they define the control flow of the program, such as when should a loop break or an action throws critical errors.",
Conditional:
"A cover of ConditionalExpression and IfStatement, which share the same AST shape.",
Declaration:
"A cover of any [Declaration](https://tc39.es/ecma262/#prod-Declaration)s.",
EnumBody: "A cover of Flow enum bodies.",
EnumMember: "A cover of Flow enum membors.",
ExportDeclaration:
"A cover of any [ExportDeclaration](https://tc39.es/ecma262/#prod-ExportDeclaration)s.",
Expression:
"A cover of any [Expression](https://tc39.es/ecma262/#sec-ecmascript-language-expressions)s.",
ExpressionWrapper:
"A wrapper of expression that does not have runtime semantics.",
Flow: "A cover of AST nodes defined for Flow.",
FlowBaseAnnotation: "A cover of primary Flow type annotations.",
FlowDeclaration: "A cover of Flow declarations.",
FlowPredicate: "A cover of Flow predicates.",
FlowType: "A cover of Flow type annotations.",
For:
"A cover of [ForStatement](https://tc39.es/ecma262/#sec-for-statement)s and [ForXStatement](#forxstatement)s.",
ForXStatement:
"A cover of [ForInStatements and ForOfStatements](https://tc39.es/ecma262/#sec-for-in-and-for-of-statements).",
Function:
"A cover of functions and [method](#method)s, the must have `body` and `params`. Note: `Function` is different to `FunctionParent`.",
FunctionParent:
"A cover of AST nodes that start an execution context with new [VariableEnvironment](https://tc39.es/ecma262/#table-additional-state-components-for-ecmascript-code-execution-contexts). In other words, they define the scope of `var` declarations. FunctionParent did not include `Program` since Babel 7.",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Based on the definition, I think it should also cover StaticBlock, which does define a new var scope. Note that in Babel 6 FunctionParent includes Program, but we removed Program in #5923 because the name FunctionParent strongly indicates that it should always be a Function, which is not the case for StaticBlock and Program.

Currently FunctionParent and Function are equivalent, they include exactly the same AST nodes.

BTW we have several usage of scope.getFunctionParent() || scope.getProgramParent(). This depends on the assumption that an AST tree can only have one Program node and all the function node must placed within that program node. This is not true for module-blocks proposal.

Copy link
Member

Choose a reason for hiding this comment

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

In Babel 8 we might just rename them to LexicalScope and VariableScope, where VariableScope is a subset of LexicalScope.

This is not true for module-blocks proposal.

Can you show an example where scope.getFunctionParent() || scope.getProgramParent() would break with the module-blocks proposal? When I proposed to use Program for the ModuleExpression node it was exactly to support this existing pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah I missed that module blocks can only be at the top level.

Copy link
Member

Choose a reason for hiding this comment

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

No, they can also be nested, but we'll want to consider "the top level" the module block itself.

For example, if we have

let mod = module {
  class A {}
};

We want to get this:

let mod = module {
  function _classCallCheck() {}
  let A = function A() {
    _classCallCheck(this);
  };
};

and not this:

function _classCallCheck() {}
let mod = module {
  let A = function A() {
    _classCallCheck(this);
  };
};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I just checked module blocks proposal and you are right they can be nested.

Can you show an example where scope.getFunctionParent() || scope.getProgramParent() would break with the module-blocks proposal

function f() {
  (module {
    var x;
  })
}

scope.getFunctionParent() || scope.getProgramParent() on var x will return function f instead of the module block.

Copy link
Member

Choose a reason for hiding this comment

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

Oh right, thanks

Immutable:
"A cover of immutable objects and JSX elements. An object is [immutable](https://tc39.es/ecma262/#immutable-prototype-exotic-object) if no other properties can be defined once created.",
JSX:
"A cover of AST nodes defined for [JSX](https://facebook.github.io/jsx/).",
LVal:
"A cover of left hand side expressions used in the `left` of assignment expressions and [ForXStatement](#forxstatement)s. ",
Literal:
"A cover of [Literal](https://tc39.es/ecma262/#sec-primary-expression-literals)s, [Regular Expression Literal](https://tc39.es/ecma262/#sec-primary-expression-regular-expression-literals)s and [Template Literal](https://tc39.es/ecma262/#sec-template-literals)s.",
Loop: "A cover of loop statements.",
Method: "A cover of object methods and class methods.",
ModuleDeclaration:
"A cover of ImportDeclaration and [ExportDeclaration](#exportdeclaration)",
ModuleSpecifier:
"A cover of import and export specifiers. Note: It is _not_ the [ModuleSpecifier](https://tc39.es/ecma262/#prod-ModuleSpecifier) defined in the spec.",
ObjectMember:
"A cover of [members](https://tc39.es/ecma262/#prod-PropertyDefinitionList) in an object literal.",
Pattern:
"A cover of [BindingPattern](https://tc39.es/ecma262/#prod-BindingPattern) except Identifiers.",
PatternLike:
"A cover of [BindingPattern](https://tc39.es/ecma262/#prod-BindingPattern)s. ",
Private: "A cover of private class elements and private identifiers.",
Property: "A cover of object properties and class properties.",
Pureish:
"A cover of AST nodes which do not have side-effects. In other words, there is no observable behaviour changes if they are evaluated more than once.",
Scopable:
"A cover of [FunctionParent](#functionparent) and [BlockParent](#blockparent).",
Statement:
"A cover of any [Statement](https://tc39.es/ecma262/#prod-Statement)s.",
TSBaseType: "A cover of primary TypeScript type annotations.",
TSEntityName: "A cover of ts entities.",
TSType: "A cover of TypeScript type annotations.",
TSTypeElement: "A cover of TypeScript type declarations.",
Terminatorless:
"A cover of AST nodes whose semantic will change when a line terminator is inserted between the operator and the operand.",
Copy link
Member

Choose a reason for hiding this comment

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

This description isn't true for await and throw, but I don't know what a better description would be 🤷

Copy link
Contributor Author

@JLHwung JLHwung Apr 14, 2021

Choose a reason for hiding this comment

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

I can't find the usage in our codebase. The term terminatorless is referenced in

/**
* Set some state that will be modified if a newline has been inserted before any
* non-space characters.
*
* This is to prevent breaking semantics for terminatorless separator nodes. eg:
*
* return foo;
*
* returns `foo`. But if we do:
*
* return
* foo;
*
* `undefined` will be returned and not `foo` due to the terminator.
*/
startTerminatorless(isLabel: boolean = false): any {

Based on that I don't think ThrowStatement and AwaitExpression should belong to TerminatorLess, we can also consider just remove it.

UnaryLike: "A cover of UnaryExpression and SpreadElement.",
UserWhitespacable: "Deprecated. Will be removed in Babel 8.",
While:
"A cover of DoWhileStatement and WhileStatement, which share the same AST shape.",
};
const mapAliasToNodeTypes = generateMapAliasToNodeTypes();
readme.push("### Aliases");
readme.push("");
for (const alias of [...mapAliasToNodeTypes.keys()].sort()) {
const nodeTypes = mapAliasToNodeTypes.get(alias);
nodeTypes.sort();
if (!(alias in aliasDescriptions)) {
throw new Error(
'Missing alias descriptions of "' +
alias +
", which covers " +
nodeTypes.join(",")
);
}
readme.push("#### " + alias);
readme.push("");
readme.push(aliasDescriptions[alias]);
readme.push("```javascript");
readme.push("t.is" + alias + "(node);");
readme.push("```");
readme.push("");
readme.push("Covered nodes: ");
for (const nodeType of nodeTypes) {
readme.push("- [`" + nodeType + "`](#" + nodeType.toLowerCase() + ")");
}
readme.push("");
}

process.stdout.write(readme.join("\n"));