327 lines
15 KiB
JavaScript
327 lines
15 KiB
JavaScript
"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 = "<!-- prettier-ignore-start -->";
|
|
const ignoreEndComment = "<!-- prettier-ignore-end -->";
|
|
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 <foo>bar</foo>).
|
|
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;
|