@markuplint/astro-parser 5.0.0-dev.5 → 5.0.0-rc.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.
@@ -2,17 +2,18 @@
2
2
 
3
3
  ## 概要
4
4
 
5
- `@markuplint/astro-parser` は markuplint における Astro コンポーネントファイル(`.astro`)のパーサーです。`astro-eslint-parser`(`@astrojs/compiler` をラップ)を使用して Astro ソースコードをトークン化し、その結果の AST を markuplint の統一 AST 形式(`MLASTDocument`)に変換します。フロントマターブロック(`---...---`)、式コンテナ(`{expression}`)、テンプレートディレクティブ(例: `class:list`、`set:html`)、ショートハンド属性(`{prop}`)、名前空間対応の要素解決(XHTML vs SVG)など、Astro 固有の構文を処理します。
5
+ `@markuplint/astro-parser` は markuplint における Astro コンポーネントファイル(`.astro`)のパーサーです。`astro-eslint-parser`(`@astrojs/compiler` をラップ)を使用して Astro ソースコードをトークン化し、その結果の AST を markuplint の統一 AST 形式(`MLASTDocument`)に変換します。フロントマターブロック(`---...---`)、式コンテナ(`{expression}`)、テンプレートディレクティブ(例: `class:list`、`set:html`、`client:load`)、ショートハンド属性(`{prop}`)など、Astro 固有の構文を処理します。
6
6
 
7
7
  ## ディレクトリ構成
8
8
 
9
9
  ```
10
10
  src/
11
- ├── index.ts — parser インスタンスを再エクスポート
12
- ├── parser.ts — Parser<Node, State> を拡張する AstroParser クラス
13
- ├── astro-parser.ts — astro-eslint-parser ラッパーと型の再エクスポート
14
- ├── parser.spec.ts AstroParser 統合テスト
15
- └── astro-parser.spec.ts astro-eslint-parser ラッパーテスト
11
+ ├── index.ts — parser インスタンスを再エクスポート
12
+ ├── parser.ts — Parser<Node> を拡張する AstroParser クラス
13
+ ├── astro-parser.ts — astro-eslint-parser ラッパーと型の再エクスポート
14
+ ├── detect-block-behavior.ts .map()/.filter() のブロック動作検出
15
+ ├── parser.spec.ts AstroParser 統合テスト
16
+ └── astro-parser.spec.ts — astro-eslint-parser ラッパーテスト
16
17
  ```
17
18
 
18
19
  ## アーキテクチャ図
@@ -27,8 +28,9 @@ flowchart TD
27
28
  end
28
29
 
29
30
  subgraph pkg ["@markuplint/astro-parser"]
30
- astroParser["AstroParser\nextends Parser‹Node, State›"]
31
+ astroParser["AstroParser\nextends Parser‹Node›"]
31
32
  astroParseFn["astroParse()\nastro-eslint-parser ラッパー"]
33
+ detectBlock["detectBlockBehavior()\n.map()/.filter() 検出"]
32
34
  end
33
35
 
34
36
  subgraph downstream ["下流"]
@@ -40,6 +42,7 @@ flowchart TD
40
42
  astroEslintParser -->|"parseTemplate()"| astroParseFn
41
43
  astroCompiler -->|"Node 型"| astroParseFn
42
44
  astroParseFn -->|"RootNode.children"| astroParser
45
+ detectBlock -->|"blockBehavior"| astroParser
43
46
  astroParser -->|"MLASTDocument を生成"| mlCore
44
47
  ```
45
48
 
@@ -48,7 +51,7 @@ flowchart TD
48
51
  ### 継承関係
49
52
 
50
53
  ```
51
- Parser<Node, State> (@markuplint/parser-utils)
54
+ Parser<Node> (@markuplint/parser-utils)
52
55
  └── AstroParser (このパッケージ)
