diff --git a/src/print.js b/src/print.js index e4bd9a5..ef1100a 100644 --- a/src/print.js +++ b/src/print.js @@ -83,6 +83,12 @@ export function print(node) { case "ChainExpression": return print(node.expression); + case "V8IntrinsicExpression": { + const name = typeof node.name === "string" ? node.name : print(node.name); + const args = (node.arguments ?? []).map(print).join(", "); + return `%${name}(${args})`; + } + case "ParenthesizedExpression": return `(${print(node.expression)})`; @@ -544,6 +550,16 @@ export function print(node) { case "TSParenthesizedType": return `(${print(node.typeAnnotation)})`; + // JSDoc type syntax (`?Foo`, `!Foo`, `?`) — `postfix` flips prefix/suffix. + case "TSJSDocNullableType": + return node.postfix ? `${print(node.typeAnnotation)}?` : `?${print(node.typeAnnotation)}`; + + case "TSJSDocNonNullableType": + return node.postfix ? `${print(node.typeAnnotation)}!` : `!${print(node.typeAnnotation)}`; + + case "TSJSDocUnknownType": + return "?"; + case "TSTupleType": { const elems = (node.elementTypes ?? []).map(print).join(", "); return `[${elems}]`; @@ -683,10 +699,17 @@ export function print(node) { const declare = node.declare ? "declare " : ""; const constKw = node.const ? "const " : ""; const id = print(node.id); - const members = (node.members ?? []).map(print).join(",\n"); + // Newer oxc nests members in a TSEnumBody child; older versions put + // `members` directly on the declaration. + const members = (node.body?.members ?? node.members ?? []).map(print).join(",\n"); return `${declare}${constKw}enum ${id} {\n${members}\n}`; } + case "TSEnumBody": { + const members = (node.members ?? []).map(print).join(",\n"); + return `{\n${members}\n}`; + } + case "TSEnumMember": { const id = print(node.id); return node.initializer ? `${id} = ${print(node.initializer)}` : id; diff --git a/tests/parse-print-gts.test.js b/tests/parse-print-gts.test.js index e17dd40..2ab0b19 100644 --- a/tests/parse-print-gts.test.js +++ b/tests/parse-print-gts.test.js @@ -164,9 +164,25 @@ const Badge = ; ], }; const printed = print(enumNode); - expect(printed).toContain("enum Color"); - expect(printed).toContain("Red"); - expect(printed).toContain("Blue = 1"); + expect(printed).toMatchInlineSnapshot(` + "enum Color { + Red, + Blue = 1 + }" + `); + }); + + it("preserves enum members through parse + print", () => { + // Newer oxc nests members under a TSEnumBody; the printed enum must + // still contain them rather than collapsing to an empty body. + const printed = print(parse(`enum Color { Red, Green, Blue }`, { filePath: "x.gts" }).program); + expect(printed).toMatchInlineSnapshot(` + "enum Color { + Red, + Green, + Blue + }" + `); }); it("handles filePath with gts extension", () => { diff --git a/tests/print.test.js b/tests/print.test.js index a90e996..0371882 100644 --- a/tests/print.test.js +++ b/tests/print.test.js @@ -451,6 +451,51 @@ describe("print", () => { expect(print(node)).toBe("(string | number)"); }); + it("prints JSDoc type nodes", () => { + const str = { type: "TSStringKeyword" }; + expect(print({ type: "TSJSDocNullableType", typeAnnotation: str })).toBe("?string"); + expect(print({ type: "TSJSDocNullableType", typeAnnotation: str, postfix: true })).toBe( + "string?", + ); + expect(print({ type: "TSJSDocNonNullableType", typeAnnotation: str })).toBe("!string"); + expect(print({ type: "TSJSDocUnknownType" })).toBe("?"); + }); + + it("prints V8IntrinsicExpression", () => { + const node = { + type: "V8IntrinsicExpression", + name: "DebugPrint", + arguments: [{ type: "Identifier", name: "x" }], + }; + expect(print(node)).toBe("%DebugPrint(x)"); + }); + + it("prints TSEnumBody", () => { + const node = { + type: "TSEnumBody", + members: [ + { type: "TSEnumMember", id: { type: "Identifier", name: "A" } }, + { type: "TSEnumMember", id: { type: "Identifier", name: "B" } }, + ], + }; + expect(print(node)).toBe("{\nA,\nB\n}"); + }); + + it("prints TSEnumDeclaration with a TSEnumBody (newer oxc shape)", () => { + const node = { + type: "TSEnumDeclaration", + id: { type: "Identifier", name: "Color" }, + body: { + type: "TSEnumBody", + members: [ + { type: "TSEnumMember", id: { type: "Identifier", name: "Red" } }, + { type: "TSEnumMember", id: { type: "Identifier", name: "Green" } }, + ], + }, + }; + expect(print(node)).toBe("enum Color {\nRed,\nGreen\n}"); + }); + it("prints TSTupleType", () => { const node = { type: "TSTupleType",