UNPKG

@stedi/prettier-plugin-jsonata

Version:
499 lines (498 loc) 18.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.print = void 0; const doc_1 = require("prettier/doc"); // Due to the nature of the prettier printer API and the lack of proper support for comments in Jsonata AST, // we have to populate `jsonataComments` from within the initial `print` function call, // and then track `previousNodePosition` position to know when to print comments. let jsonataComments = []; let previousNodePosition = -1; const print = (path, options, printChildren) => { var _a, _b; const node = path.getValue(); if ("jsonataComments" in node) { previousNodePosition = -1; jsonataComments = node.jsonataComments; } const nodePosition = (_b = (node.type === "path" ? (_a = node.steps[0]) === null || _a === void 0 ? void 0 : _a.position : node.position)) !== null && _b !== void 0 ? _b : previousNodePosition; const matchingComments = jsonataComments.filter((comment) => comment.position > previousNodePosition && comment.position < nodePosition); previousNodePosition = nodePosition; const commentsDoc = matchingComments.map(printComment); const result = printNode(node, path, options, printChildren); if (commentsDoc.length > 0) { return group([commentsDoc, result]); } return result; }; exports.print = print; const printNode = (node, ...commonPrintArgs) => { if (node.type === "binary") { return printBinaryNode(node, ...commonPrintArgs); } else if (node.type === "function") { return printFunctionNode(node, ...commonPrintArgs); } else if (node.type === "partial") { return printFunctionNode(node, ...commonPrintArgs); } else if (node.type === "variable") { return printVariableNode(node, ...commonPrintArgs); } else if (node.type === "wildcard") { return printWildcardNode(node, ...commonPrintArgs); } else if (node.type === "descendant") { return printDescendantNode(node, ...commonPrintArgs); } else if (node.type === "operator") { return printOperatorNode(node, ...commonPrintArgs); } else if (node.type === "number") { return printNumberNode(node, ...commonPrintArgs); } else if (node.type === "string") { return printStringNode(node, ...commonPrintArgs); } else if (node.type === "name") { return printNameNode(node, ...commonPrintArgs); } else if (node.type === "filter") { return printFilterNode(node, ...commonPrintArgs); } else if (node.type === "bind") { return printBindNode(node, ...commonPrintArgs); } else if (node.type === "lambda") { return printLambdaNode(node, ...commonPrintArgs); } else if (node.type === "condition") { return printConditionNode(node, ...commonPrintArgs); } else if (node.type === "value") { return printValueNode(node, ...commonPrintArgs); } else if (node.type === "block") { return printBlockNode(node, ...commonPrintArgs); } else if (node.type === "path") { return printPathNode(node, ...commonPrintArgs); } else if (node.type === "apply") { return printApplyNode(node, ...commonPrintArgs); } else if (node.type === "sort") { return printSortNode(node, ...commonPrintArgs); } else if (node.type === "unary") { return printUnaryNode(node, ...commonPrintArgs); } else if (node.type === "parent") { return printParentNode(node, ...commonPrintArgs); } else if (node.type === "regex") { return printRegExNode(node, ...commonPrintArgs); } else if (node.type === "transform") { return printTransformNode(node, ...commonPrintArgs); } throw new Error(`Unknown node type: ${node.type}`); }; const { group, indent, join, line, hardline, breakParent, softline } = doc_1.builders; const printComment = (comment) => { return group(["/* ", comment.value, " */", hardline]); }; const printBinaryNode = (node, path, options, printChildren) => { if (node.value === "..") { return group([printChildren("lhs"), indent([softline, node.value, printChildren("rhs")])]); } return group([printChildren("lhs"), indent([line, node.value, " ", printChildren("rhs")])]); }; const printNameNode = (node, path, options, printChildren) => { return group([ printEscapedNameNodeValue(node.value), printNodeFocus(node), printNodeIndex(node), printStages(node, path, options, printChildren), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printNumberNode = (node, path, options, printChildren) => { return group([ JSON.stringify(node.value), printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printStringNode = (node, path, options, printChildren) => { return group([ JSON.stringify(node.value), printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printPathNode = (node, path, options, printChildren) => { const steps = node.steps.flatMap((step, idx) => { if (idx === 0) { return printChildren(["steps", idx]); } if (step.type === "sort") { return indent(["^", printChildren(["steps", idx])]); } return indent([softline, ".", printChildren(["steps", idx])]); }); return group([...steps, printNodeGroup(node, path, options, printChildren)]); }; const printFunctionNode = (node, path, options, printChildren) => { return group([ printChildren("procedure"), "(", printFunctionArguments(node, path, options, printChildren), ")", printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), printStages(node, path, options, printChildren), ]); }; const printFunctionArguments = (node, path, options, printChildren) => { var _a; if (!((_a = node.arguments) === null || _a === void 0 ? void 0 : _a.length)) { return ""; } if (node.arguments.length === 1) { return printChildren(["arguments", 0]); } const joinedArguments = join([",", line], path.map(printChildren, "arguments")); return [indent([softline, joinedArguments]), softline]; }; const printVariableNode = (node, path, options, printChildren) => { return group([ "$", node.value, printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), printStages(node, path, options, printChildren), ]); }; const printOperatorNode = (node) => { return node.value; }; const printWildcardNode = (node) => { return node.value; }; const printDescendantNode = (node) => { return node.value; }; const printFilterNode = (node, path, options, printChildren) => { return group(["[", indent([softline, printChildren("expr")]), softline, "]"]); }; const printBindNode = (node, path, options, printChildren) => { return group([printChildren("lhs"), " ", node.value, " ", printChildren("rhs")]); }; const printLambdaNode = (node, path, options, printChildren) => { if (node.thunk) { return printChildren("body"); } return group([ "function(", printFunctionArguments(node, path, options, printChildren), ") {", indent([line, printChildren("body")]), line, "}", printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printConditionNode = (node, path, options, printChildren) => { const parentNode = path.getParentNode(); const isNestedCondition = (parentNode === null || parentNode === void 0 ? void 0 : parentNode.type) === "condition"; const linebreak = isNestedCondition ? [hardline, breakParent] : line; return group([ printChildren("condition"), indent([linebreak, "? ", printChildren("then")]), node.else ? indent([linebreak, ": ", printChildren("else")]) : "", ]); }; const printValueNode = (node, path, options, printChildren) => { return group([ printValueNodeValue(node, path, options, printChildren), printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printValueNodeValue = (node) => { if (node.value === null) return "null"; if (node.value === false) return "false"; if (node.value === true) return "true"; return JSON.stringify(node.value); }; const printBlockNode = (node, path, options, printChildren) => { if (node.expressions.length === 0) { return group(["()", printPredicate(node, path, options, printChildren), printKeepArray(node)]); } if (node.expressions.length === 1) { return group([ "(", printChildren(["expressions", 0]), ")", printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), printStages(node, path, options, printChildren), ]); } const joinedExpressions = join([";", hardline], path.map(printChildren, "expressions")); return group([ "(", indent([hardline, joinedExpressions]), hardline, ")", printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printApplyNode = (node, path, options, printChildren) => { return group([printChildren("lhs"), indent([line, node.value, " ", printChildren("rhs")])]); }; const printSortNode = (node, path, options, printChildren) => { const sortTerms = node.terms.map((term, idx) => { return [printSortTermPrefix(term), printChildren(["terms", idx, "expression"])]; }); const joinedSortTerms = join([",", line], sortTerms); return group([ "(", indent([softline, joinedSortTerms]), softline, ")", printNodeFocus(node), printNodeIndex(node), printStages(node, path, options, printChildren), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printSortTermPrefix = (sortTerm) => { return sortTerm.descending ? ">" : "<"; }; const printUnaryNode = (node, path, options, printChildren) => { if (node.value === "{") { return printObjectUnaryNode(node, path, options, printChildren); } if (node.value === "[") { return printArrayUnaryNode(node, path, options, printChildren); } if (node.value === "-") { return printNegationUnaryNode(node, path, options, printChildren); } throw new Error("Unhandled unary node " + node.value); }; const printObjectUnaryNode = (node, path, options, printChildren) => { return group([ "{", printUnaryTuplesForObjectUnaryNode(node, path, options, printChildren), "}", printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printUnaryTuplesForObjectUnaryNode = (node, path, options, printChildren) => { if (node.lhs.length === 0) { return ""; } const unaryTuples = node.lhs.map((tuple, idx) => group([printChildren(["lhs", idx, 0]), ": ", printChildren(["lhs", idx, 1])])); const hasNestedComplexUnaryNodeChildren = node.lhs.some((tuple) => isComplexUnaryNode(tuple[1])); const linebreak = hasNestedComplexUnaryNodeChildren ? [hardline, breakParent] : line; const joinedUnaryTuples = join([",", linebreak], unaryTuples); return [indent([linebreak, joinedUnaryTuples]), linebreak]; }; /** * Returns true, if the provided `node` argument represents an Unary Node * which could be considered complex ("negation" node is not considered context). * * This function can be used to decide which line break type to use based on AST tree complexity. */ const isComplexUnaryNode = (node) => { if (node.type !== "unary") { return false; } if (node.value === "[") { return true; } if (node.value === "{") { return true; } }; const printArrayUnaryNode = (node, path, options, printChildren) => { const joinedExpressions = join([",", line], path.map(printChildren, "expressions")); return group([ "[", indent([softline, joinedExpressions]), softline, "]", printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printNegationUnaryNode = (node, path, options, printChildren) => { return group(["-", printChildren("expression")]); }; const printParentNode = (node, path, options, printChildren) => { return group([ "%", printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), printStages(node, path, options, printChildren), ]); }; // https://github.com/jsonata-js/jsonata/blob/3cea53fe5f2bc94d9026fafb109a1c148fc7679b/src/parser.js#L95 const supportedRegexFlags = ["i", "m"]; const printRegExNode = (node, path, options, printChildren) => { const flags = supportedRegexFlags.filter((flag) => node.value.flags.includes(flag)).join(""); return group([ `/${node.value.source}/${flags}`, printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), printKeepArray(node), ]); }; const printTransformNode = (node, path, options, printChildren) => { const mutationParts = [printChildren("update")]; if (node.delete) { mutationParts.push(printChildren("delete")); } return group([ "|", join("|", [printChildren("pattern"), join([",", line], mutationParts)]), "|", printNodeFocus(node), printNodeIndex(node), printPredicate(node, path, options, printChildren), printNodeGroup(node, path, options, printChildren), ]); }; const printNodeFocus = (node) => { if (!node.focus) { return ""; } return "@$" + node.focus; }; const printNodeIndex = (node) => { if (!node.index) { return ""; } return "#$" + node.index; }; const printPredicate = (node, path, options, printChildren) => { if (!node.predicate) { return ""; } return path.map(printChildren, "predicate"); }; const printStages = (node, path, options, printChildren) => { if (!node.stages) { return ""; } return path.map(printChildren, "stages"); }; const printKeepArray = (node) => { if (!node.keepArray) { return ""; } return "[]"; }; const printNodeGroup = (node, path, options, printChildren) => { if (node.group === undefined) { return ""; } if (node.group.lhs.length === 0) { return "{}"; } const unaryTuples = node.group.lhs.map((tuple, idx) => group([printChildren(["group", "lhs", idx, 0]), ": ", printChildren(["group", "lhs", idx, 1])])); const hasNestedComplexUnaryNodeChildren = node.group.lhs.some((tuple) => isComplexUnaryNode(tuple[1])); const linebreak = hasNestedComplexUnaryNodeChildren ? [hardline, breakParent] : line; const joinedUnaryTuples = join([",", linebreak], unaryTuples); return group(["{", indent([linebreak, joinedUnaryTuples]), linebreak, "}"]); }; const printEscapedNameNodeValue = (value) => { if (value.startsWith("`") && value.endsWith("`")) { // If it's already wrapped in backticks - skip escaping logic below return value; } if (value.startsWith('"') && value.endsWith('"')) { // If it's wrapped in double-quotes - rewrap in backticks for consistency return `\`${value.slice(1, -1)}\``; } /** * The RegExp was split into multiple ones to avoid unnecessary complexity. */ const containsWhitespace = /\s/.test(value); if (containsWhitespace) { return `\`${value}\``; } const conflictsWithReservedWords = ["null", "false", "true"].includes(value); if (conflictsWithReservedWords) { return `\`${value}\``; } /** * ^ -> starts with * \d+ match 1 or more digits */ const startsWithDigit = /^\d+/.test(value); if (startsWithDigit) { return `\`${value}\``; } /** * [^ -> match single character NOT present in * a-z -> lower case letters * A-Z -> upper case letters * _ -> underscore * [ -> the '[' symbol * \] -> the ']' * -> \d a digit. The case where the path starts with a digit is handled in the previous if statement * ] -> close the group */ if (/[^a-zA-Z_[\]\d]/.test(value)) { return `\`${value}\``; } return value; };