@markuplint/astro-parser 5.0.0-rc.0 → 5.0.0-rc.1
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/ARCHITECTURE.ja.md +13 -6
- package/ARCHITECTURE.md +13 -6
- package/CHANGELOG.md +15 -0
- package/SKILL.md +24 -0
- package/lib/component-scanner.d.ts +37 -0
- package/lib/component-scanner.js +88 -0
- package/lib/parser.js +2 -2
- package/package.json +10 -6
package/ARCHITECTURE.ja.md
CHANGED
|
@@ -12,8 +12,10 @@ src/
|
|
|
12
12
|
├── parser.ts — Parser<Node> を拡張する AstroParser クラス
|
|
13
13
|
├── astro-parser.ts — astro-eslint-parser ラッパーと型の再エクスポート
|
|
14
14
|
├── detect-block-behavior.ts — .map()/.filter() のブロック動作検出
|
|
15
|
+
├── component-scanner.ts — pretenders 自動スキャン用コンポーネントスキャナー(サブパスエクスポート)
|
|
15
16
|
├── parser.spec.ts — AstroParser 統合テスト
|
|
16
|
-
|
|
17
|
+
├── astro-parser.spec.ts — astro-eslint-parser ラッパーテスト
|
|
18
|
+
└── component-scanner.spec.ts — コンポーネントスキャナーのテスト
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
## アーキテクチャ図
|
|
@@ -31,10 +33,12 @@ flowchart TD
|
|
|
31
33
|
astroParser["AstroParser\nextends Parser‹Node›"]
|
|
32
34
|
astroParseFn["astroParse()\nastro-eslint-parser ラッパー"]
|
|
33
35
|
detectBlock["detectBlockBehavior()\n.map()/.filter() 検出"]
|
|
36
|
+
compScanner["componentScanner\n(サブパス: ./component-scanner)"]
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
subgraph downstream ["下流"]
|
|
37
40
|
mlCore["@markuplint/ml-core\n(MLASTDocument → MLDOM)"]
|
|
41
|
+
pretenders["@markuplint/pretenders\n(自動スキャン)"]
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
mlAst -->|"AST 型"| astroParser
|
|
@@ -44,6 +48,8 @@ flowchart TD
|
|
|
44
48
|
astroParseFn -->|"RootNode.children"| astroParser
|
|
45
49
|
detectBlock -->|"blockBehavior"| astroParser
|
|
46
50
|
astroParser -->|"MLASTDocument を生成"| mlCore
|
|
51
|
+
astroParser -->|"parse()"| compScanner
|
|
52
|
+
compScanner -->|"ComponentScanResult"| pretenders
|
|
47
53
|
```
|
|
48
54
|
|
|
49
55
|
## AstroParser クラス
|
|
@@ -185,11 +191,12 @@ astro-eslint-parser → @astrojs/compiler → Astro 構文サポート
|
|
|
185
191
|
|
|
186
192
|
## 主要ソースファイル
|
|
187
193
|
|
|
188
|
-
| ファイル
|
|
189
|
-
|
|
|
190
|
-
| `parser.ts`
|
|
191
|
-
| `astro-parser.ts`
|
|
192
|
-
| `index.ts`
|
|
194
|
+
| ファイル | 用途 |
|
|
195
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
196
|
+
| `parser.ts` | `AstroParser` クラス — 全オーバーライドメソッドと名前空間スコーピング |
|
|
197
|
+
| `astro-parser.ts` | `astroParse()` ラッパー — `astro-eslint-parser` に委譲し、診断を `ParserError` に変換 |
|
|
198
|
+
| `index.ts` | 公開 API — シングルトン `parser` インスタンスを再エクスポート |
|
|
199
|
+
| `component-scanner.ts` | `@markuplint/pretenders` 自動スキャン用コンポーネントスキャナー(サブパスエクスポート `./component-scanner`) |
|
|
193
200
|
|
|
194
201
|
## ドキュメントマップ
|
|
195
202
|
|
package/ARCHITECTURE.md
CHANGED
|
@@ -12,8 +12,10 @@ src/
|
|
|
12
12
|
├── parser.ts — AstroParser class extending Parser<Node>
|
|
13
13
|
├── astro-parser.ts — astro-eslint-parser wrapper and type re-exports
|
|
14
14
|
├── detect-block-behavior.ts — Detects .map()/.filter() for block behavior
|
|
15
|
+
├── component-scanner.ts — Component scanner for pretenders auto scan (subpath export)
|
|
15
16
|
├── parser.spec.ts — AstroParser integration tests
|
|
16
|
-
|
|
17
|
+
├── astro-parser.spec.ts — astro-eslint-parser wrapper tests
|
|
18
|
+
└── component-scanner.spec.ts — Tests for component scanner
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
## Architecture Diagram
|
|
@@ -31,10 +33,12 @@ flowchart TD
|
|
|
31
33
|
astroParser["AstroParser\nextends Parser‹Node›"]
|
|
32
34
|
astroParseFn["astroParse()\nastro-eslint-parser wrapper"]
|
|
33
35
|
detectBlock["detectBlockBehavior()\n.map()/.filter() detection"]
|
|
36
|
+
compScanner["componentScanner\n(subpath: ./component-scanner)"]
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
subgraph downstream ["Downstream"]
|
|
37
40
|
mlCore["@markuplint/ml-core\n(MLASTDocument → MLDOM)"]
|
|
41
|
+
pretenders["@markuplint/pretenders\n(auto scan)"]
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
mlAst -->|"AST types"| astroParser
|
|
@@ -44,6 +48,8 @@ flowchart TD
|
|
|
44
48
|
astroParseFn -->|"RootNode.children"| astroParser
|
|
45
49
|
detectBlock -->|"blockBehavior"| astroParser
|
|
46
50
|
astroParser -->|"produces MLASTDocument"| mlCore
|
|
51
|
+
astroParser -->|"parse()"| compScanner
|
|
52
|
+
compScanner -->|"ComponentScanResult"| pretenders
|
|
47
53
|
```
|
|
48
54
|
|
|
49
55
|
## AstroParser Class
|
|
@@ -185,11 +191,12 @@ astro-eslint-parser → @astrojs/compiler → Astro syntax support
|
|
|
185
191
|
|
|
186
192
|
## Key Source Files
|
|
187
193
|
|
|
188
|
-
| File
|
|
189
|
-
|
|
|
190
|
-
| `parser.ts`
|
|
191
|
-
| `astro-parser.ts`
|
|
192
|
-
| `index.ts`
|
|
194
|
+
| File | Purpose |
|
|
195
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------- |
|
|
196
|
+
| `parser.ts` | `AstroParser` class — all override methods and namespace scoping |
|
|
197
|
+
| `astro-parser.ts` | `astroParse()` wrapper — delegates to `astro-eslint-parser`, converts diagnostics to `ParserError` |
|
|
198
|
+
| `index.ts` | Public API — re-exports the singleton `parser` instance |
|
|
199
|
+
| `component-scanner.ts` | Component scanner for `@markuplint/pretenders` auto scan (subpath export `./component-scanner`) |
|
|
193
200
|
|
|
194
201
|
## Documentation Map
|
|
195
202
|
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
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.1](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.0...v5.0.0-rc.1) (2026-03-27)
|
|
7
|
+
|
|
8
|
+
- feat!: adapt framework parsers to UUID-based node references ([6d543b8](https://github.com/markuplint/markuplint/commit/6d543b8c11506fe113d0ceeae3526f552f4ee26d))
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
- **astro-parser:** add component-scanner subpath export for pretenders auto scan ([3d85bc5](https://github.com/markuplint/markuplint/commit/3d85bc5f5904c0157415de175227eedf89539cda))
|
|
13
|
+
|
|
14
|
+
### BREAKING CHANGES
|
|
15
|
+
|
|
16
|
+
- Parser output no longer contains parentNode/pairNode
|
|
17
|
+
object references. Use parentNodeUuid/pairNodeUuid string fields instead.
|
|
18
|
+
|
|
19
|
+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
20
|
+
|
|
6
21
|
# [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
22
|
|
|
8
23
|
**Note:** Version bump only for package @markuplint/astro-parser
|
package/SKILL.md
CHANGED
|
@@ -19,6 +19,7 @@ modify namespace scoping, update expression handling, and manage astro-eslint-pa
|
|
|
19
19
|
| `add-directive` | Add a new Astro template directive |
|
|
20
20
|
| `modify-namespace-scoping` | Modify SVG/XHTML namespace scoping logic |
|
|
21
21
|
| `update-expression-handling` | Update expression splitting or MustacheTag handling |
|
|
22
|
+
| `update-component-scanner` | Update component-scanner for pretenders auto scan |
|
|
22
23
|
|
|
23
24
|
If omitted, defaults to `add-directive`.
|
|
24
25
|
|
|
@@ -100,6 +101,29 @@ Update expression splitting or MustacheTag handling. Follow recipe #3 in `docs/m
|
|
|
100
101
|
2. Test with expressions containing nested HTML (e.g., `{list.map(item => <li>{item}</li>)}`)
|
|
101
102
|
3. Test: `yarn test --scope @markuplint/astro-parser`
|
|
102
103
|
|
|
104
|
+
## Task: update-component-scanner
|
|
105
|
+
|
|
106
|
+
Update `src/component-scanner.ts` when Astro slot syntax or frontmatter handling changes.
|
|
107
|
+
|
|
108
|
+
### When to update
|
|
109
|
+
|
|
110
|
+
- New slot-like syntax is added to Astro
|
|
111
|
+
- Frontmatter delimiter handling needs to change
|
|
112
|
+
- The `extractComponentInfo` shared logic needs a fix (also update vue-parser and svelte-parser)
|
|
113
|
+
|
|
114
|
+
### Step 1: Make the change
|
|
115
|
+
|
|
116
|
+
1. Read `src/component-scanner.ts`
|
|
117
|
+
2. Modify `detectSlots()` for new slot patterns, or `extractAstroFrontmatter()` for frontmatter changes
|
|
118
|
+
3. If modifying `extractComponentInfo()`, apply the same change to all three parsers (vue, svelte, astro)
|
|
119
|
+
|
|
120
|
+
### Step 2: Verify
|
|
121
|
+
|
|
122
|
+
1. Update tests in `src/component-scanner.spec.ts`
|
|
123
|
+
2. Build: `yarn build --scope @markuplint/astro-parser`
|
|
124
|
+
3. Test: `npx vitest run packages/@markuplint/astro-parser/src/component-scanner.spec.ts`
|
|
125
|
+
4. Run pretenders integration tests: `npx vitest run packages/@markuplint/pretenders`
|
|
126
|
+
|
|
103
127
|
## Rules
|
|
104
128
|
|
|
105
129
|
1. **Delegate tokenization to astro-eslint-parser** — never parse Astro syntax manually; always use `astroParse()`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of scanning a single component file for its root element information.
|
|
3
|
+
*/
|
|
4
|
+
export interface ComponentScanResult {
|
|
5
|
+
readonly rootElement: string | null;
|
|
6
|
+
readonly attrs: readonly ComponentScanAttr[];
|
|
7
|
+
readonly hasSlots: boolean;
|
|
8
|
+
readonly scriptSource?: ComponentScanScriptSource;
|
|
9
|
+
readonly namespace?: 'svg';
|
|
10
|
+
readonly line?: number;
|
|
11
|
+
readonly col?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A static attribute extracted from a component's root element.
|
|
15
|
+
*/
|
|
16
|
+
export interface ComponentScanAttr {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly value?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A script/ESM source block extracted from a component file.
|
|
22
|
+
*/
|
|
23
|
+
export interface ComponentScanScriptSource {
|
|
24
|
+
readonly content: string;
|
|
25
|
+
readonly offset: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Component scanner for Astro component files.
|
|
29
|
+
*
|
|
30
|
+
* Parses an Astro component using markuplint's Astro parser, extracts the root
|
|
31
|
+
* element at depth=0, detects static attributes, slot usage, and the
|
|
32
|
+
* frontmatter block for import analysis.
|
|
33
|
+
*/
|
|
34
|
+
export declare const componentScanner: {
|
|
35
|
+
scanComponent(sourceCode: string): ComponentScanResult | null;
|
|
36
|
+
extractScriptSource(sourceCode: string): ComponentScanScriptSource | null;
|
|
37
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { parser } from './parser.js';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts root element information from a parsed MLAST document.
|
|
4
|
+
*/
|
|
5
|
+
function extractComponentInfo(doc) {
|
|
6
|
+
const root = doc.nodeList.find((n) => n.type === 'starttag' && n.depth === 0 && !n.isFragment);
|
|
7
|
+
if (!root) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const attrs = [];
|
|
11
|
+
for (const attr of root.attributes) {
|
|
12
|
+
if (attr.type !== 'attr') {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const value = attr.value.raw;
|
|
16
|
+
if (value === '') {
|
|
17
|
+
attrs.push({ name: attr.nodeName });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
attrs.push({ name: attr.nodeName, value });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
rootElement: root.nodeName,
|
|
25
|
+
attrs,
|
|
26
|
+
namespace: root.namespace === 'http://www.w3.org/2000/svg' ? 'svg' : undefined,
|
|
27
|
+
line: root.line,
|
|
28
|
+
col: root.col,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Detects whether the parsed Astro template contains `<slot>` elements.
|
|
33
|
+
*/
|
|
34
|
+
function detectSlots(doc) {
|
|
35
|
+
return doc.nodeList.some(n => n.type === 'starttag' && n.nodeName === 'slot');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extracts the frontmatter block (`---...---`) from an Astro component source.
|
|
39
|
+
*/
|
|
40
|
+
function extractAstroFrontmatter(source) {
|
|
41
|
+
const re = /^(?:\s*\n)?---\r?\n/;
|
|
42
|
+
const startMatch = re.exec(source);
|
|
43
|
+
if (!startMatch) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const contentStart = startMatch[0].length;
|
|
47
|
+
const afterStart = source.slice(contentStart);
|
|
48
|
+
const endRe = /\r?\n---\r?\n/;
|
|
49
|
+
const endMatch = endRe.exec(afterStart);
|
|
50
|
+
if (!endMatch) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
content: afterStart.slice(0, endMatch.index),
|
|
55
|
+
offset: contentStart,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Component scanner for Astro component files.
|
|
60
|
+
*
|
|
61
|
+
* Parses an Astro component using markuplint's Astro parser, extracts the root
|
|
62
|
+
* element at depth=0, detects static attributes, slot usage, and the
|
|
63
|
+
* frontmatter block for import analysis.
|
|
64
|
+
*/
|
|
65
|
+
export const componentScanner = {
|
|
66
|
+
scanComponent(sourceCode) {
|
|
67
|
+
let doc;
|
|
68
|
+
try {
|
|
69
|
+
doc = parser.parse(sourceCode);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof SyntaxError || (error instanceof Error && error.constructor.name === 'ParserError')) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
const info = extractComponentInfo(doc);
|
|
78
|
+
if (!info) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const hasSlots = detectSlots(doc);
|
|
82
|
+
const scriptSource = extractAstroFrontmatter(sourceCode) ?? undefined;
|
|
83
|
+
return { ...info, hasSlots, scriptSource };
|
|
84
|
+
},
|
|
85
|
+
extractScriptSource(sourceCode) {
|
|
86
|
+
return extractAstroFrontmatter(sourceCode);
|
|
87
|
+
},
|
|
88
|
+
};
|
package/lib/parser.js
CHANGED
|
@@ -160,7 +160,7 @@ class AstroParser extends Parser {
|
|
|
160
160
|
if (!startTagNode || startTagNode.type !== 'starttag') {
|
|
161
161
|
throw new ParserError('Not found start tag', startTagNode ?? token);
|
|
162
162
|
}
|
|
163
|
-
return super.visitElement(startTagNode, childNodes, {
|
|
163
|
+
return super.visitElement({ ...startTagNode, parentNode: startTagNode.parentNode ?? null }, childNodes, {
|
|
164
164
|
// https://docs.astro.build/en/basics/astro-syntax/#fragments
|
|
165
165
|
namelessFragment: true,
|
|
166
166
|
createEndTagToken: () => {
|
|
@@ -171,7 +171,7 @@ class AstroParser extends Parser {
|
|
|
171
171
|
if (endTagNode?.type !== 'endtag') {
|
|
172
172
|
return null;
|
|
173
173
|
}
|
|
174
|
-
return endTagNode ?? null;
|
|
174
|
+
return { ...endTagNode, parentNode: endTagNode.parentNode ?? null };
|
|
175
175
|
},
|
|
176
176
|
});
|
|
177
177
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/astro-parser",
|
|
3
|
-
"version": "5.0.0-rc.
|
|
3
|
+
"version": "5.0.0-rc.1",
|
|
4
4
|
"description": "astro parser for markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
".": {
|
|
14
14
|
"import": "./lib/index.js",
|
|
15
15
|
"types": "./lib/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./component-scanner": {
|
|
18
|
+
"import": "./lib/component-scanner.js",
|
|
19
|
+
"types": "./lib/component-scanner.d.ts"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"publishConfig": {
|
|
@@ -24,12 +28,12 @@
|
|
|
24
28
|
"clean": "tsc --build --clean tsconfig.build.json"
|
|
25
29
|
},
|
|
26
30
|
"dependencies": {
|
|
27
|
-
"@markuplint/ml-ast": "5.0.0-rc.
|
|
28
|
-
"@markuplint/parser-utils": "5.0.0-rc.
|
|
29
|
-
"astro-eslint-parser": "1.
|
|
31
|
+
"@markuplint/ml-ast": "5.0.0-rc.1",
|
|
32
|
+
"@markuplint/parser-utils": "5.0.0-rc.1",
|
|
33
|
+
"astro-eslint-parser": "1.4.0"
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
32
|
-
"@astrojs/compiler": "3.0.
|
|
36
|
+
"@astrojs/compiler": "3.0.1"
|
|
33
37
|
},
|
|
34
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "0d6b4324d9a7d6b9e1ba57d4a57e45d36975cba9"
|
|
35
39
|
}
|