tutuca 0.9.38 → 0.9.39
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/dist/tutuca-cli.js +226 -58
- package/dist/tutuca-dev.js +233 -56
- package/dist/tutuca-dev.min.js +1 -1
- package/package.json +1 -1
package/dist/tutuca-cli.js
CHANGED
|
@@ -2762,7 +2762,7 @@ var init_html_tokenizer = __esm(() => {
|
|
|
2762
2762
|
});
|
|
2763
2763
|
|
|
2764
2764
|
// tools/core/htmllinter-tables.js
|
|
2765
|
-
var VOID_ELEMENTS, RAW_TEXT_ELEMENTS, RCDATA_ELEMENTS, SPECIAL_ELEMENTS, FORMATTING_ELEMENTS, DEFAULT_SCOPE_BOUNDARIES, SCOPE_LIST_ITEM, SCOPE_BUTTON, SCOPE_DEFAULT, SCOPE_TABLE, SCOPE_SELECT, STANDARD_SVG_CAMEL_ELEMENTS, STANDARD_SVG_CAMEL_ATTRS, MATHML_CAMEL_ATTRS, FOREIGN_BREAKOUT_TAGS, MATHML_TEXT_INTEGRATION_POINTS, BLOCK_LEVEL_AUTO_CLOSE_P, SELECT_VALID_CHILDREN, SELECT_BREAKOUT_TAGS, MODES, NS, FRAGMENT_CONTEXT_MODES;
|
|
2765
|
+
var VOID_ELEMENTS, RAW_TEXT_ELEMENTS, RCDATA_ELEMENTS, SPECIAL_ELEMENTS, FORMATTING_ELEMENTS, DEFAULT_SCOPE_BOUNDARIES, MATHML_TEXT_INTEGRATION_POINT_NAMES, SVG_HTML_INTEGRATION_POINT_NAMES, SCOPE_LIST_ITEM, SCOPE_BUTTON, SCOPE_DEFAULT, SCOPE_TABLE, SCOPE_SELECT, STANDARD_SVG_CAMEL_ELEMENTS, STANDARD_SVG_CAMEL_ATTRS, MATHML_CAMEL_ATTRS, SVG_ATTR_LOWERCASE_TO_CAMEL, MATHML_ATTR_LOWERCASE_TO_CAMEL, FOREIGN_BREAKOUT_TAGS, MATHML_TEXT_INTEGRATION_POINTS, BLOCK_LEVEL_AUTO_CLOSE_P, SELECT_VALID_CHILDREN, SELECT_BREAKOUT_TAGS, MODES, NS, FRAGMENT_CONTEXT_MODES;
|
|
2766
2766
|
var init_htmllinter_tables = __esm(() => {
|
|
2767
2767
|
VOID_ELEMENTS = new Set([
|
|
2768
2768
|
"area",
|
|
@@ -2895,8 +2895,11 @@ var init_htmllinter_tables = __esm(() => {
|
|
|
2895
2895
|
"th",
|
|
2896
2896
|
"marquee",
|
|
2897
2897
|
"object",
|
|
2898
|
+
"select",
|
|
2898
2899
|
"template"
|
|
2899
2900
|
]);
|
|
2901
|
+
MATHML_TEXT_INTEGRATION_POINT_NAMES = new Set(["mi", "mo", "mn", "ms", "mtext"]);
|
|
2902
|
+
SVG_HTML_INTEGRATION_POINT_NAMES = new Set(["foreignobject", "desc", "title"]);
|
|
2900
2903
|
SCOPE_LIST_ITEM = new Set([...DEFAULT_SCOPE_BOUNDARIES, "ol", "ul"]);
|
|
2901
2904
|
SCOPE_BUTTON = new Set([...DEFAULT_SCOPE_BOUNDARIES, "button"]);
|
|
2902
2905
|
SCOPE_DEFAULT = DEFAULT_SCOPE_BOUNDARIES;
|
|
@@ -3001,6 +3004,12 @@ var init_htmllinter_tables = __esm(() => {
|
|
|
3001
3004
|
"zoomAndPan"
|
|
3002
3005
|
]);
|
|
3003
3006
|
MATHML_CAMEL_ATTRS = new Set(["definitionURL"]);
|
|
3007
|
+
SVG_ATTR_LOWERCASE_TO_CAMEL = new Map;
|
|
3008
|
+
for (const camel of STANDARD_SVG_CAMEL_ATTRS)
|
|
3009
|
+
SVG_ATTR_LOWERCASE_TO_CAMEL.set(camel.toLowerCase(), camel);
|
|
3010
|
+
MATHML_ATTR_LOWERCASE_TO_CAMEL = new Map;
|
|
3011
|
+
for (const camel of MATHML_CAMEL_ATTRS)
|
|
3012
|
+
MATHML_ATTR_LOWERCASE_TO_CAMEL.set(camel.toLowerCase(), camel);
|
|
3004
3013
|
FOREIGN_BREAKOUT_TAGS = new Set([
|
|
3005
3014
|
"b",
|
|
3006
3015
|
"big",
|
|
@@ -3095,7 +3104,6 @@ var init_htmllinter_tables = __esm(() => {
|
|
|
3095
3104
|
MODES = Object.freeze({
|
|
3096
3105
|
inBody: "inBody",
|
|
3097
3106
|
inTable: "inTable",
|
|
3098
|
-
inTableText: "inTableText",
|
|
3099
3107
|
inCaption: "inCaption",
|
|
3100
3108
|
inColumnGroup: "inColumnGroup",
|
|
3101
3109
|
inTableBody: "inTableBody",
|
|
@@ -3103,8 +3111,7 @@ var init_htmllinter_tables = __esm(() => {
|
|
|
3103
3111
|
inCell: "inCell",
|
|
3104
3112
|
inSelect: "inSelect",
|
|
3105
3113
|
inSelectInTable: "inSelectInTable",
|
|
3106
|
-
inTemplate: "inTemplate"
|
|
3107
|
-
text: "text"
|
|
3114
|
+
inTemplate: "inTemplate"
|
|
3108
3115
|
});
|
|
3109
3116
|
NS = Object.freeze({
|
|
3110
3117
|
html: "html",
|
|
@@ -3171,30 +3178,75 @@ class LinterCtx {
|
|
|
3171
3178
|
this.openElements.push({ name: "select", ns: NS.html, start: -1 });
|
|
3172
3179
|
}
|
|
3173
3180
|
this.insertionMode = ctxInfo.mode;
|
|
3174
|
-
this.originalInsertionMode = MODES.inBody;
|
|
3175
3181
|
this.templateInsertionModes = ctxName === "template" ? [MODES.inTemplate] : [];
|
|
3176
3182
|
this.activeFormatting = [];
|
|
3177
|
-
this.formPointer = null;
|
|
3178
3183
|
this.framesetOk = true;
|
|
3179
3184
|
this.svgCamelElements = opts.svgCamelElements ?? STANDARD_SVG_CAMEL_ELEMENTS;
|
|
3180
|
-
this.transparentTagPrefixes = opts.transparentTagPrefixes ?? [];
|
|
3185
|
+
this.transparentTagPrefixes = (opts.transparentTagPrefixes ?? []).map((p) => p.toLowerCase());
|
|
3181
3186
|
this.currentTagName = "";
|
|
3182
3187
|
this.currentTagRawName = "";
|
|
3183
3188
|
this.currentTagStart = 0;
|
|
3189
|
+
this.currentAttrs = [];
|
|
3190
|
+
this.currentAttr = null;
|
|
3184
3191
|
this.tokenizer = null;
|
|
3185
|
-
this.textRestoreMode = null;
|
|
3186
3192
|
}
|
|
3187
3193
|
onopentagname(start, end) {
|
|
3188
3194
|
const raw = this.html.slice(start, end);
|
|
3189
3195
|
this.currentTagRawName = raw;
|
|
3190
3196
|
this.currentTagName = raw.toLowerCase();
|
|
3191
3197
|
this.currentTagStart = start;
|
|
3198
|
+
this.currentAttrs = [];
|
|
3199
|
+
this.currentAttr = null;
|
|
3200
|
+
}
|
|
3201
|
+
onattribname(start, end) {
|
|
3202
|
+
const rawName = this.html.slice(start, end);
|
|
3203
|
+
this.currentAttr = {
|
|
3204
|
+
name: rawName.toLowerCase(),
|
|
3205
|
+
rawName,
|
|
3206
|
+
nameStart: start,
|
|
3207
|
+
value: null,
|
|
3208
|
+
valueStart: -1,
|
|
3209
|
+
valueEnd: -1,
|
|
3210
|
+
quote: QuoteType.NoValue
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
onattribdata(start, end) {
|
|
3214
|
+
if (!this.currentAttr)
|
|
3215
|
+
return;
|
|
3216
|
+
this.currentAttr.valueStart = start;
|
|
3217
|
+
this.currentAttr.valueEnd = end;
|
|
3218
|
+
this.currentAttr.value = this.html.slice(start, end);
|
|
3219
|
+
}
|
|
3220
|
+
onattribend(quote, _end) {
|
|
3221
|
+
const a = this.currentAttr;
|
|
3222
|
+
if (!a)
|
|
3223
|
+
return;
|
|
3224
|
+
a.quote = quote;
|
|
3225
|
+
const dup = this.currentAttrs.find((x) => x.name === a.name);
|
|
3226
|
+
if (dup) {
|
|
3227
|
+
this.report(HTML_DUPLICATE_ATTRIBUTE, LEVEL_WARN, a.nameStart, {
|
|
3228
|
+
name: a.name,
|
|
3229
|
+
firstAt: dup.nameStart
|
|
3230
|
+
});
|
|
3231
|
+
} else {
|
|
3232
|
+
this.currentAttrs.push(a);
|
|
3233
|
+
}
|
|
3234
|
+
if (a.quote === QuoteType.Unquoted && a.value === "") {
|
|
3235
|
+
this.report(HTML_MISSING_ATTRIBUTE_VALUE, LEVEL_WARN, a.nameStart, {
|
|
3236
|
+
name: a.name
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
this.currentAttr = null;
|
|
3192
3240
|
}
|
|
3193
|
-
onattribname(_start, _end) {}
|
|
3194
|
-
onattribdata(_start, _end) {}
|
|
3195
3241
|
onattribentity(_cp) {}
|
|
3196
|
-
onattribend(_quote, _end) {}
|
|
3197
3242
|
ontextentity(_cp, _end) {}
|
|
3243
|
+
getAttr(name) {
|
|
3244
|
+
const a = this.currentAttrs.find((x) => x.name === name);
|
|
3245
|
+
return a ? a.value : null;
|
|
3246
|
+
}
|
|
3247
|
+
hasAttr(name) {
|
|
3248
|
+
return this.currentAttrs.some((x) => x.name === name);
|
|
3249
|
+
}
|
|
3198
3250
|
onopentagend(endIndex) {
|
|
3199
3251
|
this.handleStartTag(false, endIndex);
|
|
3200
3252
|
}
|
|
@@ -3204,20 +3256,56 @@ class LinterCtx {
|
|
|
3204
3256
|
onclosetag(start, end) {
|
|
3205
3257
|
const raw = this.html.slice(start, end);
|
|
3206
3258
|
const name = raw.toLowerCase();
|
|
3259
|
+
let i = end;
|
|
3260
|
+
let lastNonWs = -1;
|
|
3261
|
+
while (i < this.html.length) {
|
|
3262
|
+
const c = this.html.charCodeAt(i);
|
|
3263
|
+
if (c === 62)
|
|
3264
|
+
break;
|
|
3265
|
+
if (c !== 32 && c !== 9 && c !== 10 && c !== 13 && c !== 12)
|
|
3266
|
+
lastNonWs = i;
|
|
3267
|
+
i++;
|
|
3268
|
+
}
|
|
3269
|
+
if (lastNonWs >= 0) {
|
|
3270
|
+
if (this.html.charCodeAt(lastNonWs) === 47 && lastNonWs === i - 1) {
|
|
3271
|
+
let firstNonWs = -1;
|
|
3272
|
+
for (let j = end;j < lastNonWs; j++) {
|
|
3273
|
+
const c2 = this.html.charCodeAt(j);
|
|
3274
|
+
if (c2 !== 32 && c2 !== 9 && c2 !== 10 && c2 !== 13 && c2 !== 12) {
|
|
3275
|
+
firstNonWs = j;
|
|
3276
|
+
break;
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
if (firstNonWs < 0) {
|
|
3280
|
+
this.report(HTML_SELF_CLOSING_END_TAG, LEVEL_WARN, start, { tag: name });
|
|
3281
|
+
} else {
|
|
3282
|
+
this.report(HTML_ATTRIBUTES_ON_END_TAG, LEVEL_WARN, start, { tag: name });
|
|
3283
|
+
}
|
|
3284
|
+
} else {
|
|
3285
|
+
this.report(HTML_ATTRIBUTES_ON_END_TAG, LEVEL_WARN, start, { tag: name });
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3207
3288
|
this.handleEndTag(name, start);
|
|
3208
3289
|
}
|
|
3209
3290
|
ontext(start, end) {
|
|
3210
3291
|
if (start >= end)
|
|
3211
3292
|
return;
|
|
3293
|
+
const top = this.currentNode();
|
|
3294
|
+
if (top && top.ns !== NS.html && !this.isIntegrationPoint(top))
|
|
3295
|
+
return;
|
|
3212
3296
|
this.handleText(start, end);
|
|
3213
3297
|
}
|
|
3214
|
-
oncomment(
|
|
3298
|
+
oncomment(start, _end, endOffset) {
|
|
3299
|
+
if (endOffset === 0) {
|
|
3300
|
+
this.report(HTML_BOGUS_COMMENT, LEVEL_WARN, start, {
|
|
3301
|
+
mode: this.insertionMode
|
|
3302
|
+
});
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3215
3305
|
oncdata(start, _end, _endOffset) {
|
|
3216
3306
|
if (this.currentNamespace() === NS.html) {
|
|
3217
|
-
this.report(
|
|
3218
|
-
|
|
3219
|
-
mode: this.insertionMode,
|
|
3220
|
-
action: "ignored"
|
|
3307
|
+
this.report(HTML_CDATA_IN_HTML_NAMESPACE, LEVEL_WARN, start, {
|
|
3308
|
+
mode: this.insertionMode
|
|
3221
3309
|
});
|
|
3222
3310
|
}
|
|
3223
3311
|
}
|
|
@@ -3255,14 +3343,37 @@ class LinterCtx {
|
|
|
3255
3343
|
const f = this.openElements[i];
|
|
3256
3344
|
if (f.name === target && f.ns === NS.html)
|
|
3257
3345
|
return true;
|
|
3258
|
-
if (f.ns === NS.html
|
|
3259
|
-
|
|
3260
|
-
|
|
3346
|
+
if (f.ns === NS.html) {
|
|
3347
|
+
if (scopeSet.has(f.name))
|
|
3348
|
+
return false;
|
|
3349
|
+
} else if (this.isScopeBoundary(f)) {
|
|
3261
3350
|
return false;
|
|
3262
3351
|
}
|
|
3263
3352
|
}
|
|
3264
3353
|
return false;
|
|
3265
3354
|
}
|
|
3355
|
+
isScopeBoundary(frame) {
|
|
3356
|
+
if (frame.ns === NS.math) {
|
|
3357
|
+
return MATHML_TEXT_INTEGRATION_POINT_NAMES.has(frame.name) || frame.name === "annotation-xml";
|
|
3358
|
+
}
|
|
3359
|
+
if (frame.ns === NS.svg) {
|
|
3360
|
+
return SVG_HTML_INTEGRATION_POINT_NAMES.has(frame.name);
|
|
3361
|
+
}
|
|
3362
|
+
return false;
|
|
3363
|
+
}
|
|
3364
|
+
isIntegrationPoint(frame) {
|
|
3365
|
+
if (frame.ns === NS.math) {
|
|
3366
|
+
if (MATHML_TEXT_INTEGRATION_POINT_NAMES.has(frame.name))
|
|
3367
|
+
return true;
|
|
3368
|
+
if (frame.name === "annotation-xml" && frame.htmlIntegration)
|
|
3369
|
+
return true;
|
|
3370
|
+
return false;
|
|
3371
|
+
}
|
|
3372
|
+
if (frame.ns === NS.svg) {
|
|
3373
|
+
return SVG_HTML_INTEGRATION_POINT_NAMES.has(frame.name);
|
|
3374
|
+
}
|
|
3375
|
+
return false;
|
|
3376
|
+
}
|
|
3266
3377
|
hasInDefaultScope(target) {
|
|
3267
3378
|
return this.hasInScope(target, SCOPE_DEFAULT);
|
|
3268
3379
|
}
|
|
@@ -3333,13 +3444,43 @@ class LinterCtx {
|
|
|
3333
3444
|
});
|
|
3334
3445
|
}
|
|
3335
3446
|
}
|
|
3447
|
+
const targetNs = ns !== NS.html ? ns : name === "svg" ? NS.svg : name === "math" ? NS.math : NS.html;
|
|
3448
|
+
if (targetNs === NS.svg) {
|
|
3449
|
+
for (const a of this.currentAttrs) {
|
|
3450
|
+
const canonical = SVG_ATTR_LOWERCASE_TO_CAMEL.get(a.name);
|
|
3451
|
+
if (canonical && a.rawName !== canonical) {
|
|
3452
|
+
this.report(HTML_SVG_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, {
|
|
3453
|
+
raw: a.rawName,
|
|
3454
|
+
canonical
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
} else if (targetNs === NS.math) {
|
|
3459
|
+
for (const a of this.currentAttrs) {
|
|
3460
|
+
const canonical = MATHML_ATTR_LOWERCASE_TO_CAMEL.get(a.name);
|
|
3461
|
+
if (canonical && a.rawName !== canonical) {
|
|
3462
|
+
this.report(HTML_MATHML_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, {
|
|
3463
|
+
raw: a.rawName,
|
|
3464
|
+
canonical
|
|
3465
|
+
});
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3336
3469
|
if (this.isTransparentTag(name))
|
|
3337
3470
|
return;
|
|
3338
|
-
|
|
3471
|
+
const top = this.currentNode();
|
|
3472
|
+
const inForeign = ns !== NS.html && !(top && this.isIntegrationPoint(top));
|
|
3473
|
+
if (inForeign && !this.shouldBreakoutFromForeign(name)) {
|
|
3339
3474
|
this.startTagInForeign(name, raw, selfClosing, start);
|
|
3340
3475
|
return;
|
|
3341
3476
|
}
|
|
3342
|
-
if (
|
|
3477
|
+
if (inForeign && this.shouldBreakoutFromForeign(name)) {
|
|
3478
|
+
this.report(HTML_TAG_NOT_ALLOWED_IN_PARENT, LEVEL_WARN, start, {
|
|
3479
|
+
tag: raw,
|
|
3480
|
+
parent: this.currentNode()?.name ?? "(root)",
|
|
3481
|
+
mode: this.insertionMode,
|
|
3482
|
+
action: "foreign-breakout"
|
|
3483
|
+
});
|
|
3343
3484
|
while (this.openElements.length && this.currentNode()?.ns !== NS.html) {
|
|
3344
3485
|
this.openElements.pop();
|
|
3345
3486
|
}
|
|
@@ -3349,20 +3490,28 @@ class LinterCtx {
|
|
|
3349
3490
|
shouldBreakoutFromForeign(name) {
|
|
3350
3491
|
if (FOREIGN_BREAKOUT_TAGS.has(name))
|
|
3351
3492
|
return true;
|
|
3352
|
-
if (name === "font")
|
|
3353
|
-
return
|
|
3493
|
+
if (name === "font") {
|
|
3494
|
+
return this.hasAttr("color") || this.hasAttr("face") || this.hasAttr("size");
|
|
3495
|
+
}
|
|
3354
3496
|
return false;
|
|
3355
3497
|
}
|
|
3356
3498
|
startTagInForeign(name, raw, selfClosing, start) {
|
|
3357
3499
|
const ns = name === "svg" ? NS.svg : name === "math" ? NS.math : this.currentNamespace();
|
|
3358
3500
|
const top = this.currentNode();
|
|
3359
|
-
if (top &&
|
|
3501
|
+
if (top && this.isIntegrationPoint(top) && name !== "mglyph" && name !== "malignmark") {
|
|
3360
3502
|
this.dispatchStartTag(name, raw, selfClosing, start, start + raw.length);
|
|
3361
3503
|
return;
|
|
3362
3504
|
}
|
|
3363
3505
|
if (selfClosing)
|
|
3364
3506
|
return;
|
|
3365
|
-
|
|
3507
|
+
const frame = { name, ns, start };
|
|
3508
|
+
if (ns === NS.math && name === "annotation-xml") {
|
|
3509
|
+
const enc = (this.getAttr("encoding") ?? "").toLowerCase();
|
|
3510
|
+
if (enc === "text/html" || enc === "application/xhtml+xml") {
|
|
3511
|
+
frame.htmlIntegration = true;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
this.openElements.push(frame);
|
|
3366
3515
|
}
|
|
3367
3516
|
dispatchStartTag(name, raw, selfClosing, start, endIndex) {
|
|
3368
3517
|
switch (this.insertionMode) {
|
|
@@ -3372,10 +3521,6 @@ class LinterCtx {
|
|
|
3372
3521
|
return this.startInBody(name, raw, selfClosing, start, endIndex);
|
|
3373
3522
|
case MODES.inTable:
|
|
3374
3523
|
return this.startInTable(name, raw, selfClosing, start, endIndex);
|
|
3375
|
-
case MODES.inTableText:
|
|
3376
|
-
this.flushTableText();
|
|
3377
|
-
this.insertionMode = this.originalInsertionMode;
|
|
3378
|
-
return this.dispatchStartTag(name, raw, selfClosing, start, endIndex);
|
|
3379
3524
|
case MODES.inCaption:
|
|
3380
3525
|
return this.startInCaption(name, raw, selfClosing, start, endIndex);
|
|
3381
3526
|
case MODES.inColumnGroup:
|
|
@@ -3390,8 +3535,6 @@ class LinterCtx {
|
|
|
3390
3535
|
return this.startInSelect(name, raw, selfClosing, start, endIndex);
|
|
3391
3536
|
case MODES.inSelectInTable:
|
|
3392
3537
|
return this.startInSelectInTable(name, raw, selfClosing, start, endIndex);
|
|
3393
|
-
case MODES.text:
|
|
3394
|
-
return;
|
|
3395
3538
|
}
|
|
3396
3539
|
}
|
|
3397
3540
|
startInBody(name, raw, selfClosing, start, _endIndex) {
|
|
@@ -3433,7 +3576,7 @@ class LinterCtx {
|
|
|
3433
3576
|
return;
|
|
3434
3577
|
}
|
|
3435
3578
|
if (name === "form") {
|
|
3436
|
-
if (this.
|
|
3579
|
+
if (this.openElementsHas("form") && !this.openElementsHas("template")) {
|
|
3437
3580
|
this.report(HTML_DUPLICATE_FORM, LEVEL_ERROR, start, {
|
|
3438
3581
|
tag: raw,
|
|
3439
3582
|
mode: this.insertionMode
|
|
@@ -3443,7 +3586,6 @@ class LinterCtx {
|
|
|
3443
3586
|
if (this.hasInButtonScope("p"))
|
|
3444
3587
|
this.implicitlyClose("p", start, raw);
|
|
3445
3588
|
this.push(raw, NS.html, start);
|
|
3446
|
-
this.formPointer = this.currentNode();
|
|
3447
3589
|
return;
|
|
3448
3590
|
}
|
|
3449
3591
|
if (name === "li") {
|
|
@@ -3516,7 +3658,7 @@ class LinterCtx {
|
|
|
3516
3658
|
return;
|
|
3517
3659
|
}
|
|
3518
3660
|
if (name === "a") {
|
|
3519
|
-
if (this.
|
|
3661
|
+
if (this.activeFormattingHas("a")) {
|
|
3520
3662
|
this.report(HTML_NESTED_INTERACTIVE, LEVEL_WARN, start, {
|
|
3521
3663
|
tag: raw,
|
|
3522
3664
|
mode: this.insertionMode
|
|
@@ -3533,7 +3675,7 @@ class LinterCtx {
|
|
|
3533
3675
|
return;
|
|
3534
3676
|
}
|
|
3535
3677
|
if (name === "nobr") {
|
|
3536
|
-
if (this.
|
|
3678
|
+
if (this.activeFormattingHas("nobr")) {
|
|
3537
3679
|
this.report(HTML_NESTED_INTERACTIVE, LEVEL_WARN, start, {
|
|
3538
3680
|
tag: raw,
|
|
3539
3681
|
mode: this.insertionMode
|
|
@@ -3617,7 +3759,6 @@ class LinterCtx {
|
|
|
3617
3759
|
if (e === null)
|
|
3618
3760
|
break;
|
|
3619
3761
|
if (e.name === name) {
|
|
3620
|
-
this.activeFormatting.splice(i, 1);
|
|
3621
3762
|
const idx = this.openElements.indexOf(e);
|
|
3622
3763
|
if (idx >= 0)
|
|
3623
3764
|
this.openElements.splice(idx, 1);
|
|
@@ -3710,6 +3851,9 @@ class LinterCtx {
|
|
|
3710
3851
|
return this.startInBody(name, raw, selfClosing, start, endIndex);
|
|
3711
3852
|
}
|
|
3712
3853
|
if (name === "input") {
|
|
3854
|
+
const type = (this.getAttr("type") ?? "").toLowerCase();
|
|
3855
|
+
if (type === "hidden")
|
|
3856
|
+
return;
|
|
3713
3857
|
this.report(HTML_TAG_NOT_ALLOWED_IN_PARENT, LEVEL_WARN, start, {
|
|
3714
3858
|
tag: raw,
|
|
3715
3859
|
parent: "table",
|
|
@@ -3745,18 +3889,6 @@ class LinterCtx {
|
|
|
3745
3889
|
this.openElements.pop();
|
|
3746
3890
|
}
|
|
3747
3891
|
}
|
|
3748
|
-
flushTableText() {
|
|
3749
|
-
if (!this.pendingTableText)
|
|
3750
|
-
return;
|
|
3751
|
-
const { hasNonWhitespace, start, snippet } = this.pendingTableText;
|
|
3752
|
-
if (hasNonWhitespace) {
|
|
3753
|
-
this.report(HTML_TEXT_NOT_ALLOWED_IN_PARENT, LEVEL_ERROR, start, {
|
|
3754
|
-
mode: this.originalInsertionMode,
|
|
3755
|
-
snippet
|
|
3756
|
-
});
|
|
3757
|
-
}
|
|
3758
|
-
this.pendingTableText = null;
|
|
3759
|
-
}
|
|
3760
3892
|
startInCaption(name, raw, selfClosing, start, endIndex) {
|
|
3761
3893
|
if (name === "caption" || name === "col" || name === "colgroup" || name === "tbody" || name === "td" || name === "tfoot" || name === "th" || name === "thead" || name === "tr") {
|
|
3762
3894
|
if (this.hasInTableScope("caption")) {
|
|
@@ -3989,14 +4121,24 @@ class LinterCtx {
|
|
|
3989
4121
|
return;
|
|
3990
4122
|
const ns = this.currentNamespace();
|
|
3991
4123
|
if (ns !== NS.html) {
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
4124
|
+
let stackIdx = this.openElements.length - 1;
|
|
4125
|
+
let first = true;
|
|
4126
|
+
while (stackIdx > 0) {
|
|
4127
|
+
const f = this.openElements[stackIdx];
|
|
4128
|
+
if (!first && f.ns === NS.html)
|
|
3995
4129
|
break;
|
|
3996
|
-
if (f.name.toLowerCase() === name) {
|
|
3997
|
-
this.openElements.length =
|
|
4130
|
+
if (f.ns !== NS.html && f.name.toLowerCase() === name) {
|
|
4131
|
+
this.openElements.length = stackIdx;
|
|
3998
4132
|
return;
|
|
3999
4133
|
}
|
|
4134
|
+
if (first) {
|
|
4135
|
+
this.report(HTML_UNEXPECTED_END_TAG, LEVEL_WARN, start, {
|
|
4136
|
+
tag: name,
|
|
4137
|
+
mode: this.insertionMode
|
|
4138
|
+
});
|
|
4139
|
+
first = false;
|
|
4140
|
+
}
|
|
4141
|
+
stackIdx--;
|
|
4000
4142
|
}
|
|
4001
4143
|
}
|
|
4002
4144
|
if (VOID_ELEMENTS.has(name)) {
|
|
@@ -4027,19 +4169,33 @@ class LinterCtx {
|
|
|
4027
4169
|
});
|
|
4028
4170
|
return;
|
|
4029
4171
|
}
|
|
4172
|
+
const endIsTableStructural = TABLE_SCOPE_TAGS.has(name);
|
|
4030
4173
|
for (let i = this.openElements.length - 1;i >= 0; i--) {
|
|
4031
4174
|
const f = this.openElements[i];
|
|
4032
4175
|
if (f.ns === NS.html && f.name === name) {
|
|
4176
|
+
for (let j = this.openElements.length - 1;j > i; j--) {
|
|
4177
|
+
const popped = this.openElements[j];
|
|
4178
|
+
if (popped.ns === NS.html && popped.name !== name && !IMPLIED_END_TAGS.has(popped.name) && !(endIsTableStructural && TABLE_SCOPE_TAGS.has(popped.name))) {
|
|
4179
|
+
this.report(HTML_UNCLOSED_BEFORE_END, LEVEL_WARN, start, {
|
|
4180
|
+
tag: name,
|
|
4181
|
+
unclosed: popped.name,
|
|
4182
|
+
mode: this.insertionMode
|
|
4183
|
+
});
|
|
4184
|
+
break;
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4033
4187
|
this.openElements.length = i;
|
|
4034
|
-
if (name === "form")
|
|
4035
|
-
this.formPointer = null;
|
|
4036
4188
|
if (TABLE_SCOPE_TAGS.has(name) || name === "select" || name === "template") {
|
|
4037
4189
|
this.resetInsertionModeAppropriately();
|
|
4038
4190
|
}
|
|
4039
4191
|
return;
|
|
4040
4192
|
}
|
|
4041
|
-
if (f.ns === NS.html && SPECIAL_ELEMENTS.has(f.name)) {
|
|
4042
|
-
|
|
4193
|
+
if (f.ns === NS.html && SPECIAL_ELEMENTS.has(f.name) && !IMPLIED_END_TAGS.has(f.name) && !(endIsTableStructural && TABLE_SCOPE_TAGS.has(f.name))) {
|
|
4194
|
+
this.report(HTML_UNEXPECTED_END_TAG, LEVEL_WARN, start, {
|
|
4195
|
+
tag: name,
|
|
4196
|
+
mode: this.insertionMode
|
|
4197
|
+
});
|
|
4198
|
+
return;
|
|
4043
4199
|
}
|
|
4044
4200
|
}
|
|
4045
4201
|
}
|
|
@@ -4096,7 +4252,7 @@ function offsetToLineCol(lineStarts, offset) {
|
|
|
4096
4252
|
}
|
|
4097
4253
|
return { line: lo + 1, column: offset - lineStarts[lo] + 1 };
|
|
4098
4254
|
}
|
|
4099
|
-
var HTML_TAG_NAME_HAS_UPPERCASE = "HTML_TAG_NAME_HAS_UPPERCASE", HTML_SVG_TAG_WILL_LOWERCASE = "HTML_SVG_TAG_WILL_LOWERCASE", HTML_TAG_NOT_ALLOWED_IN_PARENT = "HTML_TAG_NOT_ALLOWED_IN_PARENT", HTML_TEXT_NOT_ALLOWED_IN_PARENT = "HTML_TEXT_NOT_ALLOWED_IN_PARENT", HTML_VOID_ELEMENT_HAS_CLOSE_TAG = "HTML_VOID_ELEMENT_HAS_CLOSE_TAG", HTML_DUPLICATE_FORM = "HTML_DUPLICATE_FORM", HTML_NESTED_INTERACTIVE = "HTML_NESTED_INTERACTIVE", HTML_MISNESTED_FORMATTING = "HTML_MISNESTED_FORMATTING", HTML_UNEXPECTED_END_TAG = "HTML_UNEXPECTED_END_TAG", LEVEL_ERROR = "error", LEVEL_WARN = "warn", TABLE_SCOPE_TAGS, TABLE_BODY_CELL_TAGS;
|
|
4255
|
+
var HTML_TAG_NAME_HAS_UPPERCASE = "HTML_TAG_NAME_HAS_UPPERCASE", HTML_SVG_TAG_WILL_LOWERCASE = "HTML_SVG_TAG_WILL_LOWERCASE", HTML_SVG_ATTR_WILL_LOWERCASE = "HTML_SVG_ATTR_WILL_LOWERCASE", HTML_MATHML_ATTR_WILL_LOWERCASE = "HTML_MATHML_ATTR_WILL_LOWERCASE", HTML_TAG_NOT_ALLOWED_IN_PARENT = "HTML_TAG_NOT_ALLOWED_IN_PARENT", HTML_TEXT_NOT_ALLOWED_IN_PARENT = "HTML_TEXT_NOT_ALLOWED_IN_PARENT", HTML_VOID_ELEMENT_HAS_CLOSE_TAG = "HTML_VOID_ELEMENT_HAS_CLOSE_TAG", HTML_DUPLICATE_FORM = "HTML_DUPLICATE_FORM", HTML_NESTED_INTERACTIVE = "HTML_NESTED_INTERACTIVE", HTML_MISNESTED_FORMATTING = "HTML_MISNESTED_FORMATTING", HTML_UNEXPECTED_END_TAG = "HTML_UNEXPECTED_END_TAG", HTML_DUPLICATE_ATTRIBUTE = "HTML_DUPLICATE_ATTRIBUTE", HTML_ATTRIBUTES_ON_END_TAG = "HTML_ATTRIBUTES_ON_END_TAG", HTML_SELF_CLOSING_END_TAG = "HTML_SELF_CLOSING_END_TAG", HTML_MISSING_ATTRIBUTE_VALUE = "HTML_MISSING_ATTRIBUTE_VALUE", HTML_CDATA_IN_HTML_NAMESPACE = "HTML_CDATA_IN_HTML_NAMESPACE", HTML_BOGUS_COMMENT = "HTML_BOGUS_COMMENT", HTML_UNCLOSED_BEFORE_END = "HTML_UNCLOSED_BEFORE_END", LEVEL_ERROR = "error", LEVEL_WARN = "warn", TABLE_SCOPE_TAGS, TABLE_BODY_CELL_TAGS, IMPLIED_END_TAGS;
|
|
4100
4256
|
var init_htmllinter = __esm(() => {
|
|
4101
4257
|
init_html_tokenizer();
|
|
4102
4258
|
init_htmllinter_tables();
|
|
@@ -4112,6 +4268,18 @@ var init_htmllinter = __esm(() => {
|
|
|
4112
4268
|
"table"
|
|
4113
4269
|
]);
|
|
4114
4270
|
TABLE_BODY_CELL_TAGS = new Set(["td", "th"]);
|
|
4271
|
+
IMPLIED_END_TAGS = new Set([
|
|
4272
|
+
"dd",
|
|
4273
|
+
"dt",
|
|
4274
|
+
"li",
|
|
4275
|
+
"optgroup",
|
|
4276
|
+
"option",
|
|
4277
|
+
"p",
|
|
4278
|
+
"rb",
|
|
4279
|
+
"rp",
|
|
4280
|
+
"rt",
|
|
4281
|
+
"rtc"
|
|
4282
|
+
]);
|
|
4115
4283
|
});
|
|
4116
4284
|
|
|
4117
4285
|
// tools/core/lint-check.js
|