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

feat: support passing a fork of ast-types #1285

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
64 changes: 45 additions & 19 deletions lib/comments.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import assert from "assert";
import * as types from "ast-types";
const n = types.namedTypes;
const isArray = types.builtInTypes.array;
const isObject = types.builtInTypes.object;
import * as AstTypes from "ast-types";
import { Lines, concat } from "./lines";
import { comparePos, fixFaultyLocations } from "./util";
import { FastPathType } from "./fast-path";

type Node = types.namedTypes.Node;
type Comment = types.namedTypes.Comment;
type Node = AstTypes.namedTypes.Node;
type Comment = AstTypes.namedTypes.Comment;

const childNodesCache = new WeakMap<Node, Node[]>();

// TODO Move a non-caching implementation of this function into ast-types,
// and implement a caching wrapper function here.
function getSortedChildNodes(node: Node, lines: Lines, resultArray?: Node[]) {
function getSortedChildNodes(
types: typeof AstTypes,
node: Node,
lines: Lines,
resultArray?: Node[],
) {
const {
namedTypes: n,
builtInTypes: { array: isArray, object: isObject },
} = types;
if (!node) {
return resultArray;
}
Expand All @@ -22,7 +29,7 @@ function getSortedChildNodes(node: Node, lines: Lines, resultArray?: Node[]) {
// are fixed by this utility function. Specifically, if it decides to
// set node.loc to null, indicating that the node's .loc information
// is unreliable, then we don't want to add node to the resultArray.
fixFaultyLocations(node, lines);
fixFaultyLocations(types, node, lines);

if (resultArray) {
if (n.Node.check(node) && n.SourceLocation.check(node.loc)) {
Expand Down Expand Up @@ -64,7 +71,7 @@ function getSortedChildNodes(node: Node, lines: Lines, resultArray?: Node[]) {
}

for (let i = 0, nameCount = names.length; i < nameCount; ++i) {
getSortedChildNodes((node as any)[names[i]], lines, resultArray);
getSortedChildNodes(types, (node as any)[names[i]], lines, resultArray);
}

return resultArray;
Expand All @@ -73,8 +80,13 @@ function getSortedChildNodes(node: Node, lines: Lines, resultArray?: Node[]) {
// As efficiently as possible, decorate the comment object with
// .precedingNode, .enclosingNode, and/or .followingNode properties, at
// least one of which is guaranteed to be defined.
function decorateComment(node: Node, comment: Comment, lines: Lines) {
const childNodes = getSortedChildNodes(node, lines);
function decorateComment(
types: typeof AstTypes,
node: Node,
comment: Comment,
lines: Lines,
) {
const childNodes = getSortedChildNodes(types, node, lines);

// Time to dust off the old binary search robes and wizard hat.
let left = 0;
Expand All @@ -91,7 +103,12 @@ function decorateComment(node: Node, comment: Comment, lines: Lines) {
comparePos(comment.loc!.end, child.loc!.end) <= 0
) {
// The comment is completely contained by this child node.
decorateComment(((comment as any).enclosingNode = child), comment, lines);
decorateComment(
types,
((comment as any).enclosingNode = child),
comment,
lines,
);
return; // Abandon the binary search at this level.
}

Expand Down Expand Up @@ -127,16 +144,21 @@ function decorateComment(node: Node, comment: Comment, lines: Lines) {
}
}

export function attach(comments: any[], ast: any, lines: any) {
if (!isArray.check(comments)) {
export function attach(
types: typeof AstTypes,
comments: any[],
ast: any,
lines: any,
) {
if (!types.builtInTypes.array.check(comments)) {
return;
}

const tiesToBreak: any[] = [];

comments.forEach(function (comment) {
comment.loc.lines = lines;
decorateComment(ast, comment, lines);
decorateComment(types, ast, comment, lines);

const pn = comment.precedingNode;
const en = comment.enclosingNode;
Expand Down Expand Up @@ -263,7 +285,8 @@ function addTrailingComment(node: any, comment: any) {
addCommentHelper(node, comment);
}

function printLeadingComment(commentPath: any, print: any) {
function printLeadingComment(commentPath: FastPathType, print: any) {
const { namedTypes: n } = commentPath.types;
const comment = commentPath.getValue();
n.Comment.assert(comment);

Expand Down Expand Up @@ -297,8 +320,9 @@ function printLeadingComment(commentPath: any, print: any) {
return concat(parts);
}

function printTrailingComment(commentPath: any, print: any) {
const comment = commentPath.getValue(commentPath);
function printTrailingComment(commentPath: FastPathType, print: any) {
const { namedTypes: n } = commentPath.types;
const comment = commentPath.getValue();
n.Comment.assert(comment);

const loc = comment.loc;
Expand All @@ -325,7 +349,9 @@ function printTrailingComment(commentPath: any, print: any) {
return concat(parts);
}

export function printComments(path: any, print: any) {
export function printComments(path: FastPathType, print: any) {
const { types } = path;
const { namedTypes: n } = types;
const value = path.getValue();
const innerLines = print(path);
const comments =
Expand Down
126 changes: 87 additions & 39 deletions lib/fast-path.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import assert from "assert";
import * as types from "ast-types";
import * as AstTypes from "ast-types";
import * as util from "./util";

const n = types.namedTypes;
const isArray = types.builtInTypes.array;
const isNumber = types.builtInTypes.number;

const PRECEDENCE: any = {};
[
["??"],
Expand All @@ -26,7 +22,10 @@ const PRECEDENCE: any = {};
});
});

interface FastPathType {
export declare class FastPathType {
constructor(value: any);
static from(obj: any): FastPathType;
get types(): typeof AstTypes;
stack: any[];
copy(): any;
getName(): any;
Expand All @@ -46,44 +45,39 @@ interface FastPathType {
firstInStatement(): any;
}

interface FastPathConstructor {
export interface FastPathConstructor {
new (value: any): FastPathType;
from(obj: any): any;
from(obj: any): FastPathType;
}

const FastPath = function FastPath(this: FastPathType, value: any) {
assert.ok(this instanceof FastPath);
assert.ok(this.types);
this.stack = [value];
} as any as FastPathConstructor;

const FPp: FastPathType = FastPath.prototype;
export { FastPath as BaseFastPath };

// Static convenience function for coercing a value to a FastPath.
FastPath.from = function (obj) {
if (obj instanceof FastPath) {
// Return a defensive copy of any existing FastPath instances.
return obj.copy();
}
FastPath.from = function () {
throw new Error("must be called in derived class from bindFastPath");
};

if (obj instanceof types.NodePath) {
// For backwards compatibility, unroll NodePath instances into
// lightweight FastPath [..., name, value] stacks.
const copy = Object.create(FastPath.prototype);
const stack = [obj.value];
for (let pp; (pp = obj.parentPath); obj = pp)
stack.push(obj.name, pp.value);
copy.stack = stack.reverse();
return copy;
}
const FPp: FastPathType = FastPath.prototype;

// Otherwise use obj as the value of the new FastPath instance.
return new FastPath(obj);
};
Object.defineProperties(FPp, {
types: {
get() {
throw new Error(`must be gotten in derived class from bindFastPath`);
},
},
});

FPp.copy = function copy() {
const copy = Object.create(FastPath.prototype);
copy.stack = this.stack.slice(0);
return copy;
throw new Error(`must be called from derived class from bindFastPath`);
// const copy = Object.create(FastPath.prototype);
// copy.stack = this.stack.slice(0);
// return copy;
};

// The name of the current property is always the penultimate element of
Expand Down Expand Up @@ -117,7 +111,7 @@ function getNodeHelper(path: any, count: number) {

for (let i = s.length - 1; i >= 0; i -= 2) {
const value = s[i];
if (n.Node.check(value) && --count < 0) {
if (path.types.namedTypes.Node.check(value) && --count < 0) {
return value;
}
}
Expand Down Expand Up @@ -314,6 +308,9 @@ FPp.getNextToken = function (node) {
FPp.needsParens = function (assumeExpressionContext) {
const node = this.getNode();

const n = this.types.namedTypes;
const isNumber = this.types.builtInTypes.number;

// This needs to come before `if (!parent) { return false }` because
// an object destructuring assignment requires parens for
// correctness even when it's the topmost expression.
Expand Down Expand Up @@ -499,7 +496,7 @@ FPp.needsParens = function (assumeExpressionContext) {
return true;
}

return isBinary(parent);
return isBinary(this.types, parent);

case "ObjectExpression":
if (
Expand Down Expand Up @@ -538,7 +535,7 @@ FPp.needsParens = function (assumeExpressionContext) {
name === "callee" &&
parent.callee === node
) {
return containsCallExpression(node);
return containsCallExpression(this.types, node);
}

if (
Expand All @@ -552,12 +549,14 @@ FPp.needsParens = function (assumeExpressionContext) {
return false;
};

function isBinary(node: any) {
function isBinary(types: typeof AstTypes, node: any) {
const n = types.namedTypes;
return n.BinaryExpression.check(node) || n.LogicalExpression.check(node);
}

// @ts-ignore 'isUnaryLike' is declared but its value is never read. [6133]
function isUnaryLike(node: any) {
function isUnaryLike(types: typeof AstTypes, node: any) {
const n = types.namedTypes;
return (
n.UnaryExpression.check(node) ||
// I considered making SpreadElement and SpreadProperty subtypes of
Expand All @@ -567,7 +566,10 @@ function isUnaryLike(node: any) {
);
}

function containsCallExpression(node: any): any {
function containsCallExpression(types: typeof AstTypes, node: any): any {
const n = types.namedTypes;
const isArray = types.builtInTypes.array;

if (n.CallExpression.check(node)) {
return true;
}
Expand All @@ -578,7 +580,7 @@ function containsCallExpression(node: any): any {

if (n.Node.check(node)) {
return types.someField(node, (_name: any, child: any) =>
containsCallExpression(child),
containsCallExpression(types, child),
);
}

Expand All @@ -587,6 +589,7 @@ function containsCallExpression(node: any): any {

FPp.canBeFirstInStatement = function () {
const node = this.getNode();
const n = this.types.namedTypes;

if (n.FunctionExpression.check(node)) {
return false;
Expand All @@ -604,6 +607,7 @@ FPp.canBeFirstInStatement = function () {
};

FPp.firstInStatement = function () {
const n = this.types.namedTypes;
const s = this.stack;
let parentName, parent;
let childName, child;
Expand Down Expand Up @@ -670,7 +674,7 @@ FPp.firstInStatement = function () {
continue;
}

if (isBinary(parent) && childName === "left") {
if (isBinary(this.types, parent) && childName === "left") {
assert.strictEqual(parent.left, child);
continue;
}
Expand All @@ -690,4 +694,48 @@ FPp.firstInStatement = function () {
return true;
};

export default FastPath;
const boundFastPaths: WeakMap<typeof AstTypes, FastPathConstructor> =
new WeakMap();

export const bindFastPath = function bindFastPath(
types: typeof AstTypes,
): FastPathConstructor {
let bound = boundFastPaths.get(types);
if (bound) return bound;
bound = class FastPathWithTypes extends FastPath {
get types(): typeof AstTypes {
return types;
}

static from(obj: any): FastPathWithTypes {
if (obj instanceof FastPath) {
// Return a defensive copy of any existing FastPath instances.
return obj.copy();
}

if (obj instanceof types.NodePath) {
// For backwards compatibility, unroll NodePath instances into
// lightweight FastPath [..., name, value] stacks.
const copy = Object.create(FastPath.prototype);
const stack = [obj.value];
for (let pp; (pp = obj.parentPath); obj = pp)
stack.push(obj.name, pp.value);
copy.stack = stack.reverse();
return copy;
}

// Otherwise use obj as the value of the new FastPath instance.
return new FastPathWithTypes(obj);
}

copy() {
const copy = Object.create(FastPathWithTypes.prototype);
copy.stack = this.stack.slice(0);
return copy;
}
};
boundFastPaths.set(types, bound);
return bound;
};

export default bindFastPath(AstTypes);
5 changes: 2 additions & 3 deletions lib/lines.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import assert from "assert";
import sourceMap from "source-map";
import sourceMap, { Position } from "source-map";
import { normalize as normalizeOptions, Options } from "./options";
import { namedTypes } from "ast-types";
import { comparePos } from "./util";
import Mapping from "./mapping";

type Pos = namedTypes.Position;
type Pos = Position;

// Goals:
// 1. Minimize new string creation.
Expand Down
Loading