@markuplint/ml-core 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 +37 -0
- package/README.md +0 -5
- package/lib/cursor-offset.js +0 -3
- package/lib/fix-applier.js +3 -9
- package/lib/ml-core.d.ts +18 -2
- package/lib/ml-core.js +223 -61
- package/lib/ml-dom/helper/accname.d.ts +0 -8
- package/lib/ml-dom/helper/accname.js +7 -10
- package/lib/ml-dom/node/attr.js +3 -1
- package/lib/ml-dom/node/block.d.ts +6 -0
- package/lib/ml-dom/node/block.js +6 -0
- package/lib/ml-dom/node/child-node.d.ts +0 -9
- package/lib/ml-dom/node/child-node.js +0 -9
- package/lib/ml-dom/node/document.d.ts +20 -1
- package/lib/ml-dom/node/document.js +24 -13
- package/lib/ml-dom/node/element-close-tag.d.ts +12 -0
- package/lib/ml-dom/node/element-close-tag.js +12 -0
- package/lib/ml-dom/node/element.d.ts +22 -0
- package/lib/ml-dom/node/element.js +37 -11
- package/lib/ml-dom/node/node-store.d.ts +0 -3
- package/lib/ml-dom/node/node-store.js +0 -3
- package/lib/ml-dom/node/node.d.ts +34 -1
- package/lib/ml-dom/node/node.js +34 -16
- package/lib/ml-dom/node/parent-node.js +0 -6
- package/lib/ml-dom/node/rule-mapper.d.ts +8 -0
- package/lib/ml-dom/node/rule-mapper.js +8 -0
- package/lib/ml-rule/ml-rule.d.ts +19 -0
- package/lib/ml-rule/ml-rule.js +38 -7
- package/lib/ml-rule/types.d.ts +110 -1
- package/lib/ml-rule/types.js +28 -1
- package/lib/ruleset/index.d.ts +2 -1
- package/lib/ruleset/index.js +2 -1
- package/lib/test/index.js +1 -1
- package/lib/virtual-rule.d.ts +10 -0
- package/lib/virtual-rule.js +1 -24
- package/package.json +14 -14
- package/ARCHITECTURE.ja.md +0 -676
- package/ARCHITECTURE.md +0 -726
- package/SKILL.md +0 -61
- package/docs/linting-pipeline.ja.md +0 -307
- package/docs/linting-pipeline.md +0 -307
- package/docs/maintenance.ja.md +0 -210
- package/docs/maintenance.md +0 -210
- package/docs/ml-dom/attr.ja.md +0 -103
- package/docs/ml-dom/attr.md +0 -103
- package/docs/ml-dom/block.ja.md +0 -272
- package/docs/ml-dom/block.md +0 -272
- package/docs/ml-dom/document.ja.md +0 -134
- package/docs/ml-dom/document.md +0 -134
- package/docs/ml-dom/element.ja.md +0 -161
- package/docs/ml-dom/element.md +0 -161
- package/docs/ml-dom/helpers.ja.md +0 -203
- package/docs/ml-dom/helpers.md +0 -203
- package/docs/ml-dom/node.ja.md +0 -199
- package/docs/ml-dom/node.md +0 -199
- package/docs/ml-dom/others.ja.md +0 -120
- package/docs/ml-dom/others.md +0 -120
- package/docs/ml-dom/overview.ja.md +0 -102
- package/docs/ml-dom/overview.md +0 -102
- package/docs/ml-dom/pretender.ja.md +0 -269
- package/docs/ml-dom/pretender.md +0 -269
- package/docs/ml-dom/rule-mapping.ja.md +0 -371
- package/docs/ml-dom/rule-mapping.md +0 -371
- package/docs/ml-dom.ja.md +0 -18
- package/docs/ml-dom.md +0 -18
- package/docs/rule-system.ja.md +0 -287
- package/docs/rule-system.md +0 -287
package/ARCHITECTURE.md
DELETED
|
@@ -1,726 +0,0 @@
|
|
|
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
|
-
│ ├── token/
|
|
36
|
-
│ │ └── token.ts — MLToken (base positional token)
|
|
37
|
-
│ ├── helper/
|
|
38
|
-
│ │ ├── accname.ts — Accessible name computation
|
|
39
|
-
│ │ ├── create-node.ts — AST → MLDOM node factory
|
|
40
|
-
│ │ ├── walkers.ts — Tree traversal (sync/async walkers)
|
|
41
|
-
│ │ ├── get-indent.ts — Indentation analysis
|
|
42
|
-
│ │ └── debug.ts — Debug map generation
|
|
43
|
-
│ └── manipulations/
|
|
44
|
-
│ ├── child-node-methods.ts — ChildNode interface stubs
|
|
45
|
-
│ └── get-children.ts — Element children extraction
|
|
46
|
-
├── virtual-rule.ts — Named nodeRule expansion (expandNamedNodeRules)
|
|
47
|
-
├── virtual-rule.spec.ts — Virtual rule unit tests
|
|
48
|
-
├── ml-rule/
|
|
49
|
-
│ ├── ml-rule.ts — MLRule class (rule execution)
|
|
50
|
-
│ ├── ml-rule-context.ts — MLRuleContext (report collection)
|
|
51
|
-
│ ├── rule-fixer.ts — RuleFixer (TextEdit builder for fix callbacks)
|
|
52
|
-
│ ├── create-rule.ts — createRule factory
|
|
53
|
-
│ ├── create-test-rule.ts — Test rule factory
|
|
54
|
-
│ └── types.ts — RuleSeed, Checker types
|
|
55
|
-
├── cursor-offset.ts — computeCursorOffset (cursor remapping after edits)
|
|
56
|
-
├── fix-applier.ts — applyFixes (overlap-aware TextEdit applicator)
|
|
57
|
-
├── ruleset/
|
|
58
|
-
│ └── index.ts — Ruleset class (rules + nodeRules + childNodeRules)
|
|
59
|
-
├── plugin/
|
|
60
|
-
│ ├── plugin.ts — createPlugin factory
|
|
61
|
-
│ ├── types.ts — Plugin, PluginCreator types
|
|
62
|
-
│ └── index.ts — Plugin exports
|
|
63
|
-
├── test/
|
|
64
|
-
│ └── index.ts — createTestDocument, createTestElement, dummySchemas
|
|
65
|
-
└── utils/
|
|
66
|
-
├── index.ts — Utility exports
|
|
67
|
-
├── get-location-from-chars.ts — Character location resolver
|
|
68
|
-
└── string-splice.ts — String splice helper
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Architecture Diagram
|
|
72
|
-
|
|
73
|
-
```mermaid
|
|
74
|
-
flowchart TD
|
|
75
|
-
subgraph upstream ["Upstream Dependencies"]
|
|
76
|
-
mlAst["@markuplint/ml-ast\n(AST types)"]
|
|
77
|
-
mlConfig["@markuplint/ml-config\n(Config, RuleConfigValue)"]
|
|
78
|
-
mlSpec["@markuplint/ml-spec\n(HTML/ARIA specs)"]
|
|
79
|
-
htmlSpec["@markuplint/html-spec\n(Default spec data)"]
|
|
80
|
-
htmlParser["@markuplint/html-parser\n(Default parser)"]
|
|
81
|
-
parserUtils["@markuplint/parser-utils\n(ParserOptions)"]
|
|
82
|
-
selector["@markuplint/selector\n(CSS selector matching)"]
|
|
83
|
-
i18n["@markuplint/i18n\n(Locale, Translator)"]
|
|
84
|
-
shared["@markuplint/shared\n(Utilities)"]
|
|
85
|
-
configPresets["@markuplint/config-presets\n(Built-in presets)"]
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
subgraph pkg ["@markuplint/ml-core"]
|
|
89
|
-
subgraph mldom ["MLDOM"]
|
|
90
|
-
document["MLDocument"]
|
|
91
|
-
element["MLElement"]
|
|
92
|
-
node["MLNode / MLToken"]
|
|
93
|
-
ruleMapper["RuleMapper"]
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
subgraph mlRule ["MLRule"]
|
|
97
|
-
rule["MLRule"]
|
|
98
|
-
ruleContext["MLRuleContext"]
|
|
99
|
-
createRule["createRule()"]
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
subgraph engine ["Engine"]
|
|
103
|
-
core["MLCore"]
|
|
104
|
-
ruleset["Ruleset"]
|
|
105
|
-
convertRuleset["convertRuleset()"]
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
subgraph extras ["Extras"]
|
|
109
|
-
plugin["Plugin / createPlugin()"]
|
|
110
|
-
testUtils["Test Utilities"]
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
subgraph downstream ["Downstream"]
|
|
115
|
-
rules["@markuplint/rules\n(Built-in rules)"]
|
|
116
|
-
markuplint["markuplint\n(CLI & API)"]
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
upstream -->|"types, parsing, specs"| pkg
|
|
120
|
-
core --> document
|
|
121
|
-
core --> rule
|
|
122
|
-
document --> ruleMapper
|
|
123
|
-
rule --> ruleContext
|
|
124
|
-
pkg -->|"MLDOM, MLRule, MLCore"| downstream
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Linting Pipeline
|
|
128
|
-
|
|
129
|
-
The `MLCore.verify()` method orchestrates the full linting flow:
|
|
130
|
-
|
|
131
|
-
```mermaid
|
|
132
|
-
flowchart LR
|
|
133
|
-
A["MLCore\nconstructor"]
|
|
134
|
-
B["_parse()\nParser → MLASTDocument"]
|
|
135
|
-
C["_createDocument()\nMLASTDocument → MLDocument"]
|
|
136
|
-
D["verify(fix? | options?)\nFor each rule:"]
|
|
137
|
-
E["document.setRule(rule)\nRuleMapper maps config → nodes"]
|
|
138
|
-
F["rule.verify(document)\nMLRuleContext collects reports"]
|
|
139
|
-
G["Violations[]"]
|
|
140
|
-
|
|
141
|
-
A --> B --> C --> D --> E --> F --> G
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### Step-by-step
|
|
145
|
-
|
|
146
|
-
1. **Parse**: `MLCore` invokes the configured parser (`MLParser`) to produce an `MLASTDocument`
|
|
147
|
-
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
|
|
148
|
-
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`. Rules may attach inline `fix` callbacks to reports that return `TextEdit` objects
|
|
149
|
-
4. **Fix** (optional): When `fix=true`, fix callbacks on reports are executed via `RuleFixer` to produce `TextEdit[]`. `FixApplier.applyFixes(sourceCode, fixes)` applies all edits to the source text with overlap detection. When fixes require multiple passes, `_multiPassFix()` orchestrates re-parsing and re-verification, returning a `FixSummary` with pass count, applied/skipped totals, and first-pass edits for cursor offset computation
|
|
150
|
-
|
|
151
|
-
## MLDOM Class Hierarchy
|
|
152
|
-
|
|
153
|
-
```
|
|
154
|
-
MLToken<A extends MLASTToken>
|
|
155
|
-
└── MLNode<T, O, A extends MLASTNode>
|
|
156
|
-
├── MLAttr<T, O>
|
|
157
|
-
├── MLCharacterData<T, O, A> (abstract)
|
|
158
|
-
│ ├── MLText<T, O>
|
|
159
|
-
│ └── MLComment<T, O>
|
|
160
|
-
├── MLDocumentType<T, O>
|
|
161
|
-
├── MLBlock<T, O>
|
|
162
|
-
├── MLElementCloseTag<T, O>
|
|
163
|
-
└── MLParentNode<T, O, A> (abstract)
|
|
164
|
-
├── MLElement<T, O>
|
|
165
|
-
├── MLDocumentFragment<T, O>
|
|
166
|
-
└── MLDocument<T, O>
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Class Responsibilities
|
|
170
|
-
|
|
171
|
-
| Class | DOM Interface | Key Responsibility |
|
|
172
|
-
| -------------------- | ------------------ | ------------------------------------------------------------------------------------------- |
|
|
173
|
-
| `MLToken` | — | Base token with position tracking (`startLine`, `endCol`, `raw`, `fixed`), `fix()` method |
|
|
174
|
-
| `MLNode` | `Node` | Tree structure (`parentNode`, `childNodes`, `nextSibling`), rule storage, `is()` type guard |
|
|
175
|
-
| `MLAttr` | `Attr` | Attribute name/value tokens, `isDynamicValue`, `isDirective`, `valueType`, `tokenList` |
|
|
176
|
-
| `MLCharacterData` | `CharacterData` | Abstract base for text content nodes (`data`, `nodeValue`) |
|
|
177
|
-
| `MLText` | `Text` | Text nodes, `isWhitespace()`, `isRawTextElementContent()` |
|
|
178
|
-
| `MLComment` | `Comment` | Comment nodes with `textContent` |
|
|
179
|
-
| `MLDocumentType` | `DocumentType` | `<!DOCTYPE>` with `name`, `publicId`, `systemId` |
|
|
180
|
-
| `MLBlock` | — | Preprocessor-specific blocks (if/each/switch), `blockBehavior`, `isTransparent` |
|
|
181
|
-
| `MLElementCloseTag` | — | Close tag paired with its open tag element |
|
|
182
|
-
| `MLParentNode` | `ParentNode` | `querySelector()`, `querySelectorAll()`, `children`, `childElementCount` |
|
|
183
|
-
| `MLElement` | `Element` | Attributes, selectors, namespaces, pretender context, `elementType`, `closeTag` |
|
|
184
|
-
| `MLDocumentFragment` | `DocumentFragment` | Fragment root node |
|
|
185
|
-
| `MLDocument` | `Document` | Root node, `nodeList`, `walkOn()`, `setRule()`, rule mapping, spec access |
|
|
186
|
-
|
|
187
|
-
## MLDocument
|
|
188
|
-
|
|
189
|
-
`MLDocument` is the root of the MLDOM tree and the primary interface for rule execution.
|
|
190
|
-
|
|
191
|
-
### Construction
|
|
192
|
-
|
|
193
|
-
The constructor receives an `MLASTDocument`, a `Ruleset`, and an `MLSchema` tuple. It:
|
|
194
|
-
|
|
195
|
-
1. Builds the flat `nodeList` by traversing the AST and calling `createNode()` for each AST node
|
|
196
|
-
2. Initializes `RuleMapper` to distribute rule configuration across nodes
|
|
197
|
-
3. Sets up pretender contexts when pretender definitions are provided
|
|
198
|
-
|
|
199
|
-
### Key Properties
|
|
200
|
-
|
|
201
|
-
| Property | Type | Description |
|
|
202
|
-
| ------------- | ----------------------- | --------------------------------------------------------- |
|
|
203
|
-
| `nodeList` | `ReadonlyArray<MLNode>` | Flat list of all nodes in document order |
|
|
204
|
-
| `specs` | `MLMLSpec` | HTML/ARIA specification data |
|
|
205
|
-
| `isFragment` | `boolean` | Whether the document is a fragment |
|
|
206
|
-
| `currentRule` | `MLRule \| null` | The rule currently being evaluated |
|
|
207
|
-
| `endTag` | `EndTagType` | End tag handling mode (`'xml'`, `'omittable'`, `'never'`) |
|
|
208
|
-
|
|
209
|
-
### Key Methods
|
|
210
|
-
|
|
211
|
-
| Method | Description |
|
|
212
|
-
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
213
|
-
| `walkOn(type, walker)` | Walks nodes of a given type (`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`) |
|
|
214
|
-
| `setRule(rule)` | Sets the current rule, used by `MLCore` during verification |
|
|
215
|
-
| `searchNodeByLocation(line, col)` | Finds the node at a given source position |
|
|
216
|
-
| `getAccessibilityProp(node)` | Computes ARIA accessibility properties (delegates to `MLElement.getAccessibleName()` for cached accessible name) |
|
|
217
|
-
| `toString()` | Returns the raw source code of the document |
|
|
218
|
-
|
|
219
|
-
## MLElement
|
|
220
|
-
|
|
221
|
-
`MLElement` represents an HTML/SVG/MathML element with full attribute access and selector matching.
|
|
222
|
-
|
|
223
|
-
### Key Properties
|
|
224
|
-
|
|
225
|
-
| Property | Type | Description |
|
|
226
|
-
| ------------------ | --------------------------- | -------------------------------------------- |
|
|
227
|
-
| `localName` | `string` | Lowercase tag name (for HTML) |
|
|
228
|
-
| `namespaceURI` | `NamespaceURI` | Element namespace (HTML, SVG, MathML) |
|
|
229
|
-
| `attributes` | `MLNamedNodeMap` | Named attribute collection |
|
|
230
|
-
| `elementType` | `ElementType` | `'html'`, `'web-component'`, or `'authored'` |
|
|
231
|
-
| `closeTag` | `MLElementCloseTag \| null` | Paired close tag |
|
|
232
|
-
| `pretenderContext` | `PretenderContext \| null` | Pretender mapping context |
|
|
233
|
-
| `isForeignElement` | `boolean` | `true` for SVG/MathML elements |
|
|
234
|
-
| `isOmitted` | `boolean` | `true` for implicitly inserted elements |
|
|
235
|
-
|
|
236
|
-
### Key Methods
|
|
237
|
-
|
|
238
|
-
| Method | Description |
|
|
239
|
-
| ---------------------------- | ---------------------------------------------------------------- |
|
|
240
|
-
| `getAttribute(name)` | Returns attribute value or `null` |
|
|
241
|
-
| `getAttributeToken(name)` | Returns `MLAttr[]` for the named attribute |
|
|
242
|
-
| `hasAttribute(name)` | Checks attribute existence |
|
|
243
|
-
| `getAccessibleName(version)` | Cached accessible name computation (memoized per ARIA version) |
|
|
244
|
-
| `matches(selector)` | CSS selector matching |
|
|
245
|
-
| `matchMLSelector(selector)` | Extended markuplint selector matching (supports `RegexSelector`) |
|
|
246
|
-
| `querySelector(selector)` | Finds first matching descendant |
|
|
247
|
-
| `querySelectorAll(selector)` | Finds all matching descendants |
|
|
248
|
-
|
|
249
|
-
## Rule System
|
|
250
|
-
|
|
251
|
-
### MLRule
|
|
252
|
-
|
|
253
|
-
`MLRule<T, O>` encapsulates a linting rule with verification and optional fix logic.
|
|
254
|
-
|
|
255
|
-
| Property/Method | Description |
|
|
256
|
-
| --------------------------------- | --------------------------------------------------------------------------- |
|
|
257
|
-
| `name` | Rule identifier (e.g., `"attr-duplication"`) |
|
|
258
|
-
| `defaultSeverity` | Default severity level |
|
|
259
|
-
| `defaultValue` / `defaultOptions` | Default configuration |
|
|
260
|
-
| `baseRuleId` | For virtual rules: the base rule's name (e.g., `"required-attr"`) |
|
|
261
|
-
| `groupName` | For multi-entry virtual rules: group name for batch disable |
|
|
262
|
-
| `specConformance` | For virtual rules: `'normative'` or `'non-normative'` (from named nodeRule) |
|
|
263
|
-
| `verify(document, locale, fix)` | Executes the rule and returns violations |
|
|
264
|
-
| `createAlias(name, options?)` | Creates a virtual rule that reuses this rule's verify/fix logic |
|
|
265
|
-
| `optimizeOption(settings)` | Normalizes raw rule configuration into `RuleInfo` |
|
|
266
|
-
|
|
267
|
-
### RuleSeed
|
|
268
|
-
|
|
269
|
-
The `RuleSeed<T, O>` type defines the rule implementation:
|
|
270
|
-
|
|
271
|
-
```typescript
|
|
272
|
-
type RuleSeed<T, O> = {
|
|
273
|
-
meta?: {
|
|
274
|
-
category?: 'validation' | 'style' | 'naming-convention' | 'a11y' | 'maintainability';
|
|
275
|
-
};
|
|
276
|
-
defaultSeverity?: Severity;
|
|
277
|
-
defaultValue?: T;
|
|
278
|
-
defaultOptions?: O;
|
|
279
|
-
verify(context): void | Promise<void>;
|
|
280
|
-
fix?(context): void | Promise<void>;
|
|
281
|
-
};
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### createRule
|
|
285
|
-
|
|
286
|
-
`createRule(seed)` is a factory function for type-safe rule seed creation. It returns the seed as-is, serving primarily as a type helper.
|
|
287
|
-
|
|
288
|
-
### MLRuleContext
|
|
289
|
-
|
|
290
|
-
`MLRuleContext<T, O>` provides the execution context for rules:
|
|
291
|
-
|
|
292
|
-
- `document` — The current `MLDocument`
|
|
293
|
-
- `translate` / `t` — Locale-aware message translator
|
|
294
|
-
- `report(report)` — Reports a violation with node, message, and optional fix
|
|
295
|
-
|
|
296
|
-
The `provide()` method returns the context object passed to `RuleSeed.verify()`. Auto-fix logic is provided as an inline `fix` callback on individual `report()` calls, not as a separate lifecycle method.
|
|
297
|
-
|
|
298
|
-
### Rule Configuration Resolution
|
|
299
|
-
|
|
300
|
-
Rules are configured at three levels, resolved by `RuleMapper`:
|
|
301
|
-
|
|
302
|
-
1. **Global rules** (`rules`) — Apply to all nodes; lowest priority
|
|
303
|
-
2. **Node rules** (`nodeRules`) — Apply to nodes matching a selector; medium priority
|
|
304
|
-
3. **Child node rules** (`childNodeRules`) — Apply to children of nodes matching a selector; highest priority
|
|
305
|
-
|
|
306
|
-
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`.
|
|
307
|
-
|
|
308
|
-
### Rule Execution Flow
|
|
309
|
-
|
|
310
|
-
```mermaid
|
|
311
|
-
flowchart TD
|
|
312
|
-
A["MLCore.verify()"] --> B["For each MLRule"]
|
|
313
|
-
B --> C["document.setRule(rule)"]
|
|
314
|
-
C --> D["rule.verify(document, locale, fix)"]
|
|
315
|
-
D --> E["rule.getRuleInfo(ruleset)\nResolve global config"]
|
|
316
|
-
E --> F["document.walkOn(type, walker)\nIterate matching nodes"]
|
|
317
|
-
F --> G["context.report()\nCollect violations per node"]
|
|
318
|
-
G --> H["Return Violation[]"]
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Virtual Rule System
|
|
322
|
-
|
|
323
|
-
Source: `src/virtual-rule.ts`
|
|
324
|
-
|
|
325
|
-
> **Terminology policy**: "Virtual rule" is an **internal implementation term** for contributors only. User-facing documentation (website, migration guides, README) must use **"named rule"** instead. From a config user's perspective, there are only two concepts: a **base rule** (e.g., `required-attr`) and a **named rule** (e.g., `a11y/html-lang`). The internal mechanics of `MLRule` aliasing should not be exposed.
|
|
326
|
-
|
|
327
|
-
Virtual rules are independent `MLRule` instances created from **named nodeRules** — nodeRule entries with a `name` property containing `/` (e.g., `"a11y/html-lang"`). This enables per-check control: each virtual rule can be independently enabled/disabled via `rules["alias/name"]: false`.
|
|
328
|
-
|
|
329
|
-
#### Named NodeRule Expansion
|
|
330
|
-
|
|
331
|
-
`expandNamedNodeRules()` converts named nodeRules (and childNodeRules) into virtual rules during `MLCore` construction:
|
|
332
|
-
|
|
333
|
-
```
|
|
334
|
-
Named nodeRule (config) Virtual MLRule (runtime)
|
|
335
|
-
┌─────────────────────────┐ ┌──────────────────────────┐
|
|
336
|
-
│ name: "a11y/html-lang" │ │ name: "a11y/html-lang" │
|
|
337
|
-
│ specConformance: "norm."│ ──────► │ baseRuleId: "required-attr" │
|
|
338
|
-
│ selector: ":where(html)"│ │ specConformance: metadata│
|
|
339
|
-
│ rules: │ │ verify/fix: from base │
|
|
340
|
-
│ required-attr: [lang] │ └──────────────────────────┘
|
|
341
|
-
└─────────────────────────┘
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
Key behaviors:
|
|
345
|
-
|
|
346
|
-
- **False entry separation**: `false` entries in `rules` are automatically separated into unnamed nodeRules, preserving their semantics as base-rule specificity overrides
|
|
347
|
-
- **Multi-entry support**: Named nodeRules with 2+ non-false entries create derived names (`name/baseRuleName`) with a `groupName` for group disable
|
|
348
|
-
- **Metadata**: `specConformance` is attached to the virtual rule as metadata for downstream tools and reporting
|
|
349
|
-
- **Hot-reload**: Pre-expansion nodeRules are preserved in `#originalNodeRules` / `#originalChildNodeRules` so `update()` can re-expand them
|
|
350
|
-
|
|
351
|
-
#### Why `specConformance` Is Restricted to Named NodeRules
|
|
352
|
-
|
|
353
|
-
`specConformance` is intentionally available **only on named nodeRules** (in presets), not on regular built-in rules. The design rationale:
|
|
354
|
-
|
|
355
|
-
1. **Built-in rules already have correct default severity.** Rules like `permitted-contents` or `required-attr` are inherently normative (they enforce WHATWG MUST requirements), and their `defaultSeverity` is already set to `'error'`. There is no need for a separate `specConformance` flag — the severity is baked in.
|
|
356
|
-
|
|
357
|
-
2. **Named nodeRules are preset-authored spec interpretations.** When a preset like `preset.html-standard.jsonc` creates a named nodeRule `"html-standard/head-charset-utf8"`, the preset author is expressing a specific spec requirement as a check. `specConformance` lets the author declare the RFC 2119 keyword strength of that requirement, so downstream tools and reports can identify which violations originate from spec requirements and at what normative level.
|
|
358
|
-
|
|
359
|
-
3. **Users should not set `specConformance` on their own rules.** A user-defined nodeRule for a custom component (e.g., validating `<MyComponent>` props) is not a spec conformance check — it is a project convention. Allowing `specConformance` on arbitrary user config would blur the distinction between "the HTML spec requires this" and "our team prefers this". The `name` property (which requires `/`) serves as a gatekeeper: only named nodeRules can carry `specConformance`, and named nodeRules are designed for preset authors who understand the spec.
|
|
360
|
-
|
|
361
|
-
In summary: `specConformance` is a **preset-level annotation** that provides metadata about which spec requirements a check enforces. Built-in rules handle their own severity via `defaultSeverity`. User-defined rules express severity directly via the `severity` field in rule config.
|
|
362
|
-
|
|
363
|
-
#### Virtual Rule Disable
|
|
364
|
-
|
|
365
|
-
Virtual rules can be disabled at three levels in the `rules` config:
|
|
366
|
-
|
|
367
|
-
1. **Exact name**: `rules["a11y/html-lang"]: false`
|
|
368
|
-
2. **Group disable**: `rules["custom/multi"]: false` (for multi-entry named nodeRules)
|
|
369
|
-
3. **Namespace wildcard**: `rules["a11y/*"]: false` (disables all virtual rules starting with `a11y/`)
|
|
370
|
-
|
|
371
|
-
## Autofix System
|
|
372
|
-
|
|
373
|
-
The autofix system allows rules to provide automatic fixes for violations. It operates through three components: **RuleFixer** (TextEdit builder), **fix callbacks** (rule-authored logic), and **FixApplier** (edit application engine).
|
|
374
|
-
|
|
375
|
-
### Autofix Data Flow
|
|
376
|
-
|
|
377
|
-
```mermaid
|
|
378
|
-
flowchart LR
|
|
379
|
-
subgraph RulePhase ["Rule Phase"]
|
|
380
|
-
report["context.report({\n message,\n scope,\n fix: callback\n})"]
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
subgraph FixPhase ["Fix Callback Execution"]
|
|
384
|
-
callback["fix(fixer) → TextEdit[]"]
|
|
385
|
-
fixer["RuleFixer\n(shared instance)"]
|
|
386
|
-
callback --> fixer
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
subgraph ApplyPhase ["Apply Phase"]
|
|
390
|
-
fixdata["FixData\n{ edits: TextEdit[] }"]
|
|
391
|
-
applier["applyFixes(\n sourceCode,\n allFixes\n)"]
|
|
392
|
-
output["fixedCode"]
|
|
393
|
-
fixdata --> applier --> output
|
|
394
|
-
end
|
|
395
|
-
|
|
396
|
-
report --> callback
|
|
397
|
-
fixer --> fixdata
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### How Fix Callbacks Work
|
|
401
|
-
|
|
402
|
-
Rules attach an optional `fix` callback to each `report()` call. The callback is **not** executed during rule verification — it is stored and only invoked when `fix=true` is passed to `MLCore.verify()`.
|
|
403
|
-
|
|
404
|
-
```mermaid
|
|
405
|
-
sequenceDiagram
|
|
406
|
-
participant Rule as Rule (verify)
|
|
407
|
-
participant Ctx as MLRuleContext
|
|
408
|
-
participant MLR as MLRule.verify()
|
|
409
|
-
participant Fixer as RuleFixer
|
|
410
|
-
participant Core as MLCore.verify()
|
|
411
|
-
participant FA as applyFixes()
|
|
412
|
-
|
|
413
|
-
Rule->>Ctx: report({ scope, message, fix })
|
|
414
|
-
Note over Ctx: Stores report with fix callback
|
|
415
|
-
|
|
416
|
-
MLR->>Ctx: context.reports
|
|
417
|
-
loop Each report with fix callback
|
|
418
|
-
MLR->>Fixer: report.fix(sharedFixer)
|
|
419
|
-
Fixer-->>MLR: TextEdit | TextEdit[]
|
|
420
|
-
MLR->>MLR: Wrap as FixData { edits }
|
|
421
|
-
end
|
|
422
|
-
MLR-->>Core: Violation[] (with FixData)
|
|
423
|
-
|
|
424
|
-
Core->>Core: Collect all FixData from violations
|
|
425
|
-
Core->>FA: applyFixes(sourceCode, allFixes)
|
|
426
|
-
FA-->>Core: FixResult { output, applied, skipped, appliedEdits }
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
### RuleFixer API
|
|
430
|
-
|
|
431
|
-
`RuleFixer` implements `IRuleFixer` (defined in `@markuplint/ml-config`). It is a **stateless** helper — a single instance is shared across all rules. Each method builds a `TextEdit` object describing a range replacement on the source code.
|
|
432
|
-
|
|
433
|
-
| Method | Input | TextEdit Produced |
|
|
434
|
-
| --------------------------- | -------------------------------- | ------------------------------------------ |
|
|
435
|
-
| `replaceText(token, text)` | Token with `startOffset` + `raw` | `range: [start, start+len], text` |
|
|
436
|
-
| `replaceRange(range, text)` | Explicit `[start, end)` range | `range: [start, end], text` |
|
|
437
|
-
| `insertBefore(token, text)` | Token with `startOffset` | `range: [start, start], text` (zero-width) |
|
|
438
|
-
| `insertAfter(token, text)` | Token with `startOffset` + `raw` | `range: [end, end], text` (zero-width) |
|
|
439
|
-
| `remove(token)` | Token with `startOffset` + `raw` | `range: [start, start+len], text: ""` |
|
|
440
|
-
| `removeRange(range)` | Explicit `[start, end)` range | `range: [start, end], text: ""` |
|
|
441
|
-
|
|
442
|
-
The `token` parameter accepts any object satisfying the `FixToken` type (defined in `@markuplint/ml-config`) — i.e., `{ startOffset: number; raw: string }`. MLDOM tokens (`MLToken`, `MLAttr`, etc.) satisfy this naturally.
|
|
443
|
-
|
|
444
|
-
### FixApplier Algorithm
|
|
445
|
-
|
|
446
|
-
`applyFixes()` (in `fix-applier.ts`) merges all `FixData` from all rules and applies them in a single pass:
|
|
447
|
-
|
|
448
|
-
```mermaid
|
|
449
|
-
flowchart TD
|
|
450
|
-
A["Flatten: FixData[] → tagged TextEdit[]"]
|
|
451
|
-
B["Sort: by range start ascending,\nthen range end descending"]
|
|
452
|
-
C["Apply sequentially:\nfor each edit, check overlap"]
|
|
453
|
-
D{{"edit.start < lastAppliedEnd?"}}
|
|
454
|
-
E["Skip edit\n(mark parent FixData as skipped)"]
|
|
455
|
-
F["Apply edit\n(splice into output)"]
|
|
456
|
-
G["Classify: each FixData as\napplied or skipped"]
|
|
457
|
-
H["Return FixResult\n{ output, applied, skipped, appliedEdits }"]
|
|
458
|
-
|
|
459
|
-
A --> B --> C --> D
|
|
460
|
-
D -- Yes --> E --> C
|
|
461
|
-
D -- No --> F --> C
|
|
462
|
-
C -. "all edits processed" .-> G --> H
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
Key constraints:
|
|
466
|
-
|
|
467
|
-
- Edits within a single `FixData` must not overlap each other
|
|
468
|
-
- Inter-`FixData` overlap is handled by the skip mechanism
|
|
469
|
-
- If any edit in a `FixData` is skipped, the entire `FixData` is classified as skipped
|
|
470
|
-
- `appliedEdits` is a flat list of all successfully applied `TextEdit` objects, sorted by `range[0]` ascending — used for cursor offset computation
|
|
471
|
-
|
|
472
|
-
### Multi-Pass Fix Loop
|
|
473
|
-
|
|
474
|
-
When `applyFixes()` skips some fixes due to range overlap, the engine enters a multi-pass loop (`_multiPassFix()`) that re-parses and re-verifies until all fixable violations are resolved:
|
|
475
|
-
|
|
476
|
-
```mermaid
|
|
477
|
-
flowchart TD
|
|
478
|
-
A["Extract fixes from violations"] --> B["applyFixes(code, fixes)"]
|
|
479
|
-
B --> C{"applied.length === 0?"}
|
|
480
|
-
C -- Yes --> Z["Return current code"]
|
|
481
|
-
C -- No --> D{"output === currentCode?"}
|
|
482
|
-
D -- Yes --> Z
|
|
483
|
-
D -- No --> E{"Cycle detected?\n(output === code from 2 passes ago)"}
|
|
484
|
-
E -- Yes --> Z
|
|
485
|
-
E -- No --> F{"skipped.length === 0?"}
|
|
486
|
-
F -- Yes --> Z["Return fixed code\n(all fixes applied)"]
|
|
487
|
-
F -- No --> G["Re-parse + re-verify"]
|
|
488
|
-
G --> H{"ParserError?"}
|
|
489
|
-
H -- Yes --> Z["Revert to last good code"]
|
|
490
|
-
H -- No --> I{"New fixable violations?"}
|
|
491
|
-
I -- No --> Z
|
|
492
|
-
I -- Yes --> B
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
Key design points:
|
|
496
|
-
|
|
497
|
-
- **Zero-cost path**: If no violations have fixes, the multi-pass loop is skipped entirely
|
|
498
|
-
- **Single-pass fast path**: When `skipped.length === 0`, the loop exits immediately — equivalent to Phase 1 behavior
|
|
499
|
-
- **Cycle detection**: Compares current output against the output from two passes ago to detect A→B→A oscillation
|
|
500
|
-
- **Safety cap**: Maximum 10 passes (same as ESLint's `SourceCodeFixer`)
|
|
501
|
-
- **State restoration**: `verify()` saves and restores `#sourceCode`, `#ast`, and `#document` via `try/finally`
|
|
502
|
-
|
|
503
|
-
**Important**: The `violations` array in `VerifyResult` reflects the first pass only, while `fixedCode` may be the result of multiple passes. Callers needing an accurate violation list for the fixed code should re-verify the output.
|
|
504
|
-
|
|
505
|
-
### VerifyResult and FixSummary
|
|
506
|
-
|
|
507
|
-
`MLCore.verify()` accepts either a `boolean` or a `VerifyOptions` object:
|
|
508
|
-
|
|
509
|
-
```typescript
|
|
510
|
-
verify(fix?: boolean): Promise<VerifyResult>;
|
|
511
|
-
verify(options?: VerifyOptions): Promise<VerifyResult>;
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
`VerifyResult` contains:
|
|
515
|
-
|
|
516
|
-
| Field | Type | Description |
|
|
517
|
-
| ------------ | ------------------------- | ------------------------------------------------------------- |
|
|
518
|
-
| `violations` | `readonly Violation[]` | Violations from the first verification pass |
|
|
519
|
-
| `fixedCode` | `string \| undefined` | Source after all fix passes; `undefined` when fix is disabled |
|
|
520
|
-
| `fixSummary` | `FixSummary \| undefined` | Fix process summary; present when `fix=true` |
|
|
521
|
-
|
|
522
|
-
`FixSummary` provides diagnostics about the multi-pass fix process:
|
|
523
|
-
|
|
524
|
-
| Field | Type | Description |
|
|
525
|
-
| ------------------ | --------------------- | ---------------------------------------------------------------- |
|
|
526
|
-
| `passCount` | `number` | Number of fix passes executed |
|
|
527
|
-
| `totalApplied` | `number` | Total fixes applied across all passes |
|
|
528
|
-
| `totalSkipped` | `number` | Total fixes skipped (overlap) across all passes |
|
|
529
|
-
| `reachedMaxPasses` | `boolean` | Whether the 10-pass safety cap was reached |
|
|
530
|
-
| `firstPassEdits` | `readonly TextEdit[]` | Applied edits from the first pass only (original source offsets) |
|
|
531
|
-
|
|
532
|
-
`firstPassEdits` references the original source code offsets, making them suitable for cursor remapping via `computeCursorOffset()`.
|
|
533
|
-
|
|
534
|
-
### Cursor Offset Computation
|
|
535
|
-
|
|
536
|
-
`computeCursorOffset()` (in `cursor-offset.ts`) maps a cursor position from the original source to the fixed source using the first-pass applied edits:
|
|
537
|
-
|
|
538
|
-
```typescript
|
|
539
|
-
import { computeCursorOffset } from '@markuplint/ml-core';
|
|
540
|
-
|
|
541
|
-
const newOffset = computeCursorOffset(fixSummary.firstPassEdits, originalCursorOffset);
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
Algorithm:
|
|
545
|
-
|
|
546
|
-
1. Walk through edits sorted by `range[0]` ascending
|
|
547
|
-
2. For each edit before the cursor: accumulate `delta = text.length - (end - start)`
|
|
548
|
-
3. For edits after the cursor: stop (no effect)
|
|
549
|
-
4. If the cursor falls inside a replaced range `[start, end)`: place at `start + text.length`
|
|
550
|
-
|
|
551
|
-
Ranges use half-open intervals: a cursor at position `end` is considered **outside** the edit.
|
|
552
|
-
|
|
553
|
-
### Example: Rule Fix in Practice
|
|
554
|
-
|
|
555
|
-
```typescript
|
|
556
|
-
// In a rule's verify function:
|
|
557
|
-
context.report({
|
|
558
|
-
scope: node,
|
|
559
|
-
message: 'Attribute value must use double quotes',
|
|
560
|
-
fix: fixer => fixer.replaceText(node.attrValueToken, `"${value}"`),
|
|
561
|
-
});
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
This produces:
|
|
565
|
-
|
|
566
|
-
1. **Report** → stored in `MLRuleContext`
|
|
567
|
-
2. **Fix callback** → `(fixer) => fixer.replaceText(token, text)` (not yet called)
|
|
568
|
-
3. **When `fix=true`** → callback invoked with shared `RuleFixer` → returns `TextEdit`
|
|
569
|
-
4. **TextEdit** → wrapped as `FixData { edits: [{ range: [12, 17], text: '"hello"' }] }`
|
|
570
|
-
5. **applyFixes** → splices the replacement into source code
|
|
571
|
-
|
|
572
|
-
## Pretender System
|
|
573
|
-
|
|
574
|
-
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>`).
|
|
575
|
-
|
|
576
|
-
### Configuration
|
|
577
|
-
|
|
578
|
-
Pretenders are defined in the markuplint config as an array of `Pretender` objects:
|
|
579
|
-
|
|
580
|
-
```typescript
|
|
581
|
-
type Pretender = {
|
|
582
|
-
selector: string; // CSS selector matching the component
|
|
583
|
-
as: string; // HTML element to pretend as
|
|
584
|
-
aria?: PretenderARIA; // Optional ARIA overrides
|
|
585
|
-
};
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
### How It Works
|
|
589
|
-
|
|
590
|
-
1. During `MLDocument` construction, pretender definitions are processed
|
|
591
|
-
2. Each `MLElement` matching a pretender selector gets a `pretenderContext` with `type: 'pretender'`
|
|
592
|
-
3. The target HTML element gets a `pretenderContext` with `type: 'origin'`
|
|
593
|
-
4. Rules can access `element.pretenderContext` to check the semantic mapping
|
|
594
|
-
5. Accessibility computations use pretender context for role/name resolution
|
|
595
|
-
|
|
596
|
-
## Conditional Child Nodes
|
|
597
|
-
|
|
598
|
-
Template engines (Pug, EJS, Nunjucks, etc.) produce preprocessor-specific blocks represented by `MLBlock` nodes. These blocks can wrap child nodes conditionally:
|
|
599
|
-
|
|
600
|
-
| `blockBehavior.type` | Template Construct | Description |
|
|
601
|
-
| -------------------- | ------------------ | -------------------------- |
|
|
602
|
-
| `'if'` | `{% if %}` | Start of conditional block |
|
|
603
|
-
| `'if:else'` | `{% else %}` | Alternative branch |
|
|
604
|
-
| `'end'` | `{% endif %}` | End of conditional block |
|
|
605
|
-
| `'each'` | `{% for %}` | Start of loop |
|
|
606
|
-
| `'end'` | `{% endfor %}` | End of loop |
|
|
607
|
-
| `'switch:case'` | `{% switch %}` | Start of switch |
|
|
608
|
-
| `'switch:default'` | `{% case %}` | Switch case |
|
|
609
|
-
| `'end'` | `{% endswitch %}` | End of switch |
|
|
610
|
-
|
|
611
|
-
`MLNode.conditionalChildNodes()` returns an array of `NodeListOf` arrays — one per conditional branch — so rules can analyze each branch independently. Note that `'each'` blocks do not start a new conditional mode (`'if'` or `'switch'`); they are flattened into `childNodes` and their content is treated as always-present rather than as an alternative branch. Only `'if'`/`'if:elseif'` and `'switch:case'` start new modes that generate null sentinels for the "empty branch" case.
|
|
612
|
-
|
|
613
|
-
## Plugin System
|
|
614
|
-
|
|
615
|
-
Plugins extend markuplint with custom rules and shared configurations.
|
|
616
|
-
|
|
617
|
-
### Plugin Type
|
|
618
|
-
|
|
619
|
-
```typescript
|
|
620
|
-
type Plugin = {
|
|
621
|
-
readonly name: string;
|
|
622
|
-
readonly rules?: Record<string, RuleSeed<any, any>>;
|
|
623
|
-
readonly configs?: Record<string, Config>;
|
|
624
|
-
};
|
|
625
|
-
```
|
|
626
|
-
|
|
627
|
-
### PluginCreator
|
|
628
|
-
|
|
629
|
-
For plugins that accept settings:
|
|
630
|
-
|
|
631
|
-
```typescript
|
|
632
|
-
type PluginCreator<S> = {
|
|
633
|
-
readonly name: string;
|
|
634
|
-
create(setting: S): Omit<Plugin, 'name'>;
|
|
635
|
-
};
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
`createPlugin(creator)` is a factory function for type-safe plugin creator definitions.
|
|
639
|
-
|
|
640
|
-
## Test Utilities
|
|
641
|
-
|
|
642
|
-
The `test/` module provides helpers for rule testing:
|
|
643
|
-
|
|
644
|
-
| Function | Description |
|
|
645
|
-
| ------------------------------------------- | ----------------------------------------------- |
|
|
646
|
-
| `createTestDocument(sourceCode, options?)` | Parses source into an `MLDocument` for testing |
|
|
647
|
-
| `createTestElement(sourceCode, options?)` | Parses source and returns the first `MLElement` |
|
|
648
|
-
| `createTestNodeList(sourceCode, options?)` | Returns the flat node list from parsed source |
|
|
649
|
-
| `createTestTokenList(sourceCode, options?)` | Returns the flat token list from parsed source |
|
|
650
|
-
| `dummySchemas()` | Returns the default HTML spec as a schema tuple |
|
|
651
|
-
|
|
652
|
-
`CreateTestOptions` accepts `config`, `parser`, `specs`, and `pretenders` overrides.
|
|
653
|
-
|
|
654
|
-
## External Dependencies
|
|
655
|
-
|
|
656
|
-
| Dependency | Purpose |
|
|
657
|
-
| ---------------------------- | ----------------------------------------------------------------- |
|
|
658
|
-
| `@markuplint/ml-ast` | AST type definitions (`MLASTDocument`, `MLASTNode`, etc.) |
|
|
659
|
-
| `@markuplint/ml-config` | Configuration types (`Config`, `RuleConfigValue`, `Pretender`) |
|
|
660
|
-
| `@markuplint/ml-spec` | HTML/ARIA specification access (`MLMLSpec`, role/attribute specs) |
|
|
661
|
-
| `@markuplint/html-spec` | Default HTML specification data |
|
|
662
|
-
| `@markuplint/html-parser` | Default HTML parser (used in test utilities) |
|
|
663
|
-
| `@markuplint/parser-utils` | Parser options and types |
|
|
664
|
-
| `@markuplint/selector` | CSS and extended selector matching |
|
|
665
|
-
| `@markuplint/i18n` | Internationalization (`LocaleSet`, `Translator`) |
|
|
666
|
-
| `@markuplint/shared` | Shared utilities |
|
|
667
|
-
| `@markuplint/config-presets` | Built-in configuration presets |
|
|
668
|
-
| `debug` | Debug logging |
|
|
669
|
-
| `is-plain-object` | Plain object type checking |
|
|
670
|
-
| `type-fest` | TypeScript utility types |
|
|
671
|
-
|
|
672
|
-
## Integration Points
|
|
673
|
-
|
|
674
|
-
```mermaid
|
|
675
|
-
flowchart TD
|
|
676
|
-
subgraph upstream ["Upstream"]
|
|
677
|
-
mlAst["@markuplint/ml-ast"]
|
|
678
|
-
mlConfig["@markuplint/ml-config"]
|
|
679
|
-
mlSpec["@markuplint/ml-spec"]
|
|
680
|
-
htmlSpec["@markuplint/html-spec"]
|
|
681
|
-
htmlParser["@markuplint/html-parser"]
|
|
682
|
-
parserUtils["@markuplint/parser-utils"]
|
|
683
|
-
selector["@markuplint/selector"]
|
|
684
|
-
i18n["@markuplint/i18n"]
|
|
685
|
-
shared["@markuplint/shared"]
|
|
686
|
-
configPresets["@markuplint/config-presets"]
|
|
687
|
-
end
|
|
688
|
-
|
|
689
|
-
subgraph pkg ["@markuplint/ml-core"]
|
|
690
|
-
core["MLCore Engine"]
|
|
691
|
-
end
|
|
692
|
-
|
|
693
|
-
subgraph downstream ["Downstream"]
|
|
694
|
-
rules["@markuplint/rules\n(Built-in rule implementations)"]
|
|
695
|
-
markuplint["markuplint\n(CLI, API, MLEngine)"]
|
|
696
|
-
end
|
|
697
|
-
|
|
698
|
-
upstream -->|"types, parsing, specs, i18n"| core
|
|
699
|
-
core -->|"MLDOM classes, MLRule,\ncreateRule, test utils"| rules
|
|
700
|
-
core -->|"MLCore, ViolationCollector,\nconvertRuleset, Plugin types"| markuplint
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
### Upstream
|
|
704
|
-
|
|
705
|
-
- **`@markuplint/ml-ast`** — AST types used to construct the MLDOM tree
|
|
706
|
-
- **`@markuplint/ml-config`** — Config and rule configuration types
|
|
707
|
-
- **`@markuplint/ml-spec`** — HTML/ARIA specification for element validation, role computation
|
|
708
|
-
- **`@markuplint/html-spec`** — Default spec data bundle
|
|
709
|
-
- **`@markuplint/html-parser`** — Default parser used in test utilities
|
|
710
|
-
- **`@markuplint/parser-utils`** — Parser option types
|
|
711
|
-
- **`@markuplint/selector`** — CSS selector engine for `querySelector`, `matches`, and `RegexSelector`
|
|
712
|
-
- **`@markuplint/i18n`** — Locale sets and translation for rule messages
|
|
713
|
-
- **`@markuplint/shared`** — Shared utility functions
|
|
714
|
-
- **`@markuplint/config-presets`** — Built-in configuration presets
|
|
715
|
-
|
|
716
|
-
### Downstream
|
|
717
|
-
|
|
718
|
-
- **`@markuplint/rules`** — Imports MLDOM classes, `createRule`, `MLRuleContext`, and test utilities to implement built-in rules
|
|
719
|
-
- **`markuplint`** — Imports `MLCore`, `ViolationCollector`, `convertRuleset`, and plugin types to provide the CLI and API
|
|
720
|
-
|
|
721
|
-
## Documentation Map
|
|
722
|
-
|
|
723
|
-
- [MLDOM Reference](docs/ml-dom.md) ([日本語](docs/ml-dom.ja.md)) — Class hierarchy, node properties, tree traversal
|
|
724
|
-
- [Rule System](docs/rule-system.md) ([日本語](docs/rule-system.ja.md)) — MLRule, RuleSeed, MLRuleContext, configuration resolution
|
|
725
|
-
- [Linting Pipeline](docs/linting-pipeline.md) ([日本語](docs/linting-pipeline.ja.md)) — MLCore engine, verify flow, pretender, plugin system
|
|
726
|
-
- [Maintenance Guide](docs/maintenance.md) ([日本語](docs/maintenance.ja.md)) — Commands, recipes, and troubleshooting
|