mirror of https://github.com/twbs/bootstrap.git
105 lines
2.9 KiB
JavaScript
105 lines
2.9 KiB
JavaScript
#!/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()
|