@markuplint/selector 4.7.7 → 5.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md ADDED
@@ -0,0 +1,126 @@
1
+ ---
2
+ description: Perform maintenance tasks for @markuplint/selector
3
+ ---
4
+
5
+ # selector-maintenance
6
+
7
+ Perform maintenance tasks for `@markuplint/selector`: add extended pseudo-classes,
8
+ support new CSS selectors, debug matching issues, and update dependencies.
9
+
10
+ ## Input
11
+
12
+ `$ARGUMENTS` specifies the task. Supported tasks:
13
+
14
+ | Task | Description |
15
+ | ---------------------------------- | -------------------------------------------- |
16
+ | `add-pseudo-class <name>` | Add a new extended pseudo-class |
17
+ | `add-selector-support <type>` | Add support for a new CSS selector type |
18
+ | `debug-matching <selector> <html>` | Debug selector matching against HTML |
19
+ | `update-deps` | Update dependencies and verify compatibility |
20
+
21
+ If omitted, defaults to `add-pseudo-class`.
22
+
23
+ ## Reference
24
+
25
+ Before executing any task, read `docs/maintenance.md` (or `docs/maintenance.ja.md`)
26
+ for the full guide. The recipes there are the source of truth for procedures.
27
+
28
+ Also read:
29
+
30
+ - `docs/matching.md` -- Selector matching algorithm details
31
+ - `ARCHITECTURE.md` -- Package overview, module map, and class hierarchy
32
+
33
+ ## Task: add-pseudo-class
34
+
35
+ Add a new markuplint-specific extended pseudo-class. Follow recipe #1 in `docs/maintenance.md`.
36
+
37
+ ### Step 1: Create the handler
38
+
39
+ 1. Read existing handlers in `src/extended-selector/` to understand the pattern
40
+ 2. Create `src/extended-selector/<name>-pseudo-class.ts`
41
+ 3. Implement the handler following the `ExtendedPseudoClass` signature:
42
+ ```typescript
43
+ (content: string) => (el: SelectorElement) => SelectorResult;
44
+ ```
45
+ 4. Return specificity `[0, 1, 0]` for consistency with other extended pseudo-classes
46
+
47
+ ### Step 2: Register the handler
48
+
49
+ 1. Open `src/create-selector.ts`
50
+ 2. Import the new handler
51
+ 3. Add it to the extended object in `createSelector()` when `specs` is provided
52
+
53
+ ### Step 3: Test and document
54
+
55
+ 1. Add tests in `src/create-selector.spec.ts` or a new test file
56
+ 2. Update `README.md` extended selector table
57
+ 3. Build: `yarn build --scope @markuplint/selector`
58
+ 4. Run tests: `yarn test --scope @markuplint/selector`
59
+
60
+ ## Task: add-selector-support
61
+
62
+ Add support for a currently unsupported CSS pseudo-class. Follow recipe #2 in `docs/maintenance.md`.
63
+
64
+ ### Step 1: Implement matching
65
+
66
+ 1. Read `src/selector.ts`, find the `pseudoMatch()` function
67
+ 2. Move the target pseudo-class from the unsupported case list
68
+ 3. Add a new case with matching implementation
69
+ 4. Ensure correct specificity assignment
70
+
71
+ ### Step 2: Test and document
72
+
73
+ 1. Add tests covering the new selector
74
+ 2. Update `README.md` support table (change `❌` to `✅`)
75
+ 3. Build: `yarn build --scope @markuplint/selector`
76
+ 4. Run tests: `yarn test --scope @markuplint/selector`
77
+
78
+ ## Task: debug-matching
79
+
80
+ Debug why a selector matches or doesn't match against given HTML.
81
+
82
+ ### Step 1: Set up the environment
83
+
84
+ 1. Read `src/selector.ts` and `src/match-selector.ts` to understand the matching flow
85
+ 2. Identify whether the selector is a CSS string or a `RegexSelector` object
86
+
87
+ ### Step 2: Trace the matching
88
+
89
+ 1. Enable debug logging: `DEBUG=selector* yarn test --scope @markuplint/selector`
90
+ 2. For CSS selectors, trace through:
91
+ - `Ruleset.parse()` -- Check that the selector parses correctly
92
+ - `StructuredSelector` -- Check the `SelectorTarget` chain
93
+ - `SelectorTarget` -- Check each compound selector component
94
+ 3. For regex selectors, trace through:
95
+ - `regexSelectorMatches()` -- Check pattern matching results
96
+ - `SelectorTarget.match()` -- Check combinator traversal
97
+
98
+ ### Step 3: Report findings
99
+
100
+ 1. Identify the exact point where matching succeeds or fails
101
+ 2. Check if the issue is in parsing, matching, or specificity calculation
102
+ 3. Suggest a fix or workaround
103
+
104
+ ## Task: update-deps
105
+
106
+ Update package dependencies and verify compatibility.
107
+
108
+ 1. Read `package.json` for current dependency versions
109
+ 2. Check for available updates
110
+ 3. For `postcss-selector-parser` updates:
111
+ - Review the changelog for breaking AST or API changes
112
+ - Refer to recipe #3 in `docs/maintenance.md` for the integration surface
113
+ 4. For `@markuplint/ml-spec` updates:
114
+ - Check for type changes affecting extended pseudo-classes
115
+ 5. Update dependencies
116
+ 6. Build: `yarn build --scope @markuplint/selector`
117
+ 7. Run tests: `yarn test --scope @markuplint/selector`
118
+ 8. Verify no regressions in selector matching
119
+
120
+ ## Rules
121
+
122
+ 1. **Always build after source changes.** Run `yarn build --scope @markuplint/selector` before testing.
123
+ 2. **All extended pseudo-classes use `[0, 1, 0]` specificity.** Maintain this consistency.
124
+ 3. **CSS matching uses postcss-selector-parser AST.** Never manually parse CSS selector strings.
125
+ 4. **Regex matching is separate from CSS matching.** They share the `matchSelector()` entry point but use different code paths.
126
+ 5. **Selector instances are cached.** After changing parsing or matching logic, the cache may return stale instances in tests. Clear the cache or restart the test runner.
@@ -0,0 +1,211 @@
1
+ # メンテナンスガイド
2
+
3
+ `@markuplint/selector` の実践的な運用・メンテナンスガイド。
4
+
5
+ ## コマンド
6
+
7
+ | コマンド | 説明 |
8
+ | ----------------------------------------------- | --------------------------------- |
9
+ | `yarn build --scope @markuplint/selector` | TypeScript を `lib/` にコンパイル |
10
+ | `yarn workspace @markuplint/selector run dev` | ウォッチモードでコンパイル |
11
+ | `yarn workspace @markuplint/selector run clean` | コンパイル出力をクリーン |
12
+ | `yarn test --scope @markuplint/selector` | テストを実行 |
13
+
14
+ ## テスト
15
+
16
+ テストは `vitest` と `jsdom`(DOM 環境)を使用します。4 つのテストファイルがパッケージをカバーしています:
17
+
18
+ | テストファイル | カバー範囲 |
19
+ | -------------------------------- | ------------------------------------------------------------------------ |
20
+ | `selector.spec.ts` | コア `Selector` クラス、CSS セレクタマッチング、コンビネータ、擬似クラス |
21
+ | `create-selector.spec.ts` | `createSelector()` ファクトリ、キャッシュ、拡張擬似クラス統合 |
22
+ | `match-selector.spec.ts` | `matchSelector()` 統合関数、CSS と Regex セレクタのディスパッチ |
23
+ | `regex-selector-matches.spec.ts` | `regexSelectorMatches()` パターンマッチング、キャプチャグループ |
24
+
25
+ ### テストの実行
26
+
27
+ ```bash
28
+ # 全セレクタテストを実行
29
+ yarn test --scope @markuplint/selector
30
+
31
+ # 特定のテストファイルを実行
32
+ yarn workspace @markuplint/selector run vitest run src/selector.spec.ts
33
+ ```
34
+
35
+ ## レシピ
36
+
37
+ ### 1. 新しい拡張擬似クラスの追加
38
+
39
+ 新しい markuplint 固有の擬似クラス(例: `:custom()`)を追加するには:
40
+
41
+ 1. `src/extended-selector/custom-pseudo-class.ts` を作成:
42
+
43
+ ```typescript
44
+ import type { SelectorElement, SelectorResult } from '../types.js';
45
+
46
+ export function customPseudoClass() {
47
+ return (content: string) =>
48
+ (el: SelectorElement): SelectorResult => {
49
+ // content 文字列をパースして el に対してマッチング
50
+ // 完全な DOM API(例: @markuplint/ml-spec)が必要な場合は、
51
+ // その境界で `el as Element` とキャストしてください。
52
+ const matched = /* マッチングロジック */;
53
+ return {
54
+ specificity: [0, 1, 0],
55
+ matched,
56
+ ...(matched ? { nodes: [el], has: [] } : {}),
57
+ };
58
+ };
59
+ }
60
+ ```
61
+
62
+ 2. `src/create-selector.ts` で登録:
63
+
64
+ ```typescript
65
+ import { customPseudoClass } from './extended-selector/custom-pseudo-class.js';
66
+
67
+ // createSelector() 内の extended オブジェクトに追加:
68
+ instance = new Selector(
69
+ selector,
70
+ specs
71
+ ? {
72
+ model: contentModelPseudoClass(specs),
73
+ aria: ariaPseudoClass(),
74
+ role: ariaRolePseudoClass(specs),
75
+ custom: customPseudoClass(), // ここに追加
76
+ }
77
+ : undefined,
78
+ );
79
+ ```
80
+
81
+ 3. `src/create-selector.spec.ts` または新規テストファイルにテストを追加
82
+ 4. `README.md` に新しい擬似クラスのドキュメントを追加
83
+ 5. ビルド: `yarn build --scope @markuplint/selector`
84
+
85
+ ### 2. 新しい CSS セレクタタイプのサポート追加
86
+
87
+ 現在サポートされていない擬似クラス(例: `:empty`)をサポートするには:
88
+
89
+ 1. `src/selector.ts` を開く
90
+ 2. `pseudoMatch()` 関数の switch 文を見つける
91
+ 3. 擬似クラスを「非サポート」ケースリストから新しいケースに移動して実装:
92
+ ```typescript
93
+ case ':empty': {
94
+ const hasChildren = el.childNodes.length === 0;
95
+ return {
96
+ specificity: [0, 1, 0],
97
+ matched: hasChildren,
98
+ ...(hasChildren ? { nodes: [el], has: [] } : {}),
99
+ };
100
+ }
101
+ ```
102
+ 4. 新しいセレクタのテストを追加
103
+ 5. `README.md` のサポート表を更新(`❌` を `✅` に変更)
104
+ 6. ビルド: `yarn build --scope @markuplint/selector`
105
+
106
+ ### 3. postcss-selector-parser メジャー更新時の対応
107
+
108
+ `postcss-selector-parser` がメジャーバージョンをリリースした場合:
109
+
110
+ 1. 以下の破壊的変更をチェンジログで確認:
111
+ - AST ノード型(`parser.Selector`、`parser.Node` 等)
112
+ - パーサ API(`parser()`、`.processSync()`)
113
+ - ノードプロパティの名前と型
114
+ 2. `src/selector.ts` の主要な統合ポイント:
115
+ - `Ruleset.parse()` -- `parser()` と `processSync()` を使用
116
+ - `StructuredSelector` コンストラクタ -- `parser.Node` 型を走査
117
+ - `SelectorTarget` -- ノードプロパティ(`value`、`attribute`、`operator`、`raws` 等)にアクセス
118
+ 3. `package.json` の依存バージョンを更新
119
+ 4. 型エラーや API 変更を修正
120
+ 5. テストスイート全体を実行して互換性を確認
121
+ 6. ビルド: `yarn build --scope @markuplint/selector`
122
+
123
+ ### 4. 詳細度計算の修正
124
+
125
+ 詳細度が正しく計算されない場合:
126
+
127
+ 1. `src/compare-specificity.ts` の比較ロジックを確認
128
+ 2. `src/selector.ts` の `SelectorTarget` と `pseudoMatch()` での詳細度割り当てを確認
129
+ 3. Regex セレクタの場合、`src/match-selector.ts` の `uncombinedRegexSelect()` での詳細度追跡を確認
130
+ 4. 主要な詳細度の値:
131
+ - ID: `[1, 0, 0]`
132
+ - クラス、属性、擬似クラス: `[0, 1, 0]`
133
+ - タイプ(タグ): `[0, 0, 1]`
134
+ - ユニバーサル: `[0, 0, 0]`
135
+ - `:where()`: 常に `[0, 0, 0]`
136
+ 5. 誤った計算を再現するテストケースを追加
137
+ 6. 修正して確認
138
+
139
+ ### 5. 新しい Regex コンビネータの追加
140
+
141
+ Regex セレクタに新しいコンビネータを追加するには:
142
+
143
+ 1. `src/types.ts` の `RegexSelectorCombinator` 型を更新:
144
+ ```typescript
145
+ export type RegexSelectorCombinator = ' ' | '>' | '+' | '~' | ':has(+)' | ':has(~)' | ':new()';
146
+ ```
147
+ 2. `src/match-selector.ts` の `SelectorTarget.match()` の switch にトラバーサルロジックを追加:
148
+ ```typescript
149
+ case ':new()': {
150
+ // DOM トラバーサルロジックを実装
151
+ }
152
+ ```
153
+ 3. 新しいコンビネータのテストを追加
154
+ 4. ドキュメントを更新
155
+ 5. ビルド: `yarn build --scope @markuplint/selector`
156
+
157
+ ## トラブルシューティング
158
+
159
+ ### セレクタパースエラー
160
+
161
+ **症状:** 有効に見えるセレクタで `InvalidSelectorError` がスローされる。
162
+
163
+ **診断:**
164
+
165
+ 1. `postcss-selector-parser` がそのセレクタ構文をサポートしているか確認
166
+ 2. セレクタを分離してテスト:
167
+ ```typescript
168
+ import parser from 'postcss-selector-parser';
169
+ parser().processSync('your-selector');
170
+ ```
171
+ 3. 一部のセレクタには特定の `postcss-selector-parser` バージョンが必要な場合がある
172
+
173
+ ### マッチングの不一致
174
+
175
+ **症状:** セレクタがマッチすべき時にマッチしない、またはその逆。
176
+
177
+ **診断:**
178
+
179
+ 1. デバッグログを有効化: `DEBUG=selector* yarn test --scope @markuplint/selector`
180
+ 2. `SelectorTarget` のマッチング順序を確認 -- コンポーネントは順次チェックされ、最初の不一致で失敗
181
+ 3. HTML/SVG 要素の名前空間解決を `resolveNamespace()` で確認
182
+ 4. 拡張擬似クラスの場合、`specs` が `createSelector()` に渡されているか確認
183
+
184
+ ### 詳細度計算の問題
185
+
186
+ **症状:** 詳細度の高いルールが優先されない。
187
+
188
+ **診断:**
189
+
190
+ 1. `matchSelector()` または `Selector.match()` が返す詳細度の値をログ出力
191
+ 2. `compareSpecificity()` の比較ロジックを確認
192
+ 3. `:where()` が正しく `[0, 0, 0]` を返しているか確認
193
+ 4. ネストされた擬似クラス(`:not(:is(.a, .b))`)の場合、再帰的な詳細度計算をトレース
194
+
195
+ ## 依存関係メモ
196
+
197
+ ### postcss-selector-parser
198
+
199
+ - `selector.ts` 全体で使用される CSS セレクタ AST を提供
200
+ - 主要な型: `parser.Selector`、`parser.Node`、`parser.Pseudo`、`parser.Attribute`、`parser.ClassName`、`parser.Identifier`、`parser.Tag`、`parser.Universal`、`parser.Combinator`
201
+ - メジャーバージョンの破壊的変更は AST ノード構造に影響する可能性あり
202
+
203
+ ### @markuplint/ml-spec
204
+
205
+ - `getComputedRole()`、`getAccname()`、`contentModelCategoryToTagNames()`、`resolveNamespace()` を提供
206
+ - `MLMLSpec` の型変更は拡張擬似クラスの実装に影響する可能性あり
207
+
208
+ ### jsdom(開発)
209
+
210
+ - テストで DOM 環境を作成するための `JSDOM` を提供
211
+ - `jsdom` で作成された要素はセレクタマッチングロジックが使用する `SelectorElement` インターフェースを満たす
@@ -0,0 +1,211 @@
1
+ # Maintenance Guide
2
+
3
+ Practical operations and maintenance guide for `@markuplint/selector`.
4
+
5
+ ## Commands
6
+
7
+ | Command | Description |
8
+ | ----------------------------------------------- | ---------------------------- |
9
+ | `yarn build --scope @markuplint/selector` | Compile TypeScript to `lib/` |
10
+ | `yarn workspace @markuplint/selector run dev` | Watch mode compilation |
11
+ | `yarn workspace @markuplint/selector run clean` | Clean compiled output |
12
+ | `yarn test --scope @markuplint/selector` | Run tests |
13
+
14
+ ## Testing
15
+
16
+ Tests use `vitest` with `jsdom` as the DOM environment. Four test files cover the package:
17
+
18
+ | Test File | Coverage |
19
+ | -------------------------------- | ------------------------------------------------------------------------- |
20
+ | `selector.spec.ts` | Core `Selector` class, CSS selector matching, combinators, pseudo-classes |
21
+ | `create-selector.spec.ts` | `createSelector()` factory, caching, extended pseudo-class integration |
22
+ | `match-selector.spec.ts` | `matchSelector()` unified function, CSS and regex selector dispatch |
23
+ | `regex-selector-matches.spec.ts` | `regexSelectorMatches()` pattern matching, capture groups |
24
+
25
+ ### Running Tests
26
+
27
+ ```bash
28
+ # Run all selector tests
29
+ yarn test --scope @markuplint/selector
30
+
31
+ # Run a specific test file
32
+ yarn workspace @markuplint/selector run vitest run src/selector.spec.ts
33
+ ```
34
+
35
+ ## Common Recipes
36
+
37
+ ### 1. Adding a New Extended Pseudo-Class
38
+
39
+ To add a new markuplint-specific pseudo-class (e.g., `:custom()`):
40
+
41
+ 1. Create `src/extended-selector/custom-pseudo-class.ts`:
42
+
43
+ ```typescript
44
+ import type { SelectorElement, SelectorResult } from '../types.js';
45
+
46
+ export function customPseudoClass() {
47
+ return (content: string) =>
48
+ (el: SelectorElement): SelectorResult => {
49
+ // Parse content string and match against el
50
+ // If you need full DOM APIs (e.g., from @markuplint/ml-spec),
51
+ // cast with `el as Element` at that boundary.
52
+ const matched = /* your matching logic */;
53
+ return {
54
+ specificity: [0, 1, 0],
55
+ matched,
56
+ ...(matched ? { nodes: [el], has: [] } : {}),
57
+ };
58
+ };
59
+ }
60
+ ```
61
+
62
+ 2. Register it in `src/create-selector.ts`:
63
+
64
+ ```typescript
65
+ import { customPseudoClass } from './extended-selector/custom-pseudo-class.js';
66
+
67
+ // In createSelector(), add to the extended object:
68
+ instance = new Selector(
69
+ selector,
70
+ specs
71
+ ? {
72
+ model: contentModelPseudoClass(specs),
73
+ aria: ariaPseudoClass(),
74
+ role: ariaRolePseudoClass(specs),
75
+ custom: customPseudoClass(), // Add here
76
+ }
77
+ : undefined,
78
+ );
79
+ ```
80
+
81
+ 3. Add tests in `src/create-selector.spec.ts` or a new test file
82
+ 4. Update `README.md` with the new pseudo-class documentation
83
+ 5. Build: `yarn build --scope @markuplint/selector`
84
+
85
+ ### 2. Adding Support for a New CSS Selector Type
86
+
87
+ To support a currently unsupported pseudo-class (e.g., `:empty`):
88
+
89
+ 1. Open `src/selector.ts`
90
+ 2. Find the `pseudoMatch()` function's switch statement
91
+ 3. Move the pseudo-class from the "unsupported" case list to a new case with implementation:
92
+ ```typescript
93
+ case ':empty': {
94
+ const hasChildren = el.childNodes.length === 0;
95
+ return {
96
+ specificity: [0, 1, 0],
97
+ matched: hasChildren,
98
+ ...(hasChildren ? { nodes: [el], has: [] } : {}),
99
+ };
100
+ }
101
+ ```
102
+ 4. Add tests for the new selector
103
+ 5. Update `README.md` support table (change `❌` to `✅`)
104
+ 6. Build: `yarn build --scope @markuplint/selector`
105
+
106
+ ### 3. Handling postcss-selector-parser Major Updates
107
+
108
+ When `postcss-selector-parser` releases a major version:
109
+
110
+ 1. Check the changelog for breaking changes in:
111
+ - AST node types (`parser.Selector`, `parser.Node`, etc.)
112
+ - Parser API (`parser()`, `.processSync()`)
113
+ - Node property names and types
114
+ 2. Key integration points in `src/selector.ts`:
115
+ - `Ruleset.parse()` -- Uses `parser()` and `processSync()`
116
+ - `StructuredSelector` constructor -- Walks `parser.Node` types
117
+ - `SelectorTarget` -- Accesses node properties (`value`, `attribute`, `operator`, `raws`, etc.)
118
+ 3. Update the dependency version in `package.json`
119
+ 4. Fix any type errors or API changes
120
+ 5. Run the full test suite to verify compatibility
121
+ 6. Build: `yarn build --scope @markuplint/selector`
122
+
123
+ ### 4. Fixing Specificity Calculation
124
+
125
+ If specificity is calculated incorrectly:
126
+
127
+ 1. Check `src/compare-specificity.ts` for comparison logic
128
+ 2. Check `src/selector.ts` for specificity assignment in `SelectorTarget` and `pseudoMatch()`
129
+ 3. For regex selectors, check `src/match-selector.ts` `uncombinedRegexSelect()` for specificity tracking
130
+ 4. Key specificity values:
131
+ - ID: `[1, 0, 0]`
132
+ - Class, attribute, pseudo-class: `[0, 1, 0]`
133
+ - Type (tag): `[0, 0, 1]`
134
+ - Universal: `[0, 0, 0]`
135
+ - `:where()`: Always `[0, 0, 0]`
136
+ 5. Add a test case reproducing the incorrect calculation
137
+ 6. Fix and verify
138
+
139
+ ### 5. Adding a New Regex Combinator
140
+
141
+ To add a new combinator for regex selectors:
142
+
143
+ 1. Update the `RegexSelectorCombinator` type in `src/types.ts`:
144
+ ```typescript
145
+ export type RegexSelectorCombinator = ' ' | '>' | '+' | '~' | ':has(+)' | ':has(~)' | ':new()';
146
+ ```
147
+ 2. Add the traversal logic in `src/match-selector.ts` in the `SelectorTarget.match()` switch:
148
+ ```typescript
149
+ case ':new()': {
150
+ // Implement DOM traversal logic
151
+ }
152
+ ```
153
+ 3. Add tests for the new combinator
154
+ 4. Update documentation
155
+ 5. Build: `yarn build --scope @markuplint/selector`
156
+
157
+ ## Troubleshooting
158
+
159
+ ### Selector Parse Errors
160
+
161
+ **Symptom:** `InvalidSelectorError` thrown for a valid-looking selector.
162
+
163
+ **Diagnosis:**
164
+
165
+ 1. Check if `postcss-selector-parser` supports the selector syntax
166
+ 2. Test the selector in isolation:
167
+ ```typescript
168
+ import parser from 'postcss-selector-parser';
169
+ parser().processSync('your-selector');
170
+ ```
171
+ 3. Some selectors may require specific `postcss-selector-parser` versions
172
+
173
+ ### Matching Mismatches
174
+
175
+ **Symptom:** A selector matches or doesn't match when it should/shouldn't.
176
+
177
+ **Diagnosis:**
178
+
179
+ 1. Enable debug logging: `DEBUG=selector* yarn test --scope @markuplint/selector`
180
+ 2. Check the `SelectorTarget` matching order -- components are checked sequentially and fail on first mismatch
181
+ 3. Verify namespace resolution for HTML/SVG elements using `resolveNamespace()`
182
+ 4. For extended pseudo-classes, verify that `specs` are being passed to `createSelector()`
183
+
184
+ ### Specificity Calculation Issues
185
+
186
+ **Symptom:** Rules with higher specificity are not taking precedence.
187
+
188
+ **Diagnosis:**
189
+
190
+ 1. Log the specificity values returned by `matchSelector()` or `Selector.match()`
191
+ 2. Check `compareSpecificity()` comparison logic
192
+ 3. Verify `:where()` is correctly returning `[0, 0, 0]`
193
+ 4. For nested pseudo-classes (`:not(:is(.a, .b))`), trace through the recursive specificity calculation
194
+
195
+ ## Dependency Notes
196
+
197
+ ### postcss-selector-parser
198
+
199
+ - Provides the CSS selector AST used throughout `selector.ts`
200
+ - Key types: `parser.Selector`, `parser.Node`, `parser.Pseudo`, `parser.Attribute`, `parser.ClassName`, `parser.Identifier`, `parser.Tag`, `parser.Universal`, `parser.Combinator`
201
+ - Breaking changes in major versions may affect AST node structure
202
+
203
+ ### @markuplint/ml-spec
204
+
205
+ - Provides `getComputedRole()`, `getAccname()`, `contentModelCategoryToTagNames()`, `resolveNamespace()`
206
+ - Type changes in `MLMLSpec` may affect extended pseudo-class implementations
207
+
208
+ ### jsdom (dev)
209
+
210
+ - Provides `JSDOM` for creating DOM environments in tests
211
+ - Elements created via `jsdom` satisfy the `SelectorElement` interface used by the selector matching logic