@markuplint/ml-core 5.0.0-alpha.1 → 5.0.0-alpha.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.
- package/ARCHITECTURE.ja.md +158 -5
- package/ARCHITECTURE.md +209 -6
- package/CHANGELOG.md +25 -0
- package/docs/ml-dom/document.ja.md +7 -14
- package/docs/ml-dom/document.md +7 -14
- package/docs/ml-dom/element.ja.md +9 -24
- package/docs/ml-dom/element.md +9 -24
- package/docs/rule-system.ja.md +1 -1
- package/docs/rule-system.md +1 -1
- package/lib/cursor-offset.d.ts +13 -0
- package/lib/cursor-offset.js +34 -0
- package/lib/fix-applier.d.ts +32 -0
- package/lib/fix-applier.js +75 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +2 -0
- package/lib/ml-core.d.ts +53 -8
- package/lib/ml-core.js +197 -59
- package/lib/ml-dom/helper/get-indent.d.ts +0 -1
- package/lib/ml-dom/helper/get-indent.js +5 -18
- package/lib/ml-dom/node/attr.d.ts +2 -13
- package/lib/ml-dom/node/attr.js +3 -35
- package/lib/ml-dom/node/block.js +2 -1
- package/lib/ml-dom/node/character-data.d.ts +35 -0
- package/lib/ml-dom/node/character-data.js +35 -6
- package/lib/ml-dom/node/document.d.ts +2 -27
- package/lib/ml-dom/node/document.js +7 -46
- package/lib/ml-dom/node/dom-token-list.d.ts +0 -1
- package/lib/ml-dom/node/dom-token-list.js +3 -3
- package/lib/ml-dom/node/element-close-tag.d.ts +1 -1
- package/lib/ml-dom/node/element-close-tag.js +2 -16
- package/lib/ml-dom/node/element.d.ts +2 -19
- package/lib/ml-dom/node/element.js +3 -65
- package/lib/ml-dom/token/token.d.ts +3 -18
- package/lib/ml-dom/token/token.js +7 -28
- package/lib/ml-rule/index.d.ts +1 -0
- package/lib/ml-rule/index.js +1 -0
- package/lib/ml-rule/ml-rule-context.d.ts +2 -31
- package/lib/ml-rule/ml-rule-context.js +18 -18
- package/lib/ml-rule/ml-rule.d.ts +6 -11
- package/lib/ml-rule/ml-rule.js +36 -37
- package/lib/ml-rule/rule-fixer.d.ts +20 -0
- package/lib/ml-rule/rule-fixer.js +38 -0
- package/lib/ml-rule/types.d.ts +1 -2
- package/lib/test/index.d.ts +1 -10
- package/lib/test/index.js +0 -11
- package/package.json +12 -12
package/lib/ml-rule/ml-rule.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { deleteUndefProp } from '@markuplint/ml-config';
|
|
2
1
|
// @ts-ignore
|
|
3
2
|
import { isPlainObject } from 'is-plain-object';
|
|
3
|
+
import { RuleFixer } from './rule-fixer.js';
|
|
4
4
|
import { MLRuleContext } from './ml-rule-context.js';
|
|
5
|
+
// Stateless — safe to share across all rule instances
|
|
6
|
+
const sharedFixer = new RuleFixer();
|
|
5
7
|
/**
|
|
6
8
|
* Represents a single markuplint rule that can verify documents and report violations.
|
|
7
9
|
*
|
|
@@ -10,14 +12,13 @@ import { MLRuleContext } from './ml-rule-context.js';
|
|
|
10
12
|
*/
|
|
11
13
|
export class MLRule {
|
|
12
14
|
/**
|
|
13
|
-
* For virtual rules, the name of the base rule whose verify
|
|
15
|
+
* For virtual rules, the name of the base rule whose verify logic is reused.
|
|
14
16
|
* When set, violations report this as `ruleId` for backwards compatibility.
|
|
15
17
|
*/
|
|
16
18
|
baseRuleId;
|
|
17
19
|
defaultOptions;
|
|
18
20
|
defaultSeverity;
|
|
19
21
|
defaultValue;
|
|
20
|
-
#f;
|
|
21
22
|
/**
|
|
22
23
|
* For multi-entry named nodeRules, the group name shared by all derived virtual rules.
|
|
23
24
|
* Allows `rules["groupName"]: false` to disable all rules in the group.
|
|
@@ -42,16 +43,15 @@ export class MLRule {
|
|
|
42
43
|
this.defaultValue = (o.defaultValue === undefined ? true : o.defaultValue);
|
|
43
44
|
this.defaultOptions = o.defaultOptions;
|
|
44
45
|
this.#v = o.verify;
|
|
45
|
-
this.#f = o.fix;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
-
* Creates a virtual rule that reuses this rule's verify
|
|
48
|
+
* Creates a virtual rule that reuses this rule's verify logic
|
|
49
49
|
* under a different name (alias). Used by named nodeRules to produce
|
|
50
50
|
* independent rule instances that can be individually configured.
|
|
51
51
|
*
|
|
52
52
|
* @param aliasName - The alias name (must contain `/`)
|
|
53
53
|
* @param options - Override options for the virtual rule
|
|
54
|
-
* @returns A new MLRule instance sharing the same verify
|
|
54
|
+
* @returns A new MLRule instance sharing the same verify logic
|
|
55
55
|
*/
|
|
56
56
|
createAlias(aliasName, options) {
|
|
57
57
|
return new MLRule({
|
|
@@ -63,16 +63,8 @@ export class MLRule {
|
|
|
63
63
|
defaultValue: this.defaultValue,
|
|
64
64
|
defaultOptions: this.defaultOptions,
|
|
65
65
|
verify: this.#v,
|
|
66
|
-
fix: this.#f,
|
|
67
66
|
});
|
|
68
67
|
}
|
|
69
|
-
/**
|
|
70
|
-
* The following getter is unused internally,
|
|
71
|
-
* only for extending from 3rd party library
|
|
72
|
-
*/
|
|
73
|
-
get f() {
|
|
74
|
-
return this.#f;
|
|
75
|
-
}
|
|
76
68
|
/**
|
|
77
69
|
* The following getter is unused internally,
|
|
78
70
|
* only for extending from 3rd party library
|
|
@@ -89,11 +81,11 @@ export class MLRule {
|
|
|
89
81
|
* @returns The global rule info with node and child-node overrides
|
|
90
82
|
*/
|
|
91
83
|
getRuleInfo(ruleSet, ruleName) {
|
|
92
|
-
const info = this
|
|
84
|
+
const info = this.#optimize(ruleSet.rules, ruleName);
|
|
93
85
|
return {
|
|
94
86
|
...info,
|
|
95
|
-
nodeRules: ruleSet.nodeRules.map(r => this
|
|
96
|
-
childNodeRules: ruleSet.childNodeRules.map(r => this
|
|
87
|
+
nodeRules: ruleSet.nodeRules.map(r => this.#optimize(r.rules, ruleName)).filter(r => !r.disabled),
|
|
88
|
+
childNodeRules: ruleSet.childNodeRules.map(r => this.#optimize(r.rules, ruleName)).filter(r => !r.disabled),
|
|
97
89
|
};
|
|
98
90
|
}
|
|
99
91
|
/**
|
|
@@ -135,12 +127,13 @@ export class MLRule {
|
|
|
135
127
|
};
|
|
136
128
|
}
|
|
137
129
|
/**
|
|
138
|
-
* Executes this rule's verify
|
|
130
|
+
* Executes this rule's verify function against a document,
|
|
139
131
|
* then collects and returns the resulting violations.
|
|
132
|
+
* When `fix` is true, fix callbacks on reports are executed to produce {@link FixData}.
|
|
140
133
|
*
|
|
141
134
|
* @param document - The parsed document to verify
|
|
142
135
|
* @param locale - The locale set for translating violation messages
|
|
143
|
-
* @param fix - Whether to
|
|
136
|
+
* @param fix - Whether to execute fix callbacks and attach FixData to violations
|
|
144
137
|
* @returns An array of violations found by this rule
|
|
145
138
|
*/
|
|
146
139
|
async verify(
|
|
@@ -150,13 +143,19 @@ export class MLRule {
|
|
|
150
143
|
const context = new MLRuleContext(document, locale);
|
|
151
144
|
const providableContext = context.provide();
|
|
152
145
|
await this.#v(providableContext);
|
|
153
|
-
if (this.#f && fix) {
|
|
154
|
-
await this.#f(providableContext);
|
|
155
|
-
}
|
|
156
146
|
const ruleId = this.baseRuleId ?? this.name;
|
|
157
147
|
// Only include name and specConformance for virtual rules (named nodeRules)
|
|
158
148
|
const aliasName = this.baseRuleId ? this.name : undefined;
|
|
159
|
-
const
|
|
149
|
+
const violations = context.reports.map(report => {
|
|
150
|
+
// Execute fix callback if fix mode is enabled
|
|
151
|
+
let fixData;
|
|
152
|
+
if (fix && report.fix) {
|
|
153
|
+
const edits = report.fix(sharedFixer);
|
|
154
|
+
const editArray = Array.isArray(edits) ? edits : [edits];
|
|
155
|
+
if (editArray.length > 0) {
|
|
156
|
+
fixData = { edits: editArray };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
160
159
|
if ('scope' in report) {
|
|
161
160
|
let line = report.scope.startLine;
|
|
162
161
|
let col = report.scope.startCol;
|
|
@@ -166,38 +165,38 @@ export class MLRule {
|
|
|
166
165
|
col = report.col;
|
|
167
166
|
raw = report.raw;
|
|
168
167
|
}
|
|
169
|
-
|
|
168
|
+
return {
|
|
170
169
|
severity: report.scope.rule.severity,
|
|
171
170
|
message: report.message,
|
|
172
171
|
line,
|
|
173
172
|
col,
|
|
174
173
|
raw,
|
|
175
174
|
ruleId,
|
|
176
|
-
name: aliasName,
|
|
177
|
-
specConformance: this.specConformance,
|
|
178
|
-
|
|
175
|
+
...(aliasName != null && { name: aliasName }),
|
|
176
|
+
...(this.specConformance != null && { specConformance: this.specConformance }),
|
|
177
|
+
...((report.scope.rule.reason ?? document.rule.reason)
|
|
178
|
+
? { reason: report.scope.rule.reason ?? document.rule.reason }
|
|
179
|
+
: {}),
|
|
180
|
+
...(fixData != null && { fix: fixData }),
|
|
179
181
|
};
|
|
180
|
-
deleteUndefProp(violation);
|
|
181
|
-
return violation;
|
|
182
182
|
}
|
|
183
|
-
|
|
183
|
+
return {
|
|
184
184
|
severity: document.rule.severity,
|
|
185
185
|
message: report.message,
|
|
186
186
|
line: report.line,
|
|
187
187
|
col: report.col,
|
|
188
188
|
raw: report.raw,
|
|
189
189
|
ruleId,
|
|
190
|
-
name: aliasName,
|
|
191
|
-
specConformance: this.specConformance,
|
|
192
|
-
reason: document.rule.reason,
|
|
190
|
+
...(aliasName != null && { name: aliasName }),
|
|
191
|
+
...(this.specConformance != null && { specConformance: this.specConformance }),
|
|
192
|
+
...(document.rule.reason ? { reason: document.rule.reason } : {}),
|
|
193
|
+
...(fixData != null && { fix: fixData }),
|
|
193
194
|
};
|
|
194
|
-
deleteUndefProp(violation);
|
|
195
|
-
return violation;
|
|
196
195
|
});
|
|
197
196
|
document.setRule(null);
|
|
198
|
-
return
|
|
197
|
+
return violations;
|
|
199
198
|
}
|
|
200
|
-
|
|
199
|
+
#optimize(rules, ruleName) {
|
|
201
200
|
const rule = (rules?.[ruleName] ?? false);
|
|
202
201
|
const info = this.optimizeOption(rule);
|
|
203
202
|
return info;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FixToken, IRuleFixer, TextEdit } from '@markuplint/ml-config';
|
|
2
|
+
/**
|
|
3
|
+
* Stateless implementation of {@link IRuleFixer}.
|
|
4
|
+
* Provides helper methods for building {@link TextEdit} objects
|
|
5
|
+
* inside rule fix callbacks.
|
|
6
|
+
*/
|
|
7
|
+
export declare class RuleFixer implements IRuleFixer {
|
|
8
|
+
/** @see {@link IRuleFixer.replaceText} */
|
|
9
|
+
replaceText(token: FixToken, text: string): TextEdit;
|
|
10
|
+
/** @see {@link IRuleFixer.replaceRange} */
|
|
11
|
+
replaceRange(range: readonly [number, number], text: string): TextEdit;
|
|
12
|
+
/** @see {@link IRuleFixer.insertBefore} */
|
|
13
|
+
insertBefore(token: Pick<FixToken, 'startOffset'>, text: string): TextEdit;
|
|
14
|
+
/** @see {@link IRuleFixer.insertAfter} */
|
|
15
|
+
insertAfter(token: FixToken, text: string): TextEdit;
|
|
16
|
+
/** @see {@link IRuleFixer.remove} */
|
|
17
|
+
remove(token: FixToken): TextEdit;
|
|
18
|
+
/** @see {@link IRuleFixer.removeRange} */
|
|
19
|
+
removeRange(range: readonly [number, number]): TextEdit;
|
|
20
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateless implementation of {@link IRuleFixer}.
|
|
3
|
+
* Provides helper methods for building {@link TextEdit} objects
|
|
4
|
+
* inside rule fix callbacks.
|
|
5
|
+
*/
|
|
6
|
+
export class RuleFixer {
|
|
7
|
+
/** @see {@link IRuleFixer.replaceText} */
|
|
8
|
+
replaceText(token, text) {
|
|
9
|
+
return {
|
|
10
|
+
range: [token.startOffset, token.startOffset + token.raw.length],
|
|
11
|
+
text,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** @see {@link IRuleFixer.replaceRange} */
|
|
15
|
+
replaceRange(range, text) {
|
|
16
|
+
return { range, text };
|
|
17
|
+
}
|
|
18
|
+
/** @see {@link IRuleFixer.insertBefore} */
|
|
19
|
+
insertBefore(token, text) {
|
|
20
|
+
return { range: [token.startOffset, token.startOffset], text };
|
|
21
|
+
}
|
|
22
|
+
/** @see {@link IRuleFixer.insertAfter} */
|
|
23
|
+
insertAfter(token, text) {
|
|
24
|
+
const end = token.startOffset + token.raw.length;
|
|
25
|
+
return { range: [end, end], text };
|
|
26
|
+
}
|
|
27
|
+
/** @see {@link IRuleFixer.remove} */
|
|
28
|
+
remove(token) {
|
|
29
|
+
return {
|
|
30
|
+
range: [token.startOffset, token.startOffset + token.raw.length],
|
|
31
|
+
text: '',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** @see {@link IRuleFixer.removeRange} */
|
|
35
|
+
removeRange(range) {
|
|
36
|
+
return { range, text: '' };
|
|
37
|
+
}
|
|
38
|
+
}
|
package/lib/ml-rule/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Attr, Element } from '../ml-dom/index.js';
|
|
|
3
3
|
import type { Translator } from '@markuplint/i18n';
|
|
4
4
|
import type { PlainData, Report, RuleConfigValue, Severity } from '@markuplint/ml-config';
|
|
5
5
|
/**
|
|
6
|
-
* The definition of a markuplint rule, including verification logic
|
|
6
|
+
* The definition of a markuplint rule, including verification logic
|
|
7
7
|
* and default configuration values.
|
|
8
8
|
*
|
|
9
9
|
* @template T - The type of the rule's configuration value (defaults to boolean)
|
|
@@ -17,7 +17,6 @@ export type RuleSeed<T extends RuleConfigValue = boolean, O extends PlainData =
|
|
|
17
17
|
readonly defaultValue?: T;
|
|
18
18
|
readonly defaultOptions?: O;
|
|
19
19
|
verify(context: ReturnType<MLRuleContext<T, O>['provide']>): void | Promise<void>;
|
|
20
|
-
fix?(context: ReturnType<MLRuleContext<T, O>['provide']>): void | Promise<void>;
|
|
21
20
|
};
|
|
22
21
|
/**
|
|
23
22
|
* A generic checker function that produces a violation report from parameters.
|
package/lib/test/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { MLElement } from '../ml-dom/node/element.js';
|
|
2
2
|
import type { MLNode } from '../ml-dom/node/node.js';
|
|
3
|
-
import type {
|
|
4
|
-
import type { MLASTNode, MLASTToken, MLParser } from '@markuplint/ml-ast';
|
|
3
|
+
import type { MLASTNode, MLParser } from '@markuplint/ml-ast';
|
|
5
4
|
import type { Config, PlainData, Pretender, RuleConfigValue } from '@markuplint/ml-config';
|
|
6
5
|
import type { MLMLSpec } from '@markuplint/ml-spec';
|
|
7
6
|
import { MLDocument } from '../ml-dom/node/document.js';
|
|
@@ -38,14 +37,6 @@ export declare function createTestDocument<T extends RuleConfigValue = any, O ex
|
|
|
38
37
|
* @returns A readonly array of all nodes in the parsed document
|
|
39
38
|
*/
|
|
40
39
|
export declare function createTestNodeList(sourceCode: string, options?: CreateTestOptions): readonly MLNode<any, any, MLASTNode>[];
|
|
41
|
-
/**
|
|
42
|
-
* Parses markup source code and returns the flat list of tokens.
|
|
43
|
-
*
|
|
44
|
-
* @param sourceCode - The markup source code to parse
|
|
45
|
-
* @param options - Options for parser, config, specs, and pretenders
|
|
46
|
-
* @returns A readonly array of all tokens in the parsed document
|
|
47
|
-
*/
|
|
48
|
-
export declare function createTestTokenList(sourceCode: string, options?: CreateTestOptions): readonly MLToken<MLASTToken>[];
|
|
49
40
|
/**
|
|
50
41
|
* Parses markup source code and returns the first element node.
|
|
51
42
|
* Throws if the source does not produce an element as its first node.
|
package/lib/test/index.js
CHANGED
|
@@ -35,17 +35,6 @@ export function createTestNodeList(sourceCode, options) {
|
|
|
35
35
|
const document = createTestDocument(sourceCode, options);
|
|
36
36
|
return document.nodeList;
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Parses markup source code and returns the flat list of tokens.
|
|
40
|
-
*
|
|
41
|
-
* @param sourceCode - The markup source code to parse
|
|
42
|
-
* @param options - Options for parser, config, specs, and pretenders
|
|
43
|
-
* @returns A readonly array of all tokens in the parsed document
|
|
44
|
-
*/
|
|
45
|
-
export function createTestTokenList(sourceCode, options) {
|
|
46
|
-
const document = createTestDocument(sourceCode, options);
|
|
47
|
-
return document.getTokenList();
|
|
48
|
-
}
|
|
49
38
|
/**
|
|
50
39
|
* Parses markup source code and returns the first element node.
|
|
51
40
|
* Throws if the source does not produce an element as its first node.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/ml-core",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.3",
|
|
4
4
|
"description": "The core module of markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -30,20 +30,20 @@
|
|
|
30
30
|
"./lib/configs.js": "./lib/configs.browser.js"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@markuplint/config-presets": "5.0.0-alpha.
|
|
34
|
-
"@markuplint/html-parser": "5.0.0-alpha.
|
|
35
|
-
"@markuplint/html-spec": "5.0.0-alpha.
|
|
36
|
-
"@markuplint/i18n": "5.0.0-alpha.
|
|
37
|
-
"@markuplint/ml-ast": "5.0.0-alpha.
|
|
38
|
-
"@markuplint/ml-config": "5.0.0-alpha.
|
|
39
|
-
"@markuplint/ml-spec": "5.0.0-alpha.
|
|
40
|
-
"@markuplint/parser-utils": "5.0.0-alpha.
|
|
41
|
-
"@markuplint/selector": "5.0.0-alpha.
|
|
42
|
-
"@markuplint/shared": "5.0.0-alpha.
|
|
33
|
+
"@markuplint/config-presets": "5.0.0-alpha.3",
|
|
34
|
+
"@markuplint/html-parser": "5.0.0-alpha.3",
|
|
35
|
+
"@markuplint/html-spec": "5.0.0-alpha.3",
|
|
36
|
+
"@markuplint/i18n": "5.0.0-alpha.3",
|
|
37
|
+
"@markuplint/ml-ast": "5.0.0-alpha.3",
|
|
38
|
+
"@markuplint/ml-config": "5.0.0-alpha.3",
|
|
39
|
+
"@markuplint/ml-spec": "5.0.0-alpha.3",
|
|
40
|
+
"@markuplint/parser-utils": "5.0.0-alpha.3",
|
|
41
|
+
"@markuplint/selector": "5.0.0-alpha.3",
|
|
42
|
+
"@markuplint/shared": "5.0.0-alpha.3",
|
|
43
43
|
"@types/debug": "4.1.12",
|
|
44
44
|
"debug": "4.4.3",
|
|
45
45
|
"is-plain-object": "5.0.0",
|
|
46
46
|
"type-fest": "5.4.4"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "2fbdf26daa3d021ac628ccc2f59f0eeae6ddd53d"
|
|
49
49
|
}
|