html-validate 10.7.0 → 10.8.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
@@ -4008,7 +4008,7 @@ class Rule {
4008
4008
  }
4009
4009
  }
4010
4010
 
4011
- const defaults$z = {
4011
+ const defaults$A = {
4012
4012
  allowExternal: true,
4013
4013
  allowRelative: true,
4014
4014
  allowAbsolute: true,
@@ -4052,7 +4052,7 @@ class AllowedLinks extends Rule {
4052
4052
  allowRelative;
4053
4053
  allowAbsolute;
4054
4054
  constructor(options) {
4055
- super({ ...defaults$z, ...options });
4055
+ super({ ...defaults$A, ...options });
4056
4056
  this.allowExternal = parseAllow(this.options.allowExternal);
4057
4057
  this.allowRelative = parseAllow(this.options.allowRelative);
4058
4058
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -4220,7 +4220,7 @@ class AllowedLinks extends Rule {
4220
4220
  }
4221
4221
  }
4222
4222
 
4223
- const defaults$y = {
4223
+ const defaults$z = {
4224
4224
  accessible: true
4225
4225
  };
4226
4226
  function findByTarget(target, siblings) {
@@ -4250,7 +4250,7 @@ function getDescription$1(context) {
4250
4250
  }
4251
4251
  class AreaAlt extends Rule {
4252
4252
  constructor(options) {
4253
- super({ ...defaults$y, ...options });
4253
+ super({ ...defaults$z, ...options });
4254
4254
  }
4255
4255
  static schema() {
4256
4256
  return {
@@ -4329,7 +4329,7 @@ class AriaHiddenBody extends Rule {
4329
4329
  }
4330
4330
  }
4331
4331
 
4332
- const defaults$x = {
4332
+ const defaults$y = {
4333
4333
  allowAnyNamable: false,
4334
4334
  elements: {
4335
4335
  include: null,
@@ -4377,7 +4377,7 @@ function isValidUsage(target, meta) {
4377
4377
  }
4378
4378
  class AriaLabelMisuse extends Rule {
4379
4379
  constructor(options) {
4380
- super({ ...defaults$x, ...options });
4380
+ super({ ...defaults$y, ...options });
4381
4381
  }
4382
4382
  static schema() {
4383
4383
  return {
@@ -4546,14 +4546,14 @@ class CaseStyle {
4546
4546
  }
4547
4547
  }
4548
4548
 
4549
- const defaults$w = {
4549
+ const defaults$x = {
4550
4550
  style: "lowercase",
4551
4551
  ignoreForeign: true
4552
4552
  };
4553
4553
  class AttrCase extends Rule {
4554
4554
  style;
4555
4555
  constructor(options) {
4556
- super({ ...defaults$w, ...options });
4556
+ super({ ...defaults$x, ...options });
4557
4557
  this.style = new CaseStyle(this.options.style, "attr-case");
4558
4558
  }
4559
4559
  static schema() {
@@ -4958,7 +4958,7 @@ class AttrDelimiter extends Rule {
4958
4958
  }
4959
4959
 
4960
4960
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4961
- const defaults$v = {
4961
+ const defaults$w = {
4962
4962
  pattern: DEFAULT_PATTERN,
4963
4963
  ignoreForeign: true
4964
4964
  };
@@ -4991,7 +4991,7 @@ function generateDescription(name, pattern) {
4991
4991
  class AttrPattern extends Rule {
4992
4992
  pattern;
4993
4993
  constructor(options) {
4994
- super({ ...defaults$v, ...options });
4994
+ super({ ...defaults$w, ...options });
4995
4995
  this.pattern = generateRegexp(this.options.pattern);
4996
4996
  }
4997
4997
  static schema() {
@@ -5038,7 +5038,7 @@ class AttrPattern extends Rule {
5038
5038
  }
5039
5039
  }
5040
5040
 
5041
- const defaults$u = {
5041
+ const defaults$v = {
5042
5042
  style: "auto",
5043
5043
  unquoted: false
5044
5044
  };
@@ -5104,7 +5104,7 @@ class AttrQuotes extends Rule {
5104
5104
  };
5105
5105
  }
5106
5106
  constructor(options) {
5107
- super({ ...defaults$u, ...options });
5107
+ super({ ...defaults$v, ...options });
5108
5108
  this.style = parseStyle$3(this.options.style);
5109
5109
  }
5110
5110
  setup() {
@@ -5260,13 +5260,13 @@ class AttributeAllowedValues extends Rule {
5260
5260
  }
5261
5261
  }
5262
5262
 
5263
- const defaults$t = {
5263
+ const defaults$u = {
5264
5264
  style: "omit"
5265
5265
  };
5266
5266
  class AttributeBooleanStyle extends Rule {
5267
5267
  hasInvalidStyle;
5268
5268
  constructor(options) {
5269
- super({ ...defaults$t, ...options });
5269
+ super({ ...defaults$u, ...options });
5270
5270
  this.hasInvalidStyle = parseStyle$2(this.options.style);
5271
5271
  }
5272
5272
  static schema() {
@@ -5336,13 +5336,13 @@ function reportMessage$1(attr, style) {
5336
5336
  return "";
5337
5337
  }
5338
5338
 
5339
- const defaults$s = {
5339
+ const defaults$t = {
5340
5340
  style: "omit"
5341
5341
  };
5342
5342
  class AttributeEmptyStyle extends Rule {
5343
5343
  hasInvalidStyle;
5344
5344
  constructor(options) {
5345
- super({ ...defaults$s, ...options });
5345
+ super({ ...defaults$t, ...options });
5346
5346
  this.hasInvalidStyle = parseStyle$1(this.options.style);
5347
5347
  }
5348
5348
  static schema() {
@@ -5459,6 +5459,122 @@ class AttributeMisuse extends Rule {
5459
5459
  }
5460
5460
  }
5461
5461
 
5462
+ const defaults$s = {
5463
+ preferred: void 0
5464
+ };
5465
+ function isPasswordInput(event) {
5466
+ const { target } = event;
5467
+ if (!target.is("input")) {
5468
+ return false;
5469
+ }
5470
+ const type = target.getAttribute("type");
5471
+ return type?.value?.toString().toLowerCase() === "password";
5472
+ }
5473
+ function isGroupingToken(token) {
5474
+ return token.startsWith("section-") || token === "shipping" || token === "billing";
5475
+ }
5476
+ class AutocompletePassword extends Rule {
5477
+ preferred;
5478
+ constructor(options) {
5479
+ super({ ...defaults$s, ...options });
5480
+ this.preferred = options.preferred?.toLowerCase();
5481
+ }
5482
+ static schema() {
5483
+ return {
5484
+ preferred: {
5485
+ type: "string"
5486
+ }
5487
+ };
5488
+ }
5489
+ documentation(context) {
5490
+ const url = "https://html-validate.org/rules/autocomplete-password.html";
5491
+ switch (context.kind) {
5492
+ case "preferred-mismatch":
5493
+ return {
5494
+ description: [
5495
+ `\`<input type="password">\` should use \`autocomplete="${context.preferred}"\`.`,
5496
+ "",
5497
+ `The configured preferred autocomplete value is \`"${context.preferred}"\` but the element uses \`"${context.value}"\`.`
5498
+ ].join("\n"),
5499
+ url
5500
+ };
5501
+ case "off":
5502
+ case "missing":
5503
+ default: {
5504
+ const error = context.kind === "off" ? '`<input type="password">` should not use `autocomplete="off"`.' : '`<input type="password">` must have the `autocomplete` attribute.';
5505
+ return {
5506
+ description: [
5507
+ error,
5508
+ "",
5509
+ "Browsers and password managers often ignore the absence of autocomplete and autofill password fields anyway, which can lead to unexpected behavior where users unknowingly submit autofilled passwords for unrelated fields.",
5510
+ "",
5511
+ "Use one of the following values:",
5512
+ "",
5513
+ '- `autocomplete="new-password"` for password creation forms',
5514
+ '- `autocomplete="current-password"` for login forms'
5515
+ ].join("\n"),
5516
+ url
5517
+ };
5518
+ }
5519
+ }
5520
+ }
5521
+ setup() {
5522
+ this.on("tag:ready", isPasswordInput, (event) => {
5523
+ const { preferred } = this;
5524
+ const { target } = event;
5525
+ const autocomplete = target.getAttribute("autocomplete");
5526
+ if (!autocomplete) {
5527
+ const context = { kind: "missing" };
5528
+ this.report({
5529
+ node: target,
5530
+ message: '<input type="password"> is missing required "autocomplete" attribute',
5531
+ location: target.location,
5532
+ context
5533
+ });
5534
+ return;
5535
+ }
5536
+ if (autocomplete.isDynamic || !autocomplete.value) {
5537
+ return;
5538
+ }
5539
+ const raw = autocomplete.value.toString().toLowerCase();
5540
+ const tokens = new DOMTokenList(raw, autocomplete.valueLocation);
5541
+ const index = tokens.findIndex((token) => !isGroupingToken(token));
5542
+ const value = tokens.item(index);
5543
+ const location = tokens.location(index);
5544
+ if (!value) {
5545
+ return;
5546
+ }
5547
+ if (value === "off") {
5548
+ const context = { kind: "off" };
5549
+ this.report({
5550
+ node: target,
5551
+ message: '<input type="password"> should not use autocomplete="off"',
5552
+ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- location must be present if value is */
5553
+ location,
5554
+ context
5555
+ });
5556
+ return;
5557
+ }
5558
+ if (preferred) {
5559
+ if (value !== preferred) {
5560
+ const context = {
5561
+ kind: "preferred-mismatch",
5562
+ value,
5563
+ preferred
5564
+ };
5565
+ this.report({
5566
+ node: target,
5567
+ message: `<input type="password"> should use autocomplete="${preferred}"`,
5568
+ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- location must be present if value is */
5569
+ location,
5570
+ context
5571
+ });
5572
+ }
5573
+ }
5574
+ });
5575
+ }
5576
+ }
5577
+
5462
5578
  const patternNamesValues = [
5463
5579
  "kebabcase",
5464
5580
  "camelcase",
@@ -9755,9 +9871,7 @@ const fieldNameGroup = {
9755
9871
  nickname: "text",
9756
9872
  username: "username",
9757
9873
  "new-password": "password",
9758
- // eslint-disable-line sonarjs/no-hardcoded-passwords -- false positive, it is not used as a password
9759
9874
  "current-password": "password",
9760
- // eslint-disable-line sonarjs/no-hardcoded-passwords -- false positive, it is not used as a password
9761
9875
  "one-time-code": "password",
9762
9876
  "organization-title": "text",
9763
9877
  organization: "text",
@@ -10799,6 +10913,7 @@ const bundledRules = {
10799
10913
  "attribute-boolean-style": AttributeBooleanStyle,
10800
10914
  "attribute-empty-style": AttributeEmptyStyle,
10801
10915
  "attribute-misuse": AttributeMisuse,
10916
+ "autocomplete-password": AutocompletePassword,
10802
10917
  "class-pattern": ClassPattern,
10803
10918
  "close-attr": CloseAttr,
10804
10919
  "close-order": CloseOrder,
@@ -11164,6 +11279,7 @@ const config$1 = {
11164
11279
  "attribute-boolean-style": "error",
11165
11280
  "attribute-empty-style": "error",
11166
11281
  "attribute-misuse": "error",
11282
+ "autocomplete-password": "error",
11167
11283
  "close-attr": "error",
11168
11284
  "close-order": "error",
11169
11285
  deprecated: "error",
@@ -12178,7 +12294,7 @@ class EventHandler {
12178
12294
  }
12179
12295
 
12180
12296
  const name = "html-validate";
12181
- const version = "10.7.0";
12297
+ const version = "10.8.0";
12182
12298
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
12183
12299
 
12184
12300
  function freeze(src) {