@markuplint/ml-core 4.13.2 → 4.13.3

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.
Files changed (80) hide show
  1. package/ARCHITECTURE.ja.md +467 -0
  2. package/ARCHITECTURE.md +467 -0
  3. package/CHANGELOG.md +3 -3
  4. package/README.md +5 -0
  5. package/SKILL.md +61 -0
  6. package/docs/linting-pipeline.ja.md +303 -0
  7. package/docs/linting-pipeline.md +303 -0
  8. package/docs/maintenance.ja.md +210 -0
  9. package/docs/maintenance.md +210 -0
  10. package/docs/ml-dom/attr.ja.md +95 -0
  11. package/docs/ml-dom/attr.md +95 -0
  12. package/docs/ml-dom/block.ja.md +272 -0
  13. package/docs/ml-dom/block.md +272 -0
  14. package/docs/ml-dom/document.ja.md +141 -0
  15. package/docs/ml-dom/document.md +141 -0
  16. package/docs/ml-dom/element.ja.md +176 -0
  17. package/docs/ml-dom/element.md +176 -0
  18. package/docs/ml-dom/helpers.ja.md +203 -0
  19. package/docs/ml-dom/helpers.md +203 -0
  20. package/docs/ml-dom/node.ja.md +200 -0
  21. package/docs/ml-dom/node.md +200 -0
  22. package/docs/ml-dom/others.ja.md +119 -0
  23. package/docs/ml-dom/others.md +119 -0
  24. package/docs/ml-dom/overview.ja.md +102 -0
  25. package/docs/ml-dom/overview.md +102 -0
  26. package/docs/ml-dom/pretender.ja.md +269 -0
  27. package/docs/ml-dom/pretender.md +269 -0
  28. package/docs/ml-dom/rule-mapping.ja.md +371 -0
  29. package/docs/ml-dom/rule-mapping.md +371 -0
  30. package/docs/ml-dom.ja.md +18 -0
  31. package/docs/ml-dom.md +18 -0
  32. package/docs/rule-system.ja.md +270 -0
  33. package/docs/rule-system.md +270 -0
  34. package/lib/convert-ruleset.d.ts +7 -0
  35. package/lib/convert-ruleset.js +7 -0
  36. package/lib/debug.d.ts +4 -0
  37. package/lib/debug.js +4 -0
  38. package/lib/ml-core.d.ts +36 -0
  39. package/lib/ml-core.js +29 -0
  40. package/lib/ml-dom/helper/get-indent.d.ts +4 -1
  41. package/lib/ml-dom/helper/get-indent.js +4 -1
  42. package/lib/ml-dom/node/attr.d.ts +65 -4
  43. package/lib/ml-dom/node/attr.js +53 -4
  44. package/lib/ml-dom/node/block.d.ts +21 -0
  45. package/lib/ml-dom/node/block.js +14 -0
  46. package/lib/ml-dom/node/child-node.d.ts +9 -0
  47. package/lib/ml-dom/node/child-node.js +9 -0
  48. package/lib/ml-dom/node/comment.d.ts +7 -0
  49. package/lib/ml-dom/node/comment.js +7 -0
  50. package/lib/ml-dom/node/document-fragment.d.ts +8 -0
  51. package/lib/ml-dom/node/document-fragment.js +8 -0
  52. package/lib/ml-dom/node/document-type.d.ts +22 -0
  53. package/lib/ml-dom/node/document-type.js +13 -0
  54. package/lib/ml-dom/node/document.d.ts +74 -4
  55. package/lib/ml-dom/node/document.js +57 -2
  56. package/lib/ml-dom/node/element.d.ts +136 -2
  57. package/lib/ml-dom/node/element.js +115 -2
  58. package/lib/ml-dom/node/node.d.ts +16 -0
  59. package/lib/ml-dom/node/node.js +16 -0
  60. package/lib/ml-dom/node/text.d.ts +12 -0
  61. package/lib/ml-dom/node/text.js +12 -0
  62. package/lib/ml-dom/node/types.d.ts +68 -0
  63. package/lib/ml-dom/token/token.d.ts +42 -0
  64. package/lib/ml-dom/token/token.js +36 -0
  65. package/lib/ml-rule/create-rule.d.ts +9 -0
  66. package/lib/ml-rule/create-rule.js +9 -0
  67. package/lib/ml-rule/ml-rule.d.ts +33 -0
  68. package/lib/ml-rule/ml-rule.js +30 -0
  69. package/lib/ml-rule/types.d.ts +41 -0
  70. package/lib/plugin/plugin.d.ts +8 -0
  71. package/lib/plugin/plugin.js +8 -0
  72. package/lib/plugin/types.d.ts +21 -0
  73. package/lib/ruleset/index.d.ts +10 -0
  74. package/lib/ruleset/index.js +7 -0
  75. package/lib/test/index.d.ts +42 -1
  76. package/lib/test/index.js +35 -1
  77. package/lib/types.d.ts +8 -0
  78. package/lib/violation-collector.d.ts +33 -0
  79. package/lib/violation-collector.js +33 -0
  80. package/package.json +12 -12
