diff --git a/packages/@d-zero/markuplint-config/README.md b/packages/@d-zero/markuplint-config/README.md index d0df68fa..ecff4831 100644 --- a/packages/@d-zero/markuplint-config/README.md +++ b/packages/@d-zero/markuplint-config/README.md @@ -55,6 +55,18 @@ npm install -D @d-zero/markuplint-config - **`html`要素の`prefix`属性**: Open Graph Protocolのため許可 +### 6. Markuplint標準ルールの追加有効化 + +recommendedプリセットに含まれない以下の標準ルールを有効化しています: + +- **`attr-order`**: 属性順序を`id > class > role > aria-* > data-* > 要素固有属性`に統一 +- **`no-boolean-attr-value`**: boolean属性の冗長な値を禁止(例: `disabled="disabled"`) +- **`no-default-value`**: デフォルト値と同一の属性値指定を禁止(例: `type="text"`) +- **`no-unsupported-browser-features`**: `browserslist`設定に基づくブラウザ未サポート要素・属性の検出(`browserslist`設定がないプロジェクトでは影響なし) +- **`performance/img-aspect-ratio`**: 無効化。`img[src]`の`width`/`height`必須ルールはビルド時に自動付与されるため不要 + +なお`head-element-order`(`
`内要素の順序)・`no-event-handler-attr`(インラインイベントハンドラ属性の禁止)は`markuplint:recommended-static-html`が拡張する`performance`/`security`プリセットで既定有効のため、このconfigでは重複指定していません。 + ### 拡張 プロジェクトに合わせて設定を追加します。 @@ -90,7 +102,8 @@ export default { }), // 他の設定 rules: { - 'character-reference': false, + 'no-malformed-character-reference': false, + 'no-unescaped-char': false, }, }; ``` diff --git a/packages/@d-zero/markuplint-config/base.js b/packages/@d-zero/markuplint-config/base.js index d401580b..9d505cca 100644 --- a/packages/@d-zero/markuplint-config/base.js +++ b/packages/@d-zero/markuplint-config/base.js @@ -4,24 +4,40 @@ export default { extends: ['markuplint:recommended-static-html'], rules: { - 'disallowed-element': { - value: ['br'], - reason: - 'br要素は原則使用しません。代わりにCSSでスタイルを調整してください。使用する場合は理由が必要です。(D-ZERO独自ルール)', + // markuplint:recommended-static-html (performance preset) の + // img[src] に対する width, height 必須ルールを無効化 + // width, height はビルド時に自動的に付与されるため問題なしとする + 'performance/img-aspect-ratio': false, + 'attr-order': ['id', 'class', 'role', { group: 'aria' }, { group: 'data' }], + // head-element-order, no-event-handler-attr は + // markuplint:recommended-static-html (performance/security preset) で既定有効のため指定しない + 'no-boolean-attr-value': true, + 'no-default-value': true, + // browserslist設定がある場合にブラウザ未サポート要素・属性を検出 + 'no-unsupported-browser-features': true, + 'd-zero/no-br': { + rules: { + 'no-restricted-element': { + value: ['br'], + reason: + 'br要素は原則使用しません。代わりにCSSでスタイルを調整してください。使用する場合は理由が必要です。(D-ZERO独自ルール)', + }, + }, }, }, nodeRules: [ { - selector: "script[src^='https://'], script[src^='https://']", + selector: "script[src^='http://'], script[src^='https://']", rules: { - 'required-attr': false, + 'require-attr': false, }, }, { + name: 'd-zero/html-allow-prefix-attr', selector: 'html', rules: { // - 'invalid-attr': { + 'no-unknown-attr': { options: { allowAttrs: [ { @@ -34,11 +50,10 @@ export default { }, }, { + name: 'd-zero/img-require-alt', selector: 'img', rules: { - // https://github.com/markuplint/markuplint/blob/c35e0beb5e14093a41cee7634221dbe7f7d577f9/packages/%40markuplint/config-presets/src/preset.performance.json#L25-L35 の設定を上書き - // width, height の指定は上書きされるため、省略可能になるが、ビルド時に自動的に付与されるため問題なしとする - 'required-attr': { + 'require-attr': { value: 'alt', reason: '省略可能なケースがほとんど想定されないため、原則禁止としています。省略する場合は明確な理由が必要です。(D-ZERO独自ルール)', @@ -46,10 +61,11 @@ export default { }, }, { + name: 'd-zero/img-src-kebab-case', selector: 'img:not([src^="data:"], [src^="blob:"], [src^="https://"], [src^="http://"], [src^="//"])', rules: { - 'invalid-attr': { + 'no-restricted-attr': { options: { disallowAttrs: [ { @@ -64,9 +80,10 @@ export default { }, }, { + name: 'd-zero/media-src-kebab-case', selector: 'video, audio, source', rules: { - 'invalid-attr': { + 'no-restricted-attr': { options: { disallowAttrs: [ { @@ -85,14 +102,15 @@ export default { }, }, { + name: 'd-zero/a-href-convention', selector: 'a', rules: { - 'required-attr': { + 'require-attr': { value: 'href', reason: '省略可能なケースがほとんど想定されないため、原則禁止としています。省略する場合は明確な理由が必要です。(D-ZERO独自ルール)', }, - 'invalid-attr': { + 'no-restricted-attr': { options: { disallowAttrs: [ { @@ -107,9 +125,10 @@ export default { }, }, { + name: 'd-zero/button-require-command', selector: 'button[type=button]:not([role]):not([popovertarget])', rules: { - 'required-attr': { + 'require-attr': { value: 'command', reason: 'button要素には原則としてcommand属性が必要です。Invoker Commands APIを使用してアクセシブルなUIを実装してください。role属性を持つボタン(role="tab"など)やtype="submit"/type="reset"/typeなしのボタンは例外として許可されます。(D-ZERO独自ルール)', @@ -117,9 +136,10 @@ export default { }, }, { + name: 'd-zero/button-prefer-commandfor', selector: 'button[popovertarget]', rules: { - 'required-attr': { + 'require-attr': { value: 'commandfor', reason: 'popovertarget属性の代わりにcommandfor属性を使用してください。popovertarget属性は将来的に非推奨となる予定です。(D-ZERO独自ルール)', @@ -127,9 +147,10 @@ export default { }, }, { + name: 'd-zero/button-prefer-command-action', selector: 'button[popovertargetaction]', rules: { - 'required-attr': { + 'require-attr': { value: 'command', reason: 'popovertargetaction属性(show/hide/toggle)の代わりにcommand属性(show-popover/hide-popover/toggle-popover)を使用してください。(D-ZERO独自ルール)', diff --git a/packages/@d-zero/markuplint-config/package.json b/packages/@d-zero/markuplint-config/package.json index ff347af3..0b8eb2b5 100644 --- a/packages/@d-zero/markuplint-config/package.json +++ b/packages/@d-zero/markuplint-config/package.json @@ -9,7 +9,7 @@ "access": "public" }, "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" }, "type": "module", "exports": { @@ -22,7 +22,7 @@ "*.js" ], "dependencies": { - "@markuplint/pug-parser": "4.18.3", - "markuplint": "4.18.3" + "@markuplint/pug-parser": "5.0.0-rc.7", + "markuplint": "5.0.0-rc.7" } } diff --git a/packages/@d-zero/markuplint-config/pug.js b/packages/@d-zero/markuplint-config/pug.js index ac53ba40..bd6aec9b 100644 --- a/packages/@d-zero/markuplint-config/pug.js +++ b/packages/@d-zero/markuplint-config/pug.js @@ -11,7 +11,8 @@ export default { overrides: { [path.resolve(process.cwd(), '**', '*.pug')]: { rules: { - 'character-reference': false, + 'no-malformed-character-reference': false, + 'no-unescaped-char': false, }, }, }, diff --git a/test/cli.spec.mjs b/test/cli.spec.mjs index 9f36688b..30839855 100644 --- a/test/cli.spec.mjs +++ b/test/cli.spec.mjs @@ -139,16 +139,15 @@ describe('markuplint', () => { expect(violations).toStrictEqual([ 'test/fixtures/markuplint/test.pug:14:6 The "c-component__invalid-element-nesting" class name is unmatched with the below patterns: "/^c-component2__[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/", "/^c-(?!component2)[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/", "/^c-component2[a-z0-9]*(?:-[a-z0-9]+)*$/"', 'test/fixtures/markuplint/test.pug:9:4 The "div" element is not allowed in the "span" element in this context', - 'test/fixtures/markuplint/test.html:17:66 Illegal characters must escape in character reference', 'test/fixtures/markuplint/test.html:14:18 The "c-component__invalid-element-nesting" class name is unmatched with the below patterns: "/^c-component2__[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/", "/^c-(?!component2)[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/", "/^c-component2[a-z0-9]*(?:-[a-z0-9]+)*$/"', - 'test/fixtures/markuplint/test.html:26:3 The "br" element is disallowed', - 'test/fixtures/markuplint/test.html:25:12 The "href" attribute is matched with the below disallowed patterns: /^javascript:/i', 'test/fixtures/markuplint/test.html:9:9 The "div" element is not allowed in the "span" element in this context', 'test/fixtures/markuplint/test.html:23:3 Require accessible name', 'test/fixtures/markuplint/test.html:25:3 Require accessible name', + 'test/fixtures/markuplint/test.html:1:1 Require the "h1" element', + 'test/fixtures/markuplint/test.html:26:3 The "br" element is disallowed', 'test/fixtures/markuplint/test.html:23:3 The "img" element expects the "alt" attribute', 'test/fixtures/markuplint/test.html:24:3 The "a" element expects the "href" attribute', - 'test/fixtures/markuplint/test.html:1:1 Require the "h1" element', + 'test/fixtures/markuplint/test.html:25:12 The "href" attribute is matched with the below disallowed patterns: /^javascript:/i', ]); }); @@ -184,10 +183,11 @@ describe('markuplint', () => { 'packages/@d-zero/markuplint-config/base.js', ); expect(invalidNaming).toStrictEqual([ - 'test/fixtures/markuplint/image-naming-test.html:21:15 The "src" attribute is matched with the below disallowed patterns: /[A-Z\\s_]/', - 'test/fixtures/markuplint/image-naming-test.html:22:15 The "src" attribute is matched with the below disallowed patterns: /[A-Z\\s_]/', - 'test/fixtures/markuplint/image-naming-test.html:23:15 The "src" attribute is matched with the below disallowed patterns: /[A-Z\\s_]/', - 'test/fixtures/markuplint/image-naming-test.html:24:15 The "src" attribute is matched with the below disallowed patterns: /[A-Z\\s_]/', + 'test/fixtures/markuplint/image-naming-test.html:21:45 It includes unexpected characters (https://html.spec.whatwg.org/multipage/urls-and-fetching.html#valid-non-empty-url-potentially-surrounded-by-spaces)', + 'test/fixtures/markuplint/image-naming-test.html:21:45 The "src" attribute is matched with the below disallowed patterns: /[A-Z\\s_]/', + 'test/fixtures/markuplint/image-naming-test.html:22:39 The "src" attribute is matched with the below disallowed patterns: /[A-Z\\s_]/', + 'test/fixtures/markuplint/image-naming-test.html:23:44 The "src" attribute is matched with the below disallowed patterns: /[A-Z\\s_]/', + 'test/fixtures/markuplint/image-naming-test.html:24:41 The "src" attribute is matched with the below disallowed patterns: /[A-Z\\s_]/', ]); const validNaming = await markuplint( @@ -203,23 +203,69 @@ describe('markuplint', () => { 'packages/@d-zero/markuplint-config/base.js', ); expect(violations).toStrictEqual([ - 'test/fixtures/markuplint/button-command.html:27:17 The "btn" class name is unmatched with the below patterns: "/^c-(?
+
+ Home
+
+
+
+
+
+ href before class and id
+
+
+
diff --git a/test/fixtures/markuplint/button-command.html b/test/fixtures/markuplint/button-command.html
index 3a9aab9f..ead42ba6 100644
--- a/test/fixtures/markuplint/button-command.html
+++ b/test/fixtures/markuplint/button-command.html
@@ -24,7 +24,7 @@
-
-
+
+
-
-
+
+