53
56
  ```
54
57
 
@@ -62,16 +65,6 @@ Parser<Node, State> (@markuplint/parser-utils)
62
65
  | `selfCloseType` | `'html+xml'` | HTML void 要素と XML スタイルの自己閉じ(`<Component />`)の両方を受け入れる |
63
66
  | `tagNameCaseSensitive` | `true` | コンポーネント(`<MyComp>`)と HTML 要素(`<div>`)を区別 |
64
67
 
65
- ### State 型
66
-
67
- パーサーは `State` 型を通じて内部状態を管理します:
68
-
69
- | フィールド | 型 | 用途 |
70
- | ---------- | -------- | --------------------------------------------------------------- |
71
- | `scopeNS` | `string` | 現在の名前空間 URI、デフォルトは `http://www.w3.org/1999/xhtml` |
72
-
73
- `scopeNS` 状態は `#updateScopeNS()` によってパーサーが要素を走査する際に更新され、`<svg>` 要素内で SVG 名前空間に切り替わり、`<foreignObject>` 内で XHTML に戻ります。
74
-
75
68
  ### オーバーライドメソッド
76
69
 
77
70
  | メソッド | 用途 |
@@ -79,7 +72,7 @@ Parser<Node, State> (@markuplint/parser-utils)
79
72
  | `tokenize()` | `astroParse()` を呼び出して Astro AST を取得し、`{ ast: rootNode.children, isFragment: true }` を返す |
80
73
  | `nodeize()` | Astro AST ノードを markuplint ノードに変換。ノードタイプ(frontmatter, doctype, text, comment, element, expression)で振り分け |
81
74
  | `afterFlattenNodes()` | `{ exposeInvalidNode: false }` で親に委譲 |
82
- | `visitElement()` | `parseCodeFragment()` で `namelessFragment: true` として生の HTML フラグメントをパースし、名前空間と終了タグ処理で親に委譲 |
75
+ | `visitElement()` | `parseCodeFragment()` で `namelessFragment: true` として生の HTML フラグメントをパースし、終了タグ処理で親に委譲 |
83
76
  | `visitChildren()` | 親に委譲した後、予期しない兄弟ノードが残っていないことをアサート |
84
77
  | `visitAttr()` | 波括弧式の値、ショートハンド属性、テンプレートディレクティブを処理 |
85
78
  | `detectElementType()` | `/^[A-Z]/` パターンでコンポーネントと HTML 要素を検出(大文字始まりの名前はコンポーネント) |
@@ -119,35 +112,6 @@ Astro の式(`{expression}`)は Astro AST で `type: 'expression'` ノード
119
112
  - 最後の子の開始から式の終了までの領域が終了フラグメントになる
120
113
  - 間の子は開始フラグメントの psblock 内で通常通り訪問される
121
114
 
122
- ## 名前空間スコーピング
123
-
124
- `#updateScopeNS()` プライベートメソッドは、パーサーが要素を走査する際に名前空間コンテキストを管理します:
125
-
126
- | 条件 | アクション |
127
- | ----------------------------------------------------- | ---------------------------------------------------- |
128
- | 現在の名前空間が XHTML で、ノードが `<svg>` 要素 | `scopeNS` を `http://www.w3.org/2000/svg` に切り替え |
129
- | 現在の名前空間が SVG で、親ノードが `<foreignObject>` | `scopeNS` を `http://www.w3.org/1999/xhtml` に戻す |
130
-
131
- これは `nodeize()` のノードタイプ switch の前に呼び出されるため、すべての子ノードが正しい名前空間を継承します。名前空間は `visitElement()` 内で `overwriteProps: { namespace: this.state.scopeNS }` を通じて要素に適用されます。
132
-
133
- 名前空間解決の例:
134
-
135
- ```html
136
- <div>
137
- <!-- XHTML -->
138
- <svg>
139
- <!-- SVG -->
140
- <text />
141
- <!-- SVG -->
142
- <foreignObject>
143
- <!-- SVG -->
144
- <div />
145
- <!-- XHTML(リセット) -->
146
- </foreignObject>
147
- </svg>
148
- </div>
149
- ```
150
-
151
115
  ## 属性処理
152
116
 
153
117
  ### クォートセット
@@ -173,15 +137,18 @@ Astro の式(`{expression}`)は Astro AST で `type: 'expression'` ノード
173
137
 
