Skip to content

Commit

Permalink
chore: provide better parentPath typings
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Jul 7, 2022
1 parent e1d47f5 commit 6f18348
Show file tree
Hide file tree
Showing 9 changed files with 5,844 additions and 16 deletions.
Expand Up @@ -122,7 +122,7 @@ export function extractComputedKeys(
};
for (const computedPath of computedPaths) {
const computedKey = computedPath.get("key");
if (computedKey.isReferencedIdentifier()) {
if (computedKey.isIdentifier() && computedKey.isReferencedIdentifier()) {
handleClassTDZ(computedKey, state);
} else {
computedKey.traverse(classFieldDefinitionEvaluationTDZVisitor, state);
Expand Down
Expand Up @@ -49,7 +49,10 @@ export default declare(({ types: t, template, assertVersion }) => {
const body = classBody.get("body");
for (const path of body) {
if (path.isPrivate()) {
privateNames.add(path.get("key.id").node.name);
privateNames.add(
// @ts-expect-error TS can't infer that NodePath<t.ClassBody[body][number]> & NodePath<t.Private> equals to NodePath<t.ClassBody[body][number] & isPrivate>
path.get("key.id").node.name,
);
}
}
for (const path of body) {
Expand Down
4 changes: 3 additions & 1 deletion packages/babel-plugin-transform-block-scoping/src/index.ts
Expand Up @@ -113,7 +113,9 @@ export default declare((api, opts: Options) => {
};
});

function ignoreBlock(path: NodePath) {
function ignoreBlock(
path: NodePath<t.BlockStatement | t.SwitchStatement | t.Program>,
) {
return t.isLoop(path.parent) || t.isCatchClause(path.parent);
}

Expand Down
12 changes: 5 additions & 7 deletions packages/babel-plugin-transform-classes/src/index.ts
Expand Up @@ -5,7 +5,6 @@ import splitExportDeclaration from "@babel/helper-split-export-declaration";
import { types as t } from "@babel/core";
import globals from "globals";
import transformClass from "./transformClass";
import type { Visitor, NodePath } from "@babel/traverse";

const getBuiltinClasses = (category: keyof typeof globals) =>
Object.keys(globals[category]).filter(name => /^[A-Z]/.test(name));
Expand Down Expand Up @@ -68,7 +67,7 @@ export default declare((api, options: Options) => {

VISITED.add(node);

path.replaceWith(
const [replacedPath] = path.replaceWith(
transformClass(path, state.file, builtinClasses, loose, {
setClassMethods,
constantSuper,
Expand All @@ -77,16 +76,15 @@ export default declare((api, options: Options) => {
}),
);

if (path.isCallExpression()) {
annotateAsPure(path);
// todo: improve babel types
const callee = path.get("callee") as unknown as NodePath;
if (replacedPath.isCallExpression()) {
annotateAsPure(replacedPath);
const callee = replacedPath.get("callee");
if (callee.isArrowFunctionExpression()) {
// This is an IIFE, so we don't need to worry about the noNewArrows assumption
callee.arrowFunctionToExpression();
}
}
},
} as Visitor<any>,
},
};
});
5 changes: 4 additions & 1 deletion packages/babel-plugin-transform-typescript/src/enum.ts
Expand Up @@ -156,7 +156,10 @@ export function translateEnumValues(
} else {
const initializerPath = memberPath.get("initializer");

if (initializerPath.isReferencedIdentifier()) {
if (
initializerPath.isIdentifier() &&
initializerPath.isReferencedIdentifier()
) {
ReferencedIdentifier(initializerPath, {
t,
seen,
Expand Down
8 changes: 5 additions & 3 deletions packages/babel-traverse/src/path/index.ts
Expand Up @@ -32,7 +32,7 @@ export const SHOULD_STOP = 1 << 1;
export const SHOULD_SKIP = 1 << 2;

class NodePath<T extends t.Node = t.Node> {
constructor(hub: HubInterface, parent: t.Node) {
constructor(hub: HubInterface, parent: t.ParentMaps[T["type"]]) {
this.parent = parent;
this.hub = hub;
this.data = null;
Expand All @@ -41,7 +41,7 @@ class NodePath<T extends t.Node = t.Node> {
this.scope = null;
}

declare parent: t.Node;
declare parent: t.ParentMaps[T["type"]];
declare hub: HubInterface;
declare data: Record<string | symbol, unknown>;
// TraversalContext is configured by setContext
Expand All @@ -54,7 +54,9 @@ class NodePath<T extends t.Node = t.Node> {
// this.shouldSkip = false; this.shouldStop = false; this.removed = false;
_traverseFlags: number = 0;
skipKeys: any = null;
parentPath: NodePath | null = null;
parentPath: t.ParentMaps[T["type"]] extends null
? null
: NodePath<t.ParentMaps[T["type"]]> | null = null;
container: t.Node | Array<t.Node> | null = null;
listKey: string | null = null;
key: string | number | null = null;
Expand Down
52 changes: 51 additions & 1 deletion packages/babel-types/scripts/generators/ast-types.js
@@ -1,5 +1,38 @@
import * as t from "../../lib/index.js";
import stringifyValidator from "../utils/stringifyValidator.js";
import stringifyValidator, {
isValueType,
} from "../utils/stringifyValidator.js";

const parentMaps = new Map([["File", new Set(["null"])]]);

function registerParentMaps(parent, nodes) {
for (const node of nodes) {
if (!parentMaps.has(node)) {
parentMaps.set(node, new Set());
}
parentMaps.get(node).add(parent);
}
}

function getNodeTypesFromValidator(validator) {
if (validator === undefined) return [];
if (validator.each) {
return getNodeTypesFromValidator(validator.each);
}
if (validator.chainOf) {
return getNodeTypesFromValidator(validator.chainOf[1]);
}
let nodeTypes = [];
if (validator.oneOfNodeTypes) {
nodeTypes = validator.oneOfNodeTypes;
}
if (validator.oneOfNodeOrValueTypes) {
nodeTypes = validator.oneOfNodeOrValueTypes.filter(
type => !isValueType(type)
);
}
return nodeTypes.flatMap(type => t.FLIPPED_ALIAS_KEYS[type] ?? type);
}

export default function generateAstTypes() {
let code = `// NOTE: This file is autogenerated. Do not modify.
Expand Down Expand Up @@ -87,6 +120,8 @@ export type Node = ${t.TYPES.filter(k => !t.FLIPPED_ALIAS_KEYS[k])
} else {
struct.push(`"${fieldName}"${optional}: ${typeAnnotation};`);
}

registerParentMaps(type, getNodeTypesFromValidator(field.validate));
});

code += `export interface ${type} extends BaseNode {
Expand Down Expand Up @@ -123,6 +158,21 @@ export interface ${deprecatedAlias[type]} extends BaseNode {
t.DEPRECATED_KEYS
).join(" | ")}\n\n`;

code += "export interface ParentMaps {\n";

registerParentMaps("null", [...Object.keys(t.DEPRECATED_KEYS)]);
// todo: provide a better parent type for Placeholder, currently it acts
// as a catch-all parent type for an abstract NodePath, s.t NodePath.parent must
// be a Node if type has not been specified
registerParentMaps("Node", ["Placeholder"]);

const parentMapsKeys = [...parentMaps.keys()].sort();
for (const type of parentMapsKeys) {
const deduplicated = [...parentMaps.get(type)].sort();
code += ` ${type}: ${deduplicated.join(" | ")};\n`;
}
code += "}\n\n";

return code;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/babel-types/scripts/utils/stringifyValidator.js
Expand Up @@ -61,6 +61,6 @@ export default function stringifyValidator(validator, nodePrefix) {
* Heuristic to decide whether or not the given type is a value type (eg. "null")
* or a Node type (eg. "Expression").
*/
function isValueType(type) {
export function isValueType(type) {
return type.charAt(0).toLowerCase() === type.charAt(0);
}

0 comments on commit 6f18348

Please sign in to comment.