From 5df907357047fab1cc8f014cbfbb31cc4c45c2a1 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Thu, 27 Apr 2023 15:49:32 +0800 Subject: [PATCH] Move estree printer into `print/` (#14769) --- .../{printer-estree.js => print/estree.js} | 166 ++++-------------- src/language-js/print/html-binding.js | 10 -- src/language-js/print/index.js | 103 +++++++++++ src/language-js/printer.js | 10 ++ src/plugins/estree.js | 2 +- 5 files changed, 150 insertions(+), 141 deletions(-) rename src/language-js/{printer-estree.js => print/estree.js} (78%) create mode 100644 src/language-js/print/index.js create mode 100644 src/language-js/printer.js diff --git a/src/language-js/printer-estree.js b/src/language-js/print/estree.js similarity index 78% rename from src/language-js/printer-estree.js rename to src/language-js/print/estree.js index 6566b52b1..ecafe3b1a 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/print/estree.js @@ -1,7 +1,5 @@ -// TODO(azz): anything that imports from main shouldn't be in a `language-*` dir. -import { printDanglingComments } from "../main/comments/print.js"; -import printIgnored from "../main/print-ignored.js"; -import hasNewline from "../utils/has-newline.js"; +import { printDanglingComments } from "../../main/comments/print.js"; +import hasNewline from "../../utils/has-newline.js"; import { join, line, @@ -9,12 +7,10 @@ import { softline, group, indent, -} from "../document/builders.js"; -import { replaceEndOfLine, inheritLabel } from "../document/utils.js"; -import UnexpectedNodeError from "../utils/unexpected-node-error.js"; -import isNonEmptyArray from "../utils/is-non-empty-array.js"; +} from "../../document/builders.js"; +import { replaceEndOfLine } from "../../document/utils.js"; +import UnexpectedNodeError from "../../utils/unexpected-node-error.js"; -import pathNeedsParens from "./needs-parens.js"; import { hasComment, CommentCheckFlags, @@ -25,17 +21,9 @@ import { isArrayOrTupleExpression, isObjectOrRecordExpression, startsWithNoLookaheadToken, - createTypeCheckFunction, -} from "./utils/index.js"; -import { locStart, locEnd } from "./loc.js"; -import isBlockComment from "./utils/is-block-comment.js"; -import isIgnored from "./utils/is-ignored.js"; - -import { printHtmlBinding } from "./print/html-binding.js"; -import { printAngular } from "./print/angular.js"; -import { printJsx } from "./print/jsx.js"; -import { printFlow } from "./print/flow.js"; -import { printTypescript } from "./print/typescript.js"; +} from "../utils/index.js"; +import { locStart, locEnd } from "../loc.js"; +import isBlockComment from "../utils/is-block-comment.js"; import { printOptionalToken, printBindExpressionCallee, @@ -43,69 +31,52 @@ import { printRestSpread, printDefiniteToken, printDeclareToken, -} from "./print/misc.js"; +} from "./misc.js"; import { printImportDeclaration, printExportDeclaration, printModuleSpecifier, -} from "./print/module.js"; -import { printTernary } from "./print/ternary.js"; +} from "./module.js"; +import { printTernary } from "./ternary.js"; import { printTaggedTemplateLiteral, printTemplateLiteral, -} from "./print/template-literal.js"; -import { printArray } from "./print/array.js"; -import { printObject } from "./print/object.js"; +} from "./template-literal.js"; +import { printArray } from "./array.js"; +import { printObject } from "./object.js"; import { printClass, printClassMethod, printClassProperty, printClassBody, -} from "./print/class.js"; -import { printProperty } from "./print/property.js"; +} from "./class.js"; +import { printProperty } from "./property.js"; import { printFunction, printMethod, printReturnStatement, printThrowStatement, -} from "./print/function.js"; -import { printArrowFunction } from "./print/arrow-function.js"; -import { printCallExpression } from "./print/call-expression.js"; +} from "./function.js"; +import { printArrowFunction } from "./arrow-function.js"; +import { printCallExpression } from "./call-expression.js"; import { printVariableDeclarator, printAssignmentExpression, -} from "./print/assignment.js"; -import { printBinaryishExpression } from "./print/binaryish.js"; -import { printStatementSequence } from "./print/statement.js"; -import { printMemberExpression } from "./print/member.js"; -import { printBlock, printBlockBody } from "./print/block.js"; -import { printLiteral } from "./print/literal.js"; -import { printDecorators } from "./print/decorators.js"; -import { printTypeAnnotationProperty } from "./print/type-annotation.js"; -import { shouldPrintLeadingSemicolon } from "./print/semicolon.js"; -import { printExpressionStatement } from "./print/expression-statement.js"; +} from "./assignment.js"; +import { printBinaryishExpression } from "./binaryish.js"; +import { printStatementSequence } from "./statement.js"; +import { printMemberExpression } from "./member.js"; +import { printBlock, printBlockBody } from "./block.js"; +import { printLiteral, isLiteral } from "./literal.js"; +import { printTypeAnnotationProperty } from "./type-annotation.js"; +import { printExpressionStatement } from "./expression-statement.js"; +import { printHtmlBinding } from "./html-binding.js"; /** - * @typedef {import("../common/ast-path.js").default} AstPath - * @typedef {import("../document/builders.js").Doc} Doc + * @typedef {import("../../common/ast-path.js").default} AstPath + * @typedef {import("../../document/builders.js").Doc} Doc */ -// Their decorators are handled themselves, and they don't need parentheses or leading semicolons -const shouldPrintDirectly = createTypeCheckFunction([ - "ClassMethod", - "ClassPrivateMethod", - "ClassProperty", - "ClassAccessorProperty", - "AccessorProperty", - "TSAbstractAccessorProperty", - "PropertyDefinition", - "TSAbstractPropertyDefinition", - "ClassPrivateProperty", - "MethodDefinition", - "TSAbstractMethodDefinition", - "TSDeclareMethod", -]); - /** * @param {AstPath} path * @param {*} options @@ -113,69 +84,13 @@ const shouldPrintDirectly = createTypeCheckFunction([ * @param {*} [args] * @returns {Doc} */ -function genericPrint(path, options, print, args) { - const doc = printPathNoParens(path, options, print, args); - if (!doc) { - return ""; - } - +function printEstree(path, options, print, args) { const { node } = path; - if (shouldPrintDirectly(node)) { - return doc; + + if (isLiteral(node)) { + return printLiteral(path, options); } - const hasDecorators = isNonEmptyArray(node.decorators); - const decoratorsDoc = printDecorators(path, options, print); - const isClassExpression = node.type === "ClassExpression"; - // Nodes (except `ClassExpression`) with decorators can't have parentheses and don't need leading semicolons - if (hasDecorators && !isClassExpression) { - return inheritLabel(doc, (doc) => group([decoratorsDoc, doc])); - } - - const needsParens = pathNeedsParens(path, options); - const needsSemi = shouldPrintLeadingSemicolon(path, options); - - if (!decoratorsDoc && !needsParens && !needsSemi) { - return doc; - } - - return inheritLabel(doc, (doc) => [ - needsSemi ? ";" : "", - needsParens ? "(" : "", - needsParens && isClassExpression && hasDecorators - ? [indent([line, decoratorsDoc, doc]), line] - : [decoratorsDoc, doc], - needsParens ? ")" : "", - ]); -} - -/** - * @param {AstPath} path - * @param {*} options - * @param {*} print - * @param {*} [args] - * @returns {Doc} - */ -function printPathNoParens(path, options, print, args) { - if (isIgnored(path)) { - return printIgnored(path, options); - } - - for (const printer of [ - printLiteral, - printHtmlBinding, - printAngular, - printJsx, - printFlow, - printTypescript, - ]) { - const printed = printer(path, options, print); - if (printed !== undefined) { - return printed; - } - } - - const { node } = path; const semi = options.semi ? ";" : ""; /** @type{Doc[]} */ let parts = []; @@ -186,7 +101,7 @@ function printPathNoParens(path, options, print, args) { case "JsonRoot": return [print("node"), hardline]; case "File": - return print("program"); + return printHtmlBinding(path, options, print) ?? print("program"); case "Program": return printBlockBody(path, options, print); @@ -738,13 +653,4 @@ function printPathNoParens(path, options, print, args) { } } -export const experimentalFeatures = { - // TODO: Make this default behavior - avoidAstMutation: true, -}; -export { genericPrint as print }; -export * from "./comments/printer-methods.js"; -export { default as embed } from "./embed/index.js"; -export { default as massageAstNode } from "./clean.js"; -export { insertPragma } from "./pragma.js"; -export { default as getVisitorKeys } from "./traverse/get-visitor-keys.js"; +export { printEstree }; diff --git a/src/language-js/print/html-binding.js b/src/language-js/print/html-binding.js index d1ba74d91..dbf02348f 100644 --- a/src/language-js/print/html-binding.js +++ b/src/language-js/print/html-binding.js @@ -7,16 +7,6 @@ import { } from "../../document/builders.js"; function printHtmlBinding(path, options, print) { - const { node, isRoot } = path; - - if (isRoot) { - options.__onHtmlBindingRoot?.(node, options); - } - - if (node.type !== "File") { - return; - } - if (options.__isVueBindings || options.__isVueForBindingLeft) { const parameterDocs = path.map(print, "program", "body", 0, "params"); diff --git a/src/language-js/print/index.js b/src/language-js/print/index.js new file mode 100644 index 000000000..60ea1ac76 --- /dev/null +++ b/src/language-js/print/index.js @@ -0,0 +1,103 @@ +import printIgnored from "../../main/print-ignored.js"; +import { line, group, indent } from "../../document/builders.js"; +import { inheritLabel } from "../../document/utils.js"; +import isNonEmptyArray from "../../utils/is-non-empty-array.js"; +import pathNeedsParens from "../needs-parens.js"; +import { createTypeCheckFunction } from "../utils/index.js"; +import isIgnored from "../utils/is-ignored.js"; +import { printEstree } from "./estree.js"; +import { printAngular } from "./angular.js"; +import { printJsx } from "./jsx.js"; +import { printFlow } from "./flow.js"; +import { printTypescript } from "./typescript.js"; +import { printDecorators } from "./decorators.js"; +import { shouldPrintLeadingSemicolon } from "./semicolon.js"; + +/** + * @typedef {import("../../common/ast-path.js").default} AstPath + * @typedef {import("../../document/builders.js").Doc} Doc + */ + +function printWithoutParentheses(path, options, print, args) { + if (isIgnored(path)) { + return printIgnored(path, options); + } + + for (const printer of [ + printAngular, + printJsx, + printFlow, + printTypescript, + printEstree, + ]) { + const doc = printer(path, options, print, args); + if (doc !== undefined) { + return doc; + } + } +} + +// Their decorators are handled themselves, and they don't need parentheses or leading semicolons +const shouldPrintDirectly = createTypeCheckFunction([ + "ClassMethod", + "ClassPrivateMethod", + "ClassProperty", + "ClassAccessorProperty", + "AccessorProperty", + "TSAbstractAccessorProperty", + "PropertyDefinition", + "TSAbstractPropertyDefinition", + "ClassPrivateProperty", + "MethodDefinition", + "TSAbstractMethodDefinition", + "TSDeclareMethod", +]); + +/** + * @param {AstPath} path + * @param {*} options + * @param {*} print + * @param {*} [args] + * @returns {Doc} + */ +function print(path, options, print, args) { + if (path.isRoot) { + options.__onHtmlBindingRoot?.(path.node, options); + } + + const doc = printWithoutParentheses(path, options, print, args); + if (!doc) { + return ""; + } + + const { node } = path; + if (shouldPrintDirectly(node)) { + return doc; + } + + const hasDecorators = isNonEmptyArray(node.decorators); + const decoratorsDoc = printDecorators(path, options, print); + const isClassExpression = node.type === "ClassExpression"; + // Nodes (except `ClassExpression`) with decorators can't have parentheses and don't need leading semicolons + if (hasDecorators && !isClassExpression) { + return inheritLabel(doc, (doc) => group([decoratorsDoc, doc])); + } + + const needsParens = pathNeedsParens(path, options); + const needsSemi = shouldPrintLeadingSemicolon(path, options); + + if (!decoratorsDoc && !needsParens && !needsSemi) { + return doc; + } + + return inheritLabel(doc, (doc) => [ + needsSemi ? ";" : "", + needsParens ? "(" : "", + needsParens && isClassExpression && hasDecorators + ? [indent([line, decoratorsDoc, doc]), line] + : [decoratorsDoc, doc], + needsParens ? ")" : "", + ]); +} + +export default print; diff --git a/src/language-js/printer.js b/src/language-js/printer.js new file mode 100644 index 000000000..d56b02e69 --- /dev/null +++ b/src/language-js/printer.js @@ -0,0 +1,10 @@ +export const experimentalFeatures = { + // TODO: Make this default behavior + avoidAstMutation: true, +}; +export { default as print } from "./print/index.js"; +export * from "./comments/printer-methods.js"; +export { default as embed } from "./embed/index.js"; +export { default as massageAstNode } from "./clean.js"; +export { insertPragma } from "./pragma.js"; +export { default as getVisitorKeys } from "./traverse/get-visitor-keys.js"; diff --git a/src/plugins/estree.js b/src/plugins/estree.js index e14eacb5c..cdc7c96df 100644 --- a/src/plugins/estree.js +++ b/src/plugins/estree.js @@ -1,4 +1,4 @@ -import * as estreePrinter from "../language-js/printer-estree.js"; +import * as estreePrinter from "../language-js/printer.js"; import jsLanguages from "../language-js/languages.evaluate.js"; import * as estreeJsonPrinter from "../language-json/printer-estree-json.js"; import jsonLanguages from "../language-json/languages.evaluate.js";