"use strict"; const { builders: { concat }, utils: { mapDoc } } = require("../doc"); const { CSS_DISPLAY_TAGS, CSS_DISPLAY_DEFAULT, CSS_WHITE_SPACE_TAGS, CSS_WHITE_SPACE_DEFAULT } = require("./constants.evaluate"); const htmlTagNames = require("html-tag-names"); const htmlElementAttributes = require("html-element-attributes"); const HTML_TAGS = arrayToMap(htmlTagNames); const HTML_ELEMENT_ATTRIBUTES = mapObject(htmlElementAttributes, arrayToMap); function arrayToMap(array) { const map = Object.create(null); for (const value of array) { map[value] = true; } return map; } function mapObject(object, fn) { const newObject = Object.create(null); for (const key of Object.keys(object)) { newObject[key] = fn(object[key], key); } return newObject; } function shouldPreserveContent(node) { if ( node.type === "element" && node.fullName === "template" && node.attrMap.lang && node.attrMap.lang !== "html" ) { return true; } // unterminated node in ie conditional comment // e.g. if ( node.type === "ieConditionalComment" && node.lastChild && !node.lastChild.isSelfClosing && !node.lastChild.endSourceSpan ) { return true; } // TODO: handle non-text children in
if ( isPreLikeNode(node) && node.children.some( child => child.type !== "text" && child.type !== "interpolation" ) ) { return true; } return false; } function hasPrettierIgnore(node) { if (node.type === "attribute" || node.type === "text") { return false; } if (!node.parent) { return false; } if (typeof node.index !== "number" || node.index === 0) { return false; } const prevNode = node.parent.children[node.index - 1]; return isPrettierIgnore(prevNode); } function isPrettierIgnore(node) { return node.type === "comment" && node.value.trim() === "prettier-ignore"; } function getPrettierIgnoreAttributeCommentData(value) { const match = value.trim().match(/^prettier-ignore-attribute(?:\s+([^]+))?$/); if (!match) { return false; } if (!match[1]) { return true; } return match[1].split(/\s+/); } function isScriptLikeTag(node) { return ( node.type === "element" && (node.fullName === "script" || node.fullName === "style" || node.fullName === "svg:style") ); } function isFrontMatterNode(node) { return node.type === "yaml" || node.type === "toml"; } function canHaveInterpolation(node) { return node.children && !isScriptLikeTag(node); } function isWhitespaceSensitiveNode(node) { return ( isScriptLikeTag(node) || node.type === "interpolation" || isIndentationSensitiveNode(node) ); } function isIndentationSensitiveNode(node) { return getNodeCssStyleWhiteSpace(node).startsWith("pre"); } function isLeadingSpaceSensitiveNode(node) { if (isFrontMatterNode(node)) { return false; } if (!node.parent || node.parent.cssDisplay === "none") { return false; } if ( !node.prev && node.parent.type === "element" && node.parent.tagDefinition.ignoreFirstLf ) { return false; } if (isPreLikeNode(node.parent)) { return true; } if ( !node.prev && (node.parent.type === "root" || isScriptLikeTag(node.parent) || !isFirstChildLeadingSpaceSensitiveCssDisplay(node.parent.cssDisplay)) ) { return false; } if ( node.prev && !isNextLeadingSpaceSensitiveCssDisplay(node.prev.cssDisplay) ) { return false; } return true; } function isTrailingSpaceSensitiveNode(node) { if (isFrontMatterNode(node)) { return false; } if (!node.parent || node.parent.cssDisplay === "none") { return false; } if (isPreLikeNode(node.parent)) { return true; } if ( !node.next && (node.parent.type === "root" || isScriptLikeTag(node.parent) || !isLastChildTrailingSpaceSensitiveCssDisplay(node.parent.cssDisplay)) ) { return false; } if ( node.next && !isPrevTrailingSpaceSensitiveCssDisplay(node.next.cssDisplay) ) { return false; } return true; } function isDanglingSpaceSensitiveNode(node) { return ( isDanglingSpaceSensitiveCssDisplay(node.cssDisplay) && !isScriptLikeTag(node) ); } function replaceNewlines(text, replacement) { return text .split(/(\n)/g) .map((data, index) => (index % 2 === 1 ? replacement : data)); } function replaceDocNewlines(doc, replacement) { return mapDoc(doc, currentDoc => typeof currentDoc === "string" && currentDoc.includes("\n") ? concat(replaceNewlines(currentDoc, replacement)) : currentDoc ); } function forceNextEmptyLine(node) { return ( isFrontMatterNode(node) || (node.next && node.sourceSpan.end.line + 1 < node.next.sourceSpan.start.line) ); } /** firstChild leadingSpaces and lastChild trailingSpaces */ function forceBreakContent(node) { return ( forceBreakChildren(node) || (node.type === "element" && node.children.length !== 0 && (["body", "template", "script", "style"].indexOf(node.name) !== -1 || node.children.some(child => hasNonTextChild(child)))) ); } /** spaces between children */ function forceBreakChildren(node) { return ( node.type === "element" && node.children.length !== 0 && (["html", "head", "ul", "ol", "select"].indexOf(node.name) !== -1 || (node.cssDisplay.startsWith("table") && node.cssDisplay !== "table-cell")) ); } function preferHardlineAsLeadingSpaces(node) { return ( preferHardlineAsSurroundingSpaces(node) || (node.prev && preferHardlineAsTrailingSpaces(node.prev)) || isCustomElementWithSurroundingLineBreak(node) ); } function preferHardlineAsTrailingSpaces(node) { return ( preferHardlineAsSurroundingSpaces(node) || (node.type === "element" && node.fullName === "br") || isCustomElementWithSurroundingLineBreak(node) ); } function isCustomElementWithSurroundingLineBreak(node) { return isCustomElement(node) && hasSurroundingLineBreak(node); } function isCustomElement(node) { return ( node.type === "element" && !node.namespace && (node.name.includes("-") || /[A-Z]/.test(node.name[0])) ); } function hasSurroundingLineBreak(node) { return hasLeadingLineBreak(node) && hasTrailingLineBreak(node); } function hasLeadingLineBreak(node) { return ( node.hasLeadingSpaces && (node.prev ? node.prev.sourceSpan.end.line < node.sourceSpan.start.line : node.parent.type === "root" || node.parent.startSourceSpan.end.line < node.sourceSpan.start.line) ); } function hasTrailingLineBreak(node) { return ( node.hasTrailingSpaces && (node.next ? node.next.sourceSpan.start.line > node.sourceSpan.end.line : node.parent.type === "root" || node.parent.endSourceSpan.start.line > node.sourceSpan.end.line) ); } function preferHardlineAsSurroundingSpaces(node) { switch (node.type) { case "ieConditionalComment": case "comment": case "directive": return true; case "element": return ["script", "select"].indexOf(node.name) !== -1; } return false; } function getLastDescendant(node) { return node.lastChild ? getLastDescendant(node.lastChild) : node; } function hasNonTextChild(node) { return node.children && node.children.some(child => child.type !== "text"); } function inferScriptParser(node) { if (node.name === "script" && !node.attrMap.src) { if ( (!node.attrMap.lang && !node.attrMap.type) || node.attrMap.type === "module" || node.attrMap.type === "text/javascript" || node.attrMap.type === "text/babel" || node.attrMap.type === "application/javascript" ) { return "babylon"; } if ( node.attrMap.type === "application/x-typescript" || node.attrMap.lang === "ts" || node.attrMap.lang === "tsx" ) { return "typescript"; } if (node.attrMap.type === "text/markdown") { return "markdown"; } } if (node.name === "style") { if (!node.attrMap.lang || node.attrMap.lang === "postcss") { return "css"; } if (node.attrMap.lang === "scss") { return "scss"; } if (node.attrMap.lang === "less") { return "less"; } } return null; } function isBlockLikeCssDisplay(cssDisplay) { return ( cssDisplay === "block" || cssDisplay === "list-item" || cssDisplay.startsWith("table") ); } function isFirstChildLeadingSpaceSensitiveCssDisplay(cssDisplay) { return !isBlockLikeCssDisplay(cssDisplay) && cssDisplay !== "inline-block"; } function isLastChildTrailingSpaceSensitiveCssDisplay(cssDisplay) { return !isBlockLikeCssDisplay(cssDisplay) && cssDisplay !== "inline-block"; } function isPrevTrailingSpaceSensitiveCssDisplay(cssDisplay) { return !isBlockLikeCssDisplay(cssDisplay); } function isNextLeadingSpaceSensitiveCssDisplay(cssDisplay) { return !isBlockLikeCssDisplay(cssDisplay); } function isDanglingSpaceSensitiveCssDisplay(cssDisplay) { return !isBlockLikeCssDisplay(cssDisplay) && cssDisplay !== "inline-block"; } function isPreLikeNode(node) { return getNodeCssStyleWhiteSpace(node).startsWith("pre"); } function countParents(path, predicate = () => true) { let counter = 0; for (let i = path.stack.length - 1; i >= 0; i--) { const value = path.stack[i]; if ( value && typeof value === "object" && !Array.isArray(value) && predicate(value) ) { counter++; } } return counter; } function hasParent(node, fn) { let current = node; while (current) { if (fn(current)) { return true; } current = current.parent; } return false; } function getNodeCssStyleDisplay(node, options) { if (node.prev && node.prev.type === "comment") { // const match = node.prev.value.match(/^\s*display:\s*([a-z]+)\s*$/); if (match) { return match[1]; } } let isInSvgForeignObject = false; if (node.type === "element" && node.namespace === "svg") { if (hasParent(node, parent => parent.fullName === "svg:foreignObject")) { isInSvgForeignObject = true; } else { return node.name === "svg" ? "inline-block" : "block"; } } switch (options.htmlWhitespaceSensitivity) { case "strict": return "inline"; case "ignore": return "block"; default: return ( (node.type === "element" && (!node.namespace || isInSvgForeignObject) && CSS_DISPLAY_TAGS[node.name]) || CSS_DISPLAY_DEFAULT ); } } function getNodeCssStyleWhiteSpace(node) { return ( (node.type === "element" && !node.namespace && CSS_WHITE_SPACE_TAGS[node.name]) || CSS_WHITE_SPACE_DEFAULT ); } function getCommentData(node) { const rightTrimmedValue = node.value.trimRight(); const hasLeadingEmptyLine = /^[^\S\n]*?\n/.test(node.value); if (hasLeadingEmptyLine) { /** * */ return dedentString(rightTrimmedValue.replace(/^\s*\n/, "")); } /** * * * * * */ if (!rightTrimmedValue.includes("\n")) { return rightTrimmedValue.trimLeft(); } const firstNewlineIndex = rightTrimmedValue.indexOf("\n"); const dataWithoutLeadingLine = rightTrimmedValue.slice(firstNewlineIndex + 1); const minIndentationForDataWithoutLeadingLine = getMinIndentation( dataWithoutLeadingLine ); const leadingSpaces = rightTrimmedValue.match(/^[^\n\S]*/)[0].length; const commentDataStartColumn = node.sourceSpan.start.col + " */ if (minIndentationForDataWithoutLeadingLine >= commentDataStartColumn) { return dedentString( " ".repeat(commentDataStartColumn) + rightTrimmedValue.slice(leadingSpaces) ); } const leadingLineValue = rightTrimmedValue.slice(0, firstNewlineIndex); /** * */ return ( leadingLineValue.trim() + "\n" + dedentString( dataWithoutLeadingLine, minIndentationForDataWithoutLeadingLine ) ); } function getMinIndentation(text) { let minIndentation = Infinity; for (const lineText of text.split("\n")) { if (lineText.length === 0) { continue; } if (/\S/.test(lineText[0])) { return 0; } const indentation = lineText.match(/^\s*/)[0].length; if (lineText.length === indentation) { continue; } if (indentation < minIndentation) { minIndentation = indentation; } } return minIndentation === Infinity ? 0 : minIndentation; } function dedentString(text, minIndent = getMinIndentation(text)) { return minIndent === 0 ? text : text .split("\n") .map(lineText => lineText.slice(minIndent)) .join("\n"); } function normalizeParts(parts) { const newParts = []; const restParts = parts.slice(); while (restParts.length !== 0) { const part = restParts.shift(); if (!part) { continue; } if (part.type === "concat") { Array.prototype.unshift.apply(restParts, part.parts); continue; } if ( newParts.length !== 0 && typeof newParts[newParts.length - 1] === "string" && typeof part === "string" ) { newParts.push(newParts.pop() + part); continue; } newParts.push(part); } return newParts; } function identity(x) { return x; } function shouldNotPrintClosingTag(node) { return ( !node.isSelfClosing && !node.endSourceSpan && (hasPrettierIgnore(node) || shouldPreserveContent(node.parent)) ); } module.exports = { HTML_ELEMENT_ATTRIBUTES, HTML_TAGS, canHaveInterpolation, countParents, dedentString, forceBreakChildren, forceBreakContent, forceNextEmptyLine, getCommentData, getLastDescendant, getNodeCssStyleDisplay, getNodeCssStyleWhiteSpace, getPrettierIgnoreAttributeCommentData, hasPrettierIgnore, identity, inferScriptParser, isDanglingSpaceSensitiveNode, isFrontMatterNode, isIndentationSensitiveNode, isLeadingSpaceSensitiveNode, isPreLikeNode, isScriptLikeTag, isTrailingSpaceSensitiveNode, isWhitespaceSensitiveNode, normalizeParts, preferHardlineAsLeadingSpaces, preferHardlineAsTrailingSpaces, replaceDocNewlines, replaceNewlines, shouldNotPrintClosingTag, shouldPreserveContent };