Skip to content

Commit

Permalink
Improve @babel/types typings (#14645)
Browse files Browse the repository at this point in the history
* bump to-fast-properties to v4

* types

* mark node comments as mutable
  • Loading branch information
JLHwung committed Jun 20, 2022
1 parent 2d9f409 commit 3ea406d
Show file tree
Hide file tree
Showing 35 changed files with 313 additions and 184 deletions.
2 changes: 1 addition & 1 deletion packages/babel-types/package.json
Expand Up @@ -25,7 +25,7 @@
},
"dependencies": {
"@babel/helper-validator-identifier": "workspace:^",
"to-fast-properties": "^2.0.0"
"to-fast-properties": "condition:BABEL_8_BREAKING ? ^4.0.0 : ^2.0.0"
},
"devDependencies": {
"@babel/generator": "workspace:^",
Expand Down
6 changes: 3 additions & 3 deletions packages/babel-types/scripts/generators/ast-types.js
Expand Up @@ -37,9 +37,9 @@ export interface SourceLocation {
interface BaseNode {
type: Node["type"];
leadingComments?: ReadonlyArray<Comment> | null;
innerComments?: ReadonlyArray<Comment> | null;
trailingComments?: ReadonlyArray<Comment> | null;
leadingComments?: Comment[] | null;
innerComments?: Comment[] | null;
trailingComments?: Comment[] | null;
start?: number | null;
end?: number | null;
loc?: SourceLocation | null;
Expand Down
6 changes: 3 additions & 3 deletions packages/babel-types/src/ast-types/generated/index.ts
Expand Up @@ -33,9 +33,9 @@ export interface SourceLocation {

interface BaseNode {
type: Node["type"];
leadingComments?: ReadonlyArray<Comment> | null;
innerComments?: ReadonlyArray<Comment> | null;
trailingComments?: ReadonlyArray<Comment> | null;
leadingComments?: Comment[] | null;
innerComments?: Comment[] | null;
trailingComments?: Comment[] | null;
start?: number | null;
end?: number | null;
loc?: SourceLocation | null;
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-types/src/builders/validateNode.ts
Expand Up @@ -4,7 +4,7 @@ import { BUILDER_KEYS } from "..";

export default function validateNode<N extends t.Node>(node: N) {
// todo: because keys not in BUILDER_KEYS are not validated - this actually allows invalid nodes in some cases
const keys = BUILDER_KEYS[node.type];
const keys = BUILDER_KEYS[node.type] as (keyof N & string)[];
for (const key of keys) {
validate(node, key, node[key]);
}
Expand Down
23 changes: 19 additions & 4 deletions packages/babel-types/src/clone/cloneNode.ts
Expand Up @@ -4,16 +4,28 @@ import { isFile, isIdentifier } from "../validators/generated";

const has = Function.call.bind(Object.prototype.hasOwnProperty);

type CommentCache = Map<t.Comment, t.Comment>;

// This function will never be called for comments, only for real nodes.
function cloneIfNode(obj, deep, withoutLoc, commentsCache) {
function cloneIfNode(
obj: t.Node | undefined | null,
deep: boolean,
withoutLoc: boolean,
commentsCache: CommentCache,
) {
if (obj && typeof obj.type === "string") {
return cloneNodeInternal(obj, deep, withoutLoc, commentsCache);
}

return obj;
}

function cloneIfNodeOrArray(obj, deep, withoutLoc, commentsCache) {
function cloneIfNodeOrArray(
obj: t.Node | undefined | null | (t.Node | undefined | null)[],
deep: boolean,
withoutLoc: boolean,
commentsCache: CommentCache,
) {
if (Array.isArray(obj)) {
return obj.map(node => cloneIfNode(node, deep, withoutLoc, commentsCache));
}
Expand All @@ -37,7 +49,7 @@ function cloneNodeInternal<T extends t.Node>(
node: T,
deep: boolean = true,
withoutLoc: boolean = false,
commentsCache: Map<t.Comment, t.Comment>,
commentsCache: CommentCache,
): T {
if (!node) return node;

Expand Down Expand Up @@ -77,13 +89,16 @@ function cloneNodeInternal<T extends t.Node>(
commentsCache,
)
: cloneIfNodeOrArray(
// @ts-expect-error node[field] has been guarded by has check
node[field],
true,
withoutLoc,
commentsCache,
);
} else {
newNode[field] = node[field];
newNode[field] =
// @ts-expect-error node[field] has been guarded by has check
node[field];
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-types/src/comments/addComments.ts
Expand Up @@ -6,11 +6,11 @@ import type * as t from "..";
export default function addComments<T extends t.Node>(
node: T,
type: t.CommentTypeShorthand,
comments: ReadonlyArray<t.Comment>,
comments: Array<t.Comment>,
): T {
if (!comments || !node) return node;

const key = `${type}Comments`;
const key = `${type}Comments` as const;

if (node[key]) {
if (type === "leading") {
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-types/src/constants/index.ts
Expand Up @@ -5,7 +5,7 @@ export const COMMENT_KEYS = [
"leadingComments",
"trailingComments",
"innerComments",
];
] as const;

export const LOGICAL_OPERATORS = ["||", "&&", "??"];
export const UPDATE_OPERATORS = ["++", "--"];
Expand Down Expand Up @@ -62,7 +62,7 @@ export const UNARY_OPERATORS = [
export const INHERIT_KEYS = {
optional: ["typeAnnotation", "typeParameters", "returnType"],
force: ["start", "loc", "end"],
};
} as const;

export const BLOCK_SCOPED_SYMBOL = Symbol.for("var used to be block scoped");
export const NOT_LOCAL_BINDING = Symbol.for(
Expand Down
8 changes: 0 additions & 8 deletions packages/babel-types/src/converters/Scope.ts

This file was deleted.

6 changes: 5 additions & 1 deletion packages/babel-types/src/converters/ensureBlock.ts
Expand Up @@ -11,5 +11,9 @@ export default function ensureBlock(
node: t.Node,
key: string = "body",
): t.BlockStatement {
return (node[key] = toBlock(node[key], node));
// @ts-ignore Fixme: key may not exist in node, consider remove key = "body"
const result = toBlock(node[key], node);
// @ts-ignore
node[key] = result;
return result;
}
14 changes: 9 additions & 5 deletions packages/babel-types/src/converters/gatherSequenceExpressions.ts
Expand Up @@ -14,14 +14,19 @@ import {
} from "../builders/generated";
import cloneNode from "../clone/cloneNode";
import type * as t from "..";
import type { Scope } from "./Scope";
import type { Scope } from "@babel/traverse";

export type DeclarationInfo = {
kind: t.VariableDeclaration["kind"];
id: t.Identifier;
};

export default function gatherSequenceExpressions(
nodes: ReadonlyArray<t.Node>,
scope: Scope,
declars: Array<any>,
): t.SequenceExpression {
const exprs = [];
declars: Array<DeclarationInfo>,
) {
const exprs: t.Expression[] = [];
let ensureLastUndefined = true;

for (const node of nodes) {
Expand Down Expand Up @@ -62,7 +67,6 @@ export default function gatherSequenceExpressions(
: scope.buildUndefinedNode();
if (!consequent || !alternate) return; // bailed

// @ts-expect-error todo(flow->ts) consequent - Argument of type 'Node' is not assignable to parameter of type 'Expression'
exprs.push(conditionalExpression(node.test, consequent, alternate));
} else if (isBlockStatement(node)) {
const body = gatherSequenceExpressions(node.body, scope, declars);
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-types/src/converters/toBlock.ts
Expand Up @@ -19,7 +19,7 @@ export default function toBlock(
return node;
}

let blockNodes = [];
let blockNodes: t.Statement[] = [];

if (isEmptyStatement(node)) {
blockNodes = [];
Expand Down
6 changes: 4 additions & 2 deletions packages/babel-types/src/converters/toSequenceExpression.ts
@@ -1,6 +1,7 @@
import gatherSequenceExpressions from "./gatherSequenceExpressions";
import type * as t from "..";
import type { Scope } from "./Scope";
import type { Scope } from "@babel/traverse";
import type { DeclarationInfo } from "./gatherSequenceExpressions";

/**
* Turn an array of statement `nodes` into a `SequenceExpression`.
Expand All @@ -16,13 +17,14 @@ export default function toSequenceExpression(
): t.SequenceExpression | undefined {
if (!nodes?.length) return;

const declars = [];
const declars: DeclarationInfo[] = [];
const result = gatherSequenceExpressions(nodes, scope, declars);
if (!result) return;

for (const declar of declars) {
scope.push(declar);
}

// @ts-ignore fixme: gatherSequenceExpressions will return an Expression when there are only one element
return result;
}
7 changes: 4 additions & 3 deletions packages/babel-types/src/converters/toStatement.ts
Expand Up @@ -33,17 +33,17 @@ function toStatement(node: t.Node, ignore?: boolean): t.Statement | false {

if (isClass(node)) {
mustHaveId = true;
newType = "ClassDeclaration";
newType = "ClassDeclaration" as const;
} else if (isFunction(node)) {
mustHaveId = true;
newType = "FunctionDeclaration";
newType = "FunctionDeclaration" as const;
} else if (isAssignmentExpression(node)) {
return expressionStatement(node);
}

// @ts-expect-error todo(flow->ts): node.id might be missing
if (mustHaveId && !node.id) {
newType = false;
newType = false as false;
}

if (!newType) {
Expand All @@ -54,6 +54,7 @@ function toStatement(node: t.Node, ignore?: boolean): t.Statement | false {
}
}

// @ts-expect-error manipulating node.type
node.type = newType;

// @ts-expect-error todo(flow->ts) refactor to avoid type unsafe mutations like reassigning node type above
Expand Down
16 changes: 12 additions & 4 deletions packages/babel-types/src/converters/valueToNode.ts
Expand Up @@ -31,15 +31,15 @@ export default valueToNode as {
(value: unknown): t.Expression;
};

const objectToString: (value: object) => string = Function.call.bind(
const objectToString: (value: unknown) => string = Function.call.bind(
Object.prototype.toString,
);

function isRegExp(value): value is RegExp {
function isRegExp(value: unknown): value is RegExp {
return objectToString(value) === "[object RegExp]";
}

function isPlainObject(value): value is object {
function isPlainObject(value: unknown): value is object {
if (
typeof value !== "object" ||
value === null ||
Expand Down Expand Up @@ -122,7 +122,15 @@ function valueToNode(value: unknown): t.Expression {
} else {
nodeKey = stringLiteral(key);
}
props.push(objectProperty(nodeKey, valueToNode(value[key])));
props.push(
objectProperty(
nodeKey,
valueToNode(
// @ts-expect-error key must present in value
value[key],
),
),
);
}
return objectExpression(props);
}
Expand Down

0 comments on commit 3ea406d

Please sign in to comment.