@@ -0,0 +1,467 @@
1
+ # @markuplint/ml-core
2
+
3
+ ## Overview
4
+
5
+ `@markuplint/ml-core` is the core linting engine of markuplint. It converts a parsed AST (`MLASTDocument`) into a DOM tree (`MLDOM`), applies configured rules against nodes, and collects violations. The package comprises three subsystems: **MLDOM** (DOM abstraction layer), **MLRule** (rule execution framework), and **MLCore** (orchestration engine).
6
+
7
+ ## Directory Structure
8
+
9
+ ```
10
+ src/
11
+ ├── index.ts — Public API re-exports
12
+ ├── ml-core.ts — MLCore engine class
13
+ ├── types.ts — MLFabric, MLSchema type definitions
14
+ ├── convert-ruleset.ts — Config → Ruleset converter
15
+ ├── debug.ts — Debug logging utilities
16
+ ├── violation-collector.ts — Multi-file violation aggregator
17
+ ├── ml-dom/
18
+ │ ├── index.ts — MLDOM public exports
19
+ │ ├── node/
20
+ │ │ ├── document.ts — MLDocument (root node, rule mapping, pretender init)
21
+ │ │ ├── element.ts — MLElement (attributes, selectors, namespaces)
22
+ │ │ ├── node.ts — MLNode (abstract base for all nodes)
23
+ │ │ ├── parent-node.ts — MLParentNode (querySelector, children)
24
+ │ │ ├── character-data.ts — MLCharacterData (abstract text base)
25
+ │ │ ├── text.ts — MLText
26
+ │ │ ├── comment.ts — MLComment
27
+ │ │ ├── attr.ts — MLAttr (attribute tokens)
28
+ │ │ ├── block.ts — MLBlock (preprocessor blocks)
29
+ │ │ ├── document-fragment.ts — MLDocumentFragment
30
+ │ │ ├── document-type.ts — MLDocumentType
31
+ │ │ ├── element-close-tag.ts — MLElementCloseTag
32
+ │ │ ├── rule-mapper.ts — RuleMapper (ruleset → node mapping)
33
+ │ │ ├── types.ts — Node type constants, AccessibilityProperties
34
+ │ │ ├── node-list.ts — NodeList/HTMLCollection utilities
35
+ │ │ └── unexpected-call-error.ts — Error for unsupported DOM methods
36
+ │ ├── token/
37
+ │ │ └── token.ts — MLToken (base positional token)
38
+ │ ├── helper/
39
+ │ │ ├── accname.ts — Accessible name computation
40
+ │ │ ├── create-node.ts — AST → MLDOM node factory
41
+ │ │ ├── walkers.ts — Tree traversal (sync/async walkers)
42
+ │ │ ├── get-indent.ts — Indentation analysis
43
+ │ │ └── debug.ts — Debug map generation
44
+ │ └── manipulations/
45
+ │ ├── child-node-methods.ts — ChildNode interface stubs
46
+ │ └── get-children.ts — Element children extraction
47
+ ├── ml-rule/
48
+ │ ├── ml-rule.ts — MLRule class (rule execution)
49
+ │ ├── ml-rule-context.ts — MLRuleContext (report collection)
50
+ │ ├── create-rule.ts — createRule factory
51
+ │ ├── create-test-rule.ts — Test rule factory
52
+ │ └── types.ts — RuleSeed, Checker types
53
+ ├── ruleset/
54
+ │ └── index.ts — Ruleset class (rules + nodeRules + childNodeRules)
55
+ ├── plugin/
56
+ │ ├── plugin.ts — createPlugin factory
57
+ │ ├── types.ts — Plugin, PluginCreator types
58
+ │ └── index.ts — Plugin exports
59
+ ├── test/
60
+ │ └── index.ts — createTestDocument, createTestElement, dummySchemas
61
+ └── utils/
62
+ ├── index.ts — Utility exports
63
+ ├── get-location-from-chars.ts — Character location resolver
64
+ └── string-splice.ts — String splice helper
65
+ ```
66
+
67
+ ## Architecture Diagram
68
+
69
+ ```mermaid
70
+ flowchart TD
71
+ subgraph upstream ["Upstream Dependencies"]
72
+ mlAst["@markuplint/ml-ast\n(AST types)"]
73
+ mlConfig["@markuplint/ml-config\n(Config, RuleConfigValue)"]
74
+ mlSpec["@markuplint/ml-spec\n(HTML/ARIA specs)"]
75
+ htmlSpec["@markuplint/html-spec\n(Default spec data)"]
76
+ htmlParser["@markuplint/html-parser\n(Default parser)"]
77
+ parserUtils["@markuplint/parser-utils\n(ParserOptions)"]
78
+ selector["@markuplint/selector\n(CSS selector matching)"]
79
+ i18n["@markuplint/i18n\n(Locale, Translator)"]
80
+ shared["@markuplint/shared\n(Utilities)"]
81
+ configPresets["@markuplint/config-presets\n(Built-in presets)"]
82
+ end
83
+
84
+ subgraph pkg ["@markuplint/ml-core"]
85
+ subgraph mldom ["MLDOM"]
86
+ document["MLDocument"]
87
+ element["MLElement"]
88
+ node["MLNode / MLToken"]
89
+ ruleMapper["RuleMapper"]
90
+ end
91
+
92
+ subgraph mlRule ["MLRule"]
93
+ rule["MLRule"]
94
+ ruleContext["MLRuleContext"]
95
+ createRule["createRule()"]
96
+ end
97
+
98
+ subgraph engine ["Engine"]
99
+ core["MLCore"]
100
+ ruleset["Ruleset"]
101
+ convertRuleset["convertRuleset()"]
102
+ end
103
+
104
+ subgraph extras ["Extras"]
105
+ plugin["Plugin / createPlugin()"]
106
+ testUtils["Test Utilities"]
107
+ end
108
+ end
109
+
110
+ subgraph downstream ["Downstream"]
111
+ rules["@markuplint/rules\n(Built-in rules)"]
112
+ markuplint["markuplint\n(CLI & API)"]
113
+ end
114
+
115
+ upstream -->|"types, parsing, specs"| pkg
116
+ core --> document
117
+ core --> rule
118
+ document --> ruleMapper
119
+ rule --> ruleContext
120
+ pkg -->|"MLDOM, MLRule, MLCore"| downstream
121
+ ```
122
+
123
+ ## Linting Pipeline
124
+
125
+ The `MLCore.verify()` method orchestrates the full linting flow:
126
+
127
+ ```mermaid
128
+ flowchart LR
129
+ A["MLCore\nconstructor"]
130
+ B["_parse()\nParser → MLASTDocument"]
131
+ C["_createDocument()\nMLASTDocument → MLDocument"]
132
+ D["verify(fix?)\nFor each rule:"]
133
+ E["document.setRule(rule)\nRuleMapper maps config → nodes"]
134
+ F["rule.verify(document)\nMLRuleContext collects reports"]
135
+ G["Violations[]"]
136
+
137
+ A --> B --> C --> D --> E --> F --> G
138
+ ```
139
+
140
+ ### Step-by-step
141
+
142
+ 1. **Parse**: `MLCore` invokes the configured parser (`MLParser`) to produce an `MLASTDocument`
143
+ 2. **Create Document**: The AST is wrapped in an `MLDocument`, which builds the full MLDOM tree via `createNode()` factory. `RuleMapper` resolves rule configuration for every node
144
+ 3. **Verify**: For each `MLRule`, the engine calls `document.setRule(rule)` then `rule.verify(document)`. The rule walks relevant nodes via `document.walkOn()` and reports violations through `MLRuleContext`
145
+ 4. **Fix** (optional): When `fix=true`, rules may call `node.fix()` to modify token content. `document.toString(true)` produces the fixed source
146
+
147
+ ## MLDOM Class Hierarchy
148
+
149
+ ```
150
+ MLToken<A extends MLASTToken>
151
+ └── MLNode<T, O, A extends MLASTNode>
152
+ ├── MLAttr<T, O>
153
+ ├── MLCharacterData<T, O, A> (abstract)
154
+ │ ├── MLText<T, O>
155
+ │ └── MLComment<T, O>
156
+ ├── MLDocumentType<T, O>
157
+ ├── MLBlock<T, O>
158
+ ├── MLElementCloseTag<T, O>
159
+ └── MLParentNode<T, O, A> (abstract)
160
+ ├── MLElement<T, O>
161
+ ├── MLDocumentFragment<T, O>
162
+ └── MLDocument<T, O>
163
+ ```
164
+
165
+ ### Class Responsibilities
166
+
167
+ | Class | DOM Interface | Key Responsibility |
168
+ | -------------------- | ------------------ | ------------------------------------------------------------------------------------------- |
169
+ | `MLToken` | — | Base token with position tracking (`startLine`, `endCol`, `raw`, `fixed`), `fix()` method |
170
+ | `MLNode` | `Node` | Tree structure (`parentNode`, `childNodes`, `nextSibling`), rule storage, `is()` type guard |
171
+ | `MLAttr` | `Attr` | Attribute name/value tokens, `isDynamicValue`, `isDirective`, `valueType`, `tokenList` |
172
+ | `MLCharacterData` | `CharacterData` | Abstract base for text content nodes (`data`, `nodeValue`) |
173
+ | `MLText` | `Text` | Text nodes, `isWhitespace()`, `isRawTextElementContent()` |
174
+ | `MLComment` | `Comment` | Comment nodes with `textContent` |
175
+ | `MLDocumentType` | `DocumentType` | `<!DOCTYPE>` with `name`, `publicId`, `systemId` |
176
+ | `MLBlock` | — | Preprocessor-specific blocks (if/each/switch), `conditionalType`, `isTransparent` |
177
+ | `MLElementCloseTag` | — | Close tag paired with its open tag element |
178
+ | `MLParentNode` | `ParentNode` | `querySelector()`, `querySelectorAll()`, `children`, `childElementCount` |
179
+ | `MLElement` | `Element` | Attributes, selectors, namespaces, pretender context, `elementType`, `closeTag` |
180
+ | `MLDocumentFragment` | `DocumentFragment` | Fragment root node |
181
+ | `MLDocument` | `Document` | Root node, `nodeList`, `walkOn()`, `setRule()`, rule mapping, spec access |
182
+
183
+ ## MLDocument
184
+
185
+ `MLDocument` is the root of the MLDOM tree and the primary interface for rule execution.
186
+
187
+ ### Construction
188
+
189
+ The constructor receives an `MLASTDocument`, a `Ruleset`, and an `MLSchema` tuple. It:
190
+
191
+ 1. Builds the flat `nodeList` by traversing the AST and calling `createNode()` for each AST node
192
+ 2. Initializes `RuleMapper` to distribute rule configuration across nodes
193
+ 3. Sets up pretender contexts when pretender definitions are provided
194
+
195
+ ### Key Properties
196
+
197
+ | Property | Type | Description |
198
+ | ------------- | ----------------------- | --------------------------------------------------------- |
199
+ | `nodeList` | `ReadonlyArray<MLNode>` | Flat list of all nodes in document order |
200
+ | `specs` | `MLMLSpec` | HTML/ARIA specification data |
201
+ | `isFragment` | `boolean` | Whether the document is a fragment |
202
+ | `currentRule` | `MLRule \| null` | The rule currently being evaluated |
203
+ | `endTag` | `EndTagType` | End tag handling mode (`'xml'`, `'omittable'`, `'never'`) |
204
+
205
+ ### Key Methods
206
+
207
+ | Method | Description |
208
+ | --------------------------------- | ----------------------------------------------------------------------------------------------- |
209
+ | `walkOn(type, walker)` | Walks nodes of a given type (`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`) |
210
+ | `setRule(rule)` | Sets the current rule, used by `MLCore` during verification |
211
+ | `getTokenList()` | Returns all tokens for source reconstruction |
212
+ | `searchNodeByLocation(line, col)` | Finds the node at a given source position |
213
+ | `getAccessibilityProp(node)` | Computes ARIA accessibility properties |
214
+ | `toString(fixed?)` | Reconstructs source code, optionally with fixes applied |
215
+
216
+ ## MLElement
217
+
218
+ `MLElement` represents an HTML/SVG/MathML element with full attribute access and selector matching.
219
+
220
+ ### Key Properties
221
+
222
+ | Property | Type | Description |
223
+ | ------------------ | --------------------------- | -------------------------------------------- |
224
+ | `localName` | `string` | Lowercase tag name (for HTML) |
225
+ | `namespaceURI` | `NamespaceURI` | Element namespace (HTML, SVG, MathML) |
226
+ | `attributes` | `MLNamedNodeMap` | Named attribute collection |
227
+ | `elementType` | `ElementType` | `'html'`, `'web-component'`, or `'authored'` |
228
+ | `closeTag` | `MLElementCloseTag \| null` | Paired close tag |
229
+ | `pretenderContext` | `PretenderContext \| null` | Pretender mapping context |
230
+ | `isForeignElement` | `boolean` | `true` for SVG/MathML elements |
231
+ | `isOmitted` | `boolean` | `true` for implicitly inserted elements |
232
+
233
+ ### Key Methods
234
+
235
+ | Method | Description |
236
+ | ---------------------------- | ---------------------------------------------------------------- |
237
+ | `getAttribute(name)` | Returns attribute value or `null` |
238
+ | `getAttributeToken(name)` | Returns `MLAttr[]` for the named attribute |
239
+ | `hasAttribute(name)` | Checks attribute existence |
240
+ | `matches(selector)` | CSS selector matching |
241
+ | `matchMLSelector(selector)` | Extended markuplint selector matching (supports `RegexSelector`) |
242
+ | `querySelector(selector)` | Finds first matching descendant |
243
+ | `querySelectorAll(selector)` | Finds all matching descendants |
244
+
245
+ ## Rule System
246
+
247
+ ### MLRule
248
+
249
+ `MLRule<T, O>` encapsulates a linting rule with verification and optional fix logic.
250
+
251
+ | Property/Method | Description |
252
+ | --------------------------------- | ------------------------------------------------- |
253
+ | `name` | Rule identifier (e.g., `"attr-duplication"`) |
254
+ | `defaultSeverity` | Default severity level |
255
+ | `defaultValue` / `defaultOptions` | Default configuration |
256
+ | `verify(document, locale, fix)` | Executes the rule and returns violations |
257
+ | `optimizeOption(settings)` | Normalizes raw rule configuration into `RuleInfo` |
258
+
259
+ ### RuleSeed
260
+
261
+ The `RuleSeed<T, O>` type defines the rule implementation:
262
+
263
+ ```typescript
264
+ type RuleSeed<T, O> = {
265
+ meta?: {
266
+ category?: 'validation' | 'style' | 'naming-convention' | 'a11y' | 'maintainability';
267
+ };
268
+ defaultSeverity?: Severity;
269
+ defaultValue?: T;
270
+ defaultOptions?: O;
271
+ verify(context): void | Promise<void>;
272
+ fix?(context): void | Promise<void>;
273
+ };
274
+ ```
275
+
276
+ ### createRule
277
+
278
+ `createRule(seed)` is a factory function for type-safe rule seed creation. It returns the seed as-is, serving primarily as a type helper.
279
+
280
+ ### MLRuleContext
281
+
282
+ `MLRuleContext<T, O>` provides the execution context for rules:
283
+
284
+ - `document` — The current `MLDocument`
285
+ - `translate` / `t` — Locale-aware message translator
286
+ - `report(report)` — Reports a violation with node, message, and optional fix
287
+
288
+ The `provide()` method returns the context object passed to `RuleSeed.verify()` and `RuleSeed.fix()`.
289
+
290
+ ### Rule Configuration Resolution
291
+
292
+ Rules are configured at three levels, resolved by `RuleMapper`:
293
+
294
+ 1. **Global rules** (`rules`) — Apply to all nodes; lowest priority
295
+ 2. **Node rules** (`nodeRules`) — Apply to nodes matching a selector; medium priority
296
+ 3. **Child node rules** (`childNodeRules`) — Apply to children of nodes matching a selector; highest priority
297
+
298
+ When multiple rules match, `RuleMapper` resolves conflicts using CSS selector specificity. The mapping is computed once during `MLDocument` construction and stored on each `MLNode.rules`.
299
+
300
+ ### Rule Execution Flow
301
+
302
+ ```mermaid
303
+ flowchart TD
304
+ A["MLCore.verify()"] --> B["For each MLRule"]
305
+ B --> C["document.setRule(rule)"]
306
+ C --> D["rule.verify(document, locale, fix)"]
307
+ D --> E["rule.getRuleInfo(ruleset)\nResolve global config"]
308
+ E --> F["document.walkOn(type, walker)\nIterate matching nodes"]
309
+ F --> G["context.report()\nCollect violations per node"]
310
+ G --> H["Return Violation[]"]
311
+ ```
312
+
313
+ ## Pretender System
314
+
315
+ The pretender system allows components to be treated as semantic HTML elements during linting. This enables rules to validate custom components (e.g., `<MyButton>`) as if they were standard elements (e.g., `<button>`).
316
+
317
+ ### Configuration
318
+
319
+ Pretenders are defined in the markuplint config as an array of `Pretender` objects:
320
+
321
+ ```typescript
322
+ type Pretender = {
323
+ selector: string; // CSS selector matching the component
324
+ as: string; // HTML element to pretend as
325
+ aria?: PretenderARIA; // Optional ARIA overrides
326
+ };
327
+ ```
328
+
329
+ ### How It Works
330
+
331
+ 1. During `MLDocument` construction, pretender definitions are processed
332
+ 2. Each `MLElement` matching a pretender selector gets a `pretenderContext` with `type: 'pretender'`
333
+ 3. The target HTML element gets a `pretenderContext` with `type: 'origin'`
334
+ 4. Rules can access `element.pretenderContext` to check the semantic mapping
335
+ 5. Accessibility computations use pretender context for role/name resolution
336
+
337
+ ## Conditional Child Nodes
338
+
339
+ Template engines (Pug, EJS, Nunjucks, etc.) produce preprocessor-specific blocks represented by `MLBlock` nodes. These blocks can wrap child nodes conditionally:
340
+
341
+ | `conditionalType` | Template Construct | Description |
342
+ | ----------------- | ------------------ | -------------------------- |
343
+ | `'if:start'` | `{% if %}` | Start of conditional block |
344
+ | `'if:else'` | `{% else %}` | Alternative branch |
345
+ | `'if:end'` | `{% endif %}` | End of conditional block |
346
+ | `'each:start'` | `{% for %}` | Start of loop |
347
+ | `'each:end'` | `{% endfor %}` | End of loop |
348
+ | `'switch:start'` | `{% switch %}` | Start of switch |
349
+ | `'switch:case'` | `{% case %}` | Switch case |
350
+ | `'switch:end'` | `{% endswitch %}` | End of switch |
351
+
352
+ `MLNode.conditionalChildNodes()` returns an array of `NodeListOf` arrays — one per conditional branch — so rules can analyze each branch independently.
353
+
354
+ ## Plugin System
355
+
356
+ Plugins extend markuplint with custom rules and shared configurations.
357
+
358
+ ### Plugin Type
359
+
360
+ ```typescript
361
+ type Plugin = {
362
+ readonly name: string;
363
+ readonly rules?: Record<string, RuleSeed<any, any>>;
364
+ readonly configs?: Record<string, Config>;
365
+ };
366
+ ```
367
+
368
+ ### PluginCreator
369
+
370
+ For plugins that accept settings:
371
+
372
+ ```typescript
373
+ type PluginCreator<S> = {
374
+ readonly name: string;
375
+ create(setting: S): Omit<Plugin, 'name'>;
376
+ };
377
+ ```
378
+
379
+ `createPlugin(creator)` is a factory function for type-safe plugin creator definitions.
380
+
381
+ ## Test Utilities
382
+
383
+ The `test/` module provides helpers for rule testing:
384
+
385
+ | Function | Description |
386
+ | ------------------------------------------- | ----------------------------------------------- |
387
+ | `createTestDocument(sourceCode, options?)` | Parses source into an `MLDocument` for testing |
388
+ | `createTestElement(sourceCode, options?)` | Parses source and returns the first `MLElement` |
389
+ | `createTestNodeList(sourceCode, options?)` | Returns the flat node list from parsed source |
390
+ | `createTestTokenList(sourceCode, options?)` | Returns the flat token list from parsed source |
391
+ | `dummySchemas()` | Returns the default HTML spec as a schema tuple |
392
+
393
+ `CreateTestOptions` accepts `config`, `parser`, `specs`, and `pretenders` overrides.
394
+
395
+ ## External Dependencies
396
+
397
+ | Dependency | Purpose |
398
+ | ---------------------------- | ----------------------------------------------------------------- |
399
+ | `@markuplint/ml-ast` | AST type definitions (`MLASTDocument`, `MLASTNode`, etc.) |
400
+ | `@markuplint/ml-config` | Configuration types (`Config`, `RuleConfigValue`, `Pretender`) |
401
+ | `@markuplint/ml-spec` | HTML/ARIA specification access (`MLMLSpec`, role/attribute specs) |
402
+ | `@markuplint/html-spec` | Default HTML specification data |
403
+ | `@markuplint/html-parser` | Default HTML parser (used in test utilities) |
404
+ | `@markuplint/parser-utils` | Parser options and types |
405
+ | `@markuplint/selector` | CSS and extended selector matching |
406
+ | `@markuplint/i18n` | Internationalization (`LocaleSet`, `Translator`) |
407
+ | `@markuplint/shared` | Shared utilities |
408
+ | `@markuplint/config-presets` | Built-in configuration presets |
409
+ | `debug` | Debug logging |
410
+ | `is-plain-object` | Plain object type checking |
411
+ | `type-fest` | TypeScript utility types |
412
+
413
+ ## Integration Points
414
+
415
+ ```mermaid
416
+ flowchart TD
417
+ subgraph upstream ["Upstream"]
418
+ mlAst["@markuplint/ml-ast"]
419
+ mlConfig["@markuplint/ml-config"]
420
+ mlSpec["@markuplint/ml-spec"]
421
+ htmlSpec["@markuplint/html-spec"]
422
+ htmlParser["@markuplint/html-parser"]
423
+ parserUtils["@markuplint/parser-utils"]
424
+ selector["@markuplint/selector"]
425
+ i18n["@markuplint/i18n"]
426
+ shared["@markuplint/shared"]
427
+ configPresets["@markuplint/config-presets"]
428
+ end
429
+
430
+ subgraph pkg ["@markuplint/ml-core"]
431
+ core["MLCore Engine"]
432
+ end
433
+
434
+ subgraph downstream ["Downstream"]
435
+ rules["@markuplint/rules\n(Built-in rule implementations)"]
436
+ markuplint["markuplint\n(CLI, API, MLEngine)"]
437
+ end
438
+
439
+ upstream -->|"types, parsing, specs, i18n"| core
440
+ core -->|"MLDOM classes, MLRule,\ncreateRule, test utils"| rules
441
+ core -->|"MLCore, ViolationCollector,\nconvertRuleset, Plugin types"| markuplint
442
+ ```
443
+
444
+ ### Upstream
445
+
446
+ - **`@markuplint/ml-ast`** — AST types used to construct the MLDOM tree
447
+ - **`@markuplint/ml-config`** — Config and rule configuration types
448
+ - **`@markuplint/ml-spec`** — HTML/ARIA specification for element validation, role computation
449
+ - **`@markuplint/html-spec`** — Default spec data bundle
450
+ - **`@markuplint/html-parser`** — Default parser used in test utilities
451
+ - **`@markuplint/parser-utils`** — Parser option types
452
+ - **`@markuplint/selector`** — CSS selector engine for `querySelector`, `matches`, and `RegexSelector`
453
+ - **`@markuplint/i18n`** — Locale sets and translation for rule messages
454
+ - **`@markuplint/shared`** — Shared utility functions
455
+ - **`@markuplint/config-presets`** — Built-in configuration presets
456
+
457
+ ### Downstream
458
+
459
+ - **`@markuplint/rules`** — Imports MLDOM classes, `createRule`, `MLRuleContext`, and test utilities to implement built-in rules
460
+ - **`markuplint`** — Imports `MLCore`, `ViolationCollector`, `convertRuleset`, and plugin types to provide the CLI and API
461
+
462
+ ## Documentation Map
463
+
464
+ - [MLDOM Reference](docs/ml-dom.md) ([日本語](docs/ml-dom.ja.md)) — Class hierarchy, node properties, tree traversal
465
+ - [Rule System](docs/rule-system.md) ([日本語](docs/rule-system.ja.md)) — MLRule, RuleSeed, MLRuleContext, configuration resolution
466
+ - [Linting Pipeline](docs/linting-pipeline.md) ([日本語](docs/linting-pipeline.ja.md)) — MLCore engine, verify flow, pretender, plugin system
467
+ - [Maintenance Guide](docs/maintenance.md) ([日本語](docs/maintenance.ja.md)) — Commands, recipes, and troubleshooting
package/CHANGELOG.md CHANGED
@@ -3,13 +3,13 @@
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
- ## [4.13.2](https://github.com/markuplint/markuplint/compare/@markuplint/ml-core@4.13.1...@markuplint/ml-core@4.13.2) (2025-11-05)
6
+ ## [4.13.3](https://github.com/markuplint/markuplint/compare/@markuplint/ml-core@4.13.2...@markuplint/ml-core@4.13.3) (2026-02-10)
7
7
 
