bootstrap/build/html-validate.mjs

105 lines
2.9 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/*!
* Script to run html-validate for HTML validation.
*
* This replaces the Java-based vnu-jar validator with a faster, Node.js-only solution.
* Benefits:
* - No Java dependency required
* - Faster execution (no JVM startup time)
* - Easy to configure with rule-based system
* - Better integration with Node.js build tools
*
* Copyright 2017-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
import { HtmlValidate } from 'html-validate'
import { globby } from 'globby'
const htmlValidate = new HtmlValidate({
rules: {
// Allow autocomplete on buttons (Bootstrap specific)
'attribute-allowed-values': 'off',
// Allow aria-disabled on links (Bootstrap specific)
'aria-label-misuse': 'off',
// Allow modern CSS syntax
'valid-id': 'off',
// Allow void elements with trailing slashes (Astro)
'void-style': 'off',
// Allow custom attributes
'no-unknown-elements': 'off',
'attribute-boolean-style': 'off',
'no-inline-style': 'off',
// KEEP duplicate ID checking enabled (this is important for HTML validity)
'no-dup-id': 'error'
},
elements: [
'html5',
{
// Allow custom attributes for Astro/framework compatibility
'*': {
attributes: {
'is:raw': { boolean: true },
switch: { boolean: true },
autocomplete: { enum: ['on', 'off', 'new-password', 'current-password'] }
}
}
}
]
})
async function validateHTML() {
try {
console.log('Running html-validate validation...')
// Find all HTML files
const files = await globby([
'_site/**/*.html',
'js/tests/**/*.html'
], {
ignore: ['**/node_modules/**']
})
console.log(`Validating ${files.length} HTML files...`)
let hasErrors = false
// Validate all files in parallel to avoid await-in-loop
const validationPromises = files.map(file =>
htmlValidate.validateFile(file).then(report => ({ file, report }))
)
const validationResults = await Promise.all(validationPromises)
// Process results and check for errors
for (const { file, report } of validationResults) {
if (!report.valid) {
hasErrors = true
console.error(`\nErrors in ${file}:`)
// Extract error messages with reduced nesting
const errorMessages = report.results.flatMap(result =>
result.messages.filter(message => message.severity === 2)
)
for (const message of errorMessages) {
console.error(` Line ${message.line}:${message.column} - ${message.message} (${message.ruleId})`)
}
}
}
if (hasErrors) {
console.error('\nHTML validation failed!')
process.exit(1)
} else {
console.log('✓ All HTML files are valid!')
}
} catch (error) {
console.error('HTML validation error:', error)
process.exit(1)
}
}
validateHTML()