release: WRNexusJS 0.3.5
This commit is contained in:
+117
-34
@@ -33,6 +33,16 @@ function splitPropDeclarations(value) {
|
||||
const beginsDeclaration = (position) => {
|
||||
let cursor = position;
|
||||
while (cursor < value.length && /[ \t]/.test(value[cursor])) cursor += 1;
|
||||
if (value.slice(cursor).startsWith("@event")) {
|
||||
cursor += "@event".length;
|
||||
if (!/\s/.test(value[cursor] || "")) return false;
|
||||
while (cursor < value.length && /\s/.test(value[cursor])) cursor += 1;
|
||||
if (!isIdentifierStart(value[cursor])) return false;
|
||||
cursor += 1;
|
||||
while (cursor < value.length && isIdentifierPart(value[cursor])) cursor += 1;
|
||||
while (cursor < value.length && /[ \t]/.test(value[cursor])) cursor += 1;
|
||||
return value[cursor] === "=";
|
||||
}
|
||||
if (!isIdentifierStart(value[cursor])) return false;
|
||||
cursor += 1;
|
||||
while (cursor < value.length && isIdentifierPart(value[cursor])) cursor += 1;
|
||||
@@ -69,6 +79,7 @@ function splitPropDeclarations(value) {
|
||||
brace === 0 &&
|
||||
paren === 0 &&
|
||||
/\s/.test(character) &&
|
||||
value.slice(start, index).trim() !== "@event" &&
|
||||
beginsDeclaration(index)
|
||||
) {
|
||||
const declaration = value.slice(start, index).trim();
|
||||
@@ -154,6 +165,10 @@ function parseAttributes(value) {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
function escapeRegExp(value) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
function parseOpeningTag(value) {
|
||||
const endIndex = findOpeningTagEnd(value);
|
||||
|
||||
@@ -162,7 +177,6 @@ function parseOpeningTag(value) {
|
||||
}
|
||||
|
||||
const openingPart = value.slice(0, endIndex + 1);
|
||||
|
||||
const remainder = value.slice(endIndex + 1).trim();
|
||||
|
||||
const match = /^<([A-Za-z][\w$:.-]*)([\s\S]*?)(\/?)>$/.exec(openingPart);
|
||||
@@ -172,26 +186,39 @@ function parseOpeningTag(value) {
|
||||
}
|
||||
|
||||
const tagName = match[1];
|
||||
|
||||
const attributes = parseAttributes(match[2].trim());
|
||||
|
||||
const selfClosing = match[3] === "/";
|
||||
|
||||
const inlineClosing = remainder === `</${tagName}>`;
|
||||
const escapedTagName = escapeRegExp(tagName);
|
||||
|
||||
const closesInRemainder = remainder.startsWith(`</${tagName}>`);
|
||||
const immediateClosing = new RegExp(`^<\\/${escapedTagName}\\s*>`, "i").test(remainder);
|
||||
|
||||
const trailingClosingMatch = new RegExp(`^([\\s\\S]*?)<\\/${escapedTagName}\\s*>$`, "i").exec(
|
||||
remainder,
|
||||
);
|
||||
|
||||
const trailingClosing = trailingClosingMatch !== null;
|
||||
|
||||
const inlineContent = trailingClosing ? trailingClosingMatch[1].trim() : "";
|
||||
|
||||
const inlineClosing = trailingClosing && inlineContent.length === 0;
|
||||
|
||||
const closesInRemainder = immediateClosing || trailingClosing;
|
||||
|
||||
return {
|
||||
tagName,
|
||||
attributes,
|
||||
selfClosing,
|
||||
inlineClosing,
|
||||
immediateClosing,
|
||||
trailingClosing,
|
||||
inlineContent,
|
||||
closesInRemainder,
|
||||
remainder,
|
||||
};
|
||||
}
|
||||
|
||||
function formatOpeningTag(value, unit, depth, printWidth = 100) {
|
||||
function formatOpeningTag(value, unit, depth, printWidth = 100, multilineAttributes = true) {
|
||||
const parsed = parseOpeningTag(value);
|
||||
|
||||
if (!parsed) {
|
||||
@@ -202,23 +229,22 @@ function formatOpeningTag(value, unit, depth, printWidth = 100) {
|
||||
}
|
||||
|
||||
const baseIndent = unit.repeat(depth);
|
||||
const childIndent = unit.repeat(depth + 1);
|
||||
|
||||
const attributeIndent = unit.repeat(depth + 1);
|
||||
|
||||
const normalizedOpening = `<${parsed.tagName}${
|
||||
parsed.attributes.length ? ` ${parsed.attributes.join(" ")}` : ""
|
||||
}${parsed.selfClosing ? " /" : ""}>`;
|
||||
const normalizedOpening =
|
||||
`<${parsed.tagName}` +
|
||||
`${parsed.attributes.length ? ` ${parsed.attributes.join(" ")}` : ""}` +
|
||||
`${parsed.selfClosing ? " /" : ""}>`;
|
||||
|
||||
const normalizedSingleLine = `${normalizedOpening}${parsed.remainder}`;
|
||||
|
||||
const shouldBreak =
|
||||
parsed.attributes.length > 1 ||
|
||||
normalizedSingleLine.length > printWidth ||
|
||||
value.includes("\n");
|
||||
value.includes("\n") ||
|
||||
(multilineAttributes && parsed.attributes.length > 0) ||
|
||||
baseIndent.length + normalizedSingleLine.length > printWidth;
|
||||
|
||||
const opensElement =
|
||||
!parsed.selfClosing &&
|
||||
!parsed.inlineClosing &&
|
||||
!parsed.closesInRemainder &&
|
||||
!VOID_ELEMENTS.has(parsed.tagName.toLowerCase());
|
||||
|
||||
@@ -231,19 +257,27 @@ function formatOpeningTag(value, unit, depth, printWidth = 100) {
|
||||
|
||||
const lines = [
|
||||
`${baseIndent}<${parsed.tagName}`,
|
||||
...parsed.attributes.map((attribute) => `${attributeIndent}${attribute}`),
|
||||
...parsed.attributes.map((attribute) => `${childIndent}${attribute}`),
|
||||
];
|
||||
|
||||
if (parsed.inlineClosing) {
|
||||
lines.push(`${baseIndent}></${parsed.tagName}>`);
|
||||
} else if (parsed.selfClosing) {
|
||||
if (parsed.selfClosing) {
|
||||
lines.push(`${baseIndent}/>`);
|
||||
} else {
|
||||
lines.push(`${baseIndent}>`);
|
||||
return {
|
||||
lines,
|
||||
opensElement,
|
||||
};
|
||||
}
|
||||
|
||||
if (parsed.remainder) {
|
||||
lines.push(`${baseIndent}${parsed.remainder}`);
|
||||
lines.push(`${baseIndent}>`);
|
||||
|
||||
if (parsed.trailingClosing) {
|
||||
if (parsed.inlineContent) {
|
||||
lines.push(`${childIndent}${parsed.inlineContent}`);
|
||||
}
|
||||
|
||||
lines.push(`${baseIndent}</${parsed.tagName}>`);
|
||||
} else if (parsed.remainder) {
|
||||
lines.push(`${parsed.immediateClosing ? baseIndent : childIndent}${parsed.remainder}`);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -273,8 +307,16 @@ function isClosingTag(value) {
|
||||
return /^<\/[A-Za-z][\w$:.-]*\s*>/.test(value);
|
||||
}
|
||||
|
||||
function isInlineElement(value) {
|
||||
return /^<([A-Za-z][\w$:.-]*)\b[^>]*>[\s\S]*<\/\1\s*>$/.test(value);
|
||||
function isControlBlockOpen(value) {
|
||||
return /^\{#(?:if|each)\b[\s\S]*\}$/.test(value);
|
||||
}
|
||||
|
||||
function isControlBlockMiddle(value) {
|
||||
return /^\{:(?:else(?:\s+if\b[\s\S]*)?|empty)\}$/.test(value);
|
||||
}
|
||||
|
||||
function isControlBlockClose(value) {
|
||||
return /^\{\/(?:if|each)\}$/.test(value);
|
||||
}
|
||||
|
||||
function countLeadingClosingBraces(value) {
|
||||
@@ -390,18 +432,47 @@ function collectOpeningTag(inputLines, startIndex) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Put WRN template control markers on their own lines before indentation.
|
||||
*
|
||||
* Authors commonly write compact fragments such as
|
||||
* `{#if loading}<span>…</span>{/if}`. Treating that as one line prevents the
|
||||
* normal HTML and control-block formatters from seeing its structure.
|
||||
*/
|
||||
function expandInlineControlBlocks(lines) {
|
||||
const marker =
|
||||
/(\{#(?:if|each)\b[^}]*\}|\{:(?:else(?:\s+if\b[^}]*)?|empty)\}|\{\/(?:if|each)\})/g;
|
||||
|
||||
return lines.flatMap((line) => {
|
||||
if (!marker.test(line)) return [line];
|
||||
marker.lastIndex = 0;
|
||||
|
||||
const indentation = line.match(/^\s*/)?.[0] ?? "";
|
||||
const segments = line
|
||||
.split(marker)
|
||||
.map((segment) => segment.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
return segments.map((segment) => `${indentation}${segment}`);
|
||||
});
|
||||
}
|
||||
|
||||
function formatWrn(source, options = {}) {
|
||||
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize || 4);
|
||||
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4);
|
||||
|
||||
const printWidth = options.printWidth || 100;
|
||||
const printWidth = options.printWidth ?? 100;
|
||||
|
||||
const inputLines = source.replace(/\r\n/g, "\n").split("\n");
|
||||
|
||||
const output = [];
|
||||
const multilineAttributes = options.multilineAttributes !== false;
|
||||
|
||||
let codeDepth = 0;
|
||||
let htmlDepth = 0;
|
||||
let controlDepth = 0;
|
||||
let index = 0;
|
||||
|
||||
const inputLines = expandInlineControlBlocks(source.replace(/\r\n/g, "\n").split("\n"));
|
||||
|
||||
const output = [];
|
||||
|
||||
let previousWasBlank = false;
|
||||
|
||||
while (index < inputLines.length) {
|
||||
@@ -451,6 +522,13 @@ function formatWrn(source, options = {}) {
|
||||
|
||||
const leadingClosingBraces = countLeadingClosingBraces(value);
|
||||
|
||||
const closesControlBlock = isControlBlockClose(value);
|
||||
|
||||
const continuesControlBlock = isControlBlockMiddle(value);
|
||||
|
||||
const lineControlDepth =
|
||||
closesControlBlock || continuesControlBlock ? Math.max(0, controlDepth - 1) : controlDepth;
|
||||
|
||||
const lineCodeDepth = Math.max(0, codeDepth - leadingClosingBraces);
|
||||
|
||||
let lineHtmlDepth = htmlDepth;
|
||||
@@ -459,17 +537,16 @@ function formatWrn(source, options = {}) {
|
||||
lineHtmlDepth = Math.max(0, htmlDepth - 1);
|
||||
}
|
||||
|
||||
const depth = lineCodeDepth + lineHtmlDepth;
|
||||
const depth = lineCodeDepth + lineHtmlDepth + lineControlDepth;
|
||||
|
||||
if (
|
||||
value.startsWith("<") &&
|
||||
!value.startsWith("</") &&
|
||||
!value.startsWith("<!--") &&
|
||||
!value.startsWith("<!") &&
|
||||
!value.startsWith("<?") &&
|
||||
!isInlineElement(value)
|
||||
!value.startsWith("<?")
|
||||
) {
|
||||
const formattedTag = formatOpeningTag(value, unit, depth, printWidth);
|
||||
const formattedTag = formatOpeningTag(value, unit, depth, printWidth, multilineAttributes);
|
||||
|
||||
output.push(...formattedTag.lines);
|
||||
|
||||
@@ -491,6 +568,12 @@ function formatWrn(source, options = {}) {
|
||||
lineCodeDepth + braces.openings - Math.max(0, braces.closings - leadingClosingBraces),
|
||||
);
|
||||
|
||||
if (isControlBlockOpen(value) || continuesControlBlock) {
|
||||
controlDepth = lineControlDepth + 1;
|
||||
} else if (closesControlBlock) {
|
||||
controlDepth = lineControlDepth;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user