From a59c2f2da0a166e6b0307cf8b6261c2d4c3085a0 Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Fri, 21 Nov 2025 00:26:01 +0900 Subject: [PATCH] Add 'allow-non-blocking' option to head-script-disabled rule Enhances the 'head-script-disabled' rule to support an 'allow-non-blocking' option, permitting non-blocking scripts (modules, deferred, and async) in the tag. Updates rule logic, types, documentation, and adds comprehensive tests for the new option. --- dist/core/rules/head-script-disabled.js | 24 +++- src/core/rules/head-script-disabled.ts | 53 ++++++-- src/core/types.ts | 2 +- test/rules/head-script-disabled.spec.js | 115 ++++++++++++++++++ .../docs/rules/head-script-disabled.mdx | 79 +++++++++++- 5 files changed, 251 insertions(+), 22 deletions(-) diff --git a/dist/core/rules/head-script-disabled.js b/dist/core/rules/head-script-disabled.js index d9ab5b3e2..729c46c56 100644 --- a/dist/core/rules/head-script-disabled.js +++ b/dist/core/rules/head-script-disabled.js @@ -3,20 +3,32 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = { id: 'head-script-disabled', description: 'The ' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Deferred script in head should not result in an error', () => { + const code = '' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Deferred script with type in head should not result in an error', () => { + const code = + '' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Async script in head should not result in an error', () => { + const code = '' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Async script with type in head should not result in an error', () => { + const code = + '' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Script with both async and defer should not result in an error', () => { + const code = '' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Module script with inline content should not result in an error', () => { + const code = + '' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Regular script in head should still result in an error', () => { + const code = '' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(1) + expect(messages[0].rule.id).toBe(ruleId) + expect(messages[0].message).toBe( + 'The ' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(1) + expect(messages[0].rule.id).toBe(ruleId) + expect(messages[0].message).toBe( + 'The ' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(1) + expect(messages[0].rule.id).toBe(ruleId) + expect(messages[0].message).toBe( + 'The ' + let messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + + code = + '' + messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Scripts in body should still not result in an error', () => { + const code = '' + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(0) + }) + + it('Multiple scripts with mixed types should handle correctly', () => { + const code = ` + + + + + + + ` + const messages = HTMLHint.verify(code, allowNonBlockingOptions) + expect(messages.length).toBe(1) + expect(messages[0].message).toBe( + 'The ``` -### The following pattern is considered a rule violation: +```html + + + +``` + +#### With `"allow-non-blocking"` option + +```html + + + +``` + +```html + + + +``` + +```html + + + +``` + +```html + + + +``` + +### The following patterns are considered rule violations + +#### Default behavior (`true`) + +```html + + + +``` + +```html + + + +``` + +#### With `"allow-non-blocking"` option ```html ``` + +```html + + + +``` + +## Why this rule is important + +Scripts in the `` section traditionally block HTML parsing and rendering, which can negatively impact page load performance. However, modern JavaScript features like ES6 modules (`type="module"`), the `defer` attribute, and the `async` attribute allow scripts to be non-blocking, making them safe to place in the head without performance penalties. + +For more information, see: + +- [MDN: Script element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) +- [MDN: defer attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#defer) +- [MDN: async attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#async) +- [MDN: JavaScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)