@marko/prettyprint
Version:
Prettyprint Marko template files in the syntax of your choice
276 lines (213 loc) • 7.55 kB
JavaScript
;
const redent = require("redent");
const hasUnenclosedNewlines = require("./util/hasUnenclosedNewlines");
const hasUnenclosedWhitespace = require("./util/hasUnenclosedWhitespace");
const getBodyText = require("./util/getBodyText");
const hasLineBreaks = require("./util/hasLineBreaks");
const printers = require("./printers");
const Writer = require("./util/Writer");
const formattingTags = require("./formatting-tags");
const formatJS = require("./util/formatJS");
const formatArgument = require("./util/formatArgument");
const formatParams = require("./util/formatParams");
const formatStyles = require("./util/formatStyles");
const codeTags = {
class: {
type: "js",
prettyprint: true
},
import: {
type: "js",
prettyprint: false
},
static: {
type: "js",
prettyprint: true
},
style: {
type: "style",
prettyprint: true
}
};
function isComponentStyleTag(node) {
var attrs = node.getAttributes();
var attrCount = attrs.length;
if (!attrCount) {
return false;
}
var lastAttr = attrs[attrCount - 1];
return /\s*\{/.test(lastAttr.name);
}
function handleCodeTag(node, printContext, writer) {
var tagName = node.tagName;
let codeTagInfo = codeTags[tagName];
if (!codeTagInfo) {
return false;
}
if (tagName === "style" && !isComponentStyleTag(node)) {
return false;
}
let outputCode = node.tagString;
if (codeTagInfo.prettyprint === true) {
if (codeTagInfo.type === "js") {
if (tagName === "static") {
outputCode = outputCode.replace(/^\s*static\s*/, "");
}
outputCode = formatJS(outputCode, printContext);
if (tagName === "static") {
outputCode = "static " + outputCode;
}
} else if (codeTagInfo.type === "style") {
outputCode = formatStyles(outputCode, printContext);
}
}
writer.write(outputCode);
return true;
}
module.exports = function printHtmlElement(node, printContext, writer) {
if (node.hasAttribute("marko-preserve-whitespace")) {
printContext = printContext.startPreservingWhitespace();
} else if (node.tagDef && node.tagDef.preserveWhitespace === true) {
printContext = printContext.startPreservingWhitespace();
}
if (printContext.depth === 0 && handleCodeTag(node, printContext, writer)) {
return;
}
var tagNameExpression = node.rawTagNameExpression;
var preserveBodyWhitespace = printContext.preserveWhitespace === true;
var maxLen = printContext.maxLen;
if (preserveBodyWhitespace || tagNameExpression) {
// We can only reliably preserve whitespace in HTML mode so we force the HTML
// syntax if we detect that whitespace preserval is enabled
printContext = printContext.switchToHtmlSyntax();
}
if (!printContext.isConciseSyntax) {
writer.write("<");
}
var tagNameString = tagNameExpression ? `\${${formatJS(tagNameExpression, printContext, true)}}` : node.tagName;
var endTagString = tagNameExpression ? "</>" : "</" + node.tagName + ">";
writer.write(tagNameString);
if (node.rawShorthandId) {
writer.write("#" + node.rawShorthandId);
}
if (node.rawShorthandClassNames) {
node.rawShorthandClassNames.forEach(className => {
if (typeof className === "string") {
writer.write("." + className);
} else {
writer.write(".${" + formatJS(className, printContext, true) + "}");
}
});
}
if (node.argument != null) {
writer.write(formatArgument(node, printContext));
}
if (node.params != null) {
writer.write(formatParams(node, printContext));
}
var attrs = node.getAttributes();
var hasBody = node.body && node.body.length;
let bodyText = getBodyText(node, printContext);
if (bodyText && printContext.preserveWhitespace !== true) {
bodyText = bodyText.trim();
}
if (bodyText != null && bodyText.length === 0) {
bodyText = null;
hasBody = false;
} // We will make one pass to generate all of the strings for each attribute. We will then
// append them to the output while avoiding putting too many attributes on one line.
var isSingleAttr = attrs.length <= 1;
var attrPrintContext = isSingleAttr ? printContext : printContext.beginNested();
var attrStringsArray = attrs.map(attr => {
var attrStr = "";
var attrValueStr = formatJS(attr.value, attrPrintContext, true);
if (attrValueStr) {
if (hasUnenclosedNewlines(attrValueStr)) {
attrValueStr = `\n${redent(attrPrintContext.currentIndentString + attrValueStr, attrPrintContext.depth + 1, attrPrintContext.indentString)}\n${attrPrintContext.currentIndentString}`;
}
if (hasUnenclosedWhitespace(attrValueStr)) {
attrValueStr = `(${attrValueStr})`;
}
}
if (attr.name) {
attrStr += attr.name;
if (attrValueStr) {
attrStr += `=${attrValueStr}`;
} else if (attr.argument != null) {
attrStr += formatArgument(attr, attrPrintContext);
}
} else if (attr.spread) {
attrStr += `...${attrValueStr}`;
} else {
attrStr += `\${${attrValueStr}}`;
}
return attrStr;
}); // Let's see if all of the attributes will fit on the same line
var oneLineAttrsStr = attrStringsArray.join(" ");
var attrsFitOneLine = isSingleAttr || writer.col + oneLineAttrsStr.length < maxLen && !/[\r\n]/.test(oneLineAttrsStr);
if (attrStringsArray.length) {
if (attrsFitOneLine) {
writer.write(` ${oneLineAttrsStr}`);
} else {
if (printContext.isConciseSyntax) {
writer.write(" [");
}
writer.write(attrStringsArray.map(attrStr => `\n${attrPrintContext.currentIndentString + attrStr}`).join(""));
if (printContext.isConciseSyntax) {
writer.write(`\n${printContext.currentIndentString}]`);
}
}
}
if (printContext.isHtmlSyntax) {
writer.write(hasBody ? ">" : "/>");
}
if (!hasBody) {
return;
}
var endTag = printContext.isHtmlSyntax ? endTagString : "";
if ((printContext.isConciseSyntax || attrsFitOneLine) && bodyText && !hasLineBreaks(bodyText)) {
let endCol = writer.col + bodyText.length + endTag.length;
if (endCol < maxLen) {
if (printContext.isConciseSyntax) {
writer.write(" -- " + bodyText);
} else {
writer.write(bodyText + endTagString);
}
return;
}
}
if (!preserveBodyWhitespace) {
writer.write(printContext.eol);
}
var nestedPrintContext = printContext.beginNested();
if (printContext.isHtmlSyntax && formattingTags[node.tagName]) {
let nestedWriter = new Writer(writer.col);
printers.printNodes(node.body.items, nestedPrintContext, nestedWriter);
let trimmedOutput = nestedWriter.getOutput();
if (preserveBodyWhitespace !== true) {
trimmedOutput = nestedWriter.getOutput().trim();
}
if (hasLineBreaks(trimmedOutput)) {
if (writer.getOutput().endsWith(printContext.eol + printContext.indentString) === false) {
writer.write(printContext.indentString);
}
writer.write(nestedWriter.getOutput());
writer.write(printContext.currentIndentString);
writer.write(endTag);
} else {
if (preserveBodyWhitespace !== true) {
writer.rtrim();
}
writer.write(trimmedOutput);
writer.write(endTag);
}
} else {
printers.printNodes(node.body.items, nestedPrintContext, writer);
if (printContext.isHtmlSyntax) {
if (!preserveBodyWhitespace) {
writer.write(printContext.currentIndentString);
}
writer.write(endTag);
}
}
};