From 780487577a94638c7cb89d6c73b42bb6339aa5f7 Mon Sep 17 00:00:00 2001 From: Brian Donovan <1938+eventualbuddha@users.noreply.github.com> Date: Tue, 13 Dec 2022 07:49:05 -0800 Subject: [PATCH] Implement pretty-printer case for ClassAccessorProperty. It's very similar to the one from Babel: https://github.com/babel/babel/blob/f5b52208f05157a348fdfaa0222c07a9a83fb101/packages/babel-generator/src/generators/classes.ts#L123-L166. However, `ast-types` doesn't seem to think a `definite` attribute exists on this node type so the type checking fails. --- lib/printer.ts | 42 ++++++++++++++++++++--- parsers/babel.ts | 2 +- test/babel.ts | 19 ++++++++++- test/printer.ts | 84 +++++++++++++++++++++++++++++++++++++++++++--- test/typescript.ts | 6 ++-- 5 files changed, 140 insertions(+), 13 deletions(-) diff --git a/lib/printer.ts b/lib/printer.ts index 1a5cd5d..36fc6dd 100644 --- a/lib/printer.ts +++ b/lib/printer.ts @@ -1,14 +1,14 @@ import assert from "assert"; +import * as types from "ast-types"; import { printComments } from "./comments"; -import { Lines, fromString, concat } from "./lines"; +import FastPath from "./fast-path"; +import { concat, fromString, Lines } from "./lines"; import { normalize as normalizeOptions } from "./options"; import { getReprinter } from "./patcher"; -import * as types from "ast-types"; +import * as util from "./util"; const namedTypes = types.namedTypes; const isString = types.builtInTypes.string; const isObject = types.builtInTypes.object; -import FastPath from "./fast-path"; -import * as util from "./util"; export interface PrintResultType { code: string; @@ -1470,6 +1470,40 @@ function genericPrintNoParens(path: any, options: any, print: any) { parts.push(";"); return concat(parts); + case "ClassAccessorProperty": { + if (n.static) { + parts.push("static "); + } + + parts.push("accessor "); + + if (n.computed) { + parts.push("[", path.call(print, "key"), "]"); + } else { + parts.push(path.call(print, "key")); + } + + if (n.optional) { + parts.push("?"); + } + + if (n.definite) { + parts.push("!"); + } + + if (n.typeAnnotation) { + parts.push(path.call(print, "typeAnnotation")); + } + + if (n.value) { + parts.push(" = ", path.call(print, "value")); + } + + parts.push(";"); + + return concat(parts); + } + case "ClassDeclaration": case "ClassExpression": case "DeclareClass": diff --git a/parsers/babel.ts b/parsers/babel.ts index 98573ea..2a47b59 100644 --- a/parsers/babel.ts +++ b/parsers/babel.ts @@ -28,6 +28,6 @@ export const parser = (function (): BabelParser { // export function parse(source: string, options?: Overrides) { const babelOptions = getBabelOptions(options); - babelOptions.plugins.push("jsx", "flow"); + babelOptions.plugins.push("jsx", "flow", "decoratorAutoAccessors"); return parser.parse(source, babelOptions); } diff --git a/test/babel.ts b/test/babel.ts index 2975b3f..2b7f29e 100644 --- a/test/babel.ts +++ b/test/babel.ts @@ -1,8 +1,8 @@ import assert from "assert"; +import { EOL as eol } from "os"; import * as recast from "../main"; const n = recast.types.namedTypes; const b = recast.types.builders; -import { EOL as eol } from "os"; const nodeMajorVersion = parseInt(process.versions.node, 10); describe("Babel", function () { @@ -478,4 +478,21 @@ describe("Babel", function () { `'use strict';\nconst a = 1;`, ); }); + + it("can handle ClassAccessorProperty elements", function () { + const code = [ + "class A {", + " accessor x;", + " static accessor y;", + " accessor z = 1;", + " static accessor w = 2;", + "}", + ]; + const ast = recast.parse(code.join(eol), parseOptions); + + assert.strictEqual( + recast.prettyPrint(ast).code, + code.join(eol), + ); + }); }); diff --git a/test/printer.ts b/test/printer.ts index a2934dd..1d6d6c8 100644 --- a/test/printer.ts +++ b/test/printer.ts @@ -1,12 +1,12 @@ import assert from "assert"; -import * as recast from "../main"; +import * as types from "ast-types"; +import { EOL as eol } from "os"; +import { fromString } from "../lib/lines"; import { parse } from "../lib/parser"; import { Printer } from "../lib/printer"; -import * as types from "ast-types"; +import * as recast from "../main"; const n = types.namedTypes; const b = types.builders; -import { fromString } from "../lib/lines"; -import { EOL as eol } from "os"; const linesModule = require("../lib/lines"); const nodeMajorVersion = parseInt(process.versions.node, 10); @@ -1272,6 +1272,82 @@ describe("printer", function () { assert.strictEqual(pretty, code); }); + it("prints ClassAccessorProperty correctly", function () { + const code = ["class A {", " accessor foo: Type = Bar;", "}"].join(eol); + + const ast = b.program([ + b.classDeclaration( + b.identifier("A"), + b.classBody([ + b.classAccessorProperty.from({ + key: b.identifier("foo"), + value: b.identifier("Bar"), + typeAnnotation: b.tsTypeAnnotation( + b.tsTypeReference(b.identifier("Type")), + ) + }) + ]), + ), + ]); + + const printer = new Printer({ + tabWidth: 2, + }); + + const pretty = printer.printGenerically(ast).code; + assert.strictEqual(pretty, code); + }); + + it("prints 'definite' ClassAccessorProperty correctly", function () { + const code = ["class A {", " foo!: string;", "}"].join(eol); + + const ast = b.program([ + b.classDeclaration( + b.identifier("A"), + b.classBody([ + Object.assign( + b.classAccessorProperty.from({ + key: b.identifier("foo"), + definite: true, + }), + { definite: true }, + ), + ]), + ), + ]); + + const printer = new Printer({ + tabWidth: 2, + }); + + const pretty = printer.printGenerically(ast).code; + assert.strictEqual(pretty, code); + }); + + it("prints static ClassAccessorProperty correctly", function () { + const code = ["class A {", " static accessor foo = Bar;", "}"].join(eol); + + const ast = b.program([ + b.classDeclaration( + b.identifier("A"), + b.classBody([ + b.classAccessorProperty.from({ + key: b.identifier("foo"), + value: b.identifier("Bar"), + static: true, + }), + ]), + ), + ]); + + const printer = new Printer({ + tabWidth: 2, + }); + + const pretty = printer.printGenerically(ast).code; + assert.strictEqual(pretty, code); + }); + it("prints template expressions correctly", function () { let code = ["graphql`query`;"].join(eol); diff --git a/test/typescript.ts b/test/typescript.ts index 46437fd..8809f3b 100644 --- a/test/typescript.ts +++ b/test/typescript.ts @@ -1,9 +1,9 @@ import assert from "assert"; -import path from "path"; -import fs from "fs"; -import * as recast from "../main"; import * as types from "ast-types"; +import fs from "fs"; import { EOL as eol } from "os"; +import path from "path"; +import * as recast from "../main"; import * as parser from "../parsers/typescript"; // Babel 7 no longer supports Node 4 or 5.