mirror of https://github.com/twbs/bootstrap.git
				
				
				
			
							parent
							
								
									387ea724ee
								
							
						
					
					
						commit
						ce2cca8601
					
				|  | @ -30,8 +30,6 @@ jobs: | |||
|           node-version: "${{ env.NODE }}" | ||||
|           cache: npm | ||||
| 
 | ||||
|       - run: java -version | ||||
| 
 | ||||
|       - name: Install npm dependencies | ||||
|         run: npm ci | ||||
| 
 | ||||
|  | @ -39,7 +37,7 @@ jobs: | |||
|         run: npm run docs-build | ||||
| 
 | ||||
|       - name: Validate HTML | ||||
|         run: npm run docs-vnu | ||||
|         run: npm run docs-html-validate | ||||
| 
 | ||||
|       - name: Run linkinator | ||||
|         uses: JustinBeckwith/linkinator-action@3d5ba091319fa7b0ac14703761eebb7d100e6f6d # v1.11.0 | ||||
|  |  | |||
|  | @ -0,0 +1,104 @@ | |||
| #!/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() | ||||
|  | @ -1,66 +0,0 @@ | |||
| #!/usr/bin/env node
 | ||||
| 
 | ||||
| /*! | ||||
|  * Script to run vnu-jar if Java is available. | ||||
|  * Copyright 2017-2025 The Bootstrap Authors | ||||
|  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 | ||||
|  */ | ||||
| 
 | ||||
| import { execFile, spawn } from 'node:child_process' | ||||
| import vnu from 'vnu-jar' | ||||
| 
 | ||||
| execFile('java', ['-version'], (error, stdout, stderr) => { | ||||
|   if (error) { | ||||
|     console.error('Skipping vnu-jar test; Java is probably missing.') | ||||
|     console.error(error) | ||||
|     return | ||||
|   } | ||||
| 
 | ||||
|   console.log('Running vnu-jar validation...') | ||||
| 
 | ||||
|   const is32bitJava = !/64-Bit/.test(stderr) | ||||
| 
 | ||||
|   // vnu-jar accepts multiple ignores joined with a `|`.
 | ||||
|   // Also note that the ignores are string regular expressions.
 | ||||
|   const ignores = [ | ||||
|     // "autocomplete" is included in <button> and checkboxes and radio <input>s due to
 | ||||
|     // Firefox's non-standard autocomplete behavior - see https://bugzilla.mozilla.org/show_bug.cgi?id=654072
 | ||||
|     'Attribute “autocomplete” is only allowed when the input type is.*', | ||||
|     'Attribute “autocomplete” not allowed on element “button” at this point.', | ||||
|     // Per https://www.w3.org/TR/html-aria/#docconformance having "aria-disabled" on a link is
 | ||||
|     // NOT RECOMMENDED, but it's still valid - we explain in the docs that it's not ideal,
 | ||||
|     // and offer more robust alternatives, but also need to show a less-than-ideal example
 | ||||
|     'An “aria-disabled” attribute whose value is “true” should not be specified on an “a” element that has an “href” attribute.', | ||||
|     // A `code` element with the `is:raw` attribute coming from remark-prismjs (Astro upstream possible bug)
 | ||||
|     'Attribute “is:raw” is not serializable as XML 1.0.', | ||||
|     'Attribute “is:raw” not allowed on element “code” at this point.', | ||||
|     // Astro's expecting trailing slashes on HTML tags such as <br />
 | ||||
|     'Trailing slash on void elements has no effect and interacts badly with unquoted attribute values.', | ||||
|     // Allow `switch` attribute.
 | ||||
|     'Attribute “switch” not allowed on element “input” at this point.' | ||||
|   ].join('|') | ||||
| 
 | ||||
|   const args = [ | ||||
|     '-jar', | ||||
|     `"${vnu}"`, | ||||
|     '--asciiquotes', | ||||
|     '--skip-non-html', | ||||
|     '--Werror', | ||||
|     `--filterpattern "${ignores}"`, | ||||
|     '_site/', | ||||
|     'js/tests/' | ||||
|   ] | ||||
| 
 | ||||
|   // For the 32-bit Java we need to pass `-Xss512k`
 | ||||
|   if (is32bitJava) { | ||||
|     args.splice(0, 0, '-Xss512k') | ||||
|   } | ||||
| 
 | ||||
|   console.log(`command used: java ${args.join(' ')}`) | ||||
| 
 | ||||
|   return spawn('java', args, { | ||||
|     shell: true, | ||||
|     stdio: 'inherit' | ||||
|   }) | ||||
|     .on('exit', process.exit) | ||||
| }) | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -77,8 +77,8 @@ | |||
|     "docs": "npm-run-all docs-build docs-lint", | ||||
|     "docs-build": "npm run astro-build", | ||||
|     "docs-compile": "npm run docs-build", | ||||
|     "docs-vnu": "node build/vnu-jar.mjs", | ||||
|     "docs-lint": "npm-run-all docs-prettier-check docs-vnu", | ||||
|     "docs-html-validate": "node build/html-validate.mjs", | ||||
|     "docs-lint": "npm-run-all docs-prettier-check docs-html-validate", | ||||
|     "docs-prettier-check": "prettier --config site/.prettierrc.json -c --cache site", | ||||
|     "docs-prettier-format": "prettier --config site/.prettierrc.json --write --cache site", | ||||
|     "docs-serve": "npm run astro-dev", | ||||
|  | @ -143,6 +143,7 @@ | |||
|     "github-slugger": "^2.0.0", | ||||
|     "globby": "^14.1.0", | ||||
|     "hammer-simulator": "0.0.1", | ||||
|     "html-validate": "^8.24.1", | ||||
|     "htmlparser2": "^10.0.0", | ||||
|     "image-size": "^2.0.2", | ||||
|     "ip": "^2.0.1", | ||||
|  | @ -179,7 +180,6 @@ | |||
|     "stylelint-config-twbs-bootstrap": "^16.1.0", | ||||
|     "terser": "^5.44.0", | ||||
|     "unist-util-visit": "^5.0.0", | ||||
|     "vnu-jar": "24.10.17", | ||||
|     "zod": "^4.1.9" | ||||
|   }, | ||||
|   "files": [ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue