diff --git a/src/common/ast-path.js b/src/common/ast-path.js index 7f44a3780..41f379d1a 100644 --- a/src/common/ast-path.js +++ b/src/common/ast-path.js @@ -3,64 +3,78 @@ class AstPath { this.stack = [value]; } + /** @type {string | null} */ get key() { const { stack, siblings } = this; return stack.at(siblings === null ? -2 : -4) ?? null; } + /** @type {number | null} */ get index() { return this.siblings === null ? null : this.stack.at(-2); } + /** @type {object} */ get node() { return this.stack.at(-1); } + /** @type {object | null} */ get parent() { return this.getNode(1); } + /** @type {object | null} */ get grandparent() { return this.getNode(2); } + /** @type {boolean} */ get isInArray() { return this.siblings !== null; } + /** @type {object[] | null} */ get siblings() { const { stack } = this; const maybeArray = stack.at(-3); return Array.isArray(maybeArray) ? maybeArray : null; } + /** @type {object | null} */ get next() { const { siblings } = this; return siblings === null ? null : siblings[this.index + 1]; } + /** @type {object | null} */ get previous() { const { siblings } = this; return siblings === null ? null : siblings[this.index - 1]; } + /** @type {boolean} */ get isFirst() { return this.index === 0; } + /** @type {boolean} */ get isLast() { const { siblings, index } = this; return siblings !== null && index === siblings.length - 1; } + /** @type {boolean} */ get isRoot() { return this.stack.length === 1; } + /** @type {object} */ get root() { return this.stack[0]; } + /** @type {object[]} */ get ancestors() { return [...this.#getAncestors()]; } diff --git a/src/language-js/print/class.js b/src/language-js/print/class.js index 0035b84d1..b42591ca0 100644 --- a/src/language-js/print/class.js +++ b/src/language-js/print/class.js @@ -21,6 +21,7 @@ import { printOptionalToken, printDefiniteToken, printDeclareToken, + printAbstractToken, printTypeScriptAccessibilityToken, } from "./misc.js"; import { printPropertyKey } from "./property.js"; @@ -50,11 +51,7 @@ const isClassProperty = createTypeCheckFunction([ function printClass(path, options, print) { const { node } = path; /** @type {Doc[]} */ - const parts = [ - printDeclareToken(path), - node.abstract ? "abstract " : "", - "class", - ]; + const parts = [printDeclareToken(path), printAbstractToken(path), "class"]; // Keep old behaviour of extends in same line // If there is only on extends and there are not comments @@ -187,9 +184,9 @@ function printClassMethod(path, options, print) { if (node.static) { parts.push("static "); } - if (node.type === "TSAbstractMethodDefinition" || node.abstract) { - parts.push("abstract "); - } + + parts.push(printAbstractToken(path)); + if (node.override) { parts.push("override "); } @@ -213,13 +210,9 @@ function printClassProperty(path, options, print) { if (node.static) { parts.push("static "); } - if ( - node.type === "TSAbstractPropertyDefinition" || - node.type === "TSAbstractAccessorProperty" || - node.abstract - ) { - parts.push("abstract "); - } + + parts.push(printAbstractToken(path)); + if (node.override) { parts.push("override "); } diff --git a/src/language-js/print/misc.js b/src/language-js/print/misc.js index d1a738eb7..367b0176f 100644 --- a/src/language-js/print/misc.js +++ b/src/language-js/print/misc.js @@ -1,6 +1,15 @@ import { indent, line } from "../../document/builders.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; +/** + * @typedef {import("../../common/ast-path.js").default} AstPath + * @typedef {import("../../document/builders.js").Doc} Doc + */ + +/** + * @param {AstPath} path + * @returns {Doc} + */ function printOptionalToken(path) { const { node } = path; if ( @@ -21,6 +30,10 @@ function printOptionalToken(path) { return "?"; } +/** + * @param {AstPath} path + * @returns {Doc} + */ function printDefiniteToken(path) { return path.node.definite || path.match( @@ -43,6 +56,10 @@ const flowDeclareNodeTypes = new Set([ "DeclareEnum", "DeclareInterface", ]); +/** + * @param {AstPath} path + * @returns {Doc} + */ function printDeclareToken(path) { const { node } = path; @@ -57,6 +74,19 @@ function printDeclareToken(path) { ); } +const tsAbstractNodeTypes = new Set([ + "TSAbstractMethodDefinition", + "TSAbstractPropertyDefinition", + "TSAbstractAccessorProperty", +]); +/** + * @param {AstPath} param0 + * @returns {Doc} + */ +function printAbstractToken({ node }) { + return node.abstract || tsAbstractNodeTypes.has(node.type) ? "abstract " : ""; +} + function printFunctionTypeParameters(path, options, print) { const fun = path.node; if (fun.typeArguments) { @@ -114,6 +144,7 @@ export { printOptionalToken, printDefiniteToken, printDeclareToken, + printAbstractToken, printFunctionTypeParameters, printBindExpressionCallee, printRestSpread, diff --git a/src/language-js/print/type-annotation.js b/src/language-js/print/type-annotation.js index 4d9a45eb9..9090cd8a4 100644 --- a/src/language-js/print/type-annotation.js +++ b/src/language-js/print/type-annotation.js @@ -23,7 +23,15 @@ import { printFunctionParameters, shouldGroupFunctionParameters, } from "./function-parameters.js"; -import { printOptionalToken, printDeclareToken } from "./misc.js"; +import { + printOptionalToken, + printDeclareToken, + printAbstractToken, +} from "./misc.js"; + +/** + * @typedef {import("../../document/builders.js").Doc} Doc + */ function shouldHugType(node) { if (isSimpleType(node) || isObjectType(node)) { @@ -253,11 +261,11 @@ function isFlowArrowFunctionTypeAnnotation(path) { */ function printFunctionType(path, options, print) { const { node } = path; - const parts = []; - - if (node.type === "TSConstructorType" && node.abstract) { - parts.push("abstract "); - } + /** @type {Doc[]} */ + const parts = [ + // `TSConstructorType` only + printAbstractToken(path), + ]; if ( node.type === "TSConstructorType" || diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index baedbee9d..8146feb8f 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -1,5 +1,3 @@ -/** @typedef {import("../document/builders.js").Doc} Doc */ - // TODO(azz): anything that imports from main shouldn't be in a `language-*` dir. import { printDanglingComments } from "../main/comments.js"; import { hasNewline } from "../common/util.js"; @@ -92,6 +90,18 @@ import { printLiteral } from "./print/literal.js"; import { printDecorators } from "./print/decorators.js"; import { printTypeAnnotationProperty } from "./print/type-annotation.js"; +/** + * @typedef {import("../common/ast-path.js").default} AstPath + * @typedef {import("../document/builders.js").Doc} Doc + */ + +/** + * @param {AstPath} path + * @param {*} options + * @param {*} print + * @param {*} [args] + * @returns {Doc} + */ function genericPrint(path, options, print, args) { const { node } = path; @@ -168,6 +178,13 @@ function genericPrint(path, options, print, args) { return parts; } +/** + * @param {AstPath} path + * @param {*} options + * @param {*} print + * @param {*} [args] + * @returns {Doc} + */ function printPathNoParens(path, options, print, args) { for (const printer of [ printLiteral,