174
138
  Astro テンプレートディレクティブは `name:modifier` 構文を使用します。パーサーは正規表現 `/^([^:]+):([^:]+)$/` でこれらを検出します:
175
139
 
176
- | ディレクティブ | `potentialName` | `isDirective` | 動作 |
177
- | -------------- | --------------- | ------------- | -------------------------------------- |
178
- | `class:list` | `'class'` | `undefined` | 標準の `class` 属性にマッピング |
179
- | `set:html` | `undefined` | `true` | Astro 固有ディレクティブとして扱われる |
180
- | `set:text` | `undefined` | `true` | Astro 固有ディレクティブとして扱われる |
181
- | `is:raw` | `undefined` | `true` | Astro 固有ディレクティブとして扱われる |
182
- | `transition:*` | `undefined` | `true` | Astro 固有ディレクティブとして扱われる |
140
+ | ディレクティブプレフィックス | `potentialName` | `isDirective` | 動作 |
141
+ | ---------------------------- | --------------- | ------------- | ----------------------------------------------------- |
142
+ | `class:` | `'class'` | `false` | 標準の `class` 属性にマッピング |
143
+ | `client:` | | `true` | Astro クライアントディレクティブ(load, idle 等) |
144
+ | `server:` | | `true` | Astro サーバーディレクティブ(defer) |
145
+ | `set:` | | `true` | コンテンツディレクティブ(html, text) |
146
+ | `is:` | | `true` | プロパティディレクティブ(inline, raw) |
147
+ | `define:` | — | `true` | スタイルディレクティブ(vars) |
148
+ | `transition:` | — | `true` | View Transition ディレクティブ(animate, name) |
149
+ | _(その他すべて)_ | — | `true` | キャッチオール: すべての `prefix:name` パターンに適用 |
183
150
 
184
- `class` ディレクティブは特別で、`potentialName: 'class'` を取得するため、`class` 属性に対する markuplint ルールが適用されます。その他すべてのディレクティブは `isDirective: true` を取得し、フレームワーク固有であり標準 HTML 属性として検証すべきでないことを markuplint に伝えます。
151
+ `class:` プレフィックスは特別扱いで、`potentialName: 'class'` を取得するため、`class` 属性に対する markuplint ルールが適用されます。その他のコロン区切りプレフィックスは `default` ケースに該当し `isDirective: true` を取得します。これはフレームワーク固有であり標準 HTML 属性として検証すべきでないことを markuplint に伝えます。
185
152
 
186
153
  ### 動的な値
187
154
 
@@ -199,7 +166,7 @@ Astro テンプレートディレクティブは `name:modifier` 構文を使用
199
166
  | **フロントマター** | サポート(`---...---` psblock) | 該当なし |
200
167
  | **式の構文** | `{expr}` を MustacheTag psblock として | `{expr}` を JSXExpressionContainer psblock として |
201
168
  | **テンプレートディレクティブ** | `class:list`、`set:html` 等 | 該当なし |
202
- | **名前空間管理** | `#updateScopeNS()` で手動管理 | html-parser の `getNamespace()` に委譲 |
169
+ | **名前空間管理** | 基底 `Parser` に委譲 | html-parser の `getNamespace()` に委譲 |
203
170
  | **コンポーネント検出** | `/^[A-Z]/` パターン | `/^[A-Z]/` パターン |
204
171
  | **自己閉じタイプ** | `html+xml` | デフォルト(XML のみ) |
205
172
  | **booleanish 属性** | 未設定 | `booleanish: true` |
package/ARCHITECTURE.md CHANGED
@@ -2,17 +2,18 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- `@markuplint/astro-parser` is a parser for Astro component files (`.astro`) in markuplint. It uses `astro-eslint-parser` (which wraps `@astrojs/compiler`) to tokenize Astro source code, then converts the resulting AST into markuplint's unified AST format (`MLASTDocument`). The parser handles Astro-specific syntax including frontmatter blocks (`---...---`), expression containers (`{expression}`), template directives (e.g., `class:list`, `set:html`), shorthand attributes (`{prop}`), and namespace-aware element resolution (XHTML vs SVG).
5
+ `@markuplint/astro-parser` is a parser for Astro component files (`.astro`) in markuplint. It uses `astro-eslint-parser` (which wraps `@astrojs/compiler`) to tokenize Astro source code, then converts the resulting AST into markuplint's unified AST format (`MLASTDocument`). The parser handles Astro-specific syntax including frontmatter blocks (`---...---`), expression containers (`{expression}`), template directives (e.g., `class:list`, `set:html`, `client:load`), and shorthand attributes (`{prop}`).
6
6
 
