1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
type ValidationResult = {
isValid: boolean;
errors: Array<{
message: string;
line: number;
column: number;
}>;
};
function validateHTML(html: string): ValidationResult {
const errors: ValidationResult['errors'] = [];
const lines = html.split('\n');
const stack: Array<{ tag: string; line: number; column: number }> = [];
const selfClosingTags = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
lines.forEach((line, lineIndex) => {
const tagRegex = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi;
let match;
while ((match = tagRegex.exec(line)) !== null) {
const [fullTag, tagName] = match;
const column = match.index;
const isClosingTag = fullTag.startsWith('</');
const isSelfClosing = selfClosingTags.has(tagName.toLowerCase()) || fullTag.endsWith('/>');
if (!isClosingTag && !isSelfClosing) {
stack.push({ tag: tagName.toLowerCase(), line: lineIndex + 1, column });
} else if (isClosingTag) {
if (stack.length === 0) {
errors.push({
message: `Unexpected closing tag: ${fullTag}`,
line: lineIndex + 1,
column
});
} else {
const lastOpenTag = stack.pop()!;
if (lastOpenTag.tag !== tagName.toLowerCase()) {
errors.push({
message: `Mismatched tags: expected </${lastOpenTag.tag}>, found ${fullTag}`,
line: lineIndex + 1,
column
});
}
}
}
}
});
stack.forEach(({ tag, line, column }) => {
errors.push({
message: `Unclosed tag: <${tag}>`,
line,
column
});
});
return { isValid: errors.length === 0, errors };
}
function validateCSS(css: string): ValidationResult {
const errors: ValidationResult['errors'] = [];
const lines = css.split('\n');
let inRule = false;
let openBraces = 0;
lines.forEach((line, lineIndex) => {
// Check for unbalanced braces
openBraces += (line.match(/{/g) || []).length;
openBraces -= (line.match(/}/g) || []).length;
if (openBraces < 0) {
errors.push({
message: 'Unexpected closing brace',
line: lineIndex + 1,
column: line.indexOf('}') + 1
});
}
// Check for missing semicolons
if (inRule && !line.trim().endsWith('{') && !line.trim().endsWith('}') && !line.trim().endsWith(';')) {
errors.push({
message: 'Missing semicolon at end of declaration',
line: lineIndex + 1,
column: line.length
});
}
// Toggle inRule state
if (line.includes('{')) inRule = true;
if (line.includes('}')) inRule = false;
});
if (openBraces > 0) {
errors.push({
message: 'Unclosed rule (missing closing brace)',
line: lines.length,
column: lines[lines.length - 1].length
});
}
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
June 24, 2024