release: WRNexusJS 0.8.0
Quality / quality (ubuntu-latest) (push) Failing after 12m19s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-04 12:19:09 +05:30
parent 4cebacadfe
commit 72e4d3eceb
108 changed files with 2276 additions and 532 deletions
+279 -99
View File
@@ -1,3 +1,5 @@
// WRN editor extension source hash: 825c406dea4b196976b1b6e53b3c71c3263bd63f403acdaf2fe7e5ad174275da
// WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728
"use strict";
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
@@ -27187,6 +27189,17 @@ ${serverFunctions}
opensElement
};
}
if (parsed.attributes.length === 0 && !parsed.selfClosing) {
const lines2 = [`${baseIndent}<${parsed.tagName}>`];
if (parsed.trailingClosing) {
if (parsed.inlineContent)
lines2.push(`${childIndent}${parsed.inlineContent}`);
lines2.push(`${baseIndent}</${parsed.tagName}>`);
} else if (parsed.remainder) {
lines2.push(`${childIndent}${parsed.remainder}`);
}
return { lines: lines2, opensElement };
}
const lines = [
`${baseIndent}<${parsed.tagName}`,
...parsed.attributes.flatMap((attribute) => formatAttribute(attribute, childIndent, unit))
@@ -27224,6 +27237,63 @@ ${serverFunctions}
function isClosingTag(value) {
return /^<\/[A-Za-z][\w$:.-]*\s*>/.test(value);
}
function isBalancedInlineHtmlFragment(value) {
if (!value.startsWith("<") || value.startsWith("</") || value.startsWith("<!--")) {
return false;
}
const stack = [];
let tagCount = 0;
let rootCount = 0;
let hasNestedElement = false;
let hasOutsideText = false;
let index = 0;
while (index < value.length) {
const tagStart = value.indexOf("<", index);
if (tagStart === -1) {
if (stack.length === 0 && value.slice(index).trim())
hasOutsideText = true;
break;
}
if (stack.length === 0 && value.slice(index, tagStart).trim())
hasOutsideText = true;
if (value.startsWith("<!--", tagStart)) {
const commentEnd = value.indexOf("-->", tagStart + 4);
if (commentEnd === -1)
return false;
index = commentEnd + 3;
continue;
}
const relativeEnd = findOpeningTagEnd(value.slice(tagStart));
if (relativeEnd === -1)
return false;
const tag = value.slice(tagStart, tagStart + relativeEnd + 1);
const match = /^<\/?([A-Za-z][\w$:.-]*)[\s\S]*?>$/.exec(tag);
if (!match) {
index = tagStart + 1;
continue;
}
tagCount += 1;
const tagName = match[1];
const normalizedName = /^[a-z]/.test(tagName) ? tagName.toLowerCase() : tagName;
const closing = tag.startsWith("</");
const selfClosing = /\/\s*>$/.test(tag);
const voidElement = VOID_ELEMENTS.has(tagName.toLowerCase());
if (closing) {
if (stack.at(-1) !== normalizedName)
return false;
stack.pop();
} else {
if (stack.length === 0)
rootCount += 1;
else
hasNestedElement = true;
if (!selfClosing && !voidElement)
stack.push(normalizedName);
}
index = tagStart + relativeEnd + 1;
}
return tagCount >= 2 && stack.length === 0 && (rootCount > 1 || hasNestedElement || hasOutsideText);
}
function isControlBlockOpen(value) {
return /^\{#(?:if|each)\b[\s\S]*\}$/.test(value);
}
@@ -27319,9 +27389,43 @@ ${serverFunctions}
endIndex: index
};
}
function isPreservedRawBlockStart(value) {
return /<pre(?:\s|>)/i.test(value) && !/<\/pre\s*>/i.test(value.slice(0, value.search(/<pre(?:\s|>)/i)));
}
function hasPreservedRawBlockEnd(value) {
return /<\/pre\s*>/i.test(value);
}
function transformOutsidePreservedRawBlocks(lines, transformLine) {
const output = [];
let preserving = false;
for (const line of lines) {
if (preserving) {
output.push(line);
if (hasPreservedRawBlockEnd(line))
preserving = false;
continue;
}
if (isPreservedRawBlockStart(line)) {
output.push(line);
preserving = !hasPreservedRawBlockEnd(line);
continue;
}
output.push(...transformLine(line));
}
return output;
}
function collectPreservedRawBlock(lines, startIndex) {
const collected = [lines[startIndex]];
let index = startIndex;
while (!hasPreservedRawBlockEnd(collected.at(-1) || "") && index + 1 < lines.length) {
index += 1;
collected.push(lines[index]);
}
return { lines: collected, endIndex: index };
}
function expandInlineControlBlocks(lines) {
const marker = /(\{#(?:if|each)\b[^}]*\}|\{:(?:else(?:\s+if\b[^}]*)?|empty)\}|\{\/(?:if|each)\})/g;
return lines.flatMap((line) => {
return transformOutsidePreservedRawBlocks(lines, (line) => {
if (!marker.test(line))
return [line];
marker.lastIndex = 0;
@@ -27331,7 +27435,7 @@ ${serverFunctions}
});
}
function expandStructuredStateDeclarations(lines, unit) {
return lines.flatMap((line) => {
return transformOutsidePreservedRawBlocks(lines, (line) => {
const match = /^(\s*state\s+[A-Za-z_$][\w$]*\s*=\s*)([\\[{][\s\S]*)$/.exec(line);
if (!match)
return [line];
@@ -27387,6 +27491,14 @@ ${serverFunctions}
index += 1;
continue;
}
if (isPreservedRawBlockStart(value)) {
const collected = collectPreservedRawBlock(inputLines, index);
const depth2 = codeDepth + htmlDepth + controlDepth;
output.push(`${unit.repeat(depth2)}${collected.lines[0].trimStart()}`);
output.push(...collected.lines.slice(1));
index = collected.endIndex + 1;
continue;
}
if (isMultilineOpeningTagStart(value)) {
const collected = collectOpeningTag(inputLines, index);
value = collected.value;
@@ -27409,7 +27521,10 @@ ${serverFunctions}
lineHtmlDepth = Math.max(0, htmlDepth - 1);
}
const depth = lineCodeDepth + lineHtmlDepth + lineControlDepth;
if (value.startsWith("<") && !value.startsWith("</") && !value.startsWith("<!--") && !value.startsWith("<!") && !value.startsWith("<?")) {
const inlineFragmentFits = unit.repeat(depth).length + value.length <= printWidth;
if (isBalancedInlineHtmlFragment(value) && inlineFragmentFits) {
output.push(`${unit.repeat(depth)}${value}`);
} else if (value.startsWith("<") && !value.startsWith("</") && !value.startsWith("<!--") && !value.startsWith("<!") && !value.startsWith("<?")) {
const formattedTag = formatOpeningTag(value, unit, depth, printWidth, multilineAttributes);
output.push(...formattedTag.lines);
if (formattedTag.opensElement) {
@@ -28895,11 +29010,13 @@ ${serverFunctions}
if (!annotation)
return "unknown";
const type = annotation.trim().replace(/^readonly\s+/, "");
if (/^(?:string|String)(?:\s*\|\s*(?:null|undefined))*$/.test(type))
const unionParts = type.split("|").map((part) => part.trim());
const concreteParts = unionParts.filter((part) => !/^(?:null|undefined)$/.test(part));
if (/^(?:string|String)(?:\s*\|\s*(?:null|undefined))*$/.test(type) || concreteParts.length > 0 && concreteParts.every((part) => /^(?:"[^"]*"|'[^']*')$/.test(part)))
return "string";
if (/^(?:number|Number)(?:\s*\|\s*(?:null|undefined))*$/.test(type))
if (/^(?:number|Number)(?:\s*\|\s*(?:null|undefined))*$/.test(type) || concreteParts.length > 0 && concreteParts.every((part) => /^-?(?:\d+\.?\d*|\.\d+)$/.test(part)))
return "number";
if (/^(?:boolean|Boolean)(?:\s*\|\s*(?:null|undefined))*$/.test(type))
if (/^(?:boolean|Boolean)(?:\s*\|\s*(?:null|undefined))*$/.test(type) || concreteParts.length > 0 && concreteParts.every((part) => /^(?:true|false)$/.test(part)))
return "boolean";
if (/^bigint(?:\s*\|\s*(?:null|undefined))*$/.test(type))
return "bigint";
@@ -30196,11 +30313,13 @@ var require_component_metadata = __commonJS((exports2, module2) => {
}
function runtimeType(type) {
const value = String(type || "").trim().replace(/^readonly\s+/, "");
if (/^(?:string|String)(?:\s*\|\s*(?:null|undefined))*$/.test(value))
const unionParts = value.split("|").map((part) => part.trim());
const concreteParts = unionParts.filter((part) => !/^(?:null|undefined)$/.test(part));
if (/^(?:string|String)(?:\s*\|\s*(?:null|undefined))*$/.test(value) || concreteParts.length > 0 && concreteParts.every((part) => /^(?:"[^"]*"|'[^']*')$/.test(part)))
return "string";
if (/^(?:number|Number)(?:\s*\|\s*(?:null|undefined))*$/.test(value))
if (/^(?:number|Number)(?:\s*\|\s*(?:null|undefined))*$/.test(value) || concreteParts.length > 0 && concreteParts.every((part) => /^-?(?:\d+\.?\d*|\.\d+)$/.test(part)))
return "number";
if (/^(?:boolean|Boolean)(?:\s*\|\s*(?:null|undefined))*$/.test(value))
if (/^(?:boolean|Boolean)(?:\s*\|\s*(?:null|undefined))*$/.test(value) || concreteParts.length > 0 && concreteParts.every((part) => /^(?:true|false)$/.test(part)))
return "boolean";
if (/^bigint(?:\s*\|\s*(?:null|undefined))*$/.test(value))
return "bigint";
@@ -30289,19 +30408,41 @@ var require_component_metadata = __commonJS((exports2, module2) => {
return trimmed.slice(1, -1).trim();
return trimmed;
}
function isWrappedExpression(value) {
const trimmed = value.trim();
return trimmed.startsWith("{") && trimmed.endsWith("}");
}
function expressionLiteralType(value) {
const expression = value.trim();
if (/^(?:true|false)$/.test(expression))
return "boolean";
if (/^-?(?:\d+\.?\d*|\.\d+)$/.test(expression))
return "number";
if (/^(?:"[\s\S]*"|'[\s\S]*'|`[\s\S]*`)$/.test(expression))
return "string";
if (expression.startsWith("["))
return "array";
if (expression.startsWith("{"))
return "object";
if (expression === "null")
return "null";
return null;
}
function attributeValueType(value, symbols = new Map) {
const expression = isWrappedExpression(value);
const unwrapped = unwrapAttributeValue(value);
if (/^[A-Za-z_$][\w$]*$/.test(unwrapped) && symbols.has(unwrapped)) {
return symbols.get(unwrapped);
}
if (/^(?:true|false)$/.test(unwrapped))
return "boolean";
if (/^-?(?:\d+\.?\d*|\.\d+)$/.test(unwrapped))
return "number";
if (unwrapped.startsWith("["))
return "array";
if (unwrapped.startsWith("{"))
return "object";
const literal = expressionLiteralType(unwrapped);
if (literal)
return literal;
if (expression) {
if (/^!\s*/.test(unwrapped) || /(?:===|!==|==|!=|<=|>=|<|>|\bin\b|\binstanceof\b)/.test(unwrapped)) {
return "boolean";
}
return "unknown";
}
return "string";
}
function isTypeCompatible(prop, value, symbols = new Map) {
@@ -30309,6 +30450,8 @@ var require_component_metadata = __commonJS((exports2, module2) => {
if (expected === "unknown" || expected === "null")
return true;
const actual = runtimeType(attributeValueType(value, symbols));
if (actual === "unknown")
return true;
if (expected === actual)
return true;
if (expected === "number" && actual === "string")
@@ -30409,8 +30552,8 @@ var require_component_metadata = __commonJS((exports2, module2) => {
end: attribute.nameEnd
});
}
const literal = stringLiteral(attribute.value) ?? attribute.value;
if (prop.options.length > 0 && !prop.options.includes(literal)) {
const optionValue = isWrappedExpression(attribute.value) ? stringLiteral(unwrapAttributeValue(attribute.value)) : stringLiteral(attribute.value) ?? attribute.value;
if (optionValue !== null && prop.options.length > 0 && !prop.options.includes(optionValue)) {
diagnostics.push({
severity: "warning",
code: "wrn-component-prop-option",
@@ -30727,58 +30870,6 @@ var require_diagnostics = __commonJS((exports2, module2) => {
}
return diagnostic;
}
function stripComments(source) {
const masked = [...source];
let quote = null;
let escaped = false;
const maskRange = (start, end) => {
for (let index = start;index < end; index += 1) {
if (source[index] !== `
` && source[index] !== "\r") {
masked[index] = " ";
}
}
};
for (let index = 0;index < source.length; index += 1) {
const character = source[index];
if (quote !== null) {
if (escaped) {
escaped = false;
} else if (character === "\\") {
escaped = true;
} else if (character === quote) {
quote = null;
}
continue;
}
if (character === '"' || character === "'" || character === "`") {
quote = character;
continue;
}
if (source.startsWith("//", index)) {
const lineEnd = source.indexOf(`
`, index + 2);
const end = lineEnd === -1 ? source.length : lineEnd;
maskRange(index, end);
index = end - 1;
continue;
}
if (source.startsWith("/*", index)) {
const commentEnd = source.indexOf("*/", index + 2);
const end = commentEnd === -1 ? source.length : commentEnd + 2;
maskRange(index, end);
index = end - 1;
continue;
}
if (source.startsWith("<!--", index)) {
const commentEnd = source.indexOf("-->", index + 4);
const end = commentEnd === -1 ? source.length : commentEnd + 3;
maskRange(index, end);
index = end - 1;
}
}
return masked.join("");
}
function maskLeadingTrivia(source) {
const masked = [...source];
let offset = 0;
@@ -30897,9 +30988,89 @@ var require_diagnostics = __commonJS((exports2, module2) => {
}
return diagnostics;
}
function maskHtmlComments(source) {
const masked = [...source];
let index = 0;
while (index < source.length) {
if (!source.startsWith("<!--", index)) {
index += 1;
continue;
}
const commentEnd = source.indexOf("-->", index + 4);
const end = commentEnd === -1 ? source.length : commentEnd + 3;
for (let cursor = index;cursor < end; cursor += 1) {
if (source[cursor] !== `
` && source[cursor] !== "\r")
masked[cursor] = " ";
}
index = end;
}
return masked.join("");
}
function maskTemplateExpressions(source) {
const masked = [...source];
let index = 0;
while (index < source.length) {
if (source[index] !== "{") {
index += 1;
continue;
}
const start = index;
let depth = 0;
let quote = null;
let escaped = false;
while (index < source.length) {
const character = source[index];
if (quote !== null) {
if (escaped)
escaped = false;
else if (character === "\\")
escaped = true;
else if (character === quote)
quote = null;
index += 1;
continue;
}
if (character === '"' || character === "'" || character === "`") {
quote = character;
index += 1;
continue;
}
if (character === "{")
depth += 1;
else if (character === "}") {
depth -= 1;
index += 1;
if (depth === 0)
break;
continue;
}
index += 1;
}
for (let cursor = start;cursor < index; cursor += 1) {
if (source[cursor] !== `
` && source[cursor] !== "\r")
masked[cursor] = " ";
}
}
return masked.join("");
}
function findViewRanges(source) {
const ranges = [];
const pattern = /\bview\s*\{/g;
let match;
while ((match = pattern.exec(source)) !== null) {
const openingBrace = source.indexOf("{", match.index);
const closingBrace = findMatchingBrace(source, openingBrace);
if (closingBrace === -1)
break;
ranges.push({ start: openingBrace + 1, end: closingBrace });
pattern.lastIndex = closingBrace + 1;
}
return ranges;
}
function validateHtmlTags(document, source) {
const diagnostics = [];
const stack = [];
const voidElements = new Set([
"area",
"base",
@@ -30916,38 +31087,47 @@ var require_diagnostics = __commonJS((exports2, module2) => {
"track",
"wbr"
]);
const cleaned = stripComments(source);
const tagPattern = /<\/?([A-Za-z][A-Za-z0-9_$:.-]*)(?:\s[\s\S]*?)?\/?>/g;
let match;
while ((match = tagPattern.exec(cleaned)) !== null) {
const completeTag = match[0];
const tagName = match[1];
const lowerTag = tagName.toLowerCase();
const isClosing = completeTag.startsWith("</");
const isSelfClosing = completeTag.endsWith("/>");
const isVoid = voidElements.has(lowerTag);
if (isClosing) {
const last = stack.pop();
if (!last) {
diagnostics.push(createDiagnostic(document, match.index, match.index + completeTag.length, `Unexpected closing tag </${tagName}>.`, vscode.DiagnosticSeverity.Error, "wrn-unexpected-html-close"));
for (const range of findViewRanges(source)) {
const fragment = source.slice(range.start, range.end);
const cleaned = maskTemplateExpressions(maskHtmlComments(fragment));
const stack = [];
const tagPattern = /<\/?([A-Za-z][A-Za-z0-9_$:.-]*)(?:\s[\s\S]*?)?\/?>/g;
let match;
while ((match = tagPattern.exec(cleaned)) !== null) {
const completeTag = match[0];
const tagName = match[1];
const normalizedName = /^[a-z]/.test(tagName) ? tagName.toLowerCase() : tagName;
const lowerTag = tagName.toLowerCase();
const absoluteStart = range.start + match.index;
const isClosing = completeTag.startsWith("</");
const isSelfClosing = /\/\s*>$/.test(completeTag);
const isVoid = voidElements.has(lowerTag);
if (isClosing) {
const matchingIndex = stack.findLastIndex((item) => item.normalizedName === normalizedName);
if (matchingIndex === -1) {
diagnostics.push(createDiagnostic(document, absoluteStart, absoluteStart + completeTag.length, `Unexpected closing tag </${tagName}>.`, vscode.DiagnosticSeverity.Error, "wrn-unexpected-html-close"));
continue;
}
const last = stack.at(-1);
if (last.normalizedName !== normalizedName) {
diagnostics.push(createDiagnostic(document, absoluteStart, absoluteStart + completeTag.length, `Mismatched closing tag </${tagName}>. Expected </${last.tagName}>.`, vscode.DiagnosticSeverity.Error, "wrn-mismatched-html-tag"));
}
stack.splice(matchingIndex);
continue;
}
if (last.tagName !== tagName) {
diagnostics.push(createDiagnostic(document, match.index, match.index + completeTag.length, `Mismatched closing tag </${tagName}>. Expected </${last.tagName}>.`, vscode.DiagnosticSeverity.Error, "wrn-mismatched-html-tag"));
if (!isSelfClosing && !isVoid) {
stack.push({
tagName,
normalizedName,
offset: absoluteStart,
length: completeTag.length
});
}
continue;
}
if (!isSelfClosing && !isVoid) {
stack.push({
tagName,
offset: match.index,
length: completeTag.length
});
for (const tag of stack) {
diagnostics.push(createDiagnostic(document, tag.offset, tag.offset + tag.length, `Missing closing tag </${tag.tagName}>.`, vscode.DiagnosticSeverity.Error, "wrn-missing-html-close"));
}
}
for (const tag of stack) {
diagnostics.push(createDiagnostic(document, tag.offset, tag.offset + tag.length, `Missing closing tag </${tag.tagName}>.`, vscode.DiagnosticSeverity.Error, "wrn-missing-html-close"));
}
return diagnostics;
}
function getRootBodyRange(source, rootMatch) {
@@ -31923,9 +32103,9 @@ async function activate(context) {
registerCompilerDiagnostics(context);
registerCompletionProvider(context);
registerDefinitionProvider(context);
registerComponentIntelligence(context);
registerFormatter(context);
}
registerComponentIntelligence(context);
registerSemanticTokens(context);
registerV060LanguageFeatures(context);
}