@markuplint/ml-ast 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.
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.5](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.4...v5.0.0-rc.5) (2026-08-28)
7
+
8
+ ### Features
9
+
10
+ - **ml-ast:** add documentMode to ParserOptions ([530493e](https://github.com/markuplint/markuplint/commit/530493e7f99eb27dd2f420666474e233db450c26)), closes [#3844](https://github.com/markuplint/markuplint/issues/3844)
11
+ - **ml-ast:** add MLASTParseError + MLASTParseErrorCode types and parseErrors field ([be34fbd](https://github.com/markuplint/markuplint/commit/be34fbd36ba7fb36b0a07cd6e865c8d81d8752ae)), closes [#3844](https://github.com/markuplint/markuplint/issues/3844)
12
+
13
+ # [5.0.0-rc.4](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.3...v5.0.0-rc.4) (2026-04-19)
14
+
15
+ **Note:** Version bump only for package @markuplint/ml-ast
16
+
17
+ # [5.0.0-rc.3](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.2...v5.0.0-rc.3) (2026-04-19)
18
+
19
+ **Note:** Version bump only for package @markuplint/ml-ast
20
+
6
21
  # [5.0.0-rc.2](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.1...v5.0.0-rc.2) (2026-04-15)
7
22
 
8
23
  **Note:** Version bump only for package @markuplint/ml-ast
package/README.md CHANGED
@@ -16,9 +16,3 @@ $ yarn add @markuplint/ml-ast
16
16
  ```
17
17
 
18
18
  </details>
19
-
20
- ## Documentation
21
-
22
- - [Architecture](ARCHITECTURE.md) -- Package overview, type hierarchy diagrams, and integration points
23
- - [Node Reference](docs/node-reference.md) -- Detailed documentation of each AST node type
24
- - [Maintenance Guide](docs/maintenance.md) -- Commands, recipes, and troubleshooting
package/lib/index.d.ts CHANGED
@@ -1 +1,17 @@
1
+ /**
2
+ * The language-independent AST contract of markuplint: every parser package
3
+ * must produce these types and `@markuplint/ml-core` consumes them, so the
4
+ * core and rules can operate on a unified AST regardless of the source
5
+ * language.
6
+ *
7
+ * This package intentionally contains zero runtime code and zero
8
+ * dependencies — keep it type-only so parsers and downstream consumers can
9
+ * share the contract without runtime coupling.
10
+ *
11
+ * Backward-compatibility convention: when adding a field to an existing node
12
+ * interface, prefer an optional field so existing parser implementations do
13
+ * not break.
14
+ *
15
+ * @module
16
+ */
1
17
  export * from './types.js';
package/lib/index.js CHANGED
@@ -1 +1,17 @@
1
+ /**
2
+ * The language-independent AST contract of markuplint: every parser package
3
+ * must produce these types and `@markuplint/ml-core` consumes them, so the
4
+ * core and rules can operate on a unified AST regardless of the source
5
+ * language.
6
+ *
7
+ * This package intentionally contains zero runtime code and zero
8
+ * dependencies — keep it type-only so parsers and downstream consumers can
9
+ * share the contract without runtime coupling.
10
+ *
11
+ * Backward-compatibility convention: when adding a field to an existing node
12
+ * interface, prefer an optional field so existing parser implementations do
13
+ * not break.
14
+ *
15
+ * @module
16
+ */
1
17
  export * from './types.js';
package/lib/types.d.ts CHANGED
@@ -11,6 +11,10 @@
11
11
  * - `'invalid'` – A node that could not be parsed correctly
12
12
  * - `'attr'` – A regular HTML attribute
13
13
  * - `'spread'` – A spread attribute (e.g. `{...props}` in JSX)
14
+ *
15
+ * Adding a value here requires a corresponding `case` in `ml-core`'s
16
+ * `createNode()` (`packages/@markuplint/ml-core/src/ml-dom/helper/create-node.ts`);
17
+ * an unhandled value throws `TypeError: Invalid AST node types` at runtime.
14
18
  */
15
19
  export type MLASTNodeType = 'doctype' | 'starttag' | 'endtag' | 'comment' | 'text' | 'omittedtag' | 'psblock' | 'invalid' | 'attr' | 'spread';
16
20
  /**
@@ -54,7 +58,14 @@ export type MLASTAttr = MLASTHTMLAttr | MLASTSpreadAttr;
54
58
  * in `@markuplint/parser-utils`.
55
59
  */
56
60
  export interface MLASTToken {
57
- /** Unique identifier for this token instance */
61
+ /**
62
+ * Unique identifier for this token instance.
63
+ *
64
+ * Cross-references between nodes (`parentNodeUuid`, `pairNodeUuid`) use
65
+ * UUID strings rather than object references so the AST stays free of
66
+ * circular references and can be serialized as JSON; consumers resolve a
67
+ * UUID against `MLASTDocument.nodeList`.
68
+ */
58
69
  readonly uuid: string;
59
70
  /** The original raw source text of this token */
60
71
  readonly raw: string;
@@ -109,7 +120,7 @@ export interface MLASTElement extends MLASTAbstractNode {
109
120
  readonly namespace: NamespaceURI;
110
121
  /** Whether the element is native HTML, a Web Component, or an authored component */
111
122
  readonly elementType: ElementType;
112
- /** Whether this element acts as a fragment (no actual DOM node) */
123
+ /** Whether this element acts as a fragment (no actual DOM node, e.g. a JSX fragment `<>` or a Vue `<template>` wrapper) */
113
124
  readonly isFragment: boolean;
114
125
  /** Attributes on this element */
115
126
  readonly attributes: readonly MLASTAttr[];
@@ -130,12 +141,17 @@ export interface MLASTElement extends MLASTAbstractNode {
130
141
  readonly tagOpenChar: string;
131
142
  /** The characters that close this tag (usually `">"`) */
132
143
  readonly tagCloseChar: string;
133
- /** Whether this element is a ghost node (omitted tag inferred by the parser) */
144
+ /** Whether this element is a ghost node (omitted tag inferred by the parser, e.g. an implicit `<tbody>`); ghost nodes have an empty `raw` */
134
145
  readonly isGhost: boolean;
135
146
  }
136
147
  /**
137
148
  * A closing element tag (e.g. `</div>`).
138
149
  * Always paired with an {@link MLASTElement} via `pairNodeUuid`.
150
+ *
151
+ * Close tags are not part of DOM tree traversal in `ml-core`: `createNode()`
152
+ * skips `'endtag'` entries in the node list, and the paired `MLElement`
153
+ * instead resolves `pairNodeUuid` to create its `MLElementCloseTag`, which
154
+ * exists only as a satellite of that element.
139
155
  */
140
156
  export interface MLASTElementCloseTag extends MLASTAbstractNode {
141
157
  readonly type: 'endtag';
@@ -157,6 +173,11 @@ export interface MLASTElementCloseTag extends MLASTAbstractNode {
157
173
  * A preprocessor-specific block node, representing control-flow constructs
158
174
  * from template engines and frameworks (e.g. `{#if}`, `{#each}` in Svelte,
159
175
  * `v-if` blocks in Vue, `<% if %>` in EJS/ERB).
176
+ *
177
+ * In `ml-core` this maps to `MLBlock` — a markuplint-specific extension with
178
+ * the custom `nodeType` `101` (no DOM Standard equivalent) — which acts as a
179
+ * transparent container: its children are treated as belonging to the parent
180
+ * node for tree traversal purposes.
160
181
  */
161
182
  export interface MLASTPreprocessorSpecificBlock extends MLASTAbstractNode {
162
183
  readonly type: 'psblock';
@@ -168,7 +189,7 @@ export interface MLASTPreprocessorSpecificBlock extends MLASTAbstractNode {
168
189
  readonly isFragment: boolean;
169
190
  /** Direct child nodes within this block */
170
191
  readonly childNodes: readonly MLASTChildNode[];
171
- /** Block behavior associated with this block, if any */
192
+ /** Block behavior associated with this block, or `null` when the block has no control-flow semantic (e.g. a pure expression output like `<%= expr %>` in EJS) */
172
193
  readonly blockBehavior: MLASTBlockBehavior | null;
173
194
  /** Whether this block is bogus (unparsable or malformed) */
174
195
  readonly isBogus: boolean;
@@ -177,6 +198,10 @@ export interface MLASTPreprocessorSpecificBlock extends MLASTAbstractNode {
177
198
  * Describes the behavior of a preprocessor block or element,
178
199
  * capturing both the kind of control-flow construct and the
179
200
  * source expression that drives it.
201
+ *
202
+ * `ml-core` uses the `type` to enumerate conditional branches
203
+ * (`conditionalChildNodes()`) so content-model rules such as
204
+ * `permitted-contents` can analyze each branch separately.
180
205
  */
181
206
  export interface MLASTBlockBehavior {
182
207
  /** The kind of block behavior (e.g. `'if'`, `'each'`, `'await'`) */
@@ -196,7 +221,12 @@ export interface MLASTComment extends MLASTAbstractNode {
196
221
  readonly nodeName: '#comment';
197
222
  /** Nesting depth in the document tree */
198
223
  readonly depth: number;
199
- /** Whether the comment is bogus (e.g. a malformed comment) */
224
+ /**
225
+ * Whether the comment is bogus (malformed per the HTML parsing spec,
226
+ * e.g. `<!...>` or a processing instruction such as `<?xml ... ?>`).
227
+ * The parser still captures these as comment nodes but flags them so
228
+ * lint rules can report them.
229
+ */
200
230
  readonly isBogus: boolean;
201
231
  }
202
232
  /**
@@ -211,6 +241,12 @@ export interface MLASTText extends MLASTAbstractNode {
211
241
  /**
212
242
  * A node representing markup that could not be parsed correctly.
213
243
  * Always marked as bogus.
244
+ *
245
+ * This is a recovery node: the parser captures unparsable content instead of
246
+ * failing the whole parse. It is never preserved as-is in the DOM —
247
+ * `ml-core` converts it to an `MLElement` named `x-invalid` (when `kind` is
248
+ * `'starttag'`) or to an `MLText` (otherwise), so lint rules can still
249
+ * operate on the content based on the parser's best guess.
214
250
  */
215
251
  export interface MLASTInvalid extends MLASTAbstractNode {
216
252
  readonly type: 'invalid';
@@ -225,6 +261,12 @@ export interface MLASTInvalid extends MLASTAbstractNode {
225
261
  /**
226
262
  * A regular HTML attribute node, decomposed into its constituent tokens
227
263
  * (name, equal sign, quotes, value, and surrounding whitespace).
264
+ *
265
+ * The decomposition exists so lint rules can validate whitespace around the
266
+ * equal sign, quoting style, and attribute naming conventions with precise
267
+ * source locations. For boolean attributes without a value (e.g.
268
+ * `disabled`), the `equal`, `startQuote`, `value`, and `endQuote` tokens
269
+ * still exist but have empty `raw` strings.
228
270
  */
229
271
  export interface MLASTHTMLAttr extends MLASTToken {
230
272
  readonly type: 'attr';
@@ -250,7 +292,7 @@ export interface MLASTHTMLAttr extends MLASTToken {
250
292
  readonly isDynamicValue?: true;
251
293
  /** Whether the attribute is a framework directive (e.g. `v-if`, `@click`) */
252
294
  readonly isDirective?: true;
253
- /** The resolved attribute name when the actual name is a framework-specific directive */
295
+ /** The resolved attribute name when the actual name is a framework-specific directive (e.g. Vue's `:class` resolves to `class`, `@click` to `onclick`) */
254
296
  readonly potentialName?: string;
255
297
  /** The resolved attribute value when the actual value is dynamic */
256
298
  readonly potentialValue?: string;
@@ -258,16 +300,66 @@ export interface MLASTHTMLAttr extends MLASTToken {
258
300
  readonly valueType?: 'string' | 'number' | 'boolean' | 'code';
259
301
  /** A candidate attribute name for auto-correction */
260
302
  readonly candidate?: string;
261
- /** Whether this attribute is allowed to appear multiple times on the same element */
303
+ /** Whether this attribute is allowed to appear multiple times on the same element (e.g. `class` in template engines that merge values) */
262
304
  readonly isDuplicatable: boolean;
263
305
  }
264
306
  /**
265
307
  * A spread attribute node (e.g. `{...props}` in JSX).
308
+ *
309
+ * Minimal by design: a spread cannot be statically decomposed into
310
+ * name/value tokens, so only the positional token information is kept.
266
311
  */
267
312
  export interface MLASTSpreadAttr extends MLASTToken {
268
313
  readonly type: 'spread';
269
314
  readonly nodeName: '#spread';
270
315
  }
316
+ /**
317
+ * Stable identifier for a non-fatal parser conformance error. The full set
318
+ * mirrors parse5's `ERR` enum (HTML LS tokenizer / tree-construction parse
319
+ * errors) — each value is a kebab-case string that appears verbatim in the
320
+ * `code` field of `MLASTParseError` and as the key in
321
+ * `severity.parseError`'s `Record` form.
322
+ *
323
+ * Source of truth: `parse5/dist/common/error-codes.d.ts`. When parse5
324
+ * adds a new code, append it here and update the migration guide.
325
+ *
326
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#parse-errors
327
+ */
328
+ export type MLASTParseErrorCode = 'control-character-in-input-stream' | 'noncharacter-in-input-stream' | 'surrogate-in-input-stream' | 'non-void-html-element-start-tag-with-trailing-solidus' | 'end-tag-with-attributes' | 'end-tag-with-trailing-solidus' | 'unexpected-solidus-in-tag' | 'unexpected-null-character' | 'unexpected-question-mark-instead-of-tag-name' | 'invalid-first-character-of-tag-name' | 'unexpected-equals-sign-before-attribute-name' | 'missing-end-tag-name' | 'unexpected-character-in-attribute-name' | 'unknown-named-character-reference' | 'missing-semicolon-after-character-reference' | 'absence-of-digits-in-numeric-character-reference' | 'null-character-reference' | 'surrogate-character-reference' | 'character-reference-outside-unicode-range' | 'control-character-reference' | 'noncharacter-character-reference' | 'unexpected-character-in-unquoted-attribute-value' | 'missing-attribute-value' | 'missing-whitespace-between-attributes' | 'duplicate-attribute' | 'unexpected-character-after-doctype-system-identifier' | 'missing-whitespace-after-doctype-public-keyword' | 'missing-whitespace-between-doctype-public-and-system-identifiers' | 'missing-whitespace-after-doctype-system-keyword' | 'missing-quote-before-doctype-public-identifier' | 'missing-quote-before-doctype-system-identifier' | 'missing-doctype-public-identifier' | 'missing-doctype-system-identifier' | 'abrupt-doctype-public-identifier' | 'abrupt-doctype-system-identifier' | 'missing-whitespace-before-doctype-name' | 'missing-doctype-name' | 'invalid-character-sequence-after-doctype-name' | 'non-conforming-doctype' | 'missing-doctype' | 'misplaced-doctype' | 'eof-in-doctype' | 'incorrectly-opened-comment' | 'incorrectly-closed-comment' | 'nested-comment' | 'abrupt-closing-of-empty-comment' | 'eof-in-comment' | 'cdata-in-html-content' | 'eof-in-cdata' | 'eof-in-script-html-comment-like-text' | 'eof-before-tag-name' | 'eof-in-tag' | 'eof-in-element-that-can-contain-only-text' | 'end-tag-without-matching-open-element' | 'closing-of-element-with-open-child-elements' | 'disallowed-content-in-noscript-in-head' | 'open-elements-left-after-eof' | 'abandoned-head-element-child' | 'misplaced-start-tag-for-head-element' | 'nested-noscript-in-head';
329
+ /**
330
+ * Non-fatal parser-level conformance error emitted by the underlying parser
331
+ * during tokenisation (e.g., parse5's `onParseError` events). Unlike
332
+ * `unknownParseError` these do not abort the parse — the document is still
333
+ * usable — but they correspond to HTML LS tokenizer / tree-construction
334
+ * conformance errors that the `parse-error` rule surfaces as lint
335
+ * violations.
336
+ *
337
+ * @see https://html.spec.whatwg.org/multipage/parsing.html#parse-errors
338
+ */
339
+ export interface MLASTParseError {
340
+ /**
341
+ * Stable kebab-case identifier from the underlying parser. The current
342
+ * full enumeration mirrors parse5's `ERR` enum and is captured by
343
+ * {@link MLASTParseErrorCode}; framework parsers that surface a code
344
+ * outside that set should still use a kebab-case identifier so user
345
+ * configuration (`severity.parseError`) can target it.
346
+ */
347
+ readonly code: MLASTParseErrorCode;
348
+ /** Zero-based offset into the source where the error starts. */
349
+ readonly startOffset: number;
350
+ /** 1-based line where the error starts. */
351
+ readonly startLine: number;
352
+ /** 1-based column where the error starts. */
353
+ readonly startCol: number;
354
+ /** Zero-based offset into the source where the error ends. */
355
+ readonly endOffset: number;
356
+ /** 1-based line where the error ends. */
357
+ readonly endLine: number;
358
+ /** 1-based column where the error ends. */
359
+ readonly endCol: number;
360
+ /** The slice of the source between `startOffset` and `endOffset`. */
361
+ readonly raw: string;
362
+ }
271
363
  /**
272
364
  * The root document node returned by a parser.
273
365
  * Contains the full node list and metadata about the parse result.
@@ -275,12 +367,34 @@ export interface MLASTSpreadAttr extends MLASTToken {
275
367
  export interface MLASTDocument {
276
368
  /** The full original source code */
277
369
  readonly raw: string;
278
- /** Flat list of top-level AST nodes in document order */
370
+ /**
371
+ * Depth-first flattened list of all AST nodes in document order (the
372
+ * order they appear in the source) — nodes also appear in their parent's
373
+ * `childNodes`. UUID cross-references (`parentNodeUuid`, `pairNodeUuid`)
374
+ * are resolved against this list, so every node referenced by UUID must
375
+ * be present in it.
376
+ */
279
377
  readonly nodeList: readonly MLASTNodeTreeItem[];
280
378
  /** Whether the document is a fragment (no root element required) */
281
379
  readonly isFragment: boolean;
282
380
  /** A description of any unknown parse error that occurred, if any */
283
381
  readonly unknownParseError?: string;
382
+ /**
383
+ * Non-fatal parser-level conformance errors collected during tokenisation.
384
+ * Populated by parsers that support it (e.g., `@markuplint/html-parser`
385
+ * via parse5's `onParseError`); omitted otherwise. Consumed by
386
+ * `@markuplint/ml-core`'s verify pipeline, which surfaces each entry as a
387
+ * `ruleId: 'parse-error'` violation (sharing the channel with fatal
388
+ * `ParserError`s; controlled by `severity.parseError`).
389
+ *
390
+ * **Order contract**: entries must appear in the order the parser emitted
391
+ * them, and `ml-core` pushes them onto the violations list **before** any
392
+ * rule iteration runs — so they always precede rule-level violations in
393
+ * test fixtures and reporter output. Custom parsers populating this field
394
+ * must preserve emit order; downstream consumers (including 80+ rule spec
395
+ * files) rely on it.
396
+ */
397
+ readonly parseErrors?: readonly MLASTParseError[];
284
398
  }
285
399
  /**
286
400
  * Interface for a markuplint-compatible parser.
@@ -333,6 +447,29 @@ export type ParserOptions = {
333
447
  readonly ignoreFrontMatter?: boolean;
334
448
  /** How to distinguish authored (component) element names from native HTML elements */
335
449
  readonly authoredElementName?: ParserAuthoredElementNameDistinguishing;
450
+ /**
451
+ * Override how the underlying HTML parser decides between full-document
452
+ * and fragment parsing.
453
+ *
454
+ * - `'auto'` (default) — inspect the source: input starting with
455
+ * `<!doctype html>` or `<html>` is treated as a full document;
456
+ * anything else as a fragment. Backwards-compatible behaviour.
457
+ * - `'document'` — force full-document parsing. Use for sources that
458
+ * are complete HTML pages without an explicit doctype, so that
459
+ * document-level parse5 errors (`missing-doctype`, `misplaced-doctype`,
460
+ * etc.) surface via the `parse-error` channel.
461
+ * - `'fragment'` — force fragment parsing. Use for SSR / template
462
+ * partials whose source intentionally starts with `<head>`, `<meta>`,
463
+ * `<title>`, … as legitimate inserted chunks (parse5 should not emit
464
+ * `missing-doctype` for those).
465
+ *
466
+ * Template-engine parsers that internally re-invoke the HTML parser for
467
+ * embedded HTML chunks (Markdown HTML blocks, Pug raw HTML output, …)
468
+ * pass `'fragment'` to that internal call by default; users can still
469
+ * override that via `parserOptions` when their template legitimately
470
+ * carries a full document.
471
+ */
472
+ readonly documentMode?: 'auto' | 'document' | 'fragment';
336
473
  };
337
474
  /**
338
475
  * Configuration for distinguishing authored (component) elements from native HTML elements.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/ml-ast",
3
- "version": "5.0.0-rc.2",
3
+ "version": "5.0.0-rc.5",
4
4
  "description": "The markuplint AST types.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,7 +10,7 @@
10
10
  "author": "Yusuke Hirao <yusukehirao@me.com>",
11
11
  "license": "MIT",
12
12
  "engines": {
13
- "node": ">=22"
13
+ "node": ">=24"
14
14
  },
15
15
  "type": "module",
16
16
  "exports": {
@@ -30,5 +30,5 @@
30
30
  "dev": "tsc --watch --project tsconfig.build.json",
31
31
  "clean": "tsc --build --clean tsconfig.build.json"
32
32
  },
33
- "gitHead": "e43763858d9234c417053becc73dbd088c1e7ea6"
33
+ "gitHead": "8d87463af2ff3f1b83fb28da20f1819362cf3555"
34
34
  }
@@ -1,309 +0,0 @@
1
- # @markuplint/ml-ast
2
-
3
- ## 概要
4
-
5
- `@markuplint/ml-ast` は、markuplint の言語非依存な抽象構文木(AST)中間表現を定義する純粋な型定義パッケージです。**ランタイムコードはゼロ**、**依存関係もゼロ**で、すべてのパーサーが生成し、すべての下流パッケージが消費する TypeScript 型定義のみを含みます。
6
-
7
- すべてのマークアップ言語パーサー(HTML、JSX、Vue、Svelte、Astro、Pug など)はソースコードをここで定義された型にパースし、markuplint のコアとルールがソース言語に関係なく統一された AST 上で動作できるようにします。
8
-
9
- ## ディレクトリ構成
10
-
11
- ```
12
- src/
13
- ├── index.ts — types.ts からすべての型を再エクスポート
14
- └── types.ts — すべての型定義(約470行)
15
- ```
16
-
17
- ## アーキテクチャ図
18
-
19
- ```mermaid
20
- flowchart TD
21
- subgraph parsers ["パーサー(上流)"]
22
- html["@markuplint/html-parser"]
23
- jsx["@markuplint/jsx-parser"]
24
- vue["@markuplint/vue-parser"]
25
- svelte["@markuplint/svelte-parser"]
26
- astro["@markuplint/astro-parser"]
27
- pug["@markuplint/pug-parser"]
28
- parserUtils["@markuplint/parser-utils"]
29
- end
30
-
31
- subgraph ast ["@markuplint/ml-ast"]
32
- types["型定義\n(MLASTDocument, MLASTElement,\nMLASTComment, MLASTText, ...)"]
33
- end
34
-
35
- subgraph downstream ["下流"]
36
- mlCore["@markuplint/ml-core\n(AST → DOM マッピング)"]
37
- mlConfig["@markuplint/ml-config"]
38
- mlSpec["@markuplint/ml-spec"]
39
- rules["@markuplint/rules"]
40
- fileResolver["@markuplint/file-resolver"]
41
- end
42
-
43
- parsers -->|"生成"| types
44
- types -->|"消費"| downstream
45
- mlCore -->|"DOM ノードを作成"| types
46
- ```
47
-
48
- ## 型継承図
49
-
50
- ```mermaid
51
- classDiagram
52
- class MLASTToken {
53
- <<interface>>
54
- +uuid: string
55
- +raw: string
56
- +offset: number
57
- +line: number
58
- +col: number
59
- }
60
-
61
- class MLASTAbstractNode {
62
- <<interface>>
63
- +type: MLASTNodeType
64
- +nodeName: string
65
- +parentNodeUuid: string | null
66
- }
67
-
68
- class MLASTDoctype {
69
- <<interface>>
70
- +type: "doctype"
71
- +depth: number
72
- +name: string
73
- +publicId: string
74
- +systemId: string
75
- }
76
-
77
- class MLASTElement {
78
- <<interface>>
79
- +type: "starttag"
80
- +depth: number
81
- +namespace: string
82
- +elementType: ElementType
83
- +attributes: MLASTAttr[]
84
- +childNodes: MLASTChildNode[]
85
- +blockBehavior: MLASTBlockBehavior | null
86
- +pairNodeUuid: string | null
87
- +isGhost: boolean
88
- +isFragment: boolean
89
- }
90
-
91
- class MLASTElementCloseTag {
92
- <<interface>>
93
- +type: "endtag"
94
- +depth: number
95
- +pairNodeUuid: string | null
96
- }
97
-
98
- class MLASTComment {
99
- <<interface>>
100
- +type: "comment"
101
- +depth: number
102
- +isBogus: boolean
103
- }
104
-
105
- class MLASTText {
106
- <<interface>>
107
- +type: "text"
108
- +depth: number
109
- }
110
-
111
- class MLASTPreprocessorSpecificBlock {
112
- <<interface>>
113
- +type: "psblock"
114
- +blockBehavior: MLASTBlockBehavior | null
115
- +depth: number
116
- +childNodes: MLASTChildNode[]
117
- +isBogus: boolean
118
- }
119
-
120
- class MLASTInvalid {
121
- <<interface>>
122
- +type: "invalid"
123
- +depth: number
124
- +kind: MLASTChildNode type
125
- +isBogus: true
126
- }
127
-
128
- class MLASTHTMLAttr {
129
- <<interface>>
130
- +type: "attr"
131
- +name: MLASTToken
132
- +value: MLASTToken
133
- +isDynamicValue: boolean
134
- +isDirective: boolean
135
- }
136
-
137
- class MLASTSpreadAttr {
138
- <<interface>>
139
- +type: "spread"
140
- }
141
-
142
- MLASTToken <|-- MLASTAbstractNode
143
- MLASTAbstractNode <|-- MLASTDoctype
144
- MLASTAbstractNode <|-- MLASTElement
145
- MLASTAbstractNode <|-- MLASTElementCloseTag
146
- MLASTAbstractNode <|-- MLASTComment
147
- MLASTAbstractNode <|-- MLASTText
148
- MLASTAbstractNode <|-- MLASTPreprocessorSpecificBlock
149
- MLASTAbstractNode <|-- MLASTInvalid
150
- MLASTToken <|-- MLASTHTMLAttr
151
- MLASTToken <|-- MLASTSpreadAttr
152
- ```
153
-
154
- ## 共用体型
155
-
156
- ```mermaid
157
- flowchart TD
158
- subgraph MLASTNode ["MLASTNode(全ノード型)"]
159
- subgraph MLASTNodeTreeItem ["MLASTNodeTreeItem"]
160
- MLASTDoctype["MLASTDoctype"]
161
- subgraph MLASTChildNode ["MLASTChildNode"]
162
- subgraph MLASTTag ["MLASTTag"]
163
- MLASTElement["MLASTElement"]
164
- MLASTElementCloseTag["MLASTElementCloseTag"]
165
- end
166
- MLASTText["MLASTText"]
167
- MLASTComment["MLASTComment"]
168
- MLASTPreprocessorSpecificBlock["MLASTPreprocessorSpecificBlock"]
169
- MLASTInvalid["MLASTInvalid"]
170
- end
171
- end
172
- subgraph MLASTAttr ["MLASTAttr"]
173
- MLASTHTMLAttr["MLASTHTMLAttr"]
174
- MLASTSpreadAttr["MLASTSpreadAttr"]
175
- end
176
- end
177
-
178
- style MLASTElement fill:#e1f5fe
179
- style MLASTPreprocessorSpecificBlock fill:#e1f5fe
180
-
181
- note1["MLASTParentNode = MLASTElement | MLASTPreprocessorSpecificBlock\n(青でハイライト)"]
182
- ```
183
-
184
- ## ノード型一覧
185
-
186
- | 型 | `type` 値 | 代表例 | 説明 |
187
- | -------------------------------- | ------------ | ---------------------- | ---------------------------------------- |
188
- | `MLASTDoctype` | `'doctype'` | `<!DOCTYPE html>` | DOCTYPE 宣言 |
189
- | `MLASTElement` | `'starttag'` | `<div class="foo">` | 開始タグ。属性・子ノード・名前空間を保持 |
190
- | `MLASTElementCloseTag` | `'endtag'` | `</div>` | 閉じタグ。開始タグとペア |
191
- | `MLASTComment` | `'comment'` | `<!-- ... -->` | HTML コメント。bogus フラグ付き |
192
- | `MLASTText` | `'text'` | テキスト内容 | 要素間の文字データ |
193
- | `MLASTPreprocessorSpecificBlock` | `'psblock'` | `{#if}`, `<% %>` | テンプレートエンジン構文 |
194
- | `MLASTInvalid` | `'invalid'` | パース不能マークアップ | 不正ノード。意図された種別のヒント付き |
195
- | `MLASTHTMLAttr` | `'attr'` | `class="foo"` | 完全分解された HTML 属性 |
196
- | `MLASTSpreadAttr` | `'spread'` | `{...props}` | JSX スプレッド属性 |
197
-
198
- 各型の詳細は[ノードリファレンス](docs/node-reference.ja.md)を参照してください。
199
-
200
- ## AST から MLDOM へのマッピング
201
-
202
- 各 AST ノードは、最終的に `@markuplint/ml-core` によって **MLDOM** ノードに変換されます。MLDOM は [DOM Standard](https://dom.spec.whatwg.org/) に準拠しており、各クラスは対応する DOM インターフェース(`Node`、`Element`、`DocumentType`、`Comment`、`Text` など)を実装しているため、リントルールは標準 DOM API を使って検査できます。
203
-
204
- | AST 型(`ml-ast`) | MLDOM クラス(`ml-core`) | DOM インターフェース | `nodeType` |
205
- | ------------------------------------ | -------------------------- | ------------------------ | ---------- |
206
- | `MLASTDoctype` | `MLDocumentType` | `DocumentType` | `10` |
207
- | `MLASTElement` | `MLElement` | `Element`, `HTMLElement` | `1` |
208
- | `MLASTComment` | `MLComment` | `Comment` | `8` |
209
- | `MLASTText` | `MLText` | `Text` | `3` |
210
- | `MLASTPreprocessorSpecificBlock` | `MLBlock` | _(markuplint 独自)_ | `101` |
211
- | `MLASTInvalid`(`kind: 'starttag'`) | `MLElement`(`x-invalid`) | `Element`, `HTMLElement` | `1` |
212
- | `MLASTInvalid`(その他) | `MLText` | `Text` | `3` |
213
- | `MLASTHTMLAttr` / `MLASTSpreadAttr` | `MLAttr` | `Attr` | `2` |
214
-
215
- **特殊なノード:**
216
-
217
- - **`MLBlock`**(`nodeType: 101`)は DOM Standard に相当するものがない markuplint 独自の拡張です。透過的なコンテナとして機能し、子ノードはツリー走査時に親に属するものとして扱われます。
218
- - **`MLElementCloseTag`** は `createNode()` で生成されません。代わりに `MLElement` が `pairNodeUuid` を解決して閉じタグの AST ノードを検索し、`MLElementCloseTag` を生成します。ペアとなる要素の付属物としてのみ存在し、DOM ツリー走査の対象ではありません。
219
- - **`MLASTInvalid`** はリカバリノードです。MLDOM にそのまま保持されることはなく、`kind` フィールドに応じて `MLElement`(タグ名 `x-invalid`)または `MLText` に変換されます。
220
-
221
- 詳細は[ノードリファレンス -- AST から MLDOM へのマッピング](docs/node-reference.ja.md#ast-から-mldom-へのマッピング)を参照してください。
222
-
223
- ## 属性分解モデル
224
-
225
- `MLASTHTMLAttr` は各属性を完全な位置情報を持つ個別のトークンに分解します:
226
-
227
- ```
228
- ·class="container"
229
- ↑ ↑↑ ↑
230
- │ ││ └─ endQuote
231
- │ │└─ value
232
- │ └─ startQuote
233
- │ equal
234
- └─ spacesBeforeName
235
- name
236
- ```
237
-
238
- これにより、リントルールは `=` 前後のホワイトスペース、引用符スタイル、属性命名規則を正確なソース位置で検証できます。完全なフィールドドキュメントは[ノードリファレンス](docs/node-reference.ja.md#mlasthtmlattr)を参照してください。
239
-
240
- ## パーサーインターフェース
241
-
242
- | 型 | 説明 |
243
- | ---------------- | -------------------------------------------------------- |
244
- | `MLParser` | markuplint 互換パーサーのインターフェース |
245
- | `MLParserModule` | パーサーインスタンスをエクスポートするモジュールラッパー |
246
-
247
- `MLParser` は `MLASTDocument` を返す `parse(sourceCode, options?)` メソッドを必要とします。オプションフィールドには `endTag`(終了タグ処理戦略)、`booleanish`(ブール属性検出)、`tagNameCaseSensitive`(XHTML/JSX 用)があります。
248
-
249
- ## 設定型
250
-
251
- | 型 | 説明 |
252
- | ----------------------------------------- | ---------------------------------------------------------------------- |
253
- | `MLASTNodeType` | ノード種別の判別共用体タグ |
254
- | `ElementType` | 要素分類:`'html' \| 'web-component' \| 'authored'` |
255
- | `EndTagType` | 終了タグ戦略:`'xml' \| 'omittable' \| 'never'` |
256
- | `Namespace` | 短い名前空間識別子:`'html' \| 'svg' \| 'mml' \| 'xlink'` |
257
- | `NamespaceURI` | HTML、SVG、MathML、XLink の完全な名前空間 URI |
258
- | `ParserOptions` | パーサーに渡すオプション(`ignoreFrontMatter`、`authoredElementName`) |
259
- | `ParserAuthoredElementNameDistinguishing` | 著者定義要素を区別するための設定 |
260
- | `Walker<Node>` | AST ノードを走査するコールバック |
261
-
262
- ## 外部依存関係
263
-
264
- なし。このパッケージはランタイム依存関係がゼロです。TypeScript の型定義のみをエクスポートします。
265
-
266
- ## 統合ポイント
267
-
268
- ```mermaid
269
- flowchart TD
270
- subgraph upstream ["上流(パーサー)"]
271
- htmlParser["@markuplint/html-parser"]
272
- parserUtils["@markuplint/parser-utils"]
273
- jsxParser["@markuplint/jsx-parser"]
274
- astroParser["@markuplint/astro-parser"]
275
- vueParser["@markuplint/vue-parser"]
276
- svelteParser["@markuplint/svelte-parser"]
277
- pugParser["@markuplint/pug-parser"]
278
- end
279
-
280
- subgraph pkg ["@markuplint/ml-ast"]
281
- astTypes["型定義"]
282
- end
283
-
284
- subgraph downstream ["下流"]
285
- mlCore["@markuplint/ml-core"]
286
- mlConfig["@markuplint/ml-config"]
287
- mlSpec["@markuplint/ml-spec"]
288
- fileResolver["@markuplint/file-resolver"]
289
- end
290
-
291
- upstream -->|"MLParser を実装\nMLASTDocument を生成"| astTypes
292
- astTypes -->|"MLASTNode 型\nMLParser インターフェース"| downstream
293
- ```
294
-
295
- ### 上流
296
-
297
- すべてのパーサーは `MLParser` インターフェースを実装し、このパッケージで定義された AST ノード型を含む `MLASTDocument` インスタンスを生成します。
298
-
299
- ### 下流
300
-
301
- - **`@markuplint/ml-core`** は AST ノードを消費し、`createNode()` を通じて DOM ノードにマッピングします。`MLASTElement` が `MLElement` に、`MLASTText` が `MLText` になるなど、主要な統合ポイントです。
302
- - **`@markuplint/ml-config`** は設定スキーマ定義で AST 型を参照します。
303
- - **`@markuplint/ml-spec`** は名前空間と要素型の定義を使用します。
304
- - **`@markuplint/file-resolver`** はパーサー関連の型を参照します。
305
-
306
- ## ドキュメントマップ
307
-
308
- - [ノードリファレンス](docs/node-reference.ja.md) -- 各 AST ノード型の詳細ドキュメント
309
- - [メンテナンスガイド](docs/maintenance.ja.md) -- コマンド、レシピ、トラブルシューティング