7
7
  ## Directory Structure
8
8
 
9
9
  ```
10
10
  src/
11
- ├── index.ts — Re-exports parser instance
12
- ├── parser.ts — AstroParser class extending Parser<Node, State>
13
- ├── astro-parser.ts — astro-eslint-parser wrapper and type re-exports
14
- ├── parser.spec.ts AstroParser integration tests
15
- └── astro-parser.spec.ts astro-eslint-parser wrapper tests
11
+ ├── index.ts — Re-exports parser instance
12
+ ├── parser.ts — AstroParser class extending Parser<Node>
13
+ ├── astro-parser.ts — astro-eslint-parser wrapper and type re-exports
14
+ ├── detect-block-behavior.ts Detects .map()/.filter() for block behavior
15
+ ├── parser.spec.ts AstroParser integration tests
16
+ └── astro-parser.spec.ts — astro-eslint-parser wrapper tests
16
17
  ```
17
18
 
18
19
  ## Architecture Diagram
@@ -27,8 +28,9 @@ flowchart TD
27
28
  end
28
29
 
29
30
  subgraph pkg ["@markuplint/astro-parser"]
30
- astroParser["AstroParser\nextends Parser‹Node, State›"]
31
+ astroParser["AstroParser\nextends Parser‹Node›"]
31
32
  astroParseFn["astroParse()\nastro-eslint-parser wrapper"]
33
+ detectBlock["detectBlockBehavior()\n.map()/.filter() detection"]
32
34
  end
33
35
 
34
36
  subgraph downstream ["Downstream"]
@@ -40,6 +42,7 @@ flowchart TD
40
42
  astroEslintParser -->|"parseTemplate()"| astroParseFn
41
43
  astroCompiler -->|"Node types"| astroParseFn
42
44
  astroParseFn -->|"RootNode.children"| astroParser
45
+ detectBlock -->|"blockBehavior"| astroParser
43
46
  astroParser -->|"produces MLASTDocument"| mlCore
44
47
  ```
45
48
 
@@ -48,7 +51,7 @@ flowchart TD
48
51
  ### Inheritance
49
52
 
50
53
  ```
51
- Parser<Node, State> (from @markuplint/parser-utils)
54
+ Parser<Node> (from @markuplint/parser-utils)
52
55
  └── AstroParser (this package)
53
56
  ```
54
57
 
@@ -62,27 +65,17 @@ The constructor configures the base `Parser` with Astro-specific options:
62
65
  | `selfCloseType` | `'html+xml'` | Accepts both HTML void elements and XML-style self-closing (`<Component />`) |
63
66
  | `tagNameCaseSensitive` | `true` | Distinguishes components (`<MyComp>`) from HTML elements (`<div>`) |
64
67
 
65
- ### State Type
66
-
67
- The parser maintains internal state through the `State` type:
68
-
69
- | Field | Type | Purpose |
70
- | --------- | -------- | ----------------------------------------------------------------- |
71
- | `scopeNS` | `string` | Current namespace URI, defaults to `http://www.w3.org/1999/xhtml` |
72
-
73
- The `scopeNS` state is updated by `#updateScopeNS()` as the parser traverses elements, switching to SVG namespace inside `<svg>` elements and back to XHTML inside `<foreignObject>`.
74
-
75
68
  ### Override Methods
76
69
 
