Refactor JS printer (#9730)
This commit is contained in:
1 parent
4ae69300e7
commit
8b7658236a
6 files changed
+259
-232
No files matched your search
Vendored
+127
@@ -0,0 +1,127 @@
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
builders: { concat, join, line, group },
|
||||
} = require("../../document");
|
||||
const { hasNode } = require("../utils");
|
||||
const { printBinaryishExpression } = require("./binaryish");
|
||||
|
||||
/** @typedef {import("../../common/fast-path")} FastPath */
|
||||
|
||||
function printAngular(path, options, print) {
|
||||
const n = path.getValue();
|
||||
switch (n.type) {
|
||||
case "NGRoot":
|
||||
return concat(
|
||||
[].concat(
|
||||
path.call(print, "node"),
|
||||
!n.node.comments || n.node.comments.length === 0
|
||||
? []
|
||||
: concat([" //", n.node.comments[0].value.trimEnd()])
|
||||
)
|
||||
);
|
||||
case "NGPipeExpression":
|
||||
return printBinaryishExpression(path, options, print);
|
||||
case "NGChainedExpression":
|
||||
return group(
|
||||
join(
|
||||
concat([";", line]),
|
||||
path.map(
|
||||
(childPath) =>
|
||||
hasNgSideEffect(childPath)
|
||||
? print(childPath)
|
||||
: concat(["(", print(childPath), ")"]),
|
||||
"expressions"
|
||||
)
|
||||
)
|
||||
);
|
||||
case "NGEmptyExpression":
|
||||
return "";
|
||||
case "NGQuotedExpression":
|
||||
return concat([n.prefix, ": ", n.value.trim()]);
|
||||
case "NGMicrosyntax":
|
||||
return concat(
|
||||
path.map(
|
||||
(childPath, index) =>
|
||||
concat([
|
||||
index === 0
|
||||
? ""
|
||||
: isNgForOf(childPath.getValue(), index, n)
|
||||
? " "
|
||||
: concat([";", line]),
|
||||
print(childPath),
|
||||
]),
|
||||
"body"
|
||||
)
|
||||
);
|
||||
case "NGMicrosyntaxKey":
|
||||
return /^[$_a-z][\w$]*(-[$_a-z][\w$])*$/i.test(n.name)
|
||||
? n.name
|
||||
: JSON.stringify(n.name);
|
||||
case "NGMicrosyntaxExpression":
|
||||
return concat([
|
||||
path.call(print, "expression"),
|
||||
n.alias === null ? "" : concat([" as ", path.call(print, "alias")]),
|
||||
]);
|
||||
case "NGMicrosyntaxKeyedExpression": {
|
||||
const index = path.getName();
|
||||
const parentNode = path.getParentNode();
|
||||
const shouldNotPrintColon =
|
||||
isNgForOf(n, index, parentNode) ||
|
||||
(((index === 1 && (n.key.name === "then" || n.key.name === "else")) ||
|
||||
(index === 2 &&
|
||||
n.key.name === "else" &&
|
||||
parentNode.body[index - 1].type ===
|
||||
"NGMicrosyntaxKeyedExpression" &&
|
||||
parentNode.body[index - 1].key.name === "then")) &&
|
||||
parentNode.body[0].type === "NGMicrosyntaxExpression");
|
||||
return concat([
|
||||
path.call(print, "key"),
|
||||
shouldNotPrintColon ? " " : ": ",
|
||||
path.call(print, "expression"),
|
||||
]);
|
||||
}
|
||||
case "NGMicrosyntaxLet":
|
||||
return concat([
|
||||
"let ",
|
||||
path.call(print, "key"),
|
||||
n.value === null ? "" : concat([" = ", path.call(print, "value")]),
|
||||
]);
|
||||
case "NGMicrosyntaxAs":
|
||||
return concat([
|
||||
path.call(print, "key"),
|
||||
" as ",
|
||||
path.call(print, "alias"),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function isNgForOf(node, index, parentNode) {
|
||||
return (
|
||||
node.type === "NGMicrosyntaxKeyedExpression" &&
|
||||
node.key.name === "of" &&
|
||||
index === 1 &&
|
||||
parentNode.body[0].type === "NGMicrosyntaxLet" &&
|
||||
parentNode.body[0].value === null
|
||||
);
|
||||
}
|
||||
|
||||
/** identify if an angular expression seems to have side effects */
|
||||
/**
|
||||
* @param {FastPath} path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasNgSideEffect(path) {
|
||||
return hasNode(path.getValue(), (node) => {
|
||||
switch (node.type) {
|
||||
case undefined:
|
||||
return false;
|
||||
case "CallExpression":
|
||||
case "OptionalCallExpression":
|
||||
case "AssignmentExpression":
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { printAngular };
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const {
|
||||
builders: { concat, join, line, group, softline, indent },
|
||||
} = require("../document");
|
||||
} = require("../../document");
|
||||
|
||||
function printHtmlBinding(path, options, print) {
|
||||
const node = path.getValue();
|
||||
@@ -13,6 +13,7 @@ const {
|
||||
fill,
|
||||
ifBreak,
|
||||
lineSuffixBoundary,
|
||||
join,
|
||||
},
|
||||
utils: { willBreak, isLineNext, isEmpty },
|
||||
} = require("../../document");
|
||||
@@ -707,15 +708,50 @@ function printJsxSpreadAttribute(path, options, print) {
|
||||
]);
|
||||
}
|
||||
|
||||
function printJsx(path, options, print) {
|
||||
const n = path.getValue();
|
||||
switch (n.type) {
|
||||
case "JSXAttribute":
|
||||
return printJsxAttribute(path, options, print);
|
||||
case "JSXIdentifier":
|
||||
return "" + n.name;
|
||||
case "JSXNamespacedName":
|
||||
return join(":", [
|
||||
path.call(print, "namespace"),
|
||||
path.call(print, "name"),
|
||||
]);
|
||||
case "JSXMemberExpression":
|
||||
return join(".", [
|
||||
path.call(print, "object"),
|
||||
path.call(print, "property"),
|
||||
]);
|
||||
case "JSXSpreadAttribute":
|
||||
return printJsxSpreadAttribute(path, options, print);
|
||||
case "JSXSpreadChild": {
|
||||
// Same as `printJsxSpreadAttribute`
|
||||
const printJsxSpreadChild = printJsxSpreadAttribute;
|
||||
return printJsxSpreadChild(path, options, print);
|
||||
}
|
||||
case "JSXExpressionContainer":
|
||||
return printJsxExpressionContainer(path, options, print);
|
||||
case "JSXFragment":
|
||||
case "JSXElement":
|
||||
return printJsxElement(path, options, print);
|
||||
case "JSXOpeningElement":
|
||||
return printJsxOpeningElement(path, options, print);
|
||||
case "JSXClosingElement":
|
||||
return printJsxClosingElement(path, options, print);
|
||||
case "JSXOpeningFragment":
|
||||
case "JSXClosingFragment":
|
||||
return printJsxOpeningClosingFragment(path, options /*, print*/);
|
||||
case "JSXEmptyExpression":
|
||||
return printJsxEmptyExpression(path, options /*, print*/);
|
||||
case "JSXText":
|
||||
/* istanbul ignore next */
|
||||
throw new Error("JSXTest should be handled by JSXElement");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
printJsxElement,
|
||||
printJsxAttribute,
|
||||
printJsxOpeningElement,
|
||||
printJsxClosingElement,
|
||||
printJsxOpeningClosingFragment,
|
||||
printJsxExpressionContainer,
|
||||
printJsxEmptyExpression,
|
||||
printJsxSpreadAttribute,
|
||||
// Alias
|
||||
printJsxSpreadChild: printJsxSpreadAttribute,
|
||||
printJsx,
|
||||
};
|
||||
@@ -8,7 +8,9 @@ const {
|
||||
hasDanglingComments,
|
||||
isTestCall,
|
||||
isBlockComment,
|
||||
isTSXFile,
|
||||
shouldPrintComma,
|
||||
getFunctionParameters,
|
||||
} = require("../utils");
|
||||
const { shouldHugType } = require("./type-annotation");
|
||||
|
||||
@@ -97,4 +99,64 @@ function printDanglingCommentsForInline(path, options) {
|
||||
return concat([printed, hardline]);
|
||||
}
|
||||
|
||||
module.exports = { printTypeParameters, getTypeParametersGroupId };
|
||||
function printTypeParameter(path, options, print) {
|
||||
const n = path.getValue();
|
||||
const parts = [];
|
||||
const parent = path.getParentNode();
|
||||
if (parent.type === "TSMappedType") {
|
||||
parts.push("[", path.call(print, "name"));
|
||||
if (n.constraint) {
|
||||
parts.push(" in ", path.call(print, "constraint"));
|
||||
}
|
||||
if (parent.nameType) {
|
||||
parts.push(
|
||||
" as ",
|
||||
path.callParent((path) => {
|
||||
return path.call(print, "nameType");
|
||||
})
|
||||
);
|
||||
}
|
||||
parts.push("]");
|
||||
return concat(parts);
|
||||
}
|
||||
|
||||
if (n.variance) {
|
||||
parts.push(path.call(print, "variance"));
|
||||
}
|
||||
|
||||
parts.push(path.call(print, "name"));
|
||||
|
||||
if (n.bound) {
|
||||
parts.push(": ");
|
||||
parts.push(path.call(print, "bound"));
|
||||
}
|
||||
|
||||
if (n.constraint) {
|
||||
parts.push(" extends ", path.call(print, "constraint"));
|
||||
}
|
||||
|
||||
if (n.default) {
|
||||
parts.push(" = ", path.call(print, "default"));
|
||||
}
|
||||
|
||||
// Keep comma if the file extension is .tsx and
|
||||
// has one type parameter that isn't extend with any types.
|
||||
// Because, otherwise formatted result will be invalid as tsx.
|
||||
const grandParent = path.getNode(2);
|
||||
if (
|
||||
getFunctionParameters(parent).length === 1 &&
|
||||
isTSXFile(options) &&
|
||||
!n.constraint &&
|
||||
grandParent.type === "ArrowFunctionExpression"
|
||||
) {
|
||||
parts.push(",");
|
||||
}
|
||||
|
||||
return concat(parts);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
printTypeParameter,
|
||||
printTypeParameters,
|
||||
getTypeParametersGroupId,
|
||||
};
|
||||
@@ -36,13 +36,8 @@ const clean = require("./clean");
|
||||
const { insertPragma } = require("./pragma");
|
||||
const handleComments = require("./comments");
|
||||
const pathNeedsParens = require("./needs-parens");
|
||||
const {
|
||||
printHtmlBinding,
|
||||
isVueEventBindingExpression,
|
||||
} = require("./html-binding");
|
||||
const preprocess = require("./print-preprocess");
|
||||
const {
|
||||
getFunctionParameters,
|
||||
getCallArguments,
|
||||
getParentExportDeclaration,
|
||||
getTypeScriptMappedTypeModifier,
|
||||
@@ -50,18 +45,15 @@ const {
|
||||
hasFlowShorthandAnnotationComment,
|
||||
hasLeadingOwnLineComment,
|
||||
hasNewlineBetweenOrAfterDecorators,
|
||||
hasNgSideEffect,
|
||||
hasPrettierIgnore,
|
||||
hasTrailingComment,
|
||||
isExportDeclaration,
|
||||
isFunctionNotation,
|
||||
isGetterOrSetter,
|
||||
isLiteral,
|
||||
isNgForOf,
|
||||
isObjectType,
|
||||
isObjectTypePropertyAFunction,
|
||||
isTheOnlyJSXElementInMarkdown,
|
||||
isTSXFile,
|
||||
isBlockComment,
|
||||
needsHardlineAfterDanglingComment,
|
||||
rawText,
|
||||
@@ -69,6 +61,12 @@ const {
|
||||
} = require("./utils");
|
||||
const { locStart, locEnd } = require("./loc");
|
||||
|
||||
const {
|
||||
printHtmlBinding,
|
||||
isVueEventBindingExpression,
|
||||
} = require("./print/html-binding");
|
||||
const { printAngular } = require("./print/angular");
|
||||
const { printJsx } = require("./print/jsx");
|
||||
const {
|
||||
printOptionalToken,
|
||||
printBindExpressionCallee,
|
||||
@@ -92,19 +90,11 @@ const {
|
||||
printTypeAnnotation,
|
||||
shouldHugType,
|
||||
} = require("./print/type-annotation");
|
||||
const {
|
||||
printJsxElement,
|
||||
printJsxAttribute,
|
||||
printJsxOpeningElement,
|
||||
printJsxClosingElement,
|
||||
printJsxOpeningClosingFragment,
|
||||
printJsxExpressionContainer,
|
||||
printJsxEmptyExpression,
|
||||
printJsxSpreadAttribute,
|
||||
printJsxSpreadChild,
|
||||
} = require("./print/jsx");
|
||||
const { printClass, printClassMethod } = require("./print/class");
|
||||
const { printTypeParameters } = require("./print/type-parameters");
|
||||
const {
|
||||
printTypeParameter,
|
||||
printTypeParameters,
|
||||
} = require("./print/type-parameters");
|
||||
const { printPropertyKey } = require("./print/property");
|
||||
const {
|
||||
printFunctionDeclaration,
|
||||
@@ -251,6 +241,16 @@ function printPathNoParens(path, options, print, args) {
|
||||
return htmlBinding;
|
||||
}
|
||||
|
||||
const printedAngular = printAngular(path, options, print);
|
||||
if (typeof printedAngular !== "undefined") {
|
||||
return printedAngular;
|
||||
}
|
||||
|
||||
const printedJsx = printJsx(path, options, print);
|
||||
if (typeof printedJsx !== "undefined") {
|
||||
return printedJsx;
|
||||
}
|
||||
|
||||
/** @type{Doc[]} */
|
||||
let parts = [];
|
||||
|
||||
@@ -359,7 +359,6 @@ function printPathNoParens(path, options, print, args) {
|
||||
return printVariableDeclarator(path, options, print);
|
||||
case "BinaryExpression":
|
||||
case "LogicalExpression":
|
||||
case "NGPipeExpression":
|
||||
return printBinaryishExpression(path, options, print);
|
||||
case "AssignmentPattern":
|
||||
return concat([
|
||||
@@ -1052,43 +1051,8 @@ function printPathNoParens(path, options, print, args) {
|
||||
// JSX extensions below.
|
||||
case "DebuggerStatement":
|
||||
return concat(["debugger", semi]);
|
||||
case "JSXAttribute":
|
||||
return printJsxAttribute(path, options, print);
|
||||
case "JSXIdentifier":
|
||||
return "" + n.name;
|
||||
case "JSXNamespacedName":
|
||||
return join(":", [
|
||||
path.call(print, "namespace"),
|
||||
path.call(print, "name"),
|
||||
]);
|
||||
case "JSXMemberExpression":
|
||||
return join(".", [
|
||||
path.call(print, "object"),
|
||||
path.call(print, "property"),
|
||||
]);
|
||||
case "TSQualifiedName":
|
||||
return join(".", [path.call(print, "left"), path.call(print, "right")]);
|
||||
case "JSXSpreadAttribute":
|
||||
return printJsxSpreadAttribute(path, options, print);
|
||||
case "JSXSpreadChild":
|
||||
return printJsxSpreadChild(path, options, print);
|
||||
case "JSXExpressionContainer":
|
||||
return printJsxExpressionContainer(path, options, print);
|
||||
case "JSXFragment":
|
||||
case "JSXElement":
|
||||
return printJsxElement(path, options, print);
|
||||
case "JSXOpeningElement":
|
||||
return printJsxOpeningElement(path, options, print);
|
||||
case "JSXClosingElement":
|
||||
return printJsxClosingElement(path, options, print);
|
||||
case "JSXOpeningFragment":
|
||||
case "JSXClosingFragment":
|
||||
return printJsxOpeningClosingFragment(path, options /*, print*/);
|
||||
case "JSXText":
|
||||
/* istanbul ignore next */
|
||||
throw new Error("JSXTest should be handled by JSXElement");
|
||||
case "JSXEmptyExpression":
|
||||
return printJsxEmptyExpression(path, options /*, print*/);
|
||||
case "ClassBody":
|
||||
if (!n.comments && n.body.length === 0) {
|
||||
return "{}";
|
||||
@@ -1743,59 +1707,8 @@ function printPathNoParens(path, options, print, args) {
|
||||
return printTypeParameters(path, options, print, "params");
|
||||
|
||||
case "TSTypeParameter":
|
||||
case "TypeParameter": {
|
||||
const parent = path.getParentNode();
|
||||
if (parent.type === "TSMappedType") {
|
||||
parts.push("[", path.call(print, "name"));
|
||||
if (n.constraint) {
|
||||
parts.push(" in ", path.call(print, "constraint"));
|
||||
}
|
||||
if (parent.nameType) {
|
||||
parts.push(
|
||||
" as ",
|
||||
path.callParent((path) => {
|
||||
return path.call(print, "nameType");
|
||||
})
|
||||
);
|
||||
}
|
||||
parts.push("]");
|
||||
return concat(parts);
|
||||
}
|
||||
|
||||
if (n.variance) {
|
||||
parts.push(path.call(print, "variance"));
|
||||
}
|
||||
|
||||
parts.push(path.call(print, "name"));
|
||||
|
||||
if (n.bound) {
|
||||
parts.push(": ");
|
||||
parts.push(path.call(print, "bound"));
|
||||
}
|
||||
|
||||
if (n.constraint) {
|
||||
parts.push(" extends ", path.call(print, "constraint"));
|
||||
}
|
||||
|
||||
if (n.default) {
|
||||
parts.push(" = ", path.call(print, "default"));
|
||||
}
|
||||
|
||||
// Keep comma if the file extension is .tsx and
|
||||
// has one type parameter that isn't extend with any types.
|
||||
// Because, otherwise formatted result will be invalid as tsx.
|
||||
const grandParent = path.getNode(2);
|
||||
if (
|
||||
getFunctionParameters(parent).length === 1 &&
|
||||
isTSXFile(options) &&
|
||||
!n.constraint &&
|
||||
grandParent.type === "ArrowFunctionExpression"
|
||||
) {
|
||||
parts.push(",");
|
||||
}
|
||||
|
||||
return concat(parts);
|
||||
}
|
||||
case "TypeParameter":
|
||||
return printTypeParameter(path, options, print);
|
||||
case "TypeofTypeAnnotation":
|
||||
return concat(["typeof ", path.call(print, "argument")]);
|
||||
case "InferredPredicate":
|
||||
@@ -2251,87 +2164,6 @@ function printPathNoParens(path, options, print, args) {
|
||||
|
||||
return concat(parts);
|
||||
|
||||
case "NGRoot":
|
||||
return concat(
|
||||
[].concat(
|
||||
path.call(print, "node"),
|
||||
!n.node.comments || n.node.comments.length === 0
|
||||
? []
|
||||
: concat([" //", n.node.comments[0].value.trimEnd()])
|
||||
)
|
||||
);
|
||||
case "NGChainedExpression":
|
||||
return group(
|
||||
join(
|
||||
concat([";", line]),
|
||||
path.map(
|
||||
(childPath) =>
|
||||
hasNgSideEffect(childPath)
|
||||
? print(childPath)
|
||||
: concat(["(", print(childPath), ")"]),
|
||||
"expressions"
|
||||
)
|
||||
)
|
||||
);
|
||||
case "NGEmptyExpression":
|
||||
return "";
|
||||
case "NGQuotedExpression":
|
||||
return concat([n.prefix, ": ", n.value.trim()]);
|
||||
case "NGMicrosyntax":
|
||||
return concat(
|
||||
path.map(
|
||||
(childPath, index) =>
|
||||
concat([
|
||||
index === 0
|
||||
? ""
|
||||
: isNgForOf(childPath.getValue(), index, n)
|
||||
? " "
|
||||
: concat([";", line]),
|
||||
print(childPath),
|
||||
]),
|
||||
"body"
|
||||
)
|
||||
);
|
||||
case "NGMicrosyntaxKey":
|
||||
return /^[$_a-z][\w$]*(-[$_a-z][\w$])*$/i.test(n.name)
|
||||
? n.name
|
||||
: JSON.stringify(n.name);
|
||||
case "NGMicrosyntaxExpression":
|
||||
return concat([
|
||||
path.call(print, "expression"),
|
||||
n.alias === null ? "" : concat([" as ", path.call(print, "alias")]),
|
||||
]);
|
||||
case "NGMicrosyntaxKeyedExpression": {
|
||||
const index = path.getName();
|
||||
const parentNode = path.getParentNode();
|
||||
const shouldNotPrintColon =
|
||||
isNgForOf(n, index, parentNode) ||
|
||||
(((index === 1 && (n.key.name === "then" || n.key.name === "else")) ||
|
||||
(index === 2 &&
|
||||
n.key.name === "else" &&
|
||||
parentNode.body[index - 1].type ===
|
||||
"NGMicrosyntaxKeyedExpression" &&
|
||||
parentNode.body[index - 1].key.name === "then")) &&
|
||||
parentNode.body[0].type === "NGMicrosyntaxExpression");
|
||||
return concat([
|
||||
path.call(print, "key"),
|
||||
shouldNotPrintColon ? " " : ": ",
|
||||
path.call(print, "expression"),
|
||||
]);
|
||||
}
|
||||
case "NGMicrosyntaxLet":
|
||||
return concat([
|
||||
"let ",
|
||||
path.call(print, "key"),
|
||||
n.value === null ? "" : concat([" = ", path.call(print, "value")]),
|
||||
]);
|
||||
case "NGMicrosyntaxAs":
|
||||
return concat([
|
||||
path.call(print, "key"),
|
||||
" as ",
|
||||
path.call(print, "alias"),
|
||||
]);
|
||||
|
||||
case "PipelineBareFunction":
|
||||
return path.call(print, "callee");
|
||||
case "PipelineTopicExpression":
|
||||
|
||||
@@ -614,34 +614,6 @@ function hasDanglingComments(node) {
|
||||
);
|
||||
}
|
||||
|
||||
/** identify if an angular expression seems to have side effects */
|
||||
/**
|
||||
* @param {FastPath} path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasNgSideEffect(path) {
|
||||
return hasNode(path.getValue(), (node) => {
|
||||
switch (node.type) {
|
||||
case undefined:
|
||||
return false;
|
||||
case "CallExpression":
|
||||
case "OptionalCallExpression":
|
||||
case "AssignmentExpression":
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isNgForOf(node, index, parentNode) {
|
||||
return (
|
||||
node.type === "NGMicrosyntaxKeyedExpression" &&
|
||||
node.key.name === "of" &&
|
||||
index === 1 &&
|
||||
parentNode.body[0].type === "NGMicrosyntaxLet" &&
|
||||
parentNode.body[0].value === null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} node
|
||||
@@ -1519,7 +1491,6 @@ module.exports = {
|
||||
hasLeadingOwnLineComment,
|
||||
hasNakedLeftSide,
|
||||
hasNewlineBetweenOrAfterDecorators,
|
||||
hasNgSideEffect,
|
||||
hasNode,
|
||||
hasPrettierIgnore,
|
||||
hasTrailingComment,
|
||||
@@ -1549,7 +1520,6 @@ module.exports = {
|
||||
isMeaningfulJSXText,
|
||||
isMemberExpressionChain,
|
||||
isMemberish,
|
||||
isNgForOf,
|
||||
isNumericLiteral,
|
||||
isObjectType,
|
||||
isObjectTypePropertyAFunction,
|
||||
|
||||
Reference in new issue
Block a user