@markuplint/astro-parser 4.6.22 → 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/ARCHITECTURE.ja.md +229 -0
- package/ARCHITECTURE.md +229 -0
- package/CHANGELOG.md +26 -2
- package/SKILL.md +109 -0
- package/docs/maintenance.ja.md +183 -0
- package/docs/maintenance.md +183 -0
- package/lib/astro-parser.d.ts +7 -0
- package/lib/astro-parser.js +7 -0
- package/lib/detect-block-behavior.d.ts +10 -0
- package/lib/detect-block-behavior.js +20 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +7 -0
- package/lib/parser.d.ts +47 -12
- package/lib/parser.js +72 -47
- package/package.json +9 -6
package/lib/parser.js
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var _AstroParser_instances, _AstroParser_updateScopeNS;
|
|
7
1
|
import { AttrState, Parser, ParserError } from '@markuplint/parser-utils';
|
|
8
2
|
import { astroParse } from './astro-parser.js';
|
|
3
|
+
import { detectBlockBehavior } from './detect-block-behavior.js';
|
|
4
|
+
/**
|
|
5
|
+
* Parser implementation for Astro component templates.
|
|
6
|
+
* Extends the base Parser to handle Astro-specific syntax including frontmatter blocks,
|
|
7
|
+
* expression containers (`{}`), component/element/fragment types, Astro directives
|
|
8
|
+
* (e.g., `class:list`, `set:html`), and shorthand attributes.
|
|
9
|
+
*/
|
|
9
10
|
class AstroParser extends Parser {
|
|
10
11
|
constructor() {
|
|
11
12
|
super({
|
|
12
13
|
endTagType: 'xml',
|
|
13
14
|
selfCloseType: 'html+xml',
|
|
14
15
|
tagNameCaseSensitive: true,
|
|
15
|
-
}, {
|
|
16
|
-
scopeNS: 'http://www.w3.org/1999/xhtml',
|
|
17
16
|
});
|
|
18
|
-
_AstroParser_instances.add(this);
|
|
19
17
|
}
|
|
20
18
|
tokenize() {
|
|
21
19
|
return {
|
|
@@ -23,16 +21,25 @@ class AstroParser extends Parser {
|
|
|
23
21
|
isFragment: true,
|
|
24
22
|
};
|
|
25
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Converts an Astro AST node into markuplint node tree items.
|
|
26
|
+
* Handles frontmatter, doctype, text, comment, component/element/fragment,
|
|
27
|
+
* and expression nodes.
|
|
28
|
+
*
|
|
29
|
+
* @param originNode - The Astro AST node to convert
|
|
30
|
+
* @param parentNode - The parent node in the markuplint tree, or null for root nodes
|
|
31
|
+
* @param depth - The nesting depth of the node
|
|
32
|
+
* @returns An array of markuplint node tree items
|
|
33
|
+
*/
|
|
26
34
|
nodeize(
|
|
27
35
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
28
36
|
originNode, parentNode, depth) {
|
|
29
37
|
if (!originNode.position) {
|
|
30
38
|
throw new TypeError("Node doesn't have position");
|
|
31
39
|
}
|
|
32
|
-
const
|
|
40
|
+
const offset = originNode.position.start.offset;
|
|
33
41
|
const endOffset = originNode.position.end?.offset;
|
|
34
|
-
const token = this.sliceFragment(
|
|
35
|
-
__classPrivateFieldGet(this, _AstroParser_instances, "m", _AstroParser_updateScopeNS).call(this, originNode, parentNode);
|
|
42
|
+
const token = this.sliceFragment(offset, endOffset);
|
|
36
43
|
switch (originNode.type) {
|
|
37
44
|
case 'frontmatter': {
|
|
38
45
|
return this.visitPsBlock({
|
|
@@ -85,34 +92,42 @@ class AstroParser extends Parser {
|
|
|
85
92
|
const firstChild = originNode.children.at(0);
|
|
86
93
|
const lastChild = originNode.children.at(-1);
|
|
87
94
|
let startExpressionRaw = token.raw;
|
|
88
|
-
let startExpressionStartLine = token.
|
|
89
|
-
let startExpressionStartCol = token.
|
|
95
|
+
let startExpressionStartLine = token.line;
|
|
96
|
+
let startExpressionStartCol = token.col;
|
|
90
97
|
const nodes = [];
|
|
98
|
+
let closeExpressionLocation = null;
|
|
91
99
|
if (firstChild && lastChild && firstChild !== lastChild) {
|
|
92
|
-
const startExpressionEndOffset = firstChild.position?.end?.offset ?? endOffset ??
|
|
93
|
-
const startExpressionLocation = this.sliceFragment(
|
|
100
|
+
const startExpressionEndOffset = firstChild.position?.end?.offset ?? endOffset ?? offset;
|
|
101
|
+
const startExpressionLocation = this.sliceFragment(offset, startExpressionEndOffset);
|
|
94
102
|
startExpressionRaw = startExpressionLocation.raw;
|
|
95
|
-
startExpressionStartLine = startExpressionLocation.
|
|
96
|
-
startExpressionStartCol = startExpressionLocation.
|
|
97
|
-
|
|
98
|
-
nodes.push(...this.visitPsBlock({
|
|
99
|
-
...closeExpressionLocation,
|
|
100
|
-
depth,
|
|
101
|
-
parentNode,
|
|
102
|
-
nodeName: 'MustacheTag',
|
|
103
|
-
isFragment: false,
|
|
104
|
-
}));
|
|
103
|
+
startExpressionStartLine = startExpressionLocation.line;
|
|
104
|
+
startExpressionStartCol = startExpressionLocation.col;
|
|
105
|
+
closeExpressionLocation = this.sliceFragment(lastChild.position?.start.offset ?? offset, endOffset);
|
|
105
106
|
}
|
|
107
|
+
const blockBehavior = detectBlockBehavior(startExpressionRaw);
|
|
106
108
|
nodes.push(...this.visitPsBlock({
|
|
107
109
|
raw: startExpressionRaw,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
offset,
|
|
111
|
+
line: startExpressionStartLine,
|
|
112
|
+
col: startExpressionStartCol,
|
|
111
113
|
depth,
|
|
112
114
|
parentNode,
|
|
113
115
|
nodeName: 'MustacheTag',
|
|
114
116
|
isFragment: true,
|
|
115
|
-
}, originNode.children)
|
|
117
|
+
}, originNode.children, blockBehavior), ...(closeExpressionLocation
|
|
118
|
+
? this.visitPsBlock({
|
|
119
|
+
...closeExpressionLocation,
|
|
120
|
+
depth,
|
|
121
|
+
parentNode,
|
|
122
|
+
nodeName: 'MustacheTag',
|
|
123
|
+
isFragment: false,
|
|
124
|
+
}, [], blockBehavior
|
|
125
|
+
? {
|
|
126
|
+
type: 'end',
|
|
127
|
+
expression: closeExpressionLocation.raw,
|
|
128
|
+
}
|
|
129
|
+
: undefined)
|
|
130
|
+
: []));
|
|
116
131
|
return nodes;
|
|
117
132
|
}
|
|
118
133
|
default: {
|
|
@@ -125,6 +140,15 @@ class AstroParser extends Parser {
|
|
|
125
140
|
exposeInvalidNode: false,
|
|
126
141
|
});
|
|
127
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Visits an element token by first parsing the raw HTML fragment to extract
|
|
145
|
+
* the start tag, then delegating to the base visitElement with Astro-specific
|
|
146
|
+
* options including nameless fragment support.
|
|
147
|
+
*
|
|
148
|
+
* @param token - The child token representing the element
|
|
149
|
+
* @param childNodes - The child Astro AST nodes within the element
|
|
150
|
+
* @returns An array of markuplint node tree items
|
|
151
|
+
*/
|
|
128
152
|
visitElement(token,
|
|
129
153
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
130
154
|
childNodes) {
|
|
@@ -139,11 +163,8 @@ class AstroParser extends Parser {
|
|
|
139
163
|
return super.visitElement(startTagNode, childNodes, {
|
|
140
164
|
// https://docs.astro.build/en/basics/astro-syntax/#fragments
|
|
141
165
|
namelessFragment: true,
|
|
142
|
-
overwriteProps: {
|
|
143
|
-
namespace: this.state.scopeNS,
|
|
144
|
-
},
|
|
145
166
|
createEndTagToken: () => {
|
|
146
|
-
if (startTagNode.
|
|
167
|
+
if (startTagNode.tagCloseChar.startsWith('/')) {
|
|
147
168
|
return null;
|
|
148
169
|
}
|
|
149
170
|
const endTagNode = parsedNodes.at(-1);
|
|
@@ -154,6 +175,15 @@ class AstroParser extends Parser {
|
|
|
154
175
|
},
|
|
155
176
|
});
|
|
156
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Visits child nodes and verifies that no sibling nodes with differing
|
|
180
|
+
* hierarchy levels are produced. Throws a ParserError if unexpected
|
|
181
|
+
* sibling nodes are discovered.
|
|
182
|
+
*
|
|
183
|
+
* @param children - The child Astro AST nodes to visit
|
|
184
|
+
* @param parentNode - The parent node in the markuplint tree
|
|
185
|
+
* @returns An empty array (all children are attached via the visitor)
|
|
186
|
+
*/
|
|
157
187
|
visitChildren(
|
|
158
188
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
159
189
|
children, parentNode) {
|
|
@@ -163,6 +193,14 @@ class AstroParser extends Parser {
|
|
|
163
193
|
}
|
|
164
194
|
return [];
|
|
165
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Visits an attribute token, handling Astro-specific syntax including
|
|
198
|
+
* curly-brace expression values, shorthand attributes (`{name}`),
|
|
199
|
+
* and template directives (e.g., `class:list`, `set:html`).
|
|
200
|
+
*
|
|
201
|
+
* @param token - The token representing the attribute
|
|
202
|
+
* @returns The parsed attribute node with Astro-specific metadata
|
|
203
|
+
*/
|
|
166
204
|
visitAttr(token) {
|
|
167
205
|
const attr = super.visitAttr(token, {
|
|
168
206
|
quoteSet: [
|
|
@@ -220,17 +258,4 @@ class AstroParser extends Parser {
|
|
|
220
258
|
return super.detectElementType(nodeName, /^[A-Z]/);
|
|
221
259
|
}
|
|
222
260
|
}
|
|
223
|
-
_AstroParser_instances = new WeakSet(), _AstroParser_updateScopeNS = function _AstroParser_updateScopeNS(
|
|
224
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
225
|
-
originNode, parentNode) {
|
|
226
|
-
const parentNS = this.state.scopeNS;
|
|
227
|
-
if (parentNS === 'http://www.w3.org/1999/xhtml' &&
|
|
228
|
-
originNode.type === 'element' &&
|
|
229
|
-
originNode.name?.toLowerCase() === 'svg') {
|
|
230
|
-
this.state.scopeNS = 'http://www.w3.org/2000/svg';
|
|
231
|
-
}
|
|
232
|
-
else if (parentNS === 'http://www.w3.org/2000/svg' && parentNode && parentNode.nodeName === 'foreignObject') {
|
|
233
|
-
this.state.scopeNS = 'http://www.w3.org/1999/xhtml';
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
261
|
export const parser = new AstroParser();
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/astro-parser",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-alpha.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>",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
8
11
|
"type": "module",
|
|
9
12
|
"exports": {
|
|
10
13
|
".": {
|
|
@@ -21,12 +24,12 @@
|
|
|
21
24
|
"clean": "tsc --build --clean tsconfig.build.json"
|
|
22
25
|
},
|
|
23
26
|
"dependencies": {
|
|
24
|
-
"@markuplint/ml-ast": "
|
|
25
|
-
"@markuplint/parser-utils": "
|
|
26
|
-
"astro-eslint-parser": "1.
|
|
27
|
+
"@markuplint/ml-ast": "5.0.0-alpha.0",
|
|
28
|
+
"@markuplint/parser-utils": "5.0.0-alpha.0",
|
|
29
|
+
"astro-eslint-parser": "1.3.0"
|
|
27
30
|
},
|
|
28
31
|
"devDependencies": {
|
|
29
|
-
"@astrojs/compiler": "2.13.
|
|
32
|
+
"@astrojs/compiler": "2.13.1"
|
|
30
33
|
},
|
|
31
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "13dcfc84ec83d87360c720e253383b60767e1b56"
|
|
32
35
|
}
|