77
- | Method | Purpose |
78
- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
79
- | `tokenize()` | Calls `astroParse()` to get the Astro AST, returns `{ ast: rootNode.children, isFragment: true }` |
80
- | `nodeize()` | Converts Astro AST nodes to markuplint nodes, dispatching by node type (frontmatter, doctype, text, comment, element, expression) |
81
- | `afterFlattenNodes()` | Delegates to parent with `{ exposeInvalidNode: false }` |
82
- | `visitElement()` | Parses the raw HTML fragment via `parseCodeFragment()` with `namelessFragment: true`, then delegates to parent with namespace and end tag handling |
83
- | `visitChildren()` | Delegates to parent, then asserts no unexpected sibling nodes remain |
84
- | `visitAttr()` | Handles curly-brace expression values, shorthand attributes, and template directives |
85
- | `detectElementType()` | Detects component vs HTML element using `/^[A-Z]/` pattern (capitalized names are components) |
70
+ | Method | Purpose |
71
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
72
+ | `tokenize()` | Calls `astroParse()` to get the Astro AST, returns `{ ast: rootNode.children, isFragment: true }` |
73
+ | `nodeize()` | Converts Astro AST nodes to markuplint nodes, dispatching by node type (frontmatter, doctype, text, comment, element, expression) |
74
+ | `afterFlattenNodes()` | Delegates to parent with `{ exposeInvalidNode: false }` |
75
+ | `visitElement()` | Parses the raw HTML fragment via `parseCodeFragment()` with `namelessFragment: true`, then delegates to parent with end tag handling |
76
+ | `visitChildren()` | Delegates to parent, then asserts no unexpected sibling nodes remain |
77
+ | `visitAttr()` | Handles curly-brace expression values, shorthand attributes, and template directives |
78
+ | `detectElementType()` | Detects component vs HTML element using `/^[A-Z]/` pattern (capitalized names are components) |
86
79
 
87
80
  ## Frontmatter Handling
88
81
 
@@ -119,35 +112,6 @@ The splitting logic checks whether `firstChild !== lastChild` in the expression'
119
112
  - The region from the last child's start to the expression end becomes the closing fragment
120
113
  - The children between are visited normally within the opening fragment's psblock
121
114
 
122
- ## Namespace Scoping
123
-
124
- The `#updateScopeNS()` private method manages namespace context as the parser traverses elements:
125
-
126
- | Condition | Action |
127
- | ------------------------------------------------------------- | ------------------------------------------------------- |
128
- | Current namespace is XHTML and node is `<svg>` element | Switch `scopeNS` to `http://www.w3.org/2000/svg` |
129
- | Current namespace is SVG and parent node is `<foreignObject>` | Switch `scopeNS` back to `http://www.w3.org/1999/xhtml` |
130
-
131
- This is called at the beginning of `nodeize()` before the node type switch, so all child nodes inherit the correct namespace. The namespace is applied to elements via `overwriteProps: { namespace: this.state.scopeNS }` in `visitElement()`.
132
-
133
- Example namespace resolution:
134
-
135
- ```html
136
- <div>
137
- <!-- XHTML -->
138
- <svg>
139
- <!-- SVG -->
140
- <text />
141
- <!-- SVG -->
142
- <foreignObject>
143
- <!-- SVG -->
144
- <div />
145
- <!-- XHTML (reset) -->
146
- </foreignObject>
147
- </svg>
148
- </div>
149
- ```
150
-
151
115
  ## Attribute Processing
152
116
 
153
117
  ### Quote Set
