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()
 |