html-validate 10.16.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/core.js CHANGED
@@ -1155,7 +1155,7 @@ function normalizeAriaNaming(value) {
1155
1155
  return value;
1156
1156
  }
1157
1157
  function migrateElement(src) {
1158
- const implicitRole = normalizeAriaImplicitRole(src.implicitRole ?? src.aria?.implicitRole);
1158
+ const implicitRole = normalizeAriaImplicitRole(src.aria?.implicitRole);
1159
1159
  const result = {
1160
1160
  ...src,
1161
1161
  formAssociated: void 0,
@@ -1571,12 +1571,21 @@ function sliceLocation(location, begin, end, wrap) {
1571
1571
  return sliced;
1572
1572
  }
1573
1573
 
1574
- var NodeType = /* @__PURE__ */ ((NodeType2) => {
1575
- NodeType2[NodeType2["ELEMENT_NODE"] = 1] = "ELEMENT_NODE";
1576
- NodeType2[NodeType2["TEXT_NODE"] = 3] = "TEXT_NODE";
1577
- NodeType2[NodeType2["DOCUMENT_NODE"] = 9] = "DOCUMENT_NODE";
1578
- return NodeType2;
1579
- })(NodeType || {});
1574
+ const Node = {
1575
+ ELEMENT_NODE: 1,
1576
+ TEXT_NODE: 3,
1577
+ DOCUMENT_NODE: 9,
1578
+ /** element wasn't closed */
1579
+ CLOSED_OPEN: 0,
1580
+ /** element closed with end tag <p>...</p> */
1581
+ CLOSED_END_TAG: 1,
1582
+ /** void element with omitted end tag <input> */
1583
+ CLOSED_VOID_OMITTED: 2,
1584
+ /** self-closed void element <input/> */
1585
+ CLOSED_VOID_SELF_CLOSED: 3,
1586
+ /** element with optional end tag <li>foo<li>bar */
1587
+ CLOSED_IMPLICIT_CLOSED: 4
1588
+ };
1580
1589
 
1581
1590
  const DOCUMENT_NODE_NAME = "#document";
1582
1591
  const TEXT_CONTENT = /* @__PURE__ */ Symbol("textContent");
@@ -1703,7 +1712,7 @@ class DOMNode {
1703
1712
  }
1704
1713
  }
1705
1714
  isRootElement() {
1706
- return this.nodeType === NodeType.DOCUMENT_NODE;
1715
+ return this.nodeType === Node.DOCUMENT_NODE;
1707
1716
  }
1708
1717
  /**
1709
1718
  * Tests if two nodes are the same (references the same object).
@@ -2360,7 +2369,7 @@ function parseSelector(selector) {
2360
2369
 
2361
2370
  const TEXT_NODE_NAME = "#text";
2362
2371
  function isTextNode(node) {
2363
- return node?.nodeType === NodeType.TEXT_NODE;
2372
+ return node?.nodeType === Node.TEXT_NODE;
2364
2373
  }
2365
2374
  class TextNode extends DOMNode {
2366
2375
  text;
@@ -2370,7 +2379,7 @@ class TextNode extends DOMNode {
2370
2379
  * @param location - Source code location of this node.
2371
2380
  */
2372
2381
  constructor(text, location) {
2373
- super(NodeType.TEXT_NODE, TEXT_NODE_NAME, location);
2382
+ super(Node.TEXT_NODE, TEXT_NODE_NAME, location);
2374
2383
  this.text = text;
2375
2384
  }
2376
2385
  /**
@@ -2396,16 +2405,8 @@ class TextNode extends DOMNode {
2396
2405
  const CHILD_ELEMENTS = /* @__PURE__ */ Symbol("childElements");
2397
2406
  const ROLE = /* @__PURE__ */ Symbol("role");
2398
2407
  const TABINDEX = /* @__PURE__ */ Symbol("tabindex");
2399
- var NodeClosed = /* @__PURE__ */ ((NodeClosed2) => {
2400
- NodeClosed2[NodeClosed2["Open"] = 0] = "Open";
2401
- NodeClosed2[NodeClosed2["EndTag"] = 1] = "EndTag";
2402
- NodeClosed2[NodeClosed2["VoidOmitted"] = 2] = "VoidOmitted";
2403
- NodeClosed2[NodeClosed2["VoidSelfClosed"] = 3] = "VoidSelfClosed";
2404
- NodeClosed2[NodeClosed2["ImplicitClosed"] = 4] = "ImplicitClosed";
2405
- return NodeClosed2;
2406
- })(NodeClosed || {});
2407
2408
  function isElementNode(node) {
2408
- return node?.nodeType === NodeType.ELEMENT_NODE;
2409
+ return node?.nodeType === Node.ELEMENT_NODE;
2409
2410
  }
2410
2411
  function isInvalidTagName(tagName) {
2411
2412
  return tagName === "" || tagName === "*";
@@ -2439,7 +2440,7 @@ class HtmlElement extends DOMNode {
2439
2440
  nodeType,
2440
2441
  tagName,
2441
2442
  parent = null,
2442
- closed = 1 /* EndTag */,
2443
+ closed = Node.CLOSED_END_TAG,
2443
2444
  meta = null,
2444
2445
  location
2445
2446
  } = details;
@@ -2480,9 +2481,9 @@ class HtmlElement extends DOMNode {
2480
2481
  * @param details - Additional element details.
2481
2482
  */
2482
2483
  static createElement(tagName, location, details = {}) {
2483
- const { closed = 1 /* EndTag */, meta = null, parent = null } = details;
2484
+ const { closed = Node.CLOSED_END_TAG, meta = null, parent = null } = details;
2484
2485
  return new HtmlElement({
2485
- nodeType: NodeType.ELEMENT_NODE,
2486
+ nodeType: Node.ELEMENT_NODE,
2486
2487
  tagName,
2487
2488
  parent,
2488
2489
  closed,
@@ -2495,7 +2496,7 @@ class HtmlElement extends DOMNode {
2495
2496
  */
2496
2497
  static rootNode(location) {
2497
2498
  const root = new HtmlElement({
2498
- nodeType: NodeType.DOCUMENT_NODE,
2499
+ nodeType: Node.DOCUMENT_NODE,
2499
2500
  location
2500
2501
  });
2501
2502
  root.setAnnotation("#document");
@@ -2517,7 +2518,7 @@ class HtmlElement extends DOMNode {
2517
2518
  const closed = isClosed(endToken, meta);
2518
2519
  const location = sliceLocation(startToken.location, 1);
2519
2520
  return new HtmlElement({
2520
- nodeType: NodeType.ELEMENT_NODE,
2521
+ nodeType: Node.ELEMENT_NODE,
2521
2522
  tagName,
2522
2523
  parent: open ? parent : null,
2523
2524
  closed,
@@ -2982,12 +2983,12 @@ class HtmlElement extends DOMNode {
2982
2983
  }
2983
2984
  }
2984
2985
  function isClosed(endToken, meta) {
2985
- let closed = 0 /* Open */;
2986
+ let closed = Node.CLOSED_OPEN;
2986
2987
  if (meta?.void) {
2987
- closed = 2 /* VoidOmitted */;
2988
+ closed = Node.CLOSED_VOID_OMITTED;
2988
2989
  }
2989
2990
  if (endToken.data[0] === "/>") {
2990
- closed = 3 /* VoidSelfClosed */;
2991
+ closed = Node.CLOSED_VOID_SELF_CLOSED;
2991
2992
  }
2992
2993
  return closed;
2993
2994
  }
@@ -3074,18 +3075,6 @@ class DOMTree {
3074
3075
  getElementsByTagName(tagName) {
3075
3076
  return this.root.getElementsByTagName(tagName);
3076
3077
  }
3077
- /**
3078
- * @deprecated use utility function `walk.depthFirst(..)` instead (since 8.21.0).
3079
- */
3080
- visitDepthFirst(callback) {
3081
- walk.depthFirst(this, callback);
3082
- }
3083
- /**
3084
- * @deprecated use `querySelector(..)` instead (since 8.21.0)
3085
- */
3086
- find(callback) {
3087
- return this.root.find(callback);
3088
- }
3089
3078
  querySelector(selector) {
3090
3079
  return this.root.querySelector(selector);
3091
3080
  }
@@ -5737,14 +5726,12 @@ function parsePattern(pattern) {
5737
5726
  default: {
5738
5727
  if (pattern.startsWith("/") && pattern.endsWith("/")) {
5739
5728
  const regexpSource = pattern.slice(1, -1);
5740
- const regexp2 = new RegExp(regexpSource);
5741
- return { regexp: regexp2, description: regexp2.toString() };
5729
+ const regexp = new RegExp(regexpSource);
5730
+ return { regexp, description: regexp.toString() };
5742
5731
  }
5743
- console.warn(
5744
- `Custom pattern "${pattern}" should be wrapped in forward slashes, e.g., "/${pattern}/". Support for unwrapped patterns is deprecated and will be removed in a future version.`
5732
+ throw new Error(
5733
+ `Custom pattern "${pattern}" must be wrapped in forward slashes, i.e. "/${pattern}/"`
5745
5734
  );
5746
- const regexp = new RegExp(pattern);
5747
- return { regexp, description: regexp.toString() };
5748
5735
  }
5749
5736
  }
5750
5737
  }
@@ -5942,7 +5929,7 @@ class CloseOrder extends Rule {
5942
5929
  if (current.voidElement) {
5943
5930
  return;
5944
5931
  }
5945
- if (active.closed === NodeClosed.ImplicitClosed) {
5932
+ if (active.closed === Node.CLOSED_IMPLICIT_CLOSED) {
5946
5933
  return;
5947
5934
  }
5948
5935
  if (active.isRootElement()) {
@@ -8371,7 +8358,7 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
8371
8358
  if (!by) {
8372
8359
  return;
8373
8360
  }
8374
- if (closed.closed !== NodeClosed.ImplicitClosed) {
8361
+ if (closed.closed !== Node.CLOSED_IMPLICIT_CLOSED) {
8375
8362
  return;
8376
8363
  }
8377
8364
  const parent = closed.parent;
@@ -8678,7 +8665,7 @@ class NoRawCharacters extends Rule {
8678
8665
  return;
8679
8666
  }
8680
8667
  for (const child of node.childNodes) {
8681
- if (child.nodeType !== NodeType.TEXT_NODE) {
8668
+ if (child.nodeType !== Node.TEXT_NODE) {
8682
8669
  continue;
8683
8670
  }
8684
8671
  if (matchTemplate.test(child.textContent)) {
@@ -8909,7 +8896,7 @@ class NoSelfClosing extends Rule {
8909
8896
  });
8910
8897
  }
8911
8898
  validateElement(node) {
8912
- if (node.closed !== NodeClosed.VoidSelfClosed) {
8899
+ if (node.closed !== Node.CLOSED_VOID_SELF_CLOSED) {
8913
8900
  return;
8914
8901
  }
8915
8902
  this.report(node, `<${node.tagName}> must not be self-closed`, null, node.tagName);
@@ -8981,6 +8968,55 @@ class NoTrailingWhitespace extends Rule {
8981
8968
  }
8982
8969
  }
8983
8970
 
8971
+ const skipPatterns = [
8972
+ /^data-/i,
8973
+ /^aria-/i,
8974
+ /^on[a-z]/i,
8975
+ /^xml(ns)?:/i,
8976
+ /^:/,
8977
+ /^@/,
8978
+ /^ng-/i,
8979
+ /^v-/i,
8980
+ /^x-/i,
8981
+ /^\[/
8982
+ ];
8983
+ function isKnownDynamicAttr(attr) {
8984
+ return skipPatterns.some((pattern) => pattern.test(attr));
8985
+ }
8986
+ class NoUnknownAttributes extends Rule {
8987
+ documentation(context) {
8988
+ return {
8989
+ description: `The \`${context.attr}\` attribute is not a known attribute on \`<${context.tagName}>\`.`,
8990
+ url: "https://html-validate.org/rules/no-unknown-attributes.html"
8991
+ };
8992
+ }
8993
+ setup() {
8994
+ this.on("attr", (event) => {
8995
+ const node = event.target;
8996
+ const meta = node.meta;
8997
+ const attr = event.key.toLowerCase();
8998
+ if (meta === null) {
8999
+ return;
9000
+ }
9001
+ if (attr in meta.attributes) {
9002
+ return;
9003
+ }
9004
+ if (isKnownDynamicAttr(attr)) {
9005
+ return;
9006
+ }
9007
+ this.report({
9008
+ node,
9009
+ message: `Attribute "${event.key}" is not allowed on <${node.tagName}> element`,
9010
+ location: event.keyLocation,
9011
+ context: {
9012
+ tagName: node.tagName,
9013
+ attr: event.key
9014
+ }
9015
+ });
9016
+ });
9017
+ }
9018
+ }
9019
+
8984
9020
  const defaults$b = {
8985
9021
  include: null,
8986
9022
  exclude: null
@@ -9493,7 +9529,7 @@ class ScriptElement extends Rule {
9493
9529
  if (node?.tagName !== "script") {
9494
9530
  return;
9495
9531
  }
9496
- if (node.closed !== NodeClosed.EndTag) {
9532
+ if (node.closed !== Node.CLOSED_END_TAG) {
9497
9533
  this.report(node, `End tag for <${node.tagName}> must not be omitted`);
9498
9534
  }
9499
9535
  });
@@ -10036,7 +10072,7 @@ class UnknownCharReference extends Rule {
10036
10072
  return;
10037
10073
  }
10038
10074
  for (const child of node.childNodes) {
10039
- if (child.nodeType !== NodeType.TEXT_NODE) {
10075
+ if (child.nodeType !== Node.TEXT_NODE) {
10040
10076
  continue;
10041
10077
  }
10042
10078
  this.findCharacterReferences(node, child.textContent, child.location, {
@@ -10773,7 +10809,7 @@ class VoidContent extends Rule {
10773
10809
  if (!node.voidElement) {
10774
10810
  return;
10775
10811
  }
10776
- if (node.closed === NodeClosed.EndTag) {
10812
+ if (node.closed === Node.CLOSED_END_TAG) {
10777
10813
  this.report(
10778
10814
  null,
10779
10815
  `End tag for <${node.tagName}> must be omitted`,
@@ -10826,7 +10862,7 @@ class VoidStyle extends Rule {
10826
10862
  if (!node.voidElement) {
10827
10863
  return;
10828
10864
  }
10829
- if (node.closed !== NodeClosed.VoidSelfClosed) {
10865
+ if (node.closed !== Node.CLOSED_VOID_SELF_CLOSED) {
10830
10866
  return;
10831
10867
  }
10832
10868
  this.reportError(
@@ -10838,7 +10874,7 @@ class VoidStyle extends Rule {
10838
10874
  if (!node.voidElement) {
10839
10875
  return;
10840
10876
  }
10841
- if (node.closed !== NodeClosed.VoidOmitted) {
10877
+ if (node.closed !== Node.CLOSED_VOID_OMITTED) {
10842
10878
  return;
10843
10879
  }
10844
10880
  this.reportError(
@@ -11288,6 +11324,7 @@ const bundledRules = {
11288
11324
  "no-self-closing": NoSelfClosing,
11289
11325
  "no-style-tag": NoStyleTag,
11290
11326
  "no-trailing-whitespace": NoTrailingWhitespace,
11327
+ "no-unknown-attributes": NoUnknownAttributes,
11291
11328
  "no-unknown-elements": NoUnknownElements,
11292
11329
  "no-unused-disable": NoUnusedDisable,
11293
11330
  "no-utf8-bom": NoUtf8Bom,
@@ -12602,7 +12639,7 @@ class EventHandler {
12602
12639
  }
12603
12640
 
12604
12641
  const name = "html-validate";
12605
- const version = "10.16.0";
12642
+ const version = "11.0.0";
12606
12643
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
12607
12644
 
12608
12645
  function freeze(src) {
@@ -13017,7 +13054,7 @@ class Parser {
13017
13054
  if (matches) {
13018
13055
  const intermediaryMeta = this.metaTable.getMetaFor(entry.open);
13019
13056
  return HtmlElement.createElement(entry.open, token.location, {
13020
- closed: NodeClosed.Open,
13057
+ closed: Node.CLOSED_OPEN,
13021
13058
  meta: intermediaryMeta,
13022
13059
  parent
13023
13060
  });
@@ -13100,11 +13137,11 @@ class Parser {
13100
13137
  this.metaTable,
13101
13138
  this.currentNamespace
13102
13139
  );
13103
- const isClosing = !isStartTag || node.closed !== NodeClosed.Open;
13140
+ const isClosing = !isStartTag || node.closed !== Node.CLOSED_OPEN;
13104
13141
  const isForeign = node.meta?.foreign;
13105
13142
  while (this.closeOptional(startToken)) {
13106
13143
  const active = this.dom.getActive();
13107
- active.closed = NodeClosed.ImplicitClosed;
13144
+ active.closed = Node.CLOSED_IMPLICIT_CLOSED;
13108
13145
  this.closeElement(source, node, active, startToken.location);
13109
13146
  this.dom.popActive();
13110
13147
  }
@@ -13145,7 +13182,7 @@ class Parser {
13145
13182
  if (isClosing) {
13146
13183
  const active = this.dom.getActive();
13147
13184
  if (!isStartTag) {
13148
- node.closed = NodeClosed.EndTag;
13185
+ node.closed = Node.CLOSED_END_TAG;
13149
13186
  }
13150
13187
  this.closeElement(source, node, active, endToken.location);
13151
13188
  const mismatched = node.tagName !== active.tagName;
@@ -13168,7 +13205,7 @@ class Parser {
13168
13205
  location
13169
13206
  };
13170
13207
  this.trigger("tag:end", event);
13171
- if (node && node.tagName !== active.tagName && active.closed !== NodeClosed.ImplicitClosed) {
13208
+ if (node && node.tagName !== active.tagName && active.closed !== Node.CLOSED_IMPLICIT_CLOSED) {
13172
13209
  return;
13173
13210
  }
13174
13211
  if (!active.isRootElement()) {
@@ -13525,7 +13562,7 @@ class Parser {
13525
13562
  let active = this.dom.getActive();
13526
13563
  while (!active.isRootElement()) {
13527
13564
  if (active.meta?.implicitClosed || active.meta?.optionalEnd) {
13528
- active.closed = NodeClosed.ImplicitClosed;
13565
+ active.closed = Node.CLOSED_IMPLICIT_CLOSED;
13529
13566
  this.closeElement(source, documentElement, active, location);
13530
13567
  } else {
13531
13568
  this.closeElement(source, null, active, location);
@@ -15309,7 +15346,7 @@ var ignoreExports = /*@__PURE__*/ requireIgnore();
15309
15346
  var ignore = /*@__PURE__*/getDefaultExportFromCjs(ignoreExports);
15310
15347
 
15311
15348
  const engines = {
15312
- node: "^20.19.0 || ^22.16.0 || >= 24.0.0"
15349
+ node: "^22.16.0 || >= 24.0.0"
15313
15350
  };
15314
15351
 
15315
15352
  var workerPath = "./jest-worker.js";
@@ -15328,8 +15365,7 @@ exports.HtmlElement = HtmlElement;
15328
15365
  exports.MetaCopyableProperty = MetaCopyableProperty;
15329
15366
  exports.MetaTable = MetaTable;
15330
15367
  exports.NestedError = NestedError;
15331
- exports.NodeClosed = NodeClosed;
15332
- exports.NodeType = NodeType;
15368
+ exports.Node = Node;
15333
15369
  exports.Parser = Parser;
15334
15370
  exports.PerformanceTracker = PerformanceTracker;
15335
15371
  exports.Reporter = Reporter;
@@ -15342,7 +15378,6 @@ exports.TextClassification = TextClassification;
15342
15378
  exports.TextContent = TextContent$1;
15343
15379
  exports.TextNode = TextNode;
15344
15380
  exports.UserError = UserError;
15345
- exports.Validator = Validator;
15346
15381
  exports.WrappedError = WrappedError;
15347
15382
  exports.ariaNaming = ariaNaming;
15348
15383
  exports.bugs = bugs;