UNPKG

prettierx

Version:

prettierX - a less opinionated fork of the Prettier code formatter

332 lines (290 loc) 9.47 kB
"use strict"; const { printComments, printDanglingComments } = require("../../main/comments"); const { getLast } = require("../../common/util"); const { builders: { group, join, line, softline, indent, align, ifBreak }, } = require("../../document"); const pathNeedsParens = require("../needs-parens"); const { locStart } = require("../loc"); const { isSimpleType, isObjectType, hasLeadingOwnLineComment, isObjectTypePropertyAFunction, shouldPrintComma, } = require("../utils"); const { printAssignment } = require("./assignment"); const { printFunctionParameters, shouldGroupFunctionParameters, } = require("./function-parameters"); const { printArrayItems } = require("./array"); function shouldHugType(node) { if (isSimpleType(node) || isObjectType(node)) { return true; } if (node.type === "UnionTypeAnnotation" || node.type === "TSUnionType") { const voidCount = node.types.filter( (node) => node.type === "VoidTypeAnnotation" || node.type === "TSVoidKeyword" || node.type === "NullLiteralTypeAnnotation" || node.type === "TSNullKeyword" ).length; const hasObject = node.types.some( (node) => node.type === "ObjectTypeAnnotation" || node.type === "TSTypeLiteral" || // This is a bit aggressive but captures Array<{x}> node.type === "GenericTypeAnnotation" || node.type === "TSTypeReference" ); if (node.types.length - 1 === voidCount && hasObject) { return true; } } return false; } function printOpaqueType(path, options, print) { const semi = options.semi ? ";" : ""; const node = path.getValue(); const parts = []; parts.push("opaque type ", print("id"), print("typeParameters")); if (node.supertype) { parts.push(": ", print("supertype")); } if (node.impltype) { parts.push(" = ", print("impltype")); } parts.push(semi); return parts; } function printTypeAlias(path, options, print) { const semi = options.semi ? ";" : ""; const node = path.getValue(); const parts = []; if (node.declare) { parts.push("declare "); } parts.push("type ", print("id"), print("typeParameters")); const rightPropertyName = node.type === "TSTypeAliasDeclaration" ? "typeAnnotation" : "right"; return [ printAssignment(path, options, print, parts, " =", rightPropertyName), semi, ]; } // `TSIntersectionType` and `IntersectionTypeAnnotation` function printIntersectionType(path, options, print) { const node = path.getValue(); const types = path.map(print, "types"); const result = []; let wasIndented = false; for (let i = 0; i < types.length; ++i) { if (i === 0) { result.push(types[i]); } else if (isObjectType(node.types[i - 1]) && isObjectType(node.types[i])) { // If both are objects, don't indent result.push([" & ", wasIndented ? indent(types[i]) : types[i]]); } else if ( !isObjectType(node.types[i - 1]) && !isObjectType(node.types[i]) ) { // If no object is involved, go to the next line if it breaks result.push(indent([" &", line, types[i]])); } else { // If you go from object to non-object or vis-versa, then inline it if (i > 1) { wasIndented = true; } result.push(" & ", i > 1 ? indent(types[i]) : types[i]); } } return group(result); } // `TSUnionType` and `UnionTypeAnnotation` function printUnionType(path, options, print) { const node = path.getValue(); // single-line variation // A | B | C // multi-line variation // | A // | B // | C const parent = path.getParentNode(); // If there's a leading comment, the parent is doing the indentation const shouldIndent = parent.type !== "TypeParameterInstantiation" && parent.type !== "TSTypeParameterInstantiation" && parent.type !== "GenericTypeAnnotation" && parent.type !== "TSTypeReference" && parent.type !== "TSTypeAssertion" && parent.type !== "TupleTypeAnnotation" && parent.type !== "TSTupleType" && !( parent.type === "FunctionTypeParam" && !parent.name && path.getParentNode(1).this !== parent ) && !( (parent.type === "TypeAlias" || parent.type === "VariableDeclarator" || parent.type === "TSTypeAliasDeclaration") && hasLeadingOwnLineComment(options.originalText, node) ); // { // a: string // } | null | void // should be inlined and not be printed in the multi-line variant const shouldHug = shouldHugType(node); // We want to align the children but without its comment, so it looks like // | child1 // // comment // | child2 const printed = path.map((typePath) => { let printedType = print(); if (!shouldHug) { printedType = align(2, printedType); } return printComments(typePath, printedType, options); }, "types"); if (shouldHug) { return join(" | ", printed); } const shouldAddStartLine = shouldIndent && !hasLeadingOwnLineComment(options.originalText, node); const code = [ ifBreak([shouldAddStartLine ? line : "", "| "]), join([line, "| "], printed), ]; if (pathNeedsParens(path, options)) { return group([indent(code), softline]); } if ( (parent.type === "TupleTypeAnnotation" && parent.types.length > 1) || (parent.type === "TSTupleType" && parent.elementTypes.length > 1) ) { return group([ indent([ifBreak(["(", softline]), code]), softline, ifBreak(")"), ]); } return group(shouldIndent ? indent(code) : code); } // `TSFunctionType` and `FunctionTypeAnnotation` function printFunctionType(path, options, print) { const node = path.getValue(); const parts = []; // FunctionTypeAnnotation is ambiguous: // declare function foo(a: B): void; OR // var A: (a: B) => void; const parent = path.getParentNode(0); const parentParent = path.getParentNode(1); const parentParentParent = path.getParentNode(2); let isArrowFunctionTypeAnnotation = node.type === "TSFunctionType" || !( ((parent.type === "ObjectTypeProperty" || parent.type === "ObjectTypeInternalSlot") && !parent.variance && !parent.optional && locStart(parent) === locStart(node)) || parent.type === "ObjectTypeCallProperty" || (parentParentParent && parentParentParent.type === "DeclareFunction") ); let needsColon = isArrowFunctionTypeAnnotation && (parent.type === "TypeAnnotation" || parent.type === "TSTypeAnnotation"); // Sadly we can't put it inside of AstPath::needsColon because we are // printing ":" as part of the expression and it would put parenthesis // around :( const needsParens = needsColon && isArrowFunctionTypeAnnotation && (parent.type === "TypeAnnotation" || parent.type === "TSTypeAnnotation") && parentParent.type === "ArrowFunctionExpression"; if (isObjectTypePropertyAFunction(parent)) { isArrowFunctionTypeAnnotation = true; needsColon = true; } // [prettierx] --space-in-parens option support (...) const insideSpace = options.spaceInParens ? " " : ""; if (needsParens) { // [prettierx] --space-in-parens option support (...) parts.push("(", insideSpace); } const parametersDoc = printFunctionParameters( path, print, options, /* expandArg */ false, /* printTypeParams */ true ); // The returnType is not wrapped in a TypeAnnotation, so the colon // needs to be added separately. const returnTypeDoc = node.returnType || node.predicate || node.typeAnnotation ? [ isArrowFunctionTypeAnnotation ? " => " : ": ", print("returnType"), print("predicate"), print("typeAnnotation"), ] : ""; const shouldGroupParameters = shouldGroupFunctionParameters( node, returnTypeDoc ); parts.push(shouldGroupParameters ? group(parametersDoc) : parametersDoc); if (returnTypeDoc) { parts.push(returnTypeDoc); } if (needsParens) { // [prettierx] --space-in-parens option support (...) parts.push(insideSpace, ")"); } return group(parts); } // `TSTupleType` and `TupleTypeAnnotation` function printTupleType(path, options, print) { const node = path.getValue(); const typesField = node.type === "TSTupleType" ? "elementTypes" : "types"; const hasRest = node[typesField].length > 0 && getLast(node[typesField]).type === "TSRestType"; return group([ "[", indent([softline, printArrayItems(path, options, typesField, print)]), ifBreak(shouldPrintComma(options, "all") && !hasRest ? "," : ""), printDanglingComments(path, options, /* sameIndent */ true), softline, "]", ]); } // `TSIndexedAccessType`, `IndexedAccessType`, and `OptionalIndexedAccessType` function printIndexedAccessType(path, options, print) { const node = path.getValue(); const leftDelimiter = node.type === "OptionalIndexedAccessType" && node.optional ? "?.[" : "["; return [ print("objectType"), leftDelimiter, // [prettierx] typeBracketSpacing option support (...) options.typeBracketSpacing ? " " : "", print("indexType"), // [prettierx] typeBracketSpacing option support (...) options.typeBracketSpacing ? " " : "", "]", ]; } module.exports = { printOpaqueType, printTypeAlias, printIntersectionType, printUnionType, printFunctionType, printTupleType, printIndexedAccessType, shouldHugType, };