diff --git a/lib/fast-path.ts b/lib/fast-path.ts index cfe181e7..d6fa4e7b 100644 --- a/lib/fast-path.ts +++ b/lib/fast-path.ts @@ -324,9 +324,6 @@ FPp.needsParens = function (assumeExpressionContext) { } const parent = this.getParentNode(); - if (!parent) { - return false; - } const name = this.getName(); @@ -347,13 +344,16 @@ FPp.needsParens = function (assumeExpressionContext) { return false; } - if ( - parent.type === "ParenthesizedExpression" || - (node.extra && node.extra.parenthesized) - ) { + if (parent && parent.type === "ParenthesizedExpression") { return false; } + if (node.extra && node.extra.parenthesized) { + return true; + } + + if (!parent) return false; + switch (node.type) { case "UnaryExpression": case "SpreadElement": diff --git a/lib/printer.ts b/lib/printer.ts index 97bc56f2..a53f1e76 100644 --- a/lib/printer.ts +++ b/lib/printer.ts @@ -196,14 +196,14 @@ function genericPrint(path: any, config: any, options: any, printPath: any) { return linesWithoutParens; } - let shouldAddParens = node.extra ? node.extra.parenthesized : false; + let shouldAddParens = false; const decoratorsLines = printDecorators(path, printPath); if (decoratorsLines.isEmpty()) { // Nodes with decorators can't have parentheses, so we can avoid // computing path.needsParens() except in this case. if (!options.avoidRootParens) { - shouldAddParens = shouldAddParens || path.needsParens(); + shouldAddParens = path.needsParens(); } } else { parts.push(decoratorsLines); diff --git a/parsers/babel-ts.ts b/parsers/babel-ts.ts new file mode 100644 index 00000000..34c22c4c --- /dev/null +++ b/parsers/babel-ts.ts @@ -0,0 +1,10 @@ +import { parser } from "./babel"; +import getBabelOptions, { Overrides } from "./_babel_options"; + +export { parser }; + +export function parse(source: string, options?: Overrides) { + const babelOptions = getBabelOptions(options); + babelOptions.plugins.push("jsx", "typescript"); + return parser.parse(source, babelOptions); +} diff --git a/test/parens-babylon.ts b/test/parens-extra.ts similarity index 67% rename from test/parens-babylon.ts rename to test/parens-extra.ts index 0abdc8e3..86c49553 100644 --- a/test/parens-babylon.ts +++ b/test/parens-extra.ts @@ -1,7 +1,9 @@ import assert from "assert"; +// the babel parser denotes decorative parens with extra.parenthesized import * as babylon from "@babel/parser"; import { parse as recastParse } from "../lib/parser"; import { Printer } from "../lib/printer"; +import * as parser from "../parsers/babel-ts"; import * as types from "ast-types"; const printer = new Printer(); @@ -12,23 +14,20 @@ function parseExpression(expr: any) { return n.ExpressionStatement.check(ast) ? ast.expression : ast; } -const parse = (expr: string) => - recastParse(expr, { - parser: babylon, - }); +const parse = (expr: string) => recastParse(expr, { parser }); function check(expr: string) { const ast = parse(expr); const reprinted = printer.print(ast).code; - assert.strictEqual(reprinted, expr); + assert.strictEqual(expr, reprinted); const expressionAst = parseExpression(expr); const generic = printer.printGenerically(expressionAst).code; types.astNodesAreEquivalent.assert(expressionAst, parseExpression(generic)); } -describe("babylon parens", function () { +describe("parens from node.extra.parenthesized", function () { it("AwaitExpression", function () { check("async () => ({...(await obj)})"); check("(async function* () { yield await foo })"); @@ -55,4 +54,20 @@ describe("babylon parens", function () { assert.strictEqual(printer.print(ast).code, "(1).foo"); }); + + it("prints top level parens for an expression ast", function () { + check("(() => {})()"); + check("(function () {} ())"); + }); + + describe("reprinter", function () { + it("preserves necessary parens", function () { + const ast = parse("() => ({ prop: true })"); + const expr = ast.program.body[0].expression; + + expr.body.properties = []; + + assert.strictEqual(printer.print(ast).code, "() => ({})"); + }); + }); }); diff --git a/test/run.ts b/test/run.ts index 3dfebe48..39534ed8 100644 --- a/test/run.ts +++ b/test/run.ts @@ -5,6 +5,7 @@ import "./identity"; import "./jsx"; import "./lines"; import "./mapping"; +import "./parens-extra"; import "./parens"; import "./parser"; import "./patcher"; diff --git a/test/typescript.ts b/test/typescript.ts index 11ca02f7..54b22668 100644 --- a/test/typescript.ts +++ b/test/typescript.ts @@ -5,10 +5,6 @@ import * as recast from "../main"; import * as types from "ast-types"; import { EOL as eol } from "os"; import * as parser from "../parsers/typescript"; -import { Printer } from "../lib/printer"; - -const { namedTypes: n } = types; -const printer = new Printer(); // Babel 7 no longer supports Node 4 or 5. const nodeMajorVersion = parseInt(process.versions.node, 10); @@ -311,34 +307,6 @@ const nodeMajorVersion = parseInt(process.versions.node, 10); "type Alpha = Color.a;", ]); }); - - it("parens", function () { - function parseExpression(expr: any) { - let ast: any = parser.parse(expr).program; - n.Program.assert(ast); - ast = ast.body[0]; - return n.ExpressionStatement.check(ast) ? ast.expression : ast; - } - - const parse = (expr: string) => recast.parse(expr, { parser }); - - function check(expr: string) { - const ast = parse(expr); - - const reprinted = recast.print(ast).code; - assert.strictEqual(reprinted, expr); - - const expressionAst = parseExpression(expr); - const generic = printer.printGenerically(expressionAst).code; - types.astNodesAreEquivalent.assert( - expressionAst, - parseExpression(generic), - ); - } - - check("(() => {}) as void"); - check("(function () {} as void)"); - }); }); testReprinting(