@markuplint/ml-core 5.0.0-alpha.0 → 5.0.0-alpha.2
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 +125 -5
- package/ARCHITECTURE.md +125 -5
- package/CHANGELOG.md +18 -0
- package/docs/ml-dom/attr.ja.md +1 -1
- package/docs/ml-dom/attr.md +1 -1
- 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/fix-applier.d.ts +30 -0
- package/lib/fix-applier.js +70 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +1 -0
- package/lib/ml-core.d.ts +14 -2
- package/lib/ml-core.js +25 -4
- 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 +6 -38
- package/lib/ml-dom/node/document.d.ts +2 -13
- package/lib/ml-dom/node/document.js +3 -41
- 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 -62
- 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 -30
- package/lib/ml-rule/ml-rule-context.js +15 -15
- package/lib/ml-rule/ml-rule.d.ts +6 -10
- package/lib/ml-rule/ml-rule.js +32 -33
- package/lib/ml-rule/rule-fixer.d.ts +25 -0
- package/lib/ml-rule/rule-fixer.js +32 -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
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { FixData } from '@markuplint/ml-config';
|
|
2
|
+
/**
|
|
3
|
+
* The result of applying fixes to source code.
|
|
4
|
+
*/
|
|
5
|
+
export type FixResult = {
|
|
6
|
+
/** The resulting source code after applying fixes */
|
|
7
|
+
readonly output: string;
|
|
8
|
+
/** Fixes that were successfully applied */
|
|
9
|
+
readonly applied: readonly FixData[];
|
|
10
|
+
/** Fixes that were skipped due to overlapping ranges */
|
|
11
|
+
readonly skipped: readonly FixData[];
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Applies a set of text edits to the source code.
|
|
15
|
+
*
|
|
16
|
+
* **Constraint**: Edits within a single FixData must not overlap each other,
|
|
17
|
+
* and should ideally be ordered by range[0]. Inter-FixData overlap is handled
|
|
18
|
+
* by the skip mechanism, but intra-FixData overlap leads to undefined behavior.
|
|
19
|
+
*
|
|
20
|
+
* Algorithm (modeled after ESLint's SourceCodeFixer):
|
|
21
|
+
* 1. Flatten all FixData.edits into individual edits, each tagged with its parent FixData
|
|
22
|
+
* 2. Sort by range[0] ascending (ties broken by range[1] descending)
|
|
23
|
+
* 3. Apply edits sequentially; skip any edit whose range overlaps a previously applied edit
|
|
24
|
+
* 4. Classify each FixData as applied (all edits applied) or skipped (any edit skipped)
|
|
25
|
+
*
|
|
26
|
+
* @param sourceCode - The original source code
|
|
27
|
+
* @param fixes - The fix data to apply
|
|
28
|
+
* @returns The result containing the fixed code and applied/skipped classification
|
|
29
|
+
*/
|
|
30
|
+
export declare function applyFixes(sourceCode: string, fixes: readonly FixData[]): FixResult;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies a set of text edits to the source code.
|
|
3
|
+
*
|
|
4
|
+
* **Constraint**: Edits within a single FixData must not overlap each other,
|
|
5
|
+
* and should ideally be ordered by range[0]. Inter-FixData overlap is handled
|
|
6
|
+
* by the skip mechanism, but intra-FixData overlap leads to undefined behavior.
|
|
7
|
+
*
|
|
8
|
+
* Algorithm (modeled after ESLint's SourceCodeFixer):
|
|
9
|
+
* 1. Flatten all FixData.edits into individual edits, each tagged with its parent FixData
|
|
10
|
+
* 2. Sort by range[0] ascending (ties broken by range[1] descending)
|
|
11
|
+
* 3. Apply edits sequentially; skip any edit whose range overlaps a previously applied edit
|
|
12
|
+
* 4. Classify each FixData as applied (all edits applied) or skipped (any edit skipped)
|
|
13
|
+
*
|
|
14
|
+
* @param sourceCode - The original source code
|
|
15
|
+
* @param fixes - The fix data to apply
|
|
16
|
+
* @returns The result containing the fixed code and applied/skipped classification
|
|
17
|
+
*/
|
|
18
|
+
export function applyFixes(sourceCode, fixes) {
|
|
19
|
+
if (fixes.length === 0) {
|
|
20
|
+
return { output: sourceCode, applied: [], skipped: [] };
|
|
21
|
+
}
|
|
22
|
+
// Tag each edit with its parent FixData index
|
|
23
|
+
const taggedEdits = [];
|
|
24
|
+
for (const [i, fix] of fixes.entries()) {
|
|
25
|
+
for (const edit of fix.edits) {
|
|
26
|
+
taggedEdits.push({ edit, fixIndex: i });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Sort: range[0] ascending, then range[1] descending (so larger ranges come first at the same start)
|
|
30
|
+
taggedEdits.sort((a, b) => {
|
|
31
|
+
const startDiff = a.edit.range[0] - b.edit.range[0];
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
33
|
+
return startDiff || b.edit.range[1] - a.edit.range[1];
|
|
34
|
+
});
|
|
35
|
+
// Track which FixData indices had at least one skipped edit
|
|
36
|
+
const skippedFixIndices = new Set();
|
|
37
|
+
let lastAppliedEnd = -1;
|
|
38
|
+
const parts = [];
|
|
39
|
+
let cursor = 0;
|
|
40
|
+
for (const { edit, fixIndex } of taggedEdits) {
|
|
41
|
+
const [start, end] = edit.range;
|
|
42
|
+
// Overlap check: if this edit starts before the end of the last applied edit, skip it
|
|
43
|
+
if (start < lastAppliedEnd) {
|
|
44
|
+
skippedFixIndices.add(fixIndex);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// Append the source text between the last edit and this one
|
|
48
|
+
parts.push(sourceCode.slice(cursor, start), edit.text);
|
|
49
|
+
cursor = end;
|
|
50
|
+
lastAppliedEnd = end;
|
|
51
|
+
}
|
|
52
|
+
// Append remaining source text
|
|
53
|
+
parts.push(sourceCode.slice(cursor));
|
|
54
|
+
// Classify FixData as applied or skipped
|
|
55
|
+
const applied = [];
|
|
56
|
+
const skipped = [];
|
|
57
|
+
for (const [i, fix] of fixes.entries()) {
|
|
58
|
+
if (skippedFixIndices.has(i)) {
|
|
59
|
+
skipped.push(fix);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
applied.push(fix);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
output: parts.join(''),
|
|
67
|
+
applied,
|
|
68
|
+
skipped,
|
|
69
|
+
};
|
|
70
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export type { RuleInfo, RuleConfig, RuleConfigValue } from '@markuplint/ml-confi
|
|
|
2
2
|
export { ariaSpecs, contentModelCategoryToTagNames, getAttrSpecs, getComputedRole, getImplicitRole, getPermittedRoles, getRoleSpec, getSpec, resolveNamespace, } from '@markuplint/ml-spec';
|
|
3
3
|
export { Ruleset } from './ruleset/index.js';
|
|
4
4
|
export { enableDebug } from './debug.js';
|
|
5
|
+
export { applyFixes } from './fix-applier.js';
|
|
6
|
+
export type { FixResult } from './fix-applier.js';
|
|
5
7
|
export { getIndent } from './ml-dom/helper/get-indent.js';
|
|
6
8
|
export * from './convert-ruleset.js';
|
|
7
9
|
export * from './ml-core.js';
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { ariaSpecs, contentModelCategoryToTagNames, getAttrSpecs, getComputedRole, getImplicitRole, getPermittedRoles, getRoleSpec, getSpec, resolveNamespace, } from '@markuplint/ml-spec';
|
|
2
2
|
export { Ruleset } from './ruleset/index.js';
|
|
3
3
|
export { enableDebug } from './debug.js';
|
|
4
|
+
export { applyFixes } from './fix-applier.js';
|
|
4
5
|
export { getIndent } from './ml-dom/helper/get-indent.js';
|
|
5
6
|
export * from './convert-ruleset.js';
|
|
6
7
|
export * from './ml-core.js';
|
package/lib/ml-core.d.ts
CHANGED
|
@@ -2,6 +2,15 @@ import type { MLFabric } from './types.js';
|
|
|
2
2
|
import type { PlainData, RuleConfigValue, Violation } from '@markuplint/ml-config';
|
|
3
3
|
import { ParserError } from '@markuplint/parser-utils';
|
|
4
4
|
import { Document } from './ml-dom/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* The result of running {@link MLCore.verify}.
|
|
7
|
+
*/
|
|
8
|
+
export type VerifyResult = {
|
|
9
|
+
/** Violations found during verification */
|
|
10
|
+
readonly violations: readonly Violation[];
|
|
11
|
+
/** The source code after applying fixes. `undefined` when fix is not enabled. */
|
|
12
|
+
readonly fixedCode: string | undefined;
|
|
13
|
+
};
|
|
5
14
|
/**
|
|
6
15
|
* Parameters for constructing an {@link MLCore} instance.
|
|
7
16
|
* Extends {@link MLFabric} with the source code, filename, and debug flag.
|
|
@@ -46,10 +55,13 @@ export declare class MLCore {
|
|
|
46
55
|
* If the document failed to parse, a single parse-error violation is returned
|
|
47
56
|
* (unless parse errors are suppressed via severity options).
|
|
48
57
|
*
|
|
58
|
+
* When `fix` is true, fix callbacks are executed and the resulting TextEdits
|
|
59
|
+
* are applied to produce `fixedCode`.
|
|
60
|
+
*
|
|
49
61
|
* @param fix - Whether to attempt auto-fixing violations
|
|
50
|
-
* @returns
|
|
62
|
+
* @returns Violations and the (possibly fixed) source code
|
|
51
63
|
*/
|
|
52
|
-
verify(fix?: boolean): Promise<
|
|
64
|
+
verify(fix?: boolean): Promise<VerifyResult>;
|
|
53
65
|
private _createDocument;
|
|
54
66
|
private _createParseError;
|
|
55
67
|
private _parse;
|
package/lib/ml-core.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParserError } from '@markuplint/parser-utils';
|
|
2
2
|
import { log, enableDebug } from './debug.js';
|
|
3
|
+
import { applyFixes } from './fix-applier.js';
|
|
3
4
|
import { Document } from './ml-dom/index.js';
|
|
4
5
|
import { expandNamedNodeRules, expandNamedRules } from './virtual-rule.js';
|
|
5
6
|
const resultLog = log.extend('result');
|
|
@@ -138,8 +139,11 @@ export class MLCore {
|
|
|
138
139
|
* If the document failed to parse, a single parse-error violation is returned
|
|
139
140
|
* (unless parse errors are suppressed via severity options).
|
|
140
141
|
*
|
|
142
|
+
* When `fix` is true, fix callbacks are executed and the resulting TextEdits
|
|
143
|
+
* are applied to produce `fixedCode`.
|
|
144
|
+
*
|
|
141
145
|
* @param fix - Whether to attempt auto-fixing violations
|
|
142
|
-
* @returns
|
|
146
|
+
* @returns Violations and the (possibly fixed) source code
|
|
143
147
|
*/
|
|
144
148
|
async verify(fix = false) {
|
|
145
149
|
log('verify: start');
|
|
@@ -147,11 +151,11 @@ export class MLCore {
|
|
|
147
151
|
if (this.#document instanceof ParserError) {
|
|
148
152
|
const parseError = this._createParseError(this.#document.message, this.#document.line, this.#document.col, this.#document.raw);
|
|
149
153
|
if (!parseError) {
|
|
150
|
-
return [];
|
|
154
|
+
return { violations: [], fixedCode: fix ? this.#sourceCode : undefined };
|
|
151
155
|
}
|
|
152
156
|
violations.push(parseError);
|
|
153
157
|
log('verify: error %o', this.#document.message);
|
|
154
|
-
return violations;
|
|
158
|
+
return { violations, fixedCode: fix ? this.#sourceCode : undefined };
|
|
155
159
|
}
|
|
156
160
|
const definedRuleName = new Set(this.#rules.map(rule => rule.name));
|
|
157
161
|
const setRuleNames = new Set([
|
|
@@ -236,8 +240,25 @@ export class MLCore {
|
|
|
236
240
|
resultLog('Warning: %d', w);
|
|
237
241
|
resultLog('Info: %d', i);
|
|
238
242
|
}
|
|
243
|
+
// Apply fixes if enabled
|
|
244
|
+
let fixedCode;
|
|
245
|
+
if (fix) {
|
|
246
|
+
fixedCode = this.#sourceCode;
|
|
247
|
+
const allFixes = [];
|
|
248
|
+
for (const v of violations) {
|
|
249
|
+
if (v.fix) {
|
|
250
|
+
allFixes.push(v.fix);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (allFixes.length > 0) {
|
|
254
|
+
const result = applyFixes(this.#sourceCode, allFixes);
|
|
255
|
+
fixedCode = result.output;
|
|
256
|
+
// TODO(Phase 2): If skipped fixes exist, implement multi-pass re-verify loop
|
|
257
|
+
// (similar to ESLint's 10-pass fix loop) to resolve conflicts iteratively.
|
|
258
|
+
}
|
|
259
|
+
}
|
|
239
260
|
log('verify: end');
|
|
240
|
-
return violations;
|
|
261
|
+
return { violations, fixedCode };
|
|
241
262
|
}
|
|
242
263
|
_createDocument() {
|
|
243
264
|
if (!this.#ast) {
|
|
@@ -39,7 +39,6 @@ node) {
|
|
|
39
39
|
const matched = isFirstToken(prevToken)
|
|
40
40
|
? prevToken.raw.match(/^(?:[\t ]*\r?\n)*([\t ]*)$/)
|
|
41
41
|
: prevToken.raw.match(/\r?\n([\t ]*)$/);
|
|
42
|
-
// console.log({ [`${this}`]: matched, _: prevToken.raw, f: prevToken._isFirstToken() });
|
|
43
42
|
if (matched) {
|
|
44
43
|
// Spaces will include empty string.
|
|
45
44
|
const spaces = matched[1];
|
|
@@ -50,7 +49,7 @@ node) {
|
|
|
50
49
|
return null;
|
|
51
50
|
}
|
|
52
51
|
class MLDOMIndentation {
|
|
53
|
-
#
|
|
52
|
+
#raw;
|
|
54
53
|
line;
|
|
55
54
|
#node;
|
|
56
55
|
#parent;
|
|
@@ -62,38 +61,26 @@ class MLDOMIndentation {
|
|
|
62
61
|
this.line = line;
|
|
63
62
|
this.#node = originTextNode;
|
|
64
63
|
this.#parent = parentNode;
|
|
65
|
-
this.#
|
|
64
|
+
this.#raw = raw;
|
|
66
65
|
}
|
|
67
66
|
get raw() {
|
|
68
67
|
if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
|
|
69
68
|
return '';
|
|
70
69
|
}
|
|
71
|
-
return this.#
|
|
70
|
+
return this.#raw;
|
|
72
71
|
}
|
|
73
72
|
get type() {
|
|
74
73
|
if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
|
|
75
74
|
return 'none';
|
|
76
75
|
}
|
|
77
|
-
const raw = this.#
|
|
76
|
+
const raw = this.#raw;
|
|
78
77
|
return raw === '' ? 'none' : /^\t+$/.test(raw) ? 'tab' : /^[^\t]+$/.test(raw) ? 'space' : 'mixed';
|
|
79
78
|
}
|
|
80
79
|
get width() {
|
|
81
80
|
if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
|
|
82
81
|
return 0;
|
|
83
82
|
}
|
|
84
|
-
return this.#
|
|
85
|
-
}
|
|
86
|
-
fix(raw) {
|
|
87
|
-
const current = this.#fixed;
|
|
88
|
-
this.#fixed = raw;
|
|
89
|
-
const node = this.#node;
|
|
90
|
-
const line = node.startLine;
|
|
91
|
-
const lines = node.raw.split(/\r?\n/);
|
|
92
|
-
const index = this.line - line;
|
|
93
|
-
if (lines[index] != null) {
|
|
94
|
-
lines[index] = lines[index].replace(current, this.#fixed);
|
|
95
|
-
}
|
|
96
|
-
node.fix(lines.join('\n'));
|
|
83
|
+
return this.#raw.length;
|
|
97
84
|
}
|
|
98
85
|
}
|
|
99
86
|
function isFirstToken(
|
|
@@ -152,15 +152,6 @@ export declare class MLAttr<T extends RuleConfigValue, O extends PlainData = und
|
|
|
152
152
|
* @see https://dom.spec.whatwg.org/#dom-attr-value
|
|
153
153
|
*/
|
|
154
154
|
get value(): string;
|
|
155
|
-
/**
|
|
156
|
-
* Fixes the attribute value.
|
|
157
|
-
* If the attribute is not a spread attribute, it calls the `fix` method of the `valueNode`.
|
|
158
|
-
*
|
|
159
|
-
* @implements `@markuplint/ml-core` API: `MLAttr`
|
|
160
|
-
*
|
|
161
|
-
* @param raw - The raw attribute value.
|
|
162
|
-
*/
|
|
163
|
-
fix(raw: string): void;
|
|
164
155
|
/**
|
|
165
156
|
* Returns a normalized string representation of the attribute,
|
|
166
157
|
* stripping extraneous whitespace around the name, equal sign, and value tokens.
|
|
@@ -171,12 +162,10 @@ export declare class MLAttr<T extends RuleConfigValue, O extends PlainData = und
|
|
|
171
162
|
*/
|
|
172
163
|
toNormalizeString(): string;
|
|
173
164
|
/**
|
|
174
|
-
* Returns
|
|
165
|
+
* Returns the raw string representation of the attribute.
|
|
175
166
|
*
|
|
176
167
|
* @implements DOM API: `Attr`
|
|
177
|
-
*
|
|
178
|
-
* @param includesSpacesBeforeName - Whether to include spaces before the attribute name.
|
|
179
168
|
* @returns The string representation of the attribute.
|
|
180
169
|
*/
|
|
181
|
-
toString(
|
|
170
|
+
toString(): string;
|
|
182
171
|
}
|
package/lib/ml-dom/node/attr.js
CHANGED
|
@@ -132,14 +132,14 @@ export class MLAttr extends MLNode {
|
|
|
132
132
|
this.isDuplicatable = this._astToken.isDuplicatable;
|
|
133
133
|
}
|
|
134
134
|
// IDL attribute resolution (after directivePatterns)
|
|
135
|
-
if (ownElement.ownerMLDocument.specs.
|
|
135
|
+
if (ownElement.ownerMLDocument.specs.acceptedAttrNames && !this.isDirective) {
|
|
136
136
|
const { contentAttrName, idlPropName } = searchIDLAttribute(this.#potentialName);
|
|
137
137
|
if (contentAttrName && contentAttrName !== this.#potentialName) {
|
|
138
138
|
this.#potentialName = contentAttrName;
|
|
139
139
|
}
|
|
140
140
|
// Set candidate for IDL naming suggestions (e.g., tabindex → tabIndex in JSX).
|
|
141
|
-
// Only
|
|
142
|
-
if (!resolution && idlPropName) {
|
|
141
|
+
// Only in 'idl' mode (React). In 'both' mode (Svelte), both content and IDL names are accepted.
|
|
142
|
+
if (!resolution && idlPropName && ownElement.ownerMLDocument.specs.acceptedAttrNames === 'idl') {
|
|
143
143
|
const rawName = this.nameNode?.raw ?? '';
|
|
144
144
|
if (rawName !== idlPropName) {
|
|
145
145
|
this.candidate = idlPropName;
|
|
@@ -251,21 +251,6 @@ export class MLAttr extends MLNode {
|
|
|
251
251
|
get value() {
|
|
252
252
|
return this.#potentialValue;
|
|
253
253
|
}
|
|
254
|
-
/**
|
|
255
|
-
* Fixes the attribute value.
|
|
256
|
-
* If the attribute is not a spread attribute, it calls the `fix` method of the `valueNode`.
|
|
257
|
-
*
|
|
258
|
-
* @implements `@markuplint/ml-core` API: `MLAttr`
|
|
259
|
-
*
|
|
260
|
-
* @param raw - The raw attribute value.
|
|
261
|
-
*/
|
|
262
|
-
fix(raw) {
|
|
263
|
-
if (this.localName === '#spread') {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
// `valueNode` is not null when it is no spread.
|
|
267
|
-
this.valueNode?.fix(raw);
|
|
268
|
-
}
|
|
269
254
|
/**
|
|
270
255
|
* Returns a normalized string representation of the attribute,
|
|
271
256
|
* stripping extraneous whitespace around the name, equal sign, and value tokens.
|
|
@@ -281,29 +266,12 @@ export class MLAttr extends MLNode {
|
|
|
281
266
|
return this.raw;
|
|
282
267
|
}
|
|
283
268
|
/**
|
|
284
|
-
* Returns
|
|
269
|
+
* Returns the raw string representation of the attribute.
|
|
285
270
|
*
|
|
286
271
|
* @implements DOM API: `Attr`
|
|
287
|
-
*
|
|
288
|
-
* @param includesSpacesBeforeName - Whether to include spaces before the attribute name.
|
|
289
272
|
* @returns The string representation of the attribute.
|
|
290
273
|
*/
|
|
291
|
-
toString(
|
|
292
|
-
|
|
293
|
-
return this.raw;
|
|
294
|
-
}
|
|
295
|
-
if (this.localName === '#spread') {
|
|
296
|
-
return this.raw;
|
|
297
|
-
}
|
|
298
|
-
const tokens = [this.nameNode?.toString(true) ?? ''];
|
|
299
|
-
if (this.equal && this.equal.toString(true) !== '') {
|
|
300
|
-
tokens.push(this.spacesBeforeEqual?.toString(true) ?? '', this.equal?.toString(true) ?? '', this.spacesAfterEqual?.toString(true) ?? '', this.startQuote?.toString(true) ?? '', this.valueNode?.toString(true) ?? '', this.endQuote?.toString(true) ?? '');
|
|
301
|
-
}
|
|
302
|
-
else if (this.valueNode && this.valueNode.toString(true) !== '') {
|
|
303
|
-
tokens.push(
|
|
304
|
-
//
|
|
305
|
-
'=', this.startQuote?.toString(true) || '"', this.valueNode.toString(true), this.endQuote?.toString(true) || '"');
|
|
306
|
-
}
|
|
307
|
-
return tokens.join('');
|
|
274
|
+
toString() {
|
|
275
|
+
return this.raw;
|
|
308
276
|
}
|
|
309
277
|
}
|
|
@@ -1577,14 +1577,6 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
|
|
|
1577
1577
|
* @implements DOM API: `Document`
|
|
1578
1578
|
*/
|
|
1579
1579
|
getSelection(): Selection | null;
|
|
1580
|
-
/**
|
|
1581
|
-
* Returns a flat, offset-sorted list of all tokens in the document,
|
|
1582
|
-
* including element close tags. The result is cached after the first call.
|
|
1583
|
-
*
|
|
1584
|
-
* @implements `@markuplint/ml-core` API: `MLDocument`
|
|
1585
|
-
* @returns A frozen array of tokens sorted by their starting offset
|
|
1586
|
-
*/
|
|
1587
|
-
getTokenList(): readonly MLToken<import("@markuplint/ml-ast").MLASTToken>[];
|
|
1588
1580
|
/**
|
|
1589
1581
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
1590
1582
|
*
|
|
@@ -1693,15 +1685,12 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
|
|
|
1693
1685
|
*/
|
|
1694
1686
|
startViewTransition(callbackOptions?: ViewTransitionUpdateCallback): ViewTransition;
|
|
1695
1687
|
/**
|
|
1696
|
-
* Returns
|
|
1697
|
-
* returns the document with all lint fixes applied by substituting
|
|
1698
|
-
* fixed token content at the appropriate offsets.
|
|
1688
|
+
* Returns the raw string representation of the document.
|
|
1699
1689
|
*
|
|
1700
1690
|
* @implements `@markuplint/ml-core` API: `MLDocument`
|
|
1701
|
-
* @param fixed - When true, returns the fixed content; otherwise returns the original raw content
|
|
1702
1691
|
* @returns The string content of the document
|
|
1703
1692
|
*/
|
|
1704
|
-
toString(
|
|
1693
|
+
toString(): string;
|
|
1705
1694
|
/**
|
|
1706
1695
|
* Walks the document tree, visiting nodes of the specified type and invoking
|
|
1707
1696
|
* the walker callback for each one. Supports walking Element, Text, Comment,
|
|
@@ -81,7 +81,6 @@ export class MLDocument extends MLParentNode {
|
|
|
81
81
|
* Rules use this as a fallback when their own options do not specify a value.
|
|
82
82
|
*/
|
|
83
83
|
ruleCommonSettings;
|
|
84
|
-
#tokenList = null;
|
|
85
84
|
/**
|
|
86
85
|
* @param ast node list of markuplint AST
|
|
87
86
|
* @param ruleset ruleset object
|
|
@@ -2088,27 +2087,6 @@ export class MLDocument extends MLParentNode {
|
|
|
2088
2087
|
getSelection() {
|
|
2089
2088
|
throw new UnexpectedCallError('Not supported "getSelection" method');
|
|
2090
2089
|
}
|
|
2091
|
-
/**
|
|
2092
|
-
* Returns a flat, offset-sorted list of all tokens in the document,
|
|
2093
|
-
* including element close tags. The result is cached after the first call.
|
|
2094
|
-
*
|
|
2095
|
-
* @implements `@markuplint/ml-core` API: `MLDocument`
|
|
2096
|
-
* @returns A frozen array of tokens sorted by their starting offset
|
|
2097
|
-
*/
|
|
2098
|
-
getTokenList() {
|
|
2099
|
-
if (this.#tokenList) {
|
|
2100
|
-
return this.#tokenList;
|
|
2101
|
-
}
|
|
2102
|
-
const tokens = [];
|
|
2103
|
-
for (const node of this.nodeList) {
|
|
2104
|
-
tokens.push(node);
|
|
2105
|
-
if (node.is(node.ELEMENT_NODE) && node.closeTag) {
|
|
2106
|
-
tokens.push(node.closeTag);
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2109
|
-
this.#tokenList = Object.freeze(tokens.toSorted((a, b) => a.startOffset - b.startOffset));
|
|
2110
|
-
return this.#tokenList;
|
|
2111
|
-
}
|
|
2112
2090
|
/**
|
|
2113
2091
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
2114
2092
|
*
|
|
@@ -2250,29 +2228,13 @@ export class MLDocument extends MLParentNode {
|
|
|
2250
2228
|
throw new UnexpectedCallError('Not supported "startViewTransition" method');
|
|
2251
2229
|
}
|
|
2252
2230
|
/**
|
|
2253
|
-
* Returns
|
|
2254
|
-
* returns the document with all lint fixes applied by substituting
|
|
2255
|
-
* fixed token content at the appropriate offsets.
|
|
2231
|
+
* Returns the raw string representation of the document.
|
|
2256
2232
|
*
|
|
2257
2233
|
* @implements `@markuplint/ml-core` API: `MLDocument`
|
|
2258
|
-
* @param fixed - When true, returns the fixed content; otherwise returns the original raw content
|
|
2259
2234
|
* @returns The string content of the document
|
|
2260
2235
|
*/
|
|
2261
|
-
toString(
|
|
2262
|
-
|
|
2263
|
-
return this.raw;
|
|
2264
|
-
}
|
|
2265
|
-
let raw = this.raw;
|
|
2266
|
-
let offset = 0;
|
|
2267
|
-
for (const node of this.getTokenList()) {
|
|
2268
|
-
const nodeRaw = node.toString(true);
|
|
2269
|
-
if (nodeRaw === node.raw) {
|
|
2270
|
-
continue;
|
|
2271
|
-
}
|
|
2272
|
-
raw = raw.slice(0, node.startOffset + offset) + nodeRaw + raw.slice(node.endOffset + offset);
|
|
2273
|
-
offset += nodeRaw.length - (node.endOffset - node.startOffset);
|
|
2274
|
-
}
|
|
2275
|
-
return raw;
|
|
2236
|
+
toString() {
|
|
2237
|
+
return this.raw;
|
|
2276
2238
|
}
|
|
2277
2239
|
walkOn(type, walker, skipWhenRuleIsDisabled = true) {
|
|
2278
2240
|
return sequentialWalker(this.nodeList, node => {
|
|
@@ -23,21 +23,7 @@ export class MLElementCloseTag extends MLNode {
|
|
|
23
23
|
get rawName() {
|
|
24
24
|
return this._astToken.nodeName;
|
|
25
25
|
}
|
|
26
|
-
toString(
|
|
27
|
-
|
|
28
|
-
return this.raw;
|
|
29
|
-
}
|
|
30
|
-
if (this.nodeName.startsWith('#')) {
|
|
31
|
-
return this.raw;
|
|
32
|
-
}
|
|
33
|
-
if (this.pair.isOmitted) {
|
|
34
|
-
return this.raw;
|
|
35
|
-
}
|
|
36
|
-
return [
|
|
37
|
-
this.pair.tagOpenChar,
|
|
38
|
-
this.pair.tagOpenChar === '' ? '' : '/',
|
|
39
|
-
this.pair.fixedNodeName === this.pair.rawName ? this.rawName : this.pair.fixedNodeName,
|
|
40
|
-
this.pair.tagCloseChar,
|
|
41
|
-
].join('');
|
|
26
|
+
toString() {
|
|
27
|
+
return this.raw;
|
|
42
28
|
}
|
|
43
29
|
}
|
|
@@ -632,13 +632,6 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
|
|
|
632
632
|
* @implements DOM API: `Element`
|
|
633
633
|
*/
|
|
634
634
|
get enterKeyHint(): string;
|
|
635
|
-
/**
|
|
636
|
-
* Returns the fixed (potentially corrected) node name, which may differ from the
|
|
637
|
-
* original node name after lint fixes such as case normalization.
|
|
638
|
-
*
|
|
639
|
-
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
640
|
-
*/
|
|
641
|
-
get fixedNodeName(): string;
|
|
642
635
|
/**
|
|
643
636
|
* Whether this element has any spread attributes (e.g., `{...props}` in JSX).
|
|
644
637
|
*/
|
|
@@ -1819,13 +1812,6 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
|
|
|
1819
1812
|
* @implements DOM API: `Element`
|
|
1820
1813
|
*/
|
|
1821
1814
|
computedStyleMap(): StylePropertyMapReadOnly;
|
|
1822
|
-
/**
|
|
1823
|
-
* Overrides the fixed node name for this element, used when the element's
|
|
1824
|
-
* tag name needs to be corrected during linting (e.g., case normalization).
|
|
1825
|
-
*
|
|
1826
|
-
* @param name - The new node name to set
|
|
1827
|
-
*/
|
|
1828
|
-
fixNodeName(name: string): void;
|
|
1829
1815
|
/**
|
|
1830
1816
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
1831
1817
|
*
|
|
@@ -2252,15 +2238,12 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
|
|
|
2252
2238
|
*/
|
|
2253
2239
|
toNormalizeString(): string;
|
|
2254
2240
|
/**
|
|
2255
|
-
* Returns
|
|
2256
|
-
* returns the element with any lint fixes applied to the tag name,
|
|
2257
|
-
* attributes, and embedded comment nodes.
|
|
2241
|
+
* Returns the raw string representation of this element.
|
|
2258
2242
|
*
|
|
2259
2243
|
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2260
|
-
* @param fixed - When true, returns the fixed content; otherwise returns the original raw content
|
|
2261
2244
|
* @returns The string content of this element
|
|
2262
2245
|
*/
|
|
2263
|
-
toString(
|
|
2246
|
+
toString(): string;
|
|
2264
2247
|
/**
|
|
2265
2248
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
2266
2249
|
*
|
|
@@ -34,7 +34,6 @@ export class MLElement extends MLParentNode {
|
|
|
34
34
|
* - `authored`: Authored element (JSX Element etc.) through the view framework or the template engine.
|
|
35
35
|
*/
|
|
36
36
|
elementType;
|
|
37
|
-
#fixedNodeName;
|
|
38
37
|
#getChildElementsAndTextNodeWithoutWhitespacesCache = null;
|
|
39
38
|
/**
|
|
40
39
|
* Whether this element belongs to a non-HTML namespace (e.g., SVG or MathML).
|
|
@@ -123,7 +122,6 @@ export class MLElement extends MLParentNode {
|
|
|
123
122
|
this.elementType = astNode.elementType;
|
|
124
123
|
this.#localName = ns.localName;
|
|
125
124
|
this.isForeignElement = this.namespaceURI !== HTML_NAMESPACE;
|
|
126
|
-
this.#fixedNodeName = astNode.nodeName;
|
|
127
125
|
this.isOmitted = astNode.isGhost;
|
|
128
126
|
this.tagOpenChar = astNode.tagOpenChar;
|
|
129
127
|
this.tagCloseChar = astNode.tagCloseChar;
|
|
@@ -850,15 +848,6 @@ export class MLElement extends MLParentNode {
|
|
|
850
848
|
get enterKeyHint() {
|
|
851
849
|
throw new UnexpectedCallError('Not supported "enterKeyHint" property');
|
|
852
850
|
}
|
|
853
|
-
/**
|
|
854
|
-
* Returns the fixed (potentially corrected) node name, which may differ from the
|
|
855
|
-
* original node name after lint fixes such as case normalization.
|
|
856
|
-
*
|
|
857
|
-
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
858
|
-
*/
|
|
859
|
-
get fixedNodeName() {
|
|
860
|
-
return this.#fixedNodeName;
|
|
861
|
-
}
|
|
862
851
|
/**
|
|
863
852
|
* Whether this element has any spread attributes (e.g., `{...props}` in JSX).
|
|
864
853
|
*/
|
|
@@ -2385,15 +2374,6 @@ export class MLElement extends MLParentNode {
|
|
|
2385
2374
|
computedStyleMap() {
|
|
2386
2375
|
throw new UnexpectedCallError('Not supported "computedStyleMap" method');
|
|
2387
2376
|
}
|
|
2388
|
-
/**
|
|
2389
|
-
* Overrides the fixed node name for this element, used when the element's
|
|
2390
|
-
* tag name needs to be corrected during linting (e.g., case normalization).
|
|
2391
|
-
*
|
|
2392
|
-
* @param name - The new node name to set
|
|
2393
|
-
*/
|
|
2394
|
-
fixNodeName(name) {
|
|
2395
|
-
this.#fixedNodeName = name;
|
|
2396
|
-
}
|
|
2397
2377
|
/**
|
|
2398
2378
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
2399
2379
|
*
|
|
@@ -3176,52 +3156,13 @@ export class MLElement extends MLParentNode {
|
|
|
3176
3156
|
return normalizedString;
|
|
3177
3157
|
}
|
|
3178
3158
|
/**
|
|
3179
|
-
* Returns
|
|
3180
|
-
* returns the element with any lint fixes applied to the tag name,
|
|
3181
|
-
* attributes, and embedded comment nodes.
|
|
3159
|
+
* Returns the raw string representation of this element.
|
|
3182
3160
|
*
|
|
3183
3161
|
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
3184
|
-
* @param fixed - When true, returns the fixed content; otherwise returns the original raw content
|
|
3185
3162
|
* @returns The string content of this element
|
|
3186
3163
|
*/
|
|
3187
|
-
toString(
|
|
3188
|
-
|
|
3189
|
-
return this.raw;
|
|
3190
|
-
}
|
|
3191
|
-
if (this.pretenderContext?.type === 'pretender') {
|
|
3192
|
-
return this.raw;
|
|
3193
|
-
}
|
|
3194
|
-
if (this.nodeName.startsWith('#')) {
|
|
3195
|
-
return this.raw;
|
|
3196
|
-
}
|
|
3197
|
-
if (this.isOmitted) {
|
|
3198
|
-
return this.raw;
|
|
3199
|
-
}
|
|
3200
|
-
let raw = this.raw;
|
|
3201
|
-
let offset = 0;
|
|
3202
|
-
const overriddenCommentNodes = this.ownerMLDocument.nodeList.filter(node => {
|
|
3203
|
-
if (node.is(node.COMMENT_NODE)) {
|
|
3204
|
-
return this.startOffset < node.startOffset && node.endOffset < this.endOffset;
|
|
3205
|
-
}
|
|
3206
|
-
return false;
|
|
3207
|
-
});
|
|
3208
|
-
const nodes = [
|
|
3209
|
-
{
|
|
3210
|
-
toString: () => this.tagOpenChar + this.fixedNodeName,
|
|
3211
|
-
startOffset: this.startOffset,
|
|
3212
|
-
endOffset: this.startOffset + this.tagOpenChar.length + this.nodeName.length,
|
|
3213
|
-
},
|
|
3214
|
-
...overriddenCommentNodes,
|
|
3215
|
-
...this.attributes,
|
|
3216
|
-
];
|
|
3217
|
-
for (const node of nodes) {
|
|
3218
|
-
const before = raw.slice(0, node.startOffset + offset - this.startOffset);
|
|
3219
|
-
const rawCode = node.toString(true);
|
|
3220
|
-
const after = raw.slice(node.endOffset + offset - this.startOffset);
|
|
3221
|
-
raw = before + rawCode + after;
|
|
3222
|
-
offset += rawCode.length - (node.endOffset - node.startOffset);
|
|
3223
|
-
}
|
|
3224
|
-
return raw;
|
|
3164
|
+
toString() {
|
|
3165
|
+
return this.raw;
|
|
3225
3166
|
}
|
|
3226
3167
|
/**
|
|
3227
3168
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|