@@ -173,15 +137,18 @@ When an attribute token starts with `{` (e.g., `{prop}`), the parser sets `start
173
137
 
174
138
  Astro template directives use the `name:modifier` syntax. The parser detects these with the regex `/^([^:]+):([^:]+)$/`:
175
139
 
176
- | Directive | `potentialName` | `isDirective` | Behavior |
177
- | -------------- | --------------- | ------------- | ----------------------------------- |
178
- | `class:list` | `'class'` | `undefined` | Maps to standard `class` attribute |
179
- | `set:html` | `undefined` | `true` | Treated as Astro-specific directive |
180
- | `set:text` | `undefined` | `true` | Treated as Astro-specific directive |
181
- | `is:raw` | `undefined` | `true` | Treated as Astro-specific directive |
182
- | `transition:*` | `undefined` | `true` | Treated as Astro-specific directive |
140
+ | Directive prefix | `potentialName` | `isDirective` | Behavior |
141
+ | ---------------- | --------------- | ------------- | ----------------------------------------------- |
142
+ | `class:` | `'class'` | `false` | Maps to standard `class` attribute |
143
+ | `client:` | | `true` | Astro client directive (load, idle, visible...) |
144
+ | `server:` | | `true` | Astro server directive (defer) |
145
+ | `set:` | | `true` | Content directive (html, text) |
146
+ | `is:` | | `true` | Property directive (inline, raw) |
147
+ | `define:` | — | `true` | Style directive (vars) |
148
+ | `transition:` | — | `true` | View Transition directive (animate, name) |
149
+ | _(any other)_ | — | `true` | Catch-all: any `prefix:name` pattern |
183
150
 
184
- The `class` directive is special: it gets `potentialName: 'class'` so markuplint rules for the `class` attribute apply. All other directives get `isDirective: true`, which tells markuplint they are framework-specific and should not be validated as standard HTML attributes.
151
+ The `class:` prefix is special-cased: it gets `potentialName: 'class'` so markuplint rules for the `class` attribute apply. All other colon-separated prefixes hit the `default` case and get `isDirective: true`, which tells markuplint they are framework-specific and should not be validated as standard HTML attributes.
185
152
 
186
153
  ### Dynamic Values
187
154
 
@@ -199,7 +166,7 @@ Any attribute whose start quote is `{` gets `isDynamicValue: true`. This applies
199
166
  | **Frontmatter** | Supported (`---...---` psblock) | Not applicable |
200
167
  | **Expression syntax** | `{expr}` as MustacheTag psblock | `{expr}` as JSXExpressionContainer psblock |
201
168
  | **Template directives** | `class:list`, `set:html`, etc. | Not applicable |
202
- | **Namespace management** | Manual via `#updateScopeNS()` | Delegates to `getNamespace()` from html-parser |
169
+ | **Namespace management** | Delegates to base `Parser` | Delegates to `getNamespace()` from html-parser |
203
170
  | **Component detection** | `/^[A-Z]/` pattern | `/^[A-Z]/` pattern |
204
171
  | **Self-close type** | `html+xml` | Default (XML-only) |
205
172
  | **Booleanish attributes** | Not configured | `booleanish: true` |
package/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.0.0-rc.0](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.3...v5.0.0-rc.0) (2026-03-12)
7
+
8
+ **Note:** Version bump only for package @markuplint/astro-parser
9
+
6
10
  # [5.0.0-alpha.3](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.2...v5.0.0-alpha.3) (2026-02-26)
7
11
 
8
12
  **Note:** Version bump only for package @markuplint/astro-parser
package/SKILL.md CHANGED
@@ -29,7 +29,7 @@ for the full guide. The recipes there are the source of truth for procedures.
29
29
 
30
30
  Also read:
31
31
 
32
- - `ARCHITECTURE.md` -- Package overview, attribute processing, namespace scoping
32
+ - `ARCHITECTURE.md` -- Package overview, attribute processing, directive handling
33
33
  - `src/parser.ts` -- AstroParser class (source of truth for override methods)
34
34
  - `src/astro-parser.ts` -- astro-eslint-parser wrapper
35
35
 
@@ -62,14 +62,14 @@ Modify the SVG/XHTML namespace scoping logic. Follow recipe #2 in `docs/maintena
62
62
 
63
63
  ### Step 1: Understand the current logic
64
64
 
65
- 1. Read `src/parser.ts` the `#updateScopeNS()` private method
66
- 2. Understand the two conditions: `<svg>` SVG namespace, `<foreignObject>` parent XHTML namespace
67
- 3. Note that `scopeNS` is applied in `visitElement()` via `overwriteProps`
65
+ 1. Namespace resolution is handled by the base `Parser` class from `@markuplint/parser-utils`
66
+ 2. The Astro parser does **not** override namespace logic there is no `#updateScopeNS()` method
67
+ 3. Any namespace changes require modifications in the base `Parser` class or adding an override in `AstroParser`
68
68
 
69
69
  ### Step 2: Make the change
70
70
 
