@markuplint/vue-parser 5.0.0-rc.2 → 5.0.0-rc.5

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.
@@ -1,203 +0,0 @@
1
- # メンテナンスガイド
2
-
3
- ## コマンド
4
-
5
- | コマンド | 説明 |
6
- | ------------------------------------------- | ---------------------- |
7
- | `yarn build --scope @markuplint/vue-parser` | このパッケージをビルド |
8
- | `yarn dev --scope @markuplint/vue-parser` | ウォッチモードでビルド |
9
- | `yarn clean --scope @markuplint/vue-parser` | ビルド成果物を削除 |
10
- | `yarn test --scope @markuplint/vue-parser` | テストを実行 |
11
-
12
- ## テスト
13
-
14
- テストファイルは `*.spec.ts` の命名規則に従い、`src/` ディレクトリに配置されています:
15
-
16
- | テストファイル | カバレッジ |
17
- | --------------- | ------------------------------------------------------------------------------ |
18
- | `index.spec.ts` | VueParser 統合テスト(パース、ディレクティブ、名前空間、要素タイプ、コメント) |
19
-
20
- 主なテストパターンでは `nodeListToDebugMaps` を使用したスナップショット形式のアサーションを行います:
21
-
22
- ```ts
23
- import { nodeListToDebugMaps } from '@markuplint/parser-utils';
24
- import { parser } from '@markuplint/vue-parser';
25
-
26
- const doc = parser.parse('<template><div class="foo">text</div></template>');
27
- const debugMaps = nodeListToDebugMaps(doc.nodeList, true);
28
- expect(debugMaps).toStrictEqual([
29
- // 期待されるデバッグ出力
30
- ]);
31
- ```
32
-
33
- 属性メタデータのテスト(ディレクティブ、potentialName、isDynamicValue):
34
-
35
- ```ts
36
- const doc = parser.parse('<template><div v-bind:title="val"></div></template>');
37
- expect(doc.nodeList[0].attributes[0].potentialName).toBe('title');
38
- expect(doc.nodeList[0].attributes[0].isDynamicValue).toBeTruthy();
39
- ```
40
-
41
- 要素タイプのテスト:
42
-
43
- ```ts
44
- const doc = parser.parse('<template><MyComponent/></template>');
45
- expect(doc.nodeList[0].elementType).toBe('authored');
46
- ```
47
-
48
- ## レシピ
49
-
50
- ### 1. Vue ディレクティブの追加・変更
51
-
52
- 1. `@markuplint/vue-spec`(`packages/@markuplint/vue-spec/src/index.ts`)を開き、`directivePatterns` 配列を確認する
53
- 2. 優先チェーン内の正しい位置を特定:
54
- - `v-on` / `@`(イベントバインディング) — 最初
55
- - `v-bind` / `:`(プロパティバインディング) — 2番目
56
- - `v-model` — 3番目
57
- - `v-slot` / `#` — 4番目
58
- - 汎用 `v-*` — 最後(キャッチオール)
59
- 3. 正規表現パターンを持つ新しいスコープブロックを作成:
60
- ```ts
61
- {
62
- const [, directive, name] = attr.name.raw.match(/^(v-newdir:|shorthand)(.+)$/i) ?? [];
63
- if (directive && name) {
64
- return {
65
- ...attr,
66
- potentialName: name, // HTML 属性にマッピングする場合
67
- isDirective: true as const, // Vue 専用の場合
68
- isDynamicValue: true as const, // 値が JavaScript の場合
69
- };
70
- }
71
- }
72
- ```
73
- 4. ビルド: `yarn build --scope @markuplint/vue-parser`
74
- 5. `src/index.spec.ts` にテストを追加:
75
- - 完全形: `v-newdir:value`
76
- - 省略形(該当する場合)
77
- - 修飾子付き(該当する場合)
78
- 6. テスト: `yarn test --scope @markuplint/vue-parser`
79
-
80
- ### 2. 要素タイプ検出の変更
81
-
82
- 1. `src/parser.ts` を読み、`detectElementType()` メソッドを見つける
83
- 2. マッチャー配列は以下をサポート:
84
- - **文字列リテラル** — 完全一致(例: `'Transition'`、`'component'`、`'slot'`)
85
- - **RegExp** — パターンマッチ(例: PascalCase 用の `/^[A-Z]/`)
86
- 3. 新しい Vue 組み込みコンポーネントを追加するには:
87
- ```ts
88
- detectElementType(nodeName: string) {
89
- return super.detectElementType(nodeName, [
90
- // Built-in components
91
- 'Transition',
92
- 'TransitionGroup',
93
- 'KeepAlive',
94
- 'Teleport',
95
- 'Suspense',
96
- 'NewBuiltIn', // <-- ここに追加
97
- // Special elements
98
- 'component',
99
- 'slot',
100
- // Backward compatibility
101
- /^[A-Z]/,
102
- ]);
103
- }
104
- ```
105
- 4. ビルドとテスト: `yarn build --scope @markuplint/vue-parser && yarn test --scope @markuplint/vue-parser`
106
- 5. `src/index.spec.ts` の `elementType` テストブロックにテストケースを追加
107
-
108
- ### 3. vue-eslint-parser バージョンサポートの更新
109
-
110
- 1. `src/vue-parser/index.ts` を読む — vue-eslint-parser のラッパー
111
- 2. vue-eslint-parser のリリースノートで破壊的変更を確認
112
- 3. 主な統合ポイント:
113
- - `VueESLintParser.parse(vueTemplate, { parser: false })` — メインのパース呼び出し
114
- - `ast.templateBody?.children` — テンプレートの子ノード
115
- - `ast.templateBody?.comments` — テンプレートのコメント
116
- - `VueESLintParser.AST.VElement` / `VText` / `VExpressionContainer` — ノードタイプ
117
- 4. AST 型が変更された場合、型エクスポートを更新:
118
- ```ts
119
- export type ASTNode =
120
- | VueESLintParser.AST.VElement
121
- | VueESLintParser.AST.VText
122
- | VueESLintParser.AST.VExpressionContainer;
123
- ```
124
- 5. ビルドとテスト: `yarn build --scope @markuplint/vue-parser && yarn test --scope @markuplint/vue-parser`
125
-
126
- ### 4. テンプレートコメント注入の修正
127
-
128
- 1. `src/parser.ts` の `flattenNodes()` メソッドを読む
129
- 2. コメント注入のロジック:
130
- - コメントは `tokenize()` 中に `this.state.comments` に格納される
131
- - フラット化中、隣接するノードペアごとにコメントがその間にあるか確認
132
- - 範囲チェック: `lastOffset <= comment.range[0] && comment.range[1] <= node.startOffset`
133
- 3. よくある問題:
134
- - コメントが注入されない: 範囲チェックがギャップを正しくカバーしているか確認
135
- - コメントが間違った位置にある: `lastOffset` の計算を確認(`prevNode?.endOffset ?? node.parentNode?.endOffset ?? 0`)
136
- - ボーガスコメント検出: `betweenComment.type === 'HTMLBogusComment'` を検証
137
- 4. ビルドとテスト: `yarn build --scope @markuplint/vue-parser && yarn test --scope @markuplint/vue-parser`
138
-
139
- ## 上流影響チェックリスト
140
-
141
- 上流パッケージへの変更がこのパッケージに影響を与える可能性があります:
142
-
143
- | パッケージ | 影響 |
144
- | -------------------------- | ------------------------------------------------------------------------ |
145
- | `@markuplint/parser-utils` | 基底 `Parser` クラスの変更が全オーバーライドメソッドに影響する可能性あり |
146
- | `@markuplint/ml-ast` | AST 型の変更が nodeize() の戻り値の型の更新を必要とする可能性あり |
147
- | `vue-eslint-parser` | AST 構造の変更が tokenize()、nodeize()、型の更新を必要とする可能性あり |
148
-
149
- 上流パッケージが変更された場合、以下を実行:
150
-
151
- ```shell
152
- yarn test --scope @markuplint/vue-parser
153
- ```
154
-
155
- ## トラブルシューティング
156
-
157
- ### Vue ディレクティブが認識されない
158
-
159
- **症状:** `v-custom` のような Vue ディレクティブが `isDirective: true` としてマークされず、通常の HTML 属性として扱われる。
160
-
161
- **原因:** `directivePatterns`(`@markuplint/vue-spec` で定義)でディレクティブパターンがマッチしないか、新しいパターンが汎用 `v-*` キャッチオールの後に配置されている。
162
-
163
- **解決策:**
164
-
165
- 1. ディレクティブブロックの正規表現パターンを確認 — 完全な属性名にマッチすることを確認
166
- 2. パターンが `directivePatterns` 配列内の汎用 `v-*` キャッチオールの前に配置されていることを確認
167
- 3. 返却オブジェクトに `isDirective: true as const` が含まれていることを検証
168
-
169
- ### テンプレートコメントが AST に含まれていない
170
-
171
- **症状:** Vue テンプレート内の HTML コメント(`<!-- ... -->`)がパースされたノードリストに存在しない。
172
-
173
- **原因:** `flattenNodes()` でコメントが注入されていないか、`tokenize()` でキャプチャされていない。
174
-
175
- **解決策:**
176
-
177
- 1. `tokenize()` を確認 — `ast.templateBody?.comments` が `this.state.comments` に格納されていることを検証
178
- 2. `flattenNodes()` を確認 — 範囲チェックロジックが隣接ノード間のコメントを見つけていることを検証
179
- 3. 特定のコメント位置でテストケースを追加し、`nodeListToDebugMaps` 出力を確認
180
-
181
- ### PascalCase コンポーネントが 'authored' として検出されない
182
-
183
- **症状:** `<MyComponent>` のようなコンポーネントが `'authored'` ではなく `elementType: 'html'` となる。
184
-
185
- **原因:** `detectElementType()` 内の `/^[A-Z]/` 正規表現がマッチしていないか、マッチャー配列が誤って構成されている。
186
-
187
- **解決策:**
188
-
189
- 1. コンポーネント名が大文字で始まることを確認
190
- 2. `/^[A-Z]/` 正規表現がマッチャー配列に存在することを検証
191
- 3. `<component>` や `<slot>` のような小文字の Vue 組み込みは正規表現ではなく文字列でマッチされることに注意
192
-
193
- ### SyntaxError が正しく報告されない
194
-
195
- **症状:** Vue テンプレートの構文エラーが `ParserError` を生成する代わりにプロセスをクラッシュさせる。
196
-
197
- **原因:** `parseError()` メソッドがエラーをキャッチしていないか、エラーオブジェクトに `lineNumber`/`column` プロパティがない。
198
-
199
- **解決策:**
200
-
201
- 1. `parseError()` を確認 — `instanceof SyntaxError` と `'lineNumber' in error` のチェックを検証
202
- 2. vue-eslint-parser のエラーには `lineNumber`(1ベース)と `column`(0ベース)が含まれる
203
- 3. フォールバックの `super.parseError(error)` は非 SyntaxError ケースを処理する
@@ -1,203 +0,0 @@
1
- # Maintenance Guide
2
-
3
- ## Commands
4
-
5
- | Command | Description |
6
- | ------------------------------------------- | ---------------------- |
7
- | `yarn build --scope @markuplint/vue-parser` | Build this package |
8
- | `yarn dev --scope @markuplint/vue-parser` | Watch mode build |
9
- | `yarn clean --scope @markuplint/vue-parser` | Remove build artifacts |
10
- | `yarn test --scope @markuplint/vue-parser` | Run tests |
11
-
12
- ## Testing
13
-
14
- Test files follow the `*.spec.ts` naming convention and are located in the `src/` directory:
15
-
16
- | Test File | Coverage |
17
- | --------------- | -------------------------------------------------------------------------------------- |
18
- | `index.spec.ts` | VueParser integration tests (parsing, directives, namespaces, element types, comments) |
19
-
20
- The primary testing pattern uses `nodeListToDebugMaps` for snapshot-style assertions:
21
-
22
- ```ts
23
- import { nodeListToDebugMaps } from '@markuplint/parser-utils';
24
- import { parser } from '@markuplint/vue-parser';
25
-
26
- const doc = parser.parse('<template><div class="foo">text</div></template>');
27
- const debugMaps = nodeListToDebugMaps(doc.nodeList, true);
28
- expect(debugMaps).toStrictEqual([
29
- // expected debug output
30
- ]);
31
- ```
32
-
33
- To test attribute metadata (directives, potentialName, isDynamicValue):
34
-
35
- ```ts
36
- const doc = parser.parse('<template><div v-bind:title="val"></div></template>');
37
- expect(doc.nodeList[0].attributes[0].potentialName).toBe('title');
38
- expect(doc.nodeList[0].attributes[0].isDynamicValue).toBeTruthy();
39
- ```
40
-
41
- To test element types:
42
-
43
- ```ts
44
- const doc = parser.parse('<template><MyComponent/></template>');
45
- expect(doc.nodeList[0].elementType).toBe('authored');
46
- ```
47
-
48
- ## Recipes
49
-
50
- ### 1. Adding or Modifying a Vue Directive
51
-
52
- 1. Open `@markuplint/vue-spec` (`packages/@markuplint/vue-spec/src/index.ts`) and find the `directivePatterns` array
53
- 2. Identify the correct position in the priority chain:
54
- - `v-on` / `@` (event binding) — first
55
- - `v-bind` / `:` (property binding) — second
56
- - `v-model` — third
57
- - `v-slot` / `#` — fourth
58
- - Generic `v-*` — last (catch-all)
59
- 3. Create a new scoped block with a regex pattern:
60
- ```ts
61
- {
62
- const [, directive, name] = attr.name.raw.match(/^(v-newdir:|shorthand)(.+)$/i) ?? [];
63
- if (directive && name) {
64
- return {
65
- ...attr,
66
- potentialName: name, // if it maps to an HTML attribute
67
- isDirective: true as const, // if it's Vue-only
68
- isDynamicValue: true as const, // if value is JavaScript
69
- };
70
- }
71
- }
72
- ```
73
- 4. Build: `yarn build --scope @markuplint/vue-parser`
74
- 5. Add tests to `src/index.spec.ts`:
75
- - Full form: `v-newdir:value`
76
- - Shorthand form if applicable
77
- - With modifiers if applicable
78
- 6. Test: `yarn test --scope @markuplint/vue-parser`
79
-
80
- ### 2. Modifying Element Type Detection
81
-
82
- 1. Read `src/parser.ts` and find the `detectElementType()` method
83
- 2. The matcher array supports:
84
- - **String literals** — exact match (e.g., `'Transition'`, `'component'`, `'slot'`)
85
- - **RegExp** — pattern match (e.g., `/^[A-Z]/` for PascalCase)
86
- 3. To add a new Vue built-in component:
87
- ```ts
88
- detectElementType(nodeName: string) {
89
- return super.detectElementType(nodeName, [
90
- // Built-in components
91
- 'Transition',
92
- 'TransitionGroup',
93
- 'KeepAlive',
94
- 'Teleport',
95
- 'Suspense',
96
- 'NewBuiltIn', // <-- add here
97
- // Special elements
98
- 'component',
99
- 'slot',
100
- // Backward compatibility
101
- /^[A-Z]/,
102
- ]);
103
- }
104
- ```
105
- 4. Build and test: `yarn build --scope @markuplint/vue-parser && yarn test --scope @markuplint/vue-parser`
106
- 5. Add test cases to the `elementType` test block in `src/index.spec.ts`
107
-
108
- ### 3. Updating vue-eslint-parser Version Support
109
-
110
- 1. Read `src/vue-parser/index.ts` — this is the wrapper around vue-eslint-parser
111
- 2. Check the vue-eslint-parser release notes for breaking changes
112
- 3. Key integration points:
113
- - `VueESLintParser.parse(vueTemplate, { parser: false })` — main parse call
114
- - `ast.templateBody?.children` — template child nodes
115
- - `ast.templateBody?.comments` — template comments
116
- - `VueESLintParser.AST.VElement` / `VText` / `VExpressionContainer` — node types
117
- 4. Update type exports if AST types changed:
118
- ```ts
119
- export type ASTNode =
120
- | VueESLintParser.AST.VElement
121
- | VueESLintParser.AST.VText
122
- | VueESLintParser.AST.VExpressionContainer;
123
- ```
124
- 5. Build and test: `yarn build --scope @markuplint/vue-parser && yarn test --scope @markuplint/vue-parser`
125
-
126
- ### 4. Fixing Template Comment Injection
127
-
128
- 1. Read the `flattenNodes()` method in `src/parser.ts`
129
- 2. The comment injection logic:
130
- - Comments are stored in `this.state.comments` during `tokenize()`
131
- - During flattening, for each pair of adjacent nodes, the method checks if a comment falls between them
132
- - The range check: `lastOffset <= comment.range[0] && comment.range[1] <= node.startOffset`
133
- 3. Common issues:
134
- - Comment not injected: check that the range check covers the gap correctly
135
- - Comment at wrong position: check `lastOffset` calculation (`prevNode?.endOffset ?? node.parentNode?.endOffset ?? 0`)
136
- - Bogus comment detection: verify `betweenComment.type === 'HTMLBogusComment'`
137
- 4. Build and test: `yarn build --scope @markuplint/vue-parser && yarn test --scope @markuplint/vue-parser`
138
-
139
- ## Upstream Impact Checklist
140
-
141
- Changes to upstream packages can affect this package:
142
-
143
- | Package | Impact |
144
- | -------------------------- | ----------------------------------------------------------------------------- |
145
- | `@markuplint/parser-utils` | Base `Parser` class changes may affect all override methods |
146
- | `@markuplint/ml-ast` | AST type changes may require updates to nodeize() return types |
147
- | `vue-eslint-parser` | AST structure changes may require updates to tokenize(), nodeize(), and types |
148
-
149
- When upstream packages change, run:
150
-
151
- ```shell
152
- yarn test --scope @markuplint/vue-parser
153
- ```
154
-
155
- ## Troubleshooting
156
-
157
- ### Vue directive is not recognized
158
-
159
- **Symptom:** A Vue directive like `v-custom` is treated as a regular HTML attribute instead of being marked as `isDirective: true`.
160
-
161
- **Cause:** The directive pattern does not match in `directivePatterns` (defined in `@markuplint/vue-spec`), or the new pattern is placed after the generic `v-*` catch-all.
162
-
163
- **Solution:**
164
-
165
- 1. Check the regex pattern in your directive block — ensure it matches the full attribute name
166
- 2. Ensure the pattern is placed before the generic `v-*` catch-all in the `directivePatterns` array
167
- 3. Verify the return object includes `isDirective: true as const`
168
-
169
- ### Template comments are missing from the AST
170
-
171
- **Symptom:** HTML comments (`<!-- ... -->`) in the Vue template are not present in the parsed node list.
172
-
173
- **Cause:** The comments are not being injected during `flattenNodes()`, or they are not captured during `tokenize()`.
174
-
175
- **Solution:**
176
-
177
- 1. Check `tokenize()` — verify `ast.templateBody?.comments` is being stored in `this.state.comments`
178
- 2. Check `flattenNodes()` — verify the range check logic finds the comment between adjacent nodes
179
- 3. Add a test case with the specific comment position and check `nodeListToDebugMaps` output
180
-
181
- ### PascalCase component not detected as 'authored'
182
-
183
- **Symptom:** A component like `<MyComponent>` has `elementType: 'html'` instead of `'authored'`.
184
-
185
- **Cause:** The `/^[A-Z]/` regex in `detectElementType()` is not matching, or the matcher array is misconfigured.
186
-
187
- **Solution:**
188
-
189
- 1. Check that the component name starts with an uppercase letter
190
- 2. Verify the `/^[A-Z]/` regex is still present in the matcher array
191
- 3. Note that lowercase Vue built-ins like `<component>` and `<slot>` are matched by string, not regex
192
-
193
- ### SyntaxError not properly reported
194
-
195
- **Symptom:** Vue template syntax errors crash the process instead of producing a `ParserError`.
196
-
197
- **Cause:** The `parseError()` method is not catching the error, or the error object does not have `lineNumber`/`column` properties.
198
-
199
- **Solution:**
200
-
201
- 1. Check `parseError()` — verify the `instanceof SyntaxError` and `'lineNumber' in error` checks
202
- 2. vue-eslint-parser errors include `lineNumber` (1-based) and `column` (0-based)
203
- 3. The fallback `super.parseError(error)` handles non-SyntaxError cases