@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/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 startOffset = originNode.position.start.offset;
40
+ const offset = originNode.position.start.offset;
33
41
  const endOffset = originNode.position.end?.offset;
34
- const token = this.sliceFragment(startOffset, endOffset);
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.startLine;
89
- let startExpressionStartCol = token.startCol;
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 ?? startOffset;
93
- const startExpressionLocation = this.sliceFragment(startOffset, startExpressionEndOffset);
100
+ const startExpressionEndOffset = firstChild.position?.end?.offset ?? endOffset ?? offset;
101
+ const startExpressionLocation = this.sliceFragment(offset, startExpressionEndOffset);
94
102
  startExpressionRaw = startExpressionLocation.raw;
95
- startExpressionStartLine = startExpressionLocation.startLine;
96
- startExpressionStartCol = startExpressionLocation.startCol;
97
- const closeExpressionLocation = this.sliceFragment(lastChild.position?.start.offset ?? startOffset, endOffset);
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
- startOffset,
109
- startLine: startExpressionStartLine,
110
- startCol: startExpressionStartCol,
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.selfClosingSolidus?.raw === '/') {
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": "4.6.22",
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": "4.4.10",
25
- "@markuplint/parser-utils": "4.8.10",
26
- "astro-eslint-parser": "1.2.2"
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.0"
32
+ "@astrojs/compiler": "2.13.1"
30
33
  },
31
- "gitHead": "6213ea30269ef404f030e67bbcc7fc7443ec1060"
34
+ "gitHead": "13dcfc84ec83d87360c720e253383b60767e1b56"
32
35
  }