71
- 1. Add or modify conditions in `#updateScopeNS()`
72
- 2. For new namespaces (e.g., MathML), add a new condition checking `originNode.name`
71
+ 1. If adding namespace handling to the Astro parser, override the relevant method from the base `Parser`
72
+ 2. For new namespaces (e.g., MathML), add a condition checking `originNode.name`
73
73
  3. Ensure the namespace URI constant is correct
74
74
 
75
75
  ### Step 3: Verify
@@ -61,22 +61,12 @@ expect(debugMaps).toStrictEqual([
61
61
 
62
62
  ### 2. 名前空間スコーピングの変更
63
63
 
64
- 1. `src/parser.ts` を読む — `#updateScopeNS()` プライベートメソッド
65
- 2. このメソッドには2つの条件がある:
66
- - XHTML SVG: 現在の名前空間が XHTML で、ノードが `<svg>` 要素の場合
67
- - SVG → XHTML: 現在の名前空間が SVG で、親が `<foreignObject>` の場合
68
- 3. 新しい名前空間遷移を追加する場合(例: MathML):
69
- ```ts
70
- if (
71
- parentNS === 'http://www.w3.org/1999/xhtml' &&
72
- originNode.type === 'element' &&
73
- originNode.name?.toLowerCase() === 'math'
74
- ) {
75
- this.state.scopeNS = 'http://www.w3.org/1998/Math/MathML';
76
- }
77
- ```
78
- 4. ビルドとテスト: `yarn build --scope @markuplint/astro-parser && yarn test --scope @markuplint/astro-parser`
79
- 5. `src/parser.spec.ts` に名前空間テストケースを追加:
64
+ 名前空間の解決は現在 `@markuplint/parser-utils` の基底 `Parser` クラスが処理しています。Astro パーサーは名前空間ロジックをオーバーライドしていません。
65
+
66
+ 1. カスタム名前空間処理が必要な場合は、`src/parser.ts` `AstroParser` で該当メソッドをオーバーライド
67
+ 2. 新しい名前空間(例: MathML)の場合、要素名を検出して名前空間を切り替えるオーバーライドが必要
68
+ 3. ビルドとテスト: `yarn build --scope @markuplint/astro-parser && yarn test --scope @markuplint/astro-parser`
69
+ 4. `src/parser.spec.ts` に名前空間テストケースを追加:
80
70
  ```ts
81
71
  test('MathML namespace', () => {
82
72
  const doc = parse('<div><math><mi>x</mi></math></div>');
@@ -161,14 +151,13 @@ yarn build --scope @markuplint/astro-parser && yarn test --scope @markuplint/ast
161
151
 
162
152
  **症状:** `<svg>` 内の要素が XHTML 名前空間を持つ、または `<foreignObject>` 内の要素が SVG 名前空間を持つ。
163
153
 
164
- **原因:** `#updateScopeNS()` が要素タイプを正しく検出していないか、`scopeNS` 状態がリセットされていない。
154
+ **原因:** 名前空間の解決は `@markuplint/parser-utils` の基底 `Parser` クラスが処理します。Astro パーサーは名前空間ロジックをオーバーライドしていません。
165
155
 
166
156
  **解決策:**
167
157
 
168
- 1. `originNode.type === 'element'` を確認 — 要素ノードのみが名前空間の変更をトリガー
169
- 2. `originNode.name?.toLowerCase()` を確認 — `svg` の比較はケースインセンシティブである必要がある
170
- 3. `parentNode.nodeName === 'foreignObject'` の比較を確認 — これは Astro AST の名前ではなく markuplint のノード名を使用
171
- 4. 特定のネストパターンのテストケースを `src/parser.spec.ts` に追加
158
+ 1. 問題が `@markuplint/parser-utils` の基底 `Parser` クラスにあるかどうかを確認
159
+ 2. 特定のネストパターンのテストケースを `src/parser.spec.ts` に追加して期待される動作を確認
160
+ 3. 問題が上流にある場合は、基底 `Parser` の名前空間処理を調査
172
161
 
173
162
  ### テンプレートディレクティブが検出されない
174
163
 
@@ -61,22 +61,12 @@ The second argument `true` to `nodeListToDebugMaps` includes attribute details i
61
61
 
62
62
  ### 2. Modifying Namespace Scoping
63
63
 
64
- 1. Read `src/parser.ts` the `#updateScopeNS()` private method
65
- 2. The method has two conditions:
66
- - XHTML SVG: when current namespace is XHTML and node is a `<svg>` element
67
- - SVG XHTML: when current namespace is SVG and parent is `<foreignObject>`
68
- 3. To add a new namespace transition (e.g., MathML):
69
- ```ts
70
- if (
71
- parentNS === 'http://www.w3.org/1999/xhtml' &&
72
- originNode.type === 'element' &&
73
- originNode.name?.toLowerCase() === 'math'
74
- ) {
75
- this.state.scopeNS = 'http://www.w3.org/1998/Math/MathML';
76
- }
77
- ```
78
- 4. Build and test: `yarn build --scope @markuplint/astro-parser && yarn test --scope @markuplint/astro-parser`
79
- 5. Add namespace test cases to `src/parser.spec.ts`:
64
+ Namespace resolution is currently handled by the base `Parser` class from `@markuplint/parser-utils`. The Astro parser does not override namespace logic.
65
+
66
+ 1. If you need to add custom namespace handling, you would override the relevant method in `AstroParser` in `src/parser.ts`
67
+ 2. For new namespaces (e.g., MathML), the override would need to detect the element name and switch the namespace
68
+ 3. Build and test: `yarn build --scope @markuplint/astro-parser && yarn test --scope @markuplint/astro-parser`
69
+ 4. Add namespace test cases to `src/parser.spec.ts`:
80
70
  ```ts
81
71
  test('MathML namespace', () => {
82
72
  const doc = parse('<div><math><mi>x</mi></math></div>');
@@ -161,14 +151,13 @@ yarn build --scope @markuplint/astro-parser && yarn test --scope @markuplint/ast
161
151
 
162
152
  **Symptom:** Elements inside `<svg>` have XHTML namespace, or elements inside `<foreignObject>` have SVG namespace.
163
153
 
164
- **Cause:** `#updateScopeNS()` is not detecting the element type correctly, or the `scopeNS` state is not being reset.
154
+ **Cause:** Namespace resolution is handled by the base `Parser` class from `@markuplint/parser-utils`. The Astro parser does not override namespace logic.
165
155
 
166
156
  **Solution:**
167
157
 
168
- 1. Check that `originNode.type === 'element'` only element nodes trigger namespace changes
169
- 2. Check `originNode.name?.toLowerCase()` the comparison must be case-insensitive for `svg`
170
- 3. Check the `parentNode.nodeName === 'foreignObject'` comparison — this uses the markuplint node name, not the Astro AST name
171
- 4. Add a test case with the specific nesting pattern to `src/parser.spec.ts`
158
+ 1. Check whether the issue is in the base `Parser` class in `@markuplint/parser-utils`
159
+ 2. Add a test case with the specific nesting pattern to `src/parser.spec.ts` to confirm expected behavior
160
+ 3. If the issue is upstream, investigate the base `Parser` namespace handling
172
161
 
173
162
  ### Template directive not detected
174
163
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/astro-parser",
3
- "version": "5.0.0-dev.5+e96392f56",
3
+ "version": "5.0.0-rc.0",
4
4
  "description": "astro parser for markuplint",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -24,12 +24,12 @@
24
24
  "clean": "tsc --build --clean tsconfig.build.json"
25
25
  },
26
26
  "dependencies": {
27
- "@markuplint/ml-ast": "5.0.0-dev.5+e96392f56",
28
- "@markuplint/parser-utils": "5.0.0-dev.5+e96392f56",
27
+ "@markuplint/ml-ast": "5.0.0-rc.0",
28
+ "@markuplint/parser-utils": "5.0.0-rc.0",
29
29
  "astro-eslint-parser": "1.3.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@astrojs/compiler": "2.13.1"
32
+ "@astrojs/compiler": "3.0.0"
33
33
  },
34
- "gitHead": "e96392f56e4bc8165ba59622b41c822703a96372"
34
+ "gitHead": "ebf4d7cfca0c259aead3b292c6b8a202db4cd802"
35
35
  }