@markuplint/ml-core 4.13.2 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +524 -0
- package/ARCHITECTURE.md +524 -0
- package/CHANGELOG.md +52 -2
- package/README.md +5 -0
- package/SKILL.md +61 -0
- package/docs/linting-pipeline.ja.md +307 -0
- package/docs/linting-pipeline.md +307 -0
- package/docs/maintenance.ja.md +210 -0
- package/docs/maintenance.md +210 -0
- package/docs/ml-dom/attr.ja.md +103 -0
- package/docs/ml-dom/attr.md +103 -0
- package/docs/ml-dom/block.ja.md +272 -0
- package/docs/ml-dom/block.md +272 -0
- package/docs/ml-dom/document.ja.md +141 -0
- package/docs/ml-dom/document.md +141 -0
- package/docs/ml-dom/element.ja.md +176 -0
- package/docs/ml-dom/element.md +176 -0
- package/docs/ml-dom/helpers.ja.md +203 -0
- package/docs/ml-dom/helpers.md +203 -0
- package/docs/ml-dom/node.ja.md +199 -0
- package/docs/ml-dom/node.md +199 -0
- package/docs/ml-dom/others.ja.md +120 -0
- package/docs/ml-dom/others.md +120 -0
- package/docs/ml-dom/overview.ja.md +102 -0
- package/docs/ml-dom/overview.md +102 -0
- package/docs/ml-dom/pretender.ja.md +269 -0
- package/docs/ml-dom/pretender.md +269 -0
- package/docs/ml-dom/rule-mapping.ja.md +371 -0
- package/docs/ml-dom/rule-mapping.md +371 -0
- package/docs/ml-dom.ja.md +18 -0
- package/docs/ml-dom.md +18 -0
- package/docs/rule-system.ja.md +287 -0
- package/docs/rule-system.md +287 -0
- package/lib/convert-ruleset.d.ts +7 -0
- package/lib/convert-ruleset.js +7 -0
- package/lib/debug.d.ts +4 -0
- package/lib/debug.js +4 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.js +1 -1
- package/lib/ml-core.d.ts +37 -1
- package/lib/ml-core.js +171 -82
- package/lib/ml-dom/helper/accname.d.ts +8 -0
- package/lib/ml-dom/helper/accname.js +71 -55
- package/lib/ml-dom/helper/create-node.js +1 -0
- package/lib/ml-dom/helper/get-indent.d.ts +4 -1
- package/lib/ml-dom/helper/get-indent.js +21 -30
- package/lib/ml-dom/node/attr.d.ts +65 -4
- package/lib/ml-dom/node/attr.js +151 -53
- package/lib/ml-dom/node/block.d.ts +23 -2
- package/lib/ml-dom/node/block.js +24 -1
- package/lib/ml-dom/node/child-node.d.ts +9 -0
- package/lib/ml-dom/node/child-node.js +9 -0
- package/lib/ml-dom/node/comment.d.ts +7 -0
- package/lib/ml-dom/node/comment.js +7 -0
- package/lib/ml-dom/node/document-fragment.d.ts +8 -0
- package/lib/ml-dom/node/document-fragment.js +8 -0
- package/lib/ml-dom/node/document-type.d.ts +22 -0
- package/lib/ml-dom/node/document-type.js +25 -0
- package/lib/ml-dom/node/document.d.ts +88 -7
- package/lib/ml-dom/node/document.js +128 -32
- package/lib/ml-dom/node/dom-token-list.js +17 -30
- package/lib/ml-dom/node/element-close-tag.js +1 -0
- package/lib/ml-dom/node/element.d.ts +151 -5
- package/lib/ml-dom/node/element.js +242 -50
- package/lib/ml-dom/node/node-store.js +6 -15
- package/lib/ml-dom/node/node.d.ts +19 -1
- package/lib/ml-dom/node/node.js +175 -166
- package/lib/ml-dom/node/parent-node.js +14 -30
- package/lib/ml-dom/node/rule-mapper.js +7 -20
- package/lib/ml-dom/node/text.d.ts +19 -0
- package/lib/ml-dom/node/text.js +21 -0
- package/lib/ml-dom/node/types.d.ts +68 -0
- package/lib/ml-dom/token/token.d.ts +42 -0
- package/lib/ml-dom/token/token.js +59 -39
- package/lib/ml-rule/create-rule.d.ts +17 -1
- package/lib/ml-rule/ml-rule-context.js +7 -11
- package/lib/ml-rule/ml-rule.d.ts +66 -1
- package/lib/ml-rule/ml-rule.js +95 -25
- package/lib/ml-rule/types.d.ts +41 -0
- package/lib/plugin/plugin.d.ts +8 -0
- package/lib/plugin/plugin.js +8 -0
- package/lib/plugin/types.d.ts +21 -0
- package/lib/ruleset/index.d.ts +10 -0
- package/lib/ruleset/index.js +13 -0
- package/lib/test/index.d.ts +42 -1
- package/lib/test/index.js +39 -2
- package/lib/types.d.ts +10 -1
- package/lib/violation-collector.d.ts +33 -0
- package/lib/violation-collector.js +48 -28
- package/lib/virtual-rule.d.ts +72 -0
- package/lib/virtual-rule.js +233 -0
- package/package.json +16 -13
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
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
|
+
├── virtual-rule.ts — Named nodeRule expansion (expandNamedNodeRules)
|
|
48
|
+
├── virtual-rule.spec.ts — Virtual rule unit tests
|
|
49
|
+
├── ml-rule/
|
|
50
|
+
│ ├── ml-rule.ts — MLRule class (rule execution)
|
|
51
|
+
│ ├── ml-rule-context.ts — MLRuleContext (report collection)
|
|
52
|
+
│ ├── create-rule.ts — createRule factory
|
|
53
|
+
│ ├── create-test-rule.ts — Test rule factory
|
|
54
|
+
│ └── types.ts — RuleSeed, Checker types
|
|
55
|
+
├── ruleset/
|
|
56
|
+
│ └── index.ts — Ruleset class (rules + nodeRules + childNodeRules)
|
|
57
|
+
├── plugin/
|
|
58
|
+
│ ├── plugin.ts — createPlugin factory
|
|
59
|
+
│ ├── types.ts — Plugin, PluginCreator types
|
|
60
|
+
│ └── index.ts — Plugin exports
|
|
61
|
+
├── test/
|
|
62
|
+
│ └── index.ts — createTestDocument, createTestElement, dummySchemas
|
|
63
|
+
└── utils/
|
|
64
|
+
├── index.ts — Utility exports
|
|
65
|
+
├── get-location-from-chars.ts — Character location resolver
|
|
66
|
+
└── string-splice.ts — String splice helper
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Architecture Diagram
|
|
70
|
+
|
|
71
|
+
```mermaid
|
|
72
|
+
flowchart TD
|
|
73
|
+
subgraph upstream ["Upstream Dependencies"]
|
|
74
|
+
mlAst["@markuplint/ml-ast\n(AST types)"]
|
|
75
|
+
mlConfig["@markuplint/ml-config\n(Config, RuleConfigValue)"]
|
|
76
|
+
mlSpec["@markuplint/ml-spec\n(HTML/ARIA specs)"]
|
|
77
|
+
htmlSpec["@markuplint/html-spec\n(Default spec data)"]
|
|
78
|
+
htmlParser["@markuplint/html-parser\n(Default parser)"]
|
|
79
|
+
parserUtils["@markuplint/parser-utils\n(ParserOptions)"]
|
|
80
|
+
selector["@markuplint/selector\n(CSS selector matching)"]
|
|
81
|
+
i18n["@markuplint/i18n\n(Locale, Translator)"]
|
|
82
|
+
shared["@markuplint/shared\n(Utilities)"]
|
|
83
|
+
configPresets["@markuplint/config-presets\n(Built-in presets)"]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
subgraph pkg ["@markuplint/ml-core"]
|
|
87
|
+
subgraph mldom ["MLDOM"]
|
|
88
|
+
document["MLDocument"]
|
|
89
|
+
element["MLElement"]
|
|
90
|
+
node["MLNode / MLToken"]
|
|
91
|
+
ruleMapper["RuleMapper"]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
subgraph mlRule ["MLRule"]
|
|
95
|
+
rule["MLRule"]
|
|
96
|
+
ruleContext["MLRuleContext"]
|
|
97
|
+
createRule["createRule()"]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
subgraph engine ["Engine"]
|
|
101
|
+
core["MLCore"]
|
|
102
|
+
ruleset["Ruleset"]
|
|
103
|
+
convertRuleset["convertRuleset()"]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
subgraph extras ["Extras"]
|
|
107
|
+
plugin["Plugin / createPlugin()"]
|
|
108
|
+
testUtils["Test Utilities"]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
subgraph downstream ["Downstream"]
|
|
113
|
+
rules["@markuplint/rules\n(Built-in rules)"]
|
|
114
|
+
markuplint["markuplint\n(CLI & API)"]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
upstream -->|"types, parsing, specs"| pkg
|
|
118
|
+
core --> document
|
|
119
|
+
core --> rule
|
|
120
|
+
document --> ruleMapper
|
|
121
|
+
rule --> ruleContext
|
|
122
|
+
pkg -->|"MLDOM, MLRule, MLCore"| downstream
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Linting Pipeline
|
|
126
|
+
|
|
127
|
+
The `MLCore.verify()` method orchestrates the full linting flow:
|
|
128
|
+
|
|
129
|
+
```mermaid
|
|
130
|
+
flowchart LR
|
|
131
|
+
A["MLCore\nconstructor"]
|
|
132
|
+
B["_parse()\nParser → MLASTDocument"]
|
|
133
|
+
C["_createDocument()\nMLASTDocument → MLDocument"]
|
|
134
|
+
D["verify(fix?)\nFor each rule:"]
|
|
135
|
+
E["document.setRule(rule)\nRuleMapper maps config → nodes"]
|
|
136
|
+
F["rule.verify(document)\nMLRuleContext collects reports"]
|
|
137
|
+
G["Violations[]"]
|
|
138
|
+
|
|
139
|
+
A --> B --> C --> D --> E --> F --> G
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Step-by-step
|
|
143
|
+
|
|
144
|
+
1. **Parse**: `MLCore` invokes the configured parser (`MLParser`) to produce an `MLASTDocument`
|
|
145
|
+
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
|
|
146
|
+
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`
|
|
147
|
+
4. **Fix** (optional): When `fix=true`, rules may call `node.fix()` to modify token content. `document.toString(true)` produces the fixed source
|
|
148
|
+
|
|
149
|
+
## MLDOM Class Hierarchy
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
MLToken<A extends MLASTToken>
|
|
153
|
+
└── MLNode<T, O, A extends MLASTNode>
|
|
154
|
+
├── MLAttr<T, O>
|
|
155
|
+
├── MLCharacterData<T, O, A> (abstract)
|
|
156
|
+
│ ├── MLText<T, O>
|
|
157
|
+
│ └── MLComment<T, O>
|
|
158
|
+
├── MLDocumentType<T, O>
|
|
159
|
+
├── MLBlock<T, O>
|
|
160
|
+
├── MLElementCloseTag<T, O>
|
|
161
|
+
└── MLParentNode<T, O, A> (abstract)
|
|
162
|
+
├── MLElement<T, O>
|
|
163
|
+
├── MLDocumentFragment<T, O>
|
|
164
|
+
└── MLDocument<T, O>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Class Responsibilities
|
|
168
|
+
|
|
169
|
+
| Class | DOM Interface | Key Responsibility |
|
|
170
|
+
| -------------------- | ------------------ | ------------------------------------------------------------------------------------------- |
|
|
171
|
+
| `MLToken` | — | Base token with position tracking (`startLine`, `endCol`, `raw`, `fixed`), `fix()` method |
|
|
172
|
+
| `MLNode` | `Node` | Tree structure (`parentNode`, `childNodes`, `nextSibling`), rule storage, `is()` type guard |
|
|
173
|
+
| `MLAttr` | `Attr` | Attribute name/value tokens, `isDynamicValue`, `isDirective`, `valueType`, `tokenList` |
|
|
174
|
+
| `MLCharacterData` | `CharacterData` | Abstract base for text content nodes (`data`, `nodeValue`) |
|
|
175
|
+
| `MLText` | `Text` | Text nodes, `isWhitespace()`, `isRawTextElementContent()` |
|
|
176
|
+
| `MLComment` | `Comment` | Comment nodes with `textContent` |
|
|
177
|
+
| `MLDocumentType` | `DocumentType` | `<!DOCTYPE>` with `name`, `publicId`, `systemId` |
|
|
178
|
+
| `MLBlock` | — | Preprocessor-specific blocks (if/each/switch), `blockBehavior`, `isTransparent` |
|
|
179
|
+
| `MLElementCloseTag` | — | Close tag paired with its open tag element |
|
|
180
|
+
| `MLParentNode` | `ParentNode` | `querySelector()`, `querySelectorAll()`, `children`, `childElementCount` |
|
|
181
|
+
| `MLElement` | `Element` | Attributes, selectors, namespaces, pretender context, `elementType`, `closeTag` |
|
|
182
|
+
| `MLDocumentFragment` | `DocumentFragment` | Fragment root node |
|
|
183
|
+
| `MLDocument` | `Document` | Root node, `nodeList`, `walkOn()`, `setRule()`, rule mapping, spec access |
|
|
184
|
+
|
|
185
|
+
## MLDocument
|
|
186
|
+
|
|
187
|
+
`MLDocument` is the root of the MLDOM tree and the primary interface for rule execution.
|
|
188
|
+
|
|
189
|
+
### Construction
|
|
190
|
+
|
|
191
|
+
The constructor receives an `MLASTDocument`, a `Ruleset`, and an `MLSchema` tuple. It:
|
|
192
|
+
|
|
193
|
+
1. Builds the flat `nodeList` by traversing the AST and calling `createNode()` for each AST node
|
|
194
|
+
2. Initializes `RuleMapper` to distribute rule configuration across nodes
|
|
195
|
+
3. Sets up pretender contexts when pretender definitions are provided
|
|
196
|
+
|
|
197
|
+
### Key Properties
|
|
198
|
+
|
|
199
|
+
| Property | Type | Description |
|
|
200
|
+
| ------------- | ----------------------- | --------------------------------------------------------- |
|
|
201
|
+
| `nodeList` | `ReadonlyArray<MLNode>` | Flat list of all nodes in document order |
|
|
202
|
+
| `specs` | `MLMLSpec` | HTML/ARIA specification data |
|
|
203
|
+
| `isFragment` | `boolean` | Whether the document is a fragment |
|
|
204
|
+
| `currentRule` | `MLRule \| null` | The rule currently being evaluated |
|
|
205
|
+
| `endTag` | `EndTagType` | End tag handling mode (`'xml'`, `'omittable'`, `'never'`) |
|
|
206
|
+
|
|
207
|
+
### Key Methods
|
|
208
|
+
|
|
209
|
+
| Method | Description |
|
|
210
|
+
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
211
|
+
| `walkOn(type, walker)` | Walks nodes of a given type (`'Element'`, `'Text'`, `'Comment'`, `'Attr'`, `'ElementCloseTag'`) |
|
|
212
|
+
| `setRule(rule)` | Sets the current rule, used by `MLCore` during verification |
|
|
213
|
+
| `getTokenList()` | Returns all tokens for source reconstruction |
|
|
214
|
+
| `searchNodeByLocation(line, col)` | Finds the node at a given source position |
|
|
215
|
+
| `getAccessibilityProp(node)` | Computes ARIA accessibility properties (delegates to `MLElement.getAccessibleName()` for cached accessible name) |
|
|
216
|
+
| `toString(fixed?)` | Reconstructs source code, optionally with fixes applied |
|
|
217
|
+
|
|
218
|
+
## MLElement
|
|
219
|
+
|
|
220
|
+
`MLElement` represents an HTML/SVG/MathML element with full attribute access and selector matching.
|
|
221
|
+
|
|
222
|
+
### Key Properties
|
|
223
|
+
|
|
224
|
+
| Property | Type | Description |
|
|
225
|
+
| ------------------ | --------------------------- | -------------------------------------------- |
|
|
226
|
+
| `localName` | `string` | Lowercase tag name (for HTML) |
|
|
227
|
+
| `namespaceURI` | `NamespaceURI` | Element namespace (HTML, SVG, MathML) |
|
|
228
|
+
| `attributes` | `MLNamedNodeMap` | Named attribute collection |
|
|
229
|
+
| `elementType` | `ElementType` | `'html'`, `'web-component'`, or `'authored'` |
|
|
230
|
+
| `closeTag` | `MLElementCloseTag \| null` | Paired close tag |
|
|
231
|
+
| `pretenderContext` | `PretenderContext \| null` | Pretender mapping context |
|
|
232
|
+
| `isForeignElement` | `boolean` | `true` for SVG/MathML elements |
|
|
233
|
+
| `isOmitted` | `boolean` | `true` for implicitly inserted elements |
|
|
234
|
+
|
|
235
|
+
### Key Methods
|
|
236
|
+
|
|
237
|
+
| Method | Description |
|
|
238
|
+
| ---------------------------- | ---------------------------------------------------------------- |
|
|
239
|
+
| `getAttribute(name)` | Returns attribute value or `null` |
|
|
240
|
+
| `getAttributeToken(name)` | Returns `MLAttr[]` for the named attribute |
|
|
241
|
+
| `hasAttribute(name)` | Checks attribute existence |
|
|
242
|
+
| `getAccessibleName(version)` | Cached accessible name computation (memoized per ARIA version) |
|
|
243
|
+
| `matches(selector)` | CSS selector matching |
|
|
244
|
+
| `matchMLSelector(selector)` | Extended markuplint selector matching (supports `RegexSelector`) |
|
|
245
|
+
| `querySelector(selector)` | Finds first matching descendant |
|
|
246
|
+
| `querySelectorAll(selector)` | Finds all matching descendants |
|
|
247
|
+
|
|
248
|
+
## Rule System
|
|
249
|
+
|
|
250
|
+
### MLRule
|
|
251
|
+
|
|
252
|
+
`MLRule<T, O>` encapsulates a linting rule with verification and optional fix logic.
|
|
253
|
+
|
|
254
|
+
| Property/Method | Description |
|
|
255
|
+
| --------------------------------- | --------------------------------------------------------------------------- |
|
|
256
|
+
| `name` | Rule identifier (e.g., `"attr-duplication"`) |
|
|
257
|
+
| `defaultSeverity` | Default severity level |
|
|
258
|
+
| `defaultValue` / `defaultOptions` | Default configuration |
|
|
259
|
+
| `baseRuleId` | For virtual rules: the base rule's name (e.g., `"required-attr"`) |
|
|
260
|
+
| `groupName` | For multi-entry virtual rules: group name for batch disable |
|
|
261
|
+
| `specConformance` | For virtual rules: `'normative'` or `'non-normative'` (from named nodeRule) |
|
|
262
|
+
| `verify(document, locale, fix)` | Executes the rule and returns violations |
|
|
263
|
+
| `createAlias(name, options?)` | Creates a virtual rule that reuses this rule's verify/fix logic |
|
|
264
|
+
| `optimizeOption(settings)` | Normalizes raw rule configuration into `RuleInfo` |
|
|
265
|
+
|
|
266
|
+
### RuleSeed
|
|
267
|
+
|
|
268
|
+
The `RuleSeed<T, O>` type defines the rule implementation:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
type RuleSeed<T, O> = {
|
|
272
|
+
meta?: {
|
|
273
|
+
category?: 'validation' | 'style' | 'naming-convention' | 'a11y' | 'maintainability';
|
|
274
|
+
};
|
|
275
|
+
defaultSeverity?: Severity;
|
|
276
|
+
defaultValue?: T;
|
|
277
|
+
defaultOptions?: O;
|
|
278
|
+
verify(context): void | Promise<void>;
|
|
279
|
+
fix?(context): void | Promise<void>;
|
|
280
|
+
};
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### createRule
|
|
284
|
+
|
|
285
|
+
`createRule(seed)` is a factory function for type-safe rule seed creation. It returns the seed as-is, serving primarily as a type helper.
|
|
286
|
+
|
|
287
|
+
### MLRuleContext
|
|
288
|
+
|
|
289
|
+
`MLRuleContext<T, O>` provides the execution context for rules:
|
|
290
|
+
|
|
291
|
+
- `document` — The current `MLDocument`
|
|
292
|
+
- `translate` / `t` — Locale-aware message translator
|
|
293
|
+
- `report(report)` — Reports a violation with node, message, and optional fix
|
|
294
|
+
|
|
295
|
+
The `provide()` method returns the context object passed to `RuleSeed.verify()` and `RuleSeed.fix()`.
|
|
296
|
+
|
|
297
|
+
### Rule Configuration Resolution
|
|
298
|
+
|
|
299
|
+
Rules are configured at three levels, resolved by `RuleMapper`:
|
|
300
|
+
|
|
301
|
+
1. **Global rules** (`rules`) — Apply to all nodes; lowest priority
|
|
302
|
+
2. **Node rules** (`nodeRules`) — Apply to nodes matching a selector; medium priority
|
|
303
|
+
3. **Child node rules** (`childNodeRules`) — Apply to children of nodes matching a selector; highest priority
|
|
304
|
+
|
|
305
|
+
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`.
|
|
306
|
+
|
|
307
|
+
### Rule Execution Flow
|
|
308
|
+
|
|
309
|
+
```mermaid
|
|
310
|
+
flowchart TD
|
|
311
|
+
A["MLCore.verify()"] --> B["For each MLRule"]
|
|
312
|
+
B --> C["document.setRule(rule)"]
|
|
313
|
+
C --> D["rule.verify(document, locale, fix)"]
|
|
314
|
+
D --> E["rule.getRuleInfo(ruleset)\nResolve global config"]
|
|
315
|
+
E --> F["document.walkOn(type, walker)\nIterate matching nodes"]
|
|
316
|
+
F --> G["context.report()\nCollect violations per node"]
|
|
317
|
+
G --> H["Return Violation[]"]
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Virtual Rule System
|
|
321
|
+
|
|
322
|
+
Source: `src/virtual-rule.ts`
|
|
323
|
+
|
|
324
|
+
> **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.
|
|
325
|
+
|
|
326
|
+
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`.
|
|
327
|
+
|
|
328
|
+
#### Named NodeRule Expansion
|
|
329
|
+
|
|
330
|
+
`expandNamedNodeRules()` converts named nodeRules (and childNodeRules) into virtual rules during `MLCore` construction:
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
Named nodeRule (config) Virtual MLRule (runtime)
|
|
334
|
+
┌─────────────────────────┐ ┌──────────────────────────┐
|
|
335
|
+
│ name: "a11y/html-lang" │ │ name: "a11y/html-lang" │
|
|
336
|
+
│ specConformance: "norm."│ ──────► │ baseRuleId: "required-attr" │
|
|
337
|
+
│ selector: ":where(html)"│ │ specConformance: metadata│
|
|
338
|
+
│ rules: │ │ verify/fix: from base │
|
|
339
|
+
│ required-attr: [lang] │ └──────────────────────────┘
|
|
340
|
+
└─────────────────────────┘
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Key behaviors:
|
|
344
|
+
|
|
345
|
+
- **False entry separation**: `false` entries in `rules` are automatically separated into unnamed nodeRules, preserving their semantics as base-rule specificity overrides
|
|
346
|
+
- **Multi-entry support**: Named nodeRules with 2+ non-false entries create derived names (`name/baseRuleName`) with a `groupName` for group disable
|
|
347
|
+
- **Metadata**: `specConformance` is attached to the virtual rule as metadata for downstream tools and reporting
|
|
348
|
+
- **Hot-reload**: Pre-expansion nodeRules are preserved in `#originalNodeRules` / `#originalChildNodeRules` so `update()` can re-expand them
|
|
349
|
+
|
|
350
|
+
#### Why `specConformance` Is Restricted to Named NodeRules
|
|
351
|
+
|
|
352
|
+
`specConformance` is intentionally available **only on named nodeRules** (in presets), not on regular built-in rules. The design rationale:
|
|
353
|
+
|
|
354
|
+
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.
|
|
355
|
+
|
|
356
|
+
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.
|
|
357
|
+
|
|
358
|
+
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.
|
|
359
|
+
|
|
360
|
+
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.
|
|
361
|
+
|
|
362
|
+
#### Virtual Rule Disable
|
|
363
|
+
|
|
364
|
+
Virtual rules can be disabled at three levels in the `rules` config:
|
|
365
|
+
|
|
366
|
+
1. **Exact name**: `rules["a11y/html-lang"]: false`
|
|
367
|
+
2. **Group disable**: `rules["custom/multi"]: false` (for multi-entry named nodeRules)
|
|
368
|
+
3. **Namespace wildcard**: `rules["a11y/*"]: false` (disables all virtual rules starting with `a11y/`)
|
|
369
|
+
|
|
370
|
+
## Pretender System
|
|
371
|
+
|
|
372
|
+
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>`).
|
|
373
|
+
|
|
374
|
+
### Configuration
|
|
375
|
+
|
|
376
|
+
Pretenders are defined in the markuplint config as an array of `Pretender` objects:
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
type Pretender = {
|
|
380
|
+
selector: string; // CSS selector matching the component
|
|
381
|
+
as: string; // HTML element to pretend as
|
|
382
|
+
aria?: PretenderARIA; // Optional ARIA overrides
|
|
383
|
+
};
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### How It Works
|
|
387
|
+
|
|
388
|
+
1. During `MLDocument` construction, pretender definitions are processed
|
|
389
|
+
2. Each `MLElement` matching a pretender selector gets a `pretenderContext` with `type: 'pretender'`
|
|
390
|
+
3. The target HTML element gets a `pretenderContext` with `type: 'origin'`
|
|
391
|
+
4. Rules can access `element.pretenderContext` to check the semantic mapping
|
|
392
|
+
5. Accessibility computations use pretender context for role/name resolution
|
|
393
|
+
|
|
394
|
+
## Conditional Child Nodes
|
|
395
|
+
|
|
396
|
+
Template engines (Pug, EJS, Nunjucks, etc.) produce preprocessor-specific blocks represented by `MLBlock` nodes. These blocks can wrap child nodes conditionally:
|
|
397
|
+
|
|
398
|
+
| `blockBehavior.type` | Template Construct | Description |
|
|
399
|
+
| -------------------- | ------------------ | -------------------------- |
|
|
400
|
+
| `'if'` | `{% if %}` | Start of conditional block |
|
|
401
|
+
| `'if:else'` | `{% else %}` | Alternative branch |
|
|
402
|
+
| `'end'` | `{% endif %}` | End of conditional block |
|
|
403
|
+
| `'each'` | `{% for %}` | Start of loop |
|
|
404
|
+
| `'end'` | `{% endfor %}` | End of loop |
|
|
405
|
+
| `'switch:case'` | `{% switch %}` | Start of switch |
|
|
406
|
+
| `'switch:default'` | `{% case %}` | Switch case |
|
|
407
|
+
| `'end'` | `{% endswitch %}` | End of switch |
|
|
408
|
+
|
|
409
|
+
`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.
|
|
410
|
+
|
|
411
|
+
## Plugin System
|
|
412
|
+
|
|
413
|
+
Plugins extend markuplint with custom rules and shared configurations.
|
|
414
|
+
|
|
415
|
+
### Plugin Type
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
type Plugin = {
|
|
419
|
+
readonly name: string;
|
|
420
|
+
readonly rules?: Record<string, RuleSeed<any, any>>;
|
|
421
|
+
readonly configs?: Record<string, Config>;
|
|
422
|
+
};
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### PluginCreator
|
|
426
|
+
|
|
427
|
+
For plugins that accept settings:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
type PluginCreator<S> = {
|
|
431
|
+
readonly name: string;
|
|
432
|
+
create(setting: S): Omit<Plugin, 'name'>;
|
|
433
|
+
};
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
`createPlugin(creator)` is a factory function for type-safe plugin creator definitions.
|
|
437
|
+
|
|
438
|
+
## Test Utilities
|
|
439
|
+
|
|
440
|
+
The `test/` module provides helpers for rule testing:
|
|
441
|
+
|
|
442
|
+
| Function | Description |
|
|
443
|
+
| ------------------------------------------- | ----------------------------------------------- |
|
|
444
|
+
| `createTestDocument(sourceCode, options?)` | Parses source into an `MLDocument` for testing |
|
|
445
|
+
| `createTestElement(sourceCode, options?)` | Parses source and returns the first `MLElement` |
|
|
446
|
+
| `createTestNodeList(sourceCode, options?)` | Returns the flat node list from parsed source |
|
|
447
|
+
| `createTestTokenList(sourceCode, options?)` | Returns the flat token list from parsed source |
|
|
448
|
+
| `dummySchemas()` | Returns the default HTML spec as a schema tuple |
|
|
449
|
+
|
|
450
|
+
`CreateTestOptions` accepts `config`, `parser`, `specs`, and `pretenders` overrides.
|
|
451
|
+
|
|
452
|
+
## External Dependencies
|
|
453
|
+
|
|
454
|
+
| Dependency | Purpose |
|
|
455
|
+
| ---------------------------- | ----------------------------------------------------------------- |
|
|
456
|
+
| `@markuplint/ml-ast` | AST type definitions (`MLASTDocument`, `MLASTNode`, etc.) |
|
|
457
|
+
| `@markuplint/ml-config` | Configuration types (`Config`, `RuleConfigValue`, `Pretender`) |
|
|
458
|
+
| `@markuplint/ml-spec` | HTML/ARIA specification access (`MLMLSpec`, role/attribute specs) |
|
|
459
|
+
| `@markuplint/html-spec` | Default HTML specification data |
|
|
460
|
+
| `@markuplint/html-parser` | Default HTML parser (used in test utilities) |
|
|
461
|
+
| `@markuplint/parser-utils` | Parser options and types |
|
|
462
|
+
| `@markuplint/selector` | CSS and extended selector matching |
|
|
463
|
+
| `@markuplint/i18n` | Internationalization (`LocaleSet`, `Translator`) |
|
|
464
|
+
| `@markuplint/shared` | Shared utilities |
|
|
465
|
+
| `@markuplint/config-presets` | Built-in configuration presets |
|
|
466
|
+
| `debug` | Debug logging |
|
|
467
|
+
| `is-plain-object` | Plain object type checking |
|
|
468
|
+
| `type-fest` | TypeScript utility types |
|
|
469
|
+
|
|
470
|
+
## Integration Points
|
|
471
|
+
|
|
472
|
+
```mermaid
|
|
473
|
+
flowchart TD
|
|
474
|
+
subgraph upstream ["Upstream"]
|
|
475
|
+
mlAst["@markuplint/ml-ast"]
|
|
476
|
+
mlConfig["@markuplint/ml-config"]
|
|
477
|
+
mlSpec["@markuplint/ml-spec"]
|
|
478
|
+
htmlSpec["@markuplint/html-spec"]
|
|
479
|
+
htmlParser["@markuplint/html-parser"]
|
|
480
|
+
parserUtils["@markuplint/parser-utils"]
|
|
481
|
+
selector["@markuplint/selector"]
|
|
482
|
+
i18n["@markuplint/i18n"]
|
|
483
|
+
shared["@markuplint/shared"]
|
|
484
|
+
configPresets["@markuplint/config-presets"]
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
subgraph pkg ["@markuplint/ml-core"]
|
|
488
|
+
core["MLCore Engine"]
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
subgraph downstream ["Downstream"]
|
|
492
|
+
rules["@markuplint/rules\n(Built-in rule implementations)"]
|
|
493
|
+
markuplint["markuplint\n(CLI, API, MLEngine)"]
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
upstream -->|"types, parsing, specs, i18n"| core
|
|
497
|
+
core -->|"MLDOM classes, MLRule,\ncreateRule, test utils"| rules
|
|
498
|
+
core -->|"MLCore, ViolationCollector,\nconvertRuleset, Plugin types"| markuplint
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Upstream
|
|
502
|
+
|
|
503
|
+
- **`@markuplint/ml-ast`** — AST types used to construct the MLDOM tree
|
|
504
|
+
- **`@markuplint/ml-config`** — Config and rule configuration types
|
|
505
|
+
- **`@markuplint/ml-spec`** — HTML/ARIA specification for element validation, role computation
|
|
506
|
+
- **`@markuplint/html-spec`** — Default spec data bundle
|
|
507
|
+
- **`@markuplint/html-parser`** — Default parser used in test utilities
|
|
508
|
+
- **`@markuplint/parser-utils`** — Parser option types
|
|
509
|
+
- **`@markuplint/selector`** — CSS selector engine for `querySelector`, `matches`, and `RegexSelector`
|
|
510
|
+
- **`@markuplint/i18n`** — Locale sets and translation for rule messages
|
|
511
|
+
- **`@markuplint/shared`** — Shared utility functions
|
|
512
|
+
- **`@markuplint/config-presets`** — Built-in configuration presets
|
|
513
|
+
|
|
514
|
+
### Downstream
|
|
515
|
+
|
|
516
|
+
- **`@markuplint/rules`** — Imports MLDOM classes, `createRule`, `MLRuleContext`, and test utilities to implement built-in rules
|
|
517
|
+
- **`markuplint`** — Imports `MLCore`, `ViolationCollector`, `convertRuleset`, and plugin types to provide the CLI and API
|
|
518
|
+
|
|
519
|
+
## Documentation Map
|
|
520
|
+
|
|
521
|
+
- [MLDOM Reference](docs/ml-dom.md) ([日本語](docs/ml-dom.ja.md)) — Class hierarchy, node properties, tree traversal
|
|
522
|
+
- [Rule System](docs/rule-system.md) ([日本語](docs/rule-system.ja.md)) — MLRule, RuleSeed, MLRuleContext, configuration resolution
|
|
523
|
+
- [Linting Pipeline](docs/linting-pipeline.md) ([日本語](docs/linting-pipeline.ja.md)) — MLCore engine, verify flow, pretender, plugin system
|
|
524
|
+
- [Maintenance Guide](docs/maintenance.md) ([日本語](docs/maintenance.ja.md)) — Commands, recipes, and troubleshooting
|
package/CHANGELOG.md
CHANGED
|
@@ -3,13 +3,63 @@
|
|
|
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
|
-
|
|
6
|
+
# [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **ml-core:** improve detection of namespace ([5b507ad](https://github.com/markuplint/markuplint/commit/5b507ad7c19c5015b8ce587845d901e31dfa6518))
|
|
11
|
+
- treat orphaned end tags as bogus instead of plain text ([#1575](https://github.com/markuplint/markuplint/issues/1575)) ([557199a](https://github.com/markuplint/markuplint/commit/557199a6960ab35573a544f9a33c00e98eb9967e))
|
|
12
|
+
- use explicit `export type` for type-only re-exports ([7c77c05](https://github.com/markuplint/markuplint/commit/7c77c05619518c8d18a183132040f5b2cd0ab6ec))
|
|
13
|
+
|
|
14
|
+
- feat(ml-core)!: adapt DOM layer to simplified AST types ([5d92f2b](https://github.com/markuplint/markuplint/commit/5d92f2be75ce0d45823fb26f72588aecee278ba3))
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
- delete htmx-parser, simplify alpine-parser, add migration guide and tests ([f8dbb09](https://github.com/markuplint/markuplint/commit/f8dbb090707d8cfbf3d859a9b868b2087064f89b))
|
|
19
|
+
- **ml-core:** add directive and IDL resolution to MLAttr constructor ([ba0ad66](https://github.com/markuplint/markuplint/commit/ba0ad66585c022cdb34fda8a8191bcc9af078e07))
|
|
20
|
+
- **ml-core:** add expandNamedRules for named rule groups in rules section ([7eed355](https://github.com/markuplint/markuplint/commit/7eed355075cee90b17a79c0f8a5b18213d1ce54e))
|
|
21
|
+
- **ml-core:** implement VirtualRule system for named nodeRules ([864f51d](https://github.com/markuplint/markuplint/commit/864f51d54dba26c6af2bc45eea3566db5f7d8e26))
|
|
22
|
+
- **ml-core:** require defaultValue for non-boolean rule types in createRule ([6c99908](https://github.com/markuplint/markuplint/commit/6c999087feff4fb8906cf47d564ee08ca8e5f450)), closes [#808](https://github.com/markuplint/markuplint/issues/808)
|
|
23
|
+
- **ml-core:** the each block skips linting in childNodes ([d5ca83d](https://github.com/markuplint/markuplint/commit/d5ca83d5ec6dc9b2f40b5d6599b07cc4746f3dca))
|
|
24
|
+
- **ml-core:** wire ruleCommonSettings through MLCore to Document ([28bb176](https://github.com/markuplint/markuplint/commit/28bb17601b983b3789b2ae200bd77ad887905cda))
|
|
25
|
+
- **ml-spec:** add declarative directivePatterns for parser-less framework support ([ceb9aa6](https://github.com/markuplint/markuplint/commit/ceb9aa67048e3a058b40a9e4d91eb903c8ff1861))
|
|
26
|
+
|
|
27
|
+
### Performance Improvements
|
|
28
|
+
|
|
29
|
+
- **ml-core:** add memoization cache to MLElement.getAccessibleName() ([cdbe289](https://github.com/markuplint/markuplint/commit/cdbe289755312ee30e3f02171f42bf2c00412eea)), closes [#2179](https://github.com/markuplint/markuplint/issues/2179)
|
|
30
|
+
|
|
31
|
+
### BREAKING CHANGES
|
|
32
|
+
|
|
33
|
+
- Multiple breaking changes to DOM API:
|
|
9
34
|
|
|
35
|
+
MLToken:
|
|
10
36
|
|
|
37
|
+
- Compute end positions via getEndCol/getEndLine helpers
|
|
38
|
+
instead of storing them as private fields
|
|
39
|
+
- Use \_astToken.offset/line/col directly
|
|
11
40
|
|
|
41
|
+
MLElement:
|
|
12
42
|
|
|
43
|
+
- Remove selfClosingSolidus property
|
|
44
|
+
- Add blockBehavior: MLASTBlockBehavior | null
|
|
45
|
+
|
|
46
|
+
MLBlock:
|
|
47
|
+
|
|
48
|
+
- Replace conditionalType with blockBehavior property
|
|
49
|
+
|
|
50
|
+
Node traversal:
|
|
51
|
+
|
|
52
|
+
- Use blockBehavior?.type instead of conditionalType
|
|
53
|
+
|
|
54
|
+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
55
|
+
|
|
56
|
+
## [4.13.3](https://github.com/markuplint/markuplint/compare/@markuplint/ml-core@4.13.2...@markuplint/ml-core@4.13.3) (2026-02-10)
|
|
57
|
+
|
|
58
|
+
**Note:** Version bump only for package @markuplint/ml-core
|
|
59
|
+
|
|
60
|
+
## [4.13.2](https://github.com/markuplint/markuplint/compare/@markuplint/ml-core@4.13.1...@markuplint/ml-core@4.13.2) (2025-11-05)
|
|
61
|
+
|
|
62
|
+
**Note:** Version bump only for package @markuplint/ml-core
|
|
13
63
|
|
|
14
64
|
## [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
65
|
|
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: [...] })`
|