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)