@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-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');
|
|
@@ -69,8 +70,8 @@ export class MLCore {
|
|
|
69
70
|
};
|
|
70
71
|
this.#disabledNamespaces = extractDisabledNamespaces(resolvedRules);
|
|
71
72
|
this.#configErrors.push(...namedRulesResult.errors, ...nodeRuleResult.errors, ...childNodeRuleResult.errors);
|
|
72
|
-
this
|
|
73
|
-
this
|
|
73
|
+
this.#parse();
|
|
74
|
+
this.#createDocument();
|
|
74
75
|
}
|
|
75
76
|
/**
|
|
76
77
|
* The parsed document, or a {@link ParserError} if parsing failed.
|
|
@@ -85,8 +86,8 @@ export class MLCore {
|
|
|
85
86
|
*/
|
|
86
87
|
setCode(sourceCode) {
|
|
87
88
|
this.#sourceCode = sourceCode;
|
|
88
|
-
this
|
|
89
|
-
this
|
|
89
|
+
this.#parse();
|
|
90
|
+
this.#createDocument();
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
92
93
|
* Updates the linting configuration and re-creates the document.
|
|
@@ -128,30 +129,23 @@ export class MLCore {
|
|
|
128
129
|
if (parserOptions &&
|
|
129
130
|
(parserOptions.ignoreFrontMatter !== this.#parserOptions.ignoreFrontMatter ||
|
|
130
131
|
parserOptions.authoredElementName !== this.#parserOptions.authoredElementName)) {
|
|
131
|
-
this
|
|
132
|
+
this.#parse();
|
|
132
133
|
}
|
|
133
|
-
this
|
|
134
|
+
this.#createDocument();
|
|
134
135
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
* If the document failed to parse, a single parse-error violation is returned
|
|
139
|
-
* (unless parse errors are suppressed via severity options).
|
|
140
|
-
*
|
|
141
|
-
* @param fix - Whether to attempt auto-fixing violations
|
|
142
|
-
* @returns An array of violations found during verification
|
|
143
|
-
*/
|
|
144
|
-
async verify(fix = false) {
|
|
136
|
+
async verify(fixOrOptions) {
|
|
137
|
+
const options = typeof fixOrOptions === 'boolean' ? { fix: fixOrOptions } : (fixOrOptions ?? {});
|
|
138
|
+
const fix = options.fix ?? false;
|
|
145
139
|
log('verify: start');
|
|
146
140
|
const violations = [];
|
|
147
141
|
if (this.#document instanceof ParserError) {
|
|
148
|
-
const parseError = this
|
|
142
|
+
const parseError = this.#createParseError(this.#document.message, this.#document.line, this.#document.col, this.#document.raw);
|
|
149
143
|
if (!parseError) {
|
|
150
|
-
return [];
|
|
144
|
+
return { violations: [], fixedCode: fix ? this.#sourceCode : undefined };
|
|
151
145
|
}
|
|
152
146
|
violations.push(parseError);
|
|
153
147
|
log('verify: error %o', this.#document.message);
|
|
154
|
-
return violations;
|
|
148
|
+
return { violations, fixedCode: fix ? this.#sourceCode : undefined };
|
|
155
149
|
}
|
|
156
150
|
const definedRuleName = new Set(this.#rules.map(rule => rule.name));
|
|
157
151
|
const setRuleNames = new Set([
|
|
@@ -185,42 +179,8 @@ export class MLCore {
|
|
|
185
179
|
raw: '',
|
|
186
180
|
});
|
|
187
181
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// 1. Exact name match: rules["alias/name"]: false
|
|
191
|
-
// 2. Group disable: rules["groupName"]: false (multi-entry named nodeRules)
|
|
192
|
-
// 3. Namespace wildcard: rules["scope/*"]: false
|
|
193
|
-
// Note: base rule name disable (rules["baseRuleName"]: false) is handled
|
|
194
|
-
// during expandNamedRules for named rule groups in the rules section.
|
|
195
|
-
if (rule.baseRuleId &&
|
|
196
|
-
(this.#ruleset.rules[rule.name] === false ||
|
|
197
|
-
(rule.groupName && this.#ruleset.rules[rule.groupName] === false) ||
|
|
198
|
-
this.#disabledNamespaces.some(ns => rule.name.startsWith(ns)))) {
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
const ruleInfo = rule.getRuleInfo(this.#ruleset, rule.name);
|
|
202
|
-
if (ruleInfo.disabled && ruleInfo.nodeRules.length === 0 && ruleInfo.childNodeRules.length === 0) {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
log('%s Rule: verify', rule.name);
|
|
206
|
-
const results = await rule.verify(this.#document, this.#locale, fix).catch(error => {
|
|
207
|
-
if (error instanceof ParserError) {
|
|
208
|
-
return error;
|
|
209
|
-
}
|
|
210
|
-
throw error;
|
|
211
|
-
});
|
|
212
|
-
if (results instanceof ParserError) {
|
|
213
|
-
const parseError = this._createParseError(results.message, results.line, results.col, results.raw);
|
|
214
|
-
if (parseError) {
|
|
215
|
-
log('%s Rule: verify error %o', rule.name, results.message);
|
|
216
|
-
violations.push(parseError);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
violations.push(...results);
|
|
221
|
-
}
|
|
222
|
-
log('%s Rule: verify end', rule.name);
|
|
223
|
-
}
|
|
182
|
+
const ruleViolations = await this.#runAllRules(fix);
|
|
183
|
+
violations.push(...ruleViolations);
|
|
224
184
|
if (resultLog.enabled) {
|
|
225
185
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
226
186
|
const { e, w, i } = violations.reduce((c, v) => {
|
|
@@ -236,10 +196,42 @@ export class MLCore {
|
|
|
236
196
|
resultLog('Warning: %d', w);
|
|
237
197
|
resultLog('Info: %d', i);
|
|
238
198
|
}
|
|
199
|
+
// Apply fixes if enabled
|
|
200
|
+
let fixedCode;
|
|
201
|
+
let fixSummary;
|
|
202
|
+
if (fix) {
|
|
203
|
+
const hasFixes = violations.some(v => v.fix);
|
|
204
|
+
if (hasFixes) {
|
|
205
|
+
const originalSourceCode = this.#sourceCode;
|
|
206
|
+
const originalAst = this.#ast;
|
|
207
|
+
const originalDocument = this.#document;
|
|
208
|
+
try {
|
|
209
|
+
const fixResult = await this.#multiPassFix(violations);
|
|
210
|
+
fixedCode = fixResult.code;
|
|
211
|
+
fixSummary = fixResult.summary;
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
// Restore original state - verify() must be non-mutating
|
|
215
|
+
this.#sourceCode = originalSourceCode;
|
|
216
|
+
this.#ast = originalAst;
|
|
217
|
+
this.#document = originalDocument;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
fixedCode = this.#sourceCode;
|
|
222
|
+
fixSummary = {
|
|
223
|
+
passCount: 0,
|
|
224
|
+
totalApplied: 0,
|
|
225
|
+
totalSkipped: 0,
|
|
226
|
+
reachedMaxPasses: false,
|
|
227
|
+
firstPassEdits: [],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
239
231
|
log('verify: end');
|
|
240
|
-
return violations;
|
|
232
|
+
return { violations, fixedCode, fixSummary };
|
|
241
233
|
}
|
|
242
|
-
|
|
234
|
+
#createDocument() {
|
|
243
235
|
if (!this.#ast) {
|
|
244
236
|
return;
|
|
245
237
|
}
|
|
@@ -261,7 +253,7 @@ export class MLCore {
|
|
|
261
253
|
}
|
|
262
254
|
}
|
|
263
255
|
}
|
|
264
|
-
|
|
256
|
+
#createParseError(message, line, col, raw) {
|
|
265
257
|
if (this.#severity.parseError === false || this.#severity.parseError === 'off') {
|
|
266
258
|
return null;
|
|
267
259
|
}
|
|
@@ -278,7 +270,138 @@ export class MLCore {
|
|
|
278
270
|
raw,
|
|
279
271
|
};
|
|
280
272
|
}
|
|
281
|
-
|
|
273
|
+
/**
|
|
274
|
+
* Iteratively applies fixes, re-parses, and re-verifies until no overlapping
|
|
275
|
+
* fixes remain or the maximum pass count is reached (ESLint-style multi-pass loop).
|
|
276
|
+
*
|
|
277
|
+
* **Callers must save/restore `#sourceCode`, `#ast`, and `#document`** because
|
|
278
|
+
* this method mutates them during intermediate re-parse steps.
|
|
279
|
+
*
|
|
280
|
+
* @param initialViolations - Violations from the first verification pass
|
|
281
|
+
* @returns The final fixed source code and a summary of the fix process
|
|
282
|
+
*/
|
|
283
|
+
async #multiPassFix(initialViolations) {
|
|
284
|
+
const MAX_FIX_PASSES = 10;
|
|
285
|
+
let currentCode = this.#sourceCode;
|
|
286
|
+
let previousCode;
|
|
287
|
+
let fixes = extractFixes(initialViolations);
|
|
288
|
+
let totalApplied = 0;
|
|
289
|
+
let totalSkipped = 0;
|
|
290
|
+
let firstPassEdits = [];
|
|
291
|
+
let pass = 0;
|
|
292
|
+
for (; pass < MAX_FIX_PASSES; pass++) {
|
|
293
|
+
log('fix pass %d: %d fixes', pass, fixes.length);
|
|
294
|
+
const result = applyFixes(currentCode, fixes);
|
|
295
|
+
totalApplied += result.applied.length;
|
|
296
|
+
totalSkipped += result.skipped.length;
|
|
297
|
+
if (pass === 0) {
|
|
298
|
+
firstPassEdits = result.appliedEdits;
|
|
299
|
+
}
|
|
300
|
+
if (result.applied.length === 0) {
|
|
301
|
+
log('fix pass %d: no fixes applied, stopping', pass);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
if (result.output === currentCode) {
|
|
305
|
+
log('fix pass %d: output unchanged, stopping', pass);
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
// Cycle detection: if the output matches the code from two passes ago,
|
|
309
|
+
// fixes are oscillating (A → B → A) and will never converge.
|
|
310
|
+
if (previousCode !== undefined && result.output === previousCode) {
|
|
311
|
+
log('fix pass %d: cycle detected (output matches pass %d), stopping', pass, pass - 2);
|
|
312
|
+
currentCode = result.output;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
previousCode = currentCode;
|
|
316
|
+
currentCode = result.output;
|
|
317
|
+
if (result.skipped.length === 0) {
|
|
318
|
+
log('fix pass %d: all fixes applied, stopping', pass);
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
// --- Multi-pass path (only when overlapping fixes exist) ---
|
|
322
|
+
log('fix pass %d: %d skipped, re-parsing for next pass', pass, result.skipped.length);
|
|
323
|
+
const previousGoodCode = currentCode;
|
|
324
|
+
this.#sourceCode = currentCode;
|
|
325
|
+
this.#parse();
|
|
326
|
+
this.#createDocument();
|
|
327
|
+
if (this.#document instanceof ParserError) {
|
|
328
|
+
log('fix pass %d: produced unparsable code, reverting to previous state', pass);
|
|
329
|
+
currentCode = previousGoodCode;
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
const newViolations = await this.#runAllRules(true);
|
|
333
|
+
fixes = extractFixes(newViolations);
|
|
334
|
+
if (fixes.length === 0) {
|
|
335
|
+
log('fix pass %d: no more fixable violations, stopping', pass);
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const reachedMaxPasses = pass === MAX_FIX_PASSES;
|
|
340
|
+
if (reachedMaxPasses) {
|
|
341
|
+
log('fix: reached maximum number of passes (%d), some fixes may not have been applied', MAX_FIX_PASSES);
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
code: currentCode,
|
|
345
|
+
summary: {
|
|
346
|
+
passCount: Math.min(pass + 1, MAX_FIX_PASSES),
|
|
347
|
+
totalApplied,
|
|
348
|
+
totalSkipped,
|
|
349
|
+
reachedMaxPasses,
|
|
350
|
+
firstPassEdits,
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Executes all configured rules against the current document and collects violations.
|
|
356
|
+
* Skips disabled rules and handles virtual rule disable conditions.
|
|
357
|
+
*
|
|
358
|
+
* @param fix - Whether to execute fix callbacks on violations
|
|
359
|
+
* @returns All violations produced by the rule set
|
|
360
|
+
*/
|
|
361
|
+
async #runAllRules(fix) {
|
|
362
|
+
const violations = [];
|
|
363
|
+
if (this.#document instanceof ParserError) {
|
|
364
|
+
return violations;
|
|
365
|
+
}
|
|
366
|
+
for (const rule of this.#rules) {
|
|
367
|
+
// For virtual rules, check disable conditions:
|
|
368
|
+
// 1. Exact name match: rules["alias/name"]: false
|
|
369
|
+
// 2. Group disable: rules["groupName"]: false (multi-entry named nodeRules)
|
|
370
|
+
// 3. Namespace wildcard: rules["scope/*"]: false
|
|
371
|
+
// Note: base rule name disable (rules["baseRuleName"]: false) is handled
|
|
372
|
+
// during expandNamedRules for named rule groups in the rules section.
|
|
373
|
+
if (rule.baseRuleId &&
|
|
374
|
+
(this.#ruleset.rules[rule.name] === false ||
|
|
375
|
+
(rule.groupName && this.#ruleset.rules[rule.groupName] === false) ||
|
|
376
|
+
this.#disabledNamespaces.some(ns => rule.name.startsWith(ns)))) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const ruleInfo = rule.getRuleInfo(this.#ruleset, rule.name);
|
|
380
|
+
if (ruleInfo.disabled && ruleInfo.nodeRules.length === 0 && ruleInfo.childNodeRules.length === 0) {
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
log('%s Rule: verify', rule.name);
|
|
384
|
+
const results = await rule.verify(this.#document, this.#locale, fix).catch(error => {
|
|
385
|
+
if (error instanceof ParserError) {
|
|
386
|
+
return error;
|
|
387
|
+
}
|
|
388
|
+
throw error;
|
|
389
|
+
});
|
|
390
|
+
if (results instanceof ParserError) {
|
|
391
|
+
const parseError = this.#createParseError(results.message, results.line, results.col, results.raw);
|
|
392
|
+
if (parseError) {
|
|
393
|
+
log('%s Rule: verify error %o', rule.name, results.message);
|
|
394
|
+
violations.push(parseError);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
violations.push(...results);
|
|
399
|
+
}
|
|
400
|
+
log('%s Rule: verify end', rule.name);
|
|
401
|
+
}
|
|
402
|
+
return violations;
|
|
403
|
+
}
|
|
404
|
+
#parse() {
|
|
282
405
|
try {
|
|
283
406
|
this.#ast = this.#parser.parse(this.#sourceCode, this.#parserOptions);
|
|
284
407
|
}
|
|
@@ -303,3 +426,18 @@ function extractDisabledNamespaces(rules) {
|
|
|
303
426
|
.filter(([key, value]) => key.endsWith('/*') && value === false)
|
|
304
427
|
.map(([key]) => key.slice(0, -1)); // "a11y/*" → "a11y/"
|
|
305
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Collects all `FixData` from violations that have a fix callback result.
|
|
431
|
+
*
|
|
432
|
+
* @param violations - The violations to extract fixes from
|
|
433
|
+
* @returns An array of `FixData` objects ready for `applyFixes()`
|
|
434
|
+
*/
|
|
435
|
+
function extractFixes(violations) {
|
|
436
|
+
const fixes = [];
|
|
437
|
+
for (const v of violations) {
|
|
438
|
+
if (v.fix) {
|
|
439
|
+
fixes.push(v.fix);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return fixes;
|
|
443
|
+
}
|
|
@@ -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
|
@@ -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
|
}
|
package/lib/ml-dom/node/block.js
CHANGED
|
@@ -28,7 +28,8 @@ export class MLBlock extends MLNode {
|
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
29
29
|
document) {
|
|
30
30
|
super(astNode, document, astNode.isFragment);
|
|
31
|
-
//
|
|
31
|
+
// Always transparent: blockBehavior may restrict child treatment in the future,
|
|
32
|
+
// but currently all preprocessor blocks are transparent for tree traversal.
|
|
32
33
|
this.isTransparent = true;
|
|
33
34
|
this.blockBehavior = astNode.blockBehavior;
|
|
34
35
|
}
|
|
@@ -42,21 +42,56 @@ export declare abstract class MLCharacterData<T extends RuleConfigValue, O exten
|
|
|
42
42
|
* @implements DOM API: `CharacterData`
|
|
43
43
|
*/
|
|
44
44
|
after(...nodes: (string | MLElement<any, any>)[]): void;
|
|
45
|
+
/**
|
|
46
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
47
|
+
*
|
|
48
|
+
* @unsupported
|
|
49
|
+
* @implements DOM API: `CharacterData`
|
|
50
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-appenddata
|
|
51
|
+
*/
|
|
45
52
|
appendData(data: string): void;
|
|
46
53
|
/**
|
|
47
54
|
* @implements DOM API: `CharacterData`
|
|
48
55
|
*/
|
|
49
56
|
before(...nodes: (string | MLElement<any, any>)[]): void;
|
|
57
|
+
/**
|
|
58
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
59
|
+
*
|
|
60
|
+
* @unsupported
|
|
61
|
+
* @implements DOM API: `CharacterData`
|
|
62
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-deletedata
|
|
63
|
+
*/
|
|
50
64
|
deleteData(offset: number, count: number): void;
|
|
65
|
+
/**
|
|
66
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
67
|
+
*
|
|
68
|
+
* @unsupported
|
|
69
|
+
* @implements DOM API: `CharacterData`
|
|
70
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-insertdata
|
|
71
|
+
*/
|
|
51
72
|
insertData(offset: number, data: string): void;
|
|
52
73
|
/**
|
|
53
74
|
* @implements DOM API: `CharacterData`
|
|
54
75
|
*/
|
|
55
76
|
remove(): void;
|
|
77
|
+
/**
|
|
78
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
79
|
+
*
|
|
80
|
+
* @unsupported
|
|
81
|
+
* @implements DOM API: `CharacterData`
|
|
82
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-replacedata
|
|
83
|
+
*/
|
|
56
84
|
replaceData(offset: number, count: number, data: string): void;
|
|
57
85
|
/**
|
|
58
86
|
* @implements DOM API: `CharacterData`
|
|
59
87
|
*/
|
|
60
88
|
replaceWith(...nodes: (string | MLElement<any, any>)[]): void;
|
|
89
|
+
/**
|
|
90
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
91
|
+
*
|
|
92
|
+
* @unsupported
|
|
93
|
+
* @implements DOM API: `CharacterData`
|
|
94
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-substringdata
|
|
95
|
+
*/
|
|
61
96
|
substringData(offset: number, count: number): string;
|
|
62
97
|
}
|
|
@@ -7,7 +7,6 @@ export class MLCharacterData extends MLNode {
|
|
|
7
7
|
* @see https://dom.spec.whatwg.org/#dom-characterdata-data
|
|
8
8
|
*/
|
|
9
9
|
get data() {
|
|
10
|
-
// TODO:
|
|
11
10
|
return this.raw;
|
|
12
11
|
}
|
|
13
12
|
/**
|
|
@@ -58,7 +57,13 @@ export class MLCharacterData extends MLNode {
|
|
|
58
57
|
...nodes) {
|
|
59
58
|
after(this, ...nodes);
|
|
60
59
|
}
|
|
61
|
-
|
|
60
|
+
/**
|
|
61
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
62
|
+
*
|
|
63
|
+
* @unsupported
|
|
64
|
+
* @implements DOM API: `CharacterData`
|
|
65
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-appenddata
|
|
66
|
+
*/
|
|
62
67
|
appendData(data) { }
|
|
63
68
|
/**
|
|
64
69
|
* @implements DOM API: `CharacterData`
|
|
@@ -68,9 +73,21 @@ export class MLCharacterData extends MLNode {
|
|
|
68
73
|
...nodes) {
|
|
69
74
|
before(this, ...nodes);
|
|
70
75
|
}
|
|
71
|
-
|
|
76
|
+
/**
|
|
77
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
78
|
+
*
|
|
79
|
+
* @unsupported
|
|
80
|
+
* @implements DOM API: `CharacterData`
|
|
81
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-deletedata
|
|
82
|
+
*/
|
|
72
83
|
deleteData(offset, count) { }
|
|
73
|
-
|
|
84
|
+
/**
|
|
85
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
86
|
+
*
|
|
87
|
+
* @unsupported
|
|
88
|
+
* @implements DOM API: `CharacterData`
|
|
89
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-insertdata
|
|
90
|
+
*/
|
|
74
91
|
insertData(offset, data) { }
|
|
75
92
|
/**
|
|
76
93
|
* @implements DOM API: `CharacterData`
|
|
@@ -78,7 +95,13 @@ export class MLCharacterData extends MLNode {
|
|
|
78
95
|
remove() {
|
|
79
96
|
remove(this);
|
|
80
97
|
}
|
|
81
|
-
|
|
98
|
+
/**
|
|
99
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
100
|
+
*
|
|
101
|
+
* @unsupported
|
|
102
|
+
* @implements DOM API: `CharacterData`
|
|
103
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-replacedata
|
|
104
|
+
*/
|
|
82
105
|
replaceData(offset, count, data) { }
|
|
83
106
|
/**
|
|
84
107
|
* @implements DOM API: `CharacterData`
|
|
@@ -88,7 +111,13 @@ export class MLCharacterData extends MLNode {
|
|
|
88
111
|
...nodes) {
|
|
89
112
|
replaceWith(this, ...nodes);
|
|
90
113
|
}
|
|
91
|
-
|
|
114
|
+
/**
|
|
115
|
+
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
116
|
+
*
|
|
117
|
+
* @unsupported
|
|
118
|
+
* @implements DOM API: `CharacterData`
|
|
119
|
+
* @see https://dom.spec.whatwg.org/#dom-characterdata-substringdata
|
|
120
|
+
*/
|
|
92
121
|
substringData(offset, count) {
|
|
93
122
|
return '';
|
|
94
123
|
}
|
|
@@ -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,
|
|
@@ -1732,18 +1721,4 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
|
|
|
1732
1721
|
* @implements DOM API: `Document`
|
|
1733
1722
|
*/
|
|
1734
1723
|
writeln(...text: readonly string[]): void;
|
|
1735
|
-
/**
|
|
1736
|
-
* Initializes pretender contexts for all element nodes in the document.
|
|
1737
|
-
*
|
|
1738
|
-
* @param pretenders - Optional pretender configurations from the document options
|
|
1739
|
-
*/
|
|
1740
|
-
private _pretending;
|
|
1741
|
-
/**
|
|
1742
|
-
* Maps the ruleset configuration to each node in the document.
|
|
1743
|
-
* Applies global rules, node-specific rules (by selector), and
|
|
1744
|
-
* child-node rules to build the per-node rule configuration.
|
|
1745
|
-
*
|
|
1746
|
-
* @param ruleset - The ruleset containing rules, nodeRules, and childNodeRules
|
|
1747
|
-
*/
|
|
1748
|
-
private _ruleMapping;
|
|
1749
1724
|
}
|