"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const doc_1 = require("prettier/doc"); const embed_1 = __importDefault(require("./embed")); const { fill, group, hardline, indent, join, line, literalline, softline } = doc_1.builders; const ignoreStartComment = ""; const ignoreEndComment = ""; function hasIgnoreRanges(comments) { if (!comments || comments.length === 0) { return false; } comments.sort((left, right) => left.startOffset - right.startOffset); let startFound = false; for (let idx = 0; idx < comments.length; idx += 1) { if (comments[idx].image === ignoreStartComment) { startFound = true; } else if (startFound && comments[idx].image === ignoreEndComment) { return true; } } return false; } function isWhitespaceIgnorable(node) { const { CData, Comment, reference } = node.children; return !CData && !reference && !hasIgnoreRanges(Comment); } function printIToken(path) { const node = path.getValue(); return { offset: node.startOffset, startLine: node.startLine, endLine: node.endLine, printed: node.image }; } function replaceNewlinesWithLiteralLines(content) { return content .split(/(\n)/g) .map((value, idx) => (idx % 2 === 0 ? value : literalline)); } const printer = { embed: embed_1.default, print(path, opts, print) { const node = path.getValue(); switch (node.name) { case "attribute": { const { Name, EQUALS, STRING } = node.children; return [Name[0].image, EQUALS[0].image, STRING[0].image]; } case "chardata": { const { SEA_WS, TEXT } = node.children; const [{ image }] = SEA_WS || TEXT; return image .split(/(\n)/g) .map((value, index) => (index % 2 === 0 ? value : literalline)); } case "content": { const nodePath = path; let fragments = nodePath.call((childrenPath) => { let response = []; const children = childrenPath.getValue(); if (children.CData) { response = response.concat(childrenPath.map(printIToken, "CData")); } if (children.Comment) { response = response.concat(childrenPath.map(printIToken, "Comment")); } if (children.chardata) { response = response.concat(childrenPath.map((charDataPath) => ({ offset: charDataPath.getValue().location.startOffset, printed: print(charDataPath) }), "chardata")); } if (children.element) { response = response.concat(childrenPath.map((elementPath) => ({ offset: elementPath.getValue().location.startOffset, printed: print(elementPath) }), "element")); } if (children.PROCESSING_INSTRUCTION) { response = response.concat(childrenPath.map(printIToken, "PROCESSING_INSTRUCTION")); } if (children.reference) { response = response.concat(childrenPath.map((referencePath) => { const referenceNode = referencePath.getValue(); return { offset: referenceNode.location.startOffset, printed: (referenceNode.children.CharRef || referenceNode.children.EntityRef)[0].image }; }, "reference")); } return response; }, "children"); const { Comment } = node.children; if (hasIgnoreRanges(Comment)) { Comment.sort((left, right) => left.startOffset - right.startOffset); const ignoreRanges = []; let ignoreStart = null; // Build up a list of ignored ranges from the original text based on the // special prettier-ignore-* comments Comment.forEach((comment) => { if (comment.image === ignoreStartComment) { ignoreStart = comment; } else if (ignoreStart && comment.image === ignoreEndComment) { ignoreRanges.push({ start: ignoreStart.startOffset, end: comment.endOffset }); ignoreStart = null; } }); // Filter the printed children to only include the ones that are outside // of each of the ignored ranges fragments = fragments.filter((fragment) => ignoreRanges.every(({ start, end }) => fragment.offset < start || fragment.offset > end)); // Push each of the ignored ranges into the child list as its own element // so that the original content is still included ignoreRanges.forEach(({ start, end }) => { const content = opts.originalText.slice(start, end + 1); fragments.push({ offset: start, printed: replaceNewlinesWithLiteralLines(content) }); }); } fragments.sort((left, right) => left.offset - right.offset); return group(fragments.map(({ printed }) => printed)); } case "docTypeDecl": { const { DocType, Name, externalID, CLOSE } = node.children; const parts = [DocType[0].image, " ", Name[0].image]; if (externalID) { parts.push(" ", path.call(print, "children", "externalID", 0)); } return group([...parts, CLOSE[0].image]); } case "document": { const { docTypeDecl, element, misc, prolog } = node.children; const fragments = []; if (docTypeDecl) { fragments.push({ offset: docTypeDecl[0].location.startOffset, printed: path.call(print, "children", "docTypeDecl", 0) }); } if (prolog) { fragments.push({ offset: prolog[0].location.startOffset, printed: path.call(print, "children", "prolog", 0) }); } if (misc) { misc.forEach((node) => { if (node.children.PROCESSING_INSTRUCTION) { fragments.push({ offset: node.location.startOffset, printed: node.children.PROCESSING_INSTRUCTION[0].image }); } else if (node.children.Comment) { fragments.push({ offset: node.location.startOffset, printed: node.children.Comment[0].image }); } }); } if (element) { fragments.push({ offset: element[0].location.startOffset, printed: path.call(print, "children", "element", 0) }); } fragments.sort((left, right) => left.offset - right.offset); return [ join(hardline, fragments.map(({ printed }) => printed)), hardline ]; } case "element": { const { OPEN, Name, attribute, START_CLOSE, content, SLASH_OPEN, END_NAME, END, SLASH_CLOSE } = node.children; const parts = [OPEN[0].image, Name[0].image]; if (attribute) { const separator = opts.singleAttributePerLine ? hardline : line; parts.push(indent([ line, join(separator, path.map(print, "children", "attribute")) ])); } // Determine the value that will go between the <, name, and attributes // of an element and the /> of an element. const space = opts.xmlSelfClosingSpace ? line : softline; if (SLASH_CLOSE) { return group([...parts, space, SLASH_CLOSE[0].image]); } if (Object.keys(content[0].children).length === 0) { return group([...parts, space, "/>"]); } const openTag = group([ ...parts, opts.bracketSameLine ? "" : softline, START_CLOSE[0].image ]); const closeTag = group([ SLASH_OPEN[0].image, END_NAME[0].image, END[0].image ]); if (opts.xmlWhitespaceSensitivity === "ignore" && isWhitespaceIgnorable(content[0])) { const nodePath = path; const fragments = nodePath.call((childrenPath) => { const children = childrenPath.getValue(); let response = []; if (children.Comment) { response = response.concat(childrenPath.map(printIToken, "Comment")); } if (children.chardata) { childrenPath.each((charDataPath) => { const chardata = charDataPath.getValue(); if (!chardata.children.TEXT) { return; } const content = chardata.children.TEXT[0].image.trim(); const printed = group(content.split(/(\n)/g).map((value) => { if (value === "\n") { return literalline; } return fill(value .split(/\b( +)\b/g) .map((segment, index) => index % 2 === 0 ? segment : line)); })); const location = chardata.location; response.push({ offset: location.startOffset, startLine: location.startLine, endLine: location.endLine, printed }); }, "chardata"); } if (children.element) { response = response.concat(childrenPath.map((elementPath) => { const location = elementPath.getValue().location; return { offset: location.startOffset, startLine: location.startLine, endLine: location.endLine, printed: print(elementPath) }; }, "element")); } if (children.PROCESSING_INSTRUCTION) { response = response.concat(childrenPath.map(printIToken, "PROCESSING_INSTRUCTION")); } return response; }, "children", "content", 0, "children"); fragments.sort((left, right) => left.offset - right.offset); // If the only content of this tag is chardata, then use a softline so // that we won't necessarily break (to allow bar). if (fragments.length === 1 && (content[0].children.chardata || []).filter((chardata) => chardata.children.TEXT).length === 1) { return group([ openTag, indent([softline, fragments[0].printed]), softline, closeTag ]); } if (fragments.length === 0) { return group([...parts, space, "/>"]); } const docs = []; let lastLine = fragments[0].startLine; fragments.forEach((node) => { if (node.startLine - lastLine >= 2) { docs.push(hardline, hardline); } else { docs.push(hardline); } docs.push(node.printed); lastLine = node.endLine; }); return group([openTag, indent(docs), hardline, closeTag]); } return group([ openTag, indent(path.call(print, "children", "content", 0)), closeTag ]); } case "externalID": { const { Public, PubIDLiteral, System, SystemLiteral } = node.children; if (System) { return group([ System[0].image, indent([line, SystemLiteral[0].image]) ]); } return group([ group([Public[0].image, indent([line, PubIDLiteral[0].image])]), indent([line, SystemLiteral[0].image]) ]); } case "prolog": { const { XMLDeclOpen, attribute, SPECIAL_CLOSE } = node.children; const parts = [XMLDeclOpen[0].image]; if (attribute) { parts.push(indent([ softline, join(line, path.map(print, "children", "attribute")) ])); } const space = opts.xmlSelfClosingSpace ? line : softline; return group([...parts, space, SPECIAL_CLOSE[0].image]); } } } }; exports.default = printer;