8
8
  **Note:** Version bump only for package @markuplint/ml-core
9
9
 
10
+ ## [4.13.2](https://github.com/markuplint/markuplint/compare/@markuplint/ml-core@4.13.1...@markuplint/ml-core@4.13.2) (2025-11-05)
10
11
 
11
-
12
-
12
+ **Note:** Version bump only for package @markuplint/ml-core
13
13
 
14
14
  ## [4.13.1](https://github.com/markuplint/markuplint/compare/@markuplint/ml-core@4.13.0...@markuplint/ml-core@4.13.1) (2025-08-24)
15
15
 
package/README.md CHANGED
@@ -5,6 +5,11 @@
5
5
  ## Documentation
6
6
 
7
7
  - [API Document](https://markuplint.dev/api-docs)
8
+ - [Architecture](ARCHITECTURE.md) ([日本語](ARCHITECTURE.ja.md)) — Package overview, MLDOM hierarchy, rule system, and linting pipeline
9
+ - [MLDOM Reference](docs/ml-dom.md) ([日本語](docs/ml-dom.ja.md)) — Class hierarchy, node properties, tree traversal
10
+ - [Rule System](docs/rule-system.md) ([日本語](docs/rule-system.ja.md)) — MLRule, RuleSeed, MLRuleContext, configuration resolution
11
+ - [Linting Pipeline](docs/linting-pipeline.md) ([日本語](docs/linting-pipeline.ja.md)) — MLCore engine, verify flow, pretender, plugin system
12
+ - [Maintenance Guide](docs/maintenance.md) ([日本語](docs/maintenance.ja.md)) — Commands, recipes, and troubleshooting
8
13
 
9
14
  ## Install
10
15
 
package/SKILL.md ADDED
@@ -0,0 +1,61 @@
1
+ ---
2
+ description: Maintenance tasks for @markuplint/ml-core — the core linting engine with MLDOM, MLRule, and MLCore subsystems
3
+ globs:
4
+ - packages/@markuplint/ml-core/src/**/*.ts
5
+ - packages/@markuplint/ml-core/tsconfig*.json
6
+ - packages/@markuplint/ml-core/package.json
7
+ alwaysApply: false
8
+ ---
9
+
10
+ # @markuplint/ml-core Maintenance
11
+
12
+ You are maintaining `@markuplint/ml-core`, the core linting engine of markuplint.
13
+
14
+ ## Architecture
15
+
16
+ Read [ARCHITECTURE.md](ARCHITECTURE.md) for the full package overview, MLDOM class hierarchy, rule system, and linting pipeline.
17
+
18
+ ## Tasks
19
+
20
+ ### add-node-property
21
+
22
+ Add a property to an MLDOM node class.
23
+
24
+ 1. Identify the target class in `src/ml-dom/node/` (e.g., `element.ts`, `node.ts`, `document.ts`)
25
+ 2. Add the property as a getter or readonly field
26
+ 3. Use `this.#astNode` for AST-derived data, `this.ownerMLDocument.specs` for spec data
27
+ 4. Update type definitions in `src/ml-dom/node/types.ts` if needed
28
+ 5. Build: `yarn build --scope @markuplint/ml-core`
29
+ 6. Check downstream impact on `@markuplint/rules`
30
+
31
+ ### create-rule
32
+
33
+ Create a new linting rule using the `createRule` API.
34
+
35
+ 1. Rules are implemented in `@markuplint/rules`, not in this package
36
+ 2. Use `createRule()` from `src/ml-rule/create-rule.ts` for the rule seed
37
+ 3. Define `verify()` and optionally `fix()` in the `RuleSeed`
38
+ 4. Use `document.walkOn(type, walker)` to iterate target nodes
39
+ 5. Use `context.report()` to report violations
40
+ 6. Test with `createTestDocument()` and `createTestElement()` from `src/test/index.ts`
41
+
42
+ ### modify-rule-mapping
43
+
44
+ Change how rules are mapped to nodes via RuleMapper.
45
+
46
+ 1. Open `src/ml-dom/node/rule-mapper.ts`
47
+ 2. `apply()` iterates through global rules, nodeRules, and childNodeRules
48
+ 3. `MappingLayer` includes `from`, `specificity`, and the resolved `rule`
49
+ 4. `set()` assigns layers to nodes, resolving conflicts by CSS selector specificity
50
+ 5. Build and verify: `yarn build --scope @markuplint/ml-core`
51
+ 6. Test rule resolution with `createTestDocument` using nodeRules/childNodeRules config
52
+
53
+ ### update-pretender
54
+
55
+ Update the pretender system for component-to-HTML element mapping.
56
+
57
+ 1. Pretender initialization is in `MLDocument` constructor (`src/ml-dom/node/document.ts`)
58
+ 2. `PretenderContext` types are in `src/ml-dom/node/types.ts`
59
+ 3. Elements matching pretender selectors get `pretenderContext` with `type: 'pretender'`
60
+ 4. Accessibility name computation uses pretender context (`src/ml-dom/helper/accname.ts`)
61
+ 5. Test with `createTestDocument(source, { pretenders: [...] })`