html-validate 6.11.0 → 7.1.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.
@@ -1,2 +1,2 @@
1
- export { A as AttributeData, $ as AttributeEvent, f as CSSStyleDeclaration, a1 as ConditionalEvent, C as Config, a as ConfigData, b as ConfigError, c as ConfigLoader, J as ConfigReadyEvent, a4 as DOMLoadEvent, a5 as DOMReadyEvent, t as DeferredMessage, a2 as DirectiveEvent, a3 as DoctypeEvent, D as DynamicValue, _ as ElementReadyEvent, I as Event, G as EventCallback, E as EventDump, F as EventHandler, e as HtmlElement, H as HtmlValidate, a7 as ListenEventMap, L as Location, r as Message, l as MetaCopyableProperty, M as MetaData, j as MetaElement, k as MetaTable, i as NestedError, N as NodeClosed, z as Parser, y as Plugin, P as ProcessElementContext, o as Report, q as Reporter, s as Result, R as Rule, m as RuleDocumentation, h as SchemaValidationError, S as Severity, n as Source, K as SourceReadyEvent, d as StaticConfigLoader, Y as TagCloseEvent, X as TagEndEvent, V as TagOpenEvent, Z as TagReadyEvent, Q as TagStartEvent, x as TemplateExtractor, T as TextNode, g as TokenDump, O as TokenEvent, u as TransformContext, w as Transformer, a6 as TriggerEventMap, U as UserError, a0 as WhitespaceEvent, W as WrappedError, p as configPresets, B as ruleExists, v as version } from './core.js';
1
+ export { A as AttributeData, a0 as AttributeEvent, f as CSSStyleDeclaration, a2 as ConditionalEvent, C as Config, a as ConfigData, b as ConfigError, c as ConfigLoader, K as ConfigReadyEvent, a5 as DOMLoadEvent, a6 as DOMReadyEvent, u as DeferredMessage, a3 as DirectiveEvent, a4 as DoctypeEvent, D as DynamicValue, $ as ElementReadyEvent, J as Event, I as EventCallback, E as EventDump, G as EventHandler, e as HtmlElement, H as HtmlValidate, a8 as ListenEventMap, L as Location, s as Message, m as MetaCopyableProperty, M as MetaData, j as MetaDataTable, k as MetaElement, l as MetaTable, i as NestedError, N as NodeClosed, B as Parser, z as Plugin, P as ProcessElementContext, q as Report, r as Reporter, t as Result, R as Rule, n as RuleDocumentation, h as SchemaValidationError, S as Severity, o as Source, O as SourceReadyEvent, d as StaticConfigLoader, Z as TagCloseEvent, Y as TagEndEvent, X as TagOpenEvent, _ as TagReadyEvent, V as TagStartEvent, y as TemplateExtractor, T as TextNode, g as TokenDump, Q as TokenEvent, w as TransformContext, x as Transformer, a7 as TriggerEventMap, U as UserError, a1 as WhitespaceEvent, W as WrappedError, p as configPresets, F as ruleExists, v as version } from './core.js';
2
2
  import 'ajv';
@@ -333,12 +333,13 @@ declare abstract class Rule<ContextType = void, OptionsType = void> {
333
333
  * not `"foo"`.
334
334
  *
335
335
  * @param keyword - Keyword to match against `include` and `exclude` options.
336
+ * @param matcher - Optional function to compare items with.
336
337
  * @returns `true` if keyword is not present in `include` or is present in
337
338
  * `exclude`.
338
339
  */
339
340
  isKeywordIgnored<T extends IncludeExcludeOptions>(this: {
340
341
  options: T;
341
- }, keyword: string): boolean;
342
+ }, keyword: string, matcher?: (list: string[], it: string) => boolean): boolean;
342
343
  /**
343
344
  * Find all tags which has enabled given property.
344
345
  */
@@ -1072,6 +1073,9 @@ interface MetaElement extends Omit<MetaData, "deprecatedAttributes" | "requiredA
1072
1073
  tagName: string;
1073
1074
  attributes: Record<string, MetaAttribute>;
1074
1075
  }
1076
+ interface MetaDataTable {
1077
+ [tagName: string]: MetaData;
1078
+ }
1075
1079
  interface ElementTable {
1076
1080
  [tagName: string]: MetaElement;
1077
1081
  }
@@ -2330,4 +2334,4 @@ declare type Formatter = (results: Result[]) => string;
2330
2334
  */
2331
2335
  declare function getFormatter(name: string): Formatter | null;
2332
2336
 
2333
- export { AttributeEvent as $, AttributeData as A, ruleExists as B, Config as C, DynamicValue as D, EventDump as E, EventHandler as F, EventCallback as G, HtmlValidate as H, Event as I, ConfigReadyEvent as J, SourceReadyEvent as K, Location as L, MetaData as M, NodeClosed as N, TokenEvent as O, ProcessElementContext as P, TagStartEvent as Q, Rule as R, Severity as S, TextNode as T, UserError as U, TagOpenEvent as V, WrappedError as W, TagEndEvent as X, TagCloseEvent as Y, TagReadyEvent as Z, ElementReadyEvent as _, ConfigData as a, WhitespaceEvent as a0, ConditionalEvent as a1, DirectiveEvent as a2, DoctypeEvent as a3, DOMLoadEvent as a4, DOMReadyEvent as a5, TriggerEventMap as a6, ListenEventMap as a7, FileSystemConfigLoader as a8, Formatter as a9, getFormatter as aa, compatibilityCheck as ab, CompatibilityOptions as ac, ConfigError as b, ConfigLoader as c, StaticConfigLoader as d, HtmlElement as e, CSSStyleDeclaration as f, TokenDump as g, SchemaValidationError as h, NestedError as i, MetaElement as j, MetaTable as k, MetaCopyableProperty as l, RuleDocumentation as m, Source as n, Report as o, presets as p, Reporter as q, Message as r, Result as s, DeferredMessage as t, TransformContext as u, version as v, Transformer as w, TemplateExtractor as x, Plugin as y, Parser as z };
2337
+ export { ElementReadyEvent as $, AttributeData as A, Parser as B, Config as C, DynamicValue as D, EventDump as E, ruleExists as F, EventHandler as G, HtmlValidate as H, EventCallback as I, Event as J, ConfigReadyEvent as K, Location as L, MetaData as M, NodeClosed as N, SourceReadyEvent as O, ProcessElementContext as P, TokenEvent as Q, Rule as R, Severity as S, TextNode as T, UserError as U, TagStartEvent as V, WrappedError as W, TagOpenEvent as X, TagEndEvent as Y, TagCloseEvent as Z, TagReadyEvent as _, ConfigData as a, AttributeEvent as a0, WhitespaceEvent as a1, ConditionalEvent as a2, DirectiveEvent as a3, DoctypeEvent as a4, DOMLoadEvent as a5, DOMReadyEvent as a6, TriggerEventMap as a7, ListenEventMap as a8, FileSystemConfigLoader as a9, Formatter as aa, getFormatter as ab, compatibilityCheck as ac, CompatibilityOptions as ad, ConfigError as b, ConfigLoader as c, StaticConfigLoader as d, HtmlElement as e, CSSStyleDeclaration as f, TokenDump as g, SchemaValidationError as h, NestedError as i, MetaDataTable as j, MetaElement as k, MetaTable as l, MetaCopyableProperty as m, RuleDocumentation as n, Source as o, presets as p, Report as q, Reporter as r, Message as s, Result as t, DeferredMessage as u, version as v, TransformContext as w, Transformer as x, TemplateExtractor as y, Plugin as z };
package/dist/cjs/core.js CHANGED
@@ -2102,6 +2102,7 @@ class HtmlElement extends DOMNode {
2102
2102
  }
2103
2103
  const parts = [];
2104
2104
  let root;
2105
+ /* eslint-disable-next-line @typescript-eslint/no-this-alias */
2105
2106
  for (root = this; root.parent; root = root.parent) {
2106
2107
  /* .. */
2107
2108
  }
@@ -3113,7 +3114,7 @@ var TRANSFORMER_API;
3113
3114
  /** @public */
3114
3115
  const name = "html-validate";
3115
3116
  /** @public */
3116
- const version = "6.11.0";
3117
+ const version = "7.1.0";
3117
3118
  /** @public */
3118
3119
  const homepage = "https://html-validate.org";
3119
3120
  /** @public */
@@ -3276,17 +3277,18 @@ class Rule {
3276
3277
  * not `"foo"`.
3277
3278
  *
3278
3279
  * @param keyword - Keyword to match against `include` and `exclude` options.
3280
+ * @param matcher - Optional function to compare items with.
3279
3281
  * @returns `true` if keyword is not present in `include` or is present in
3280
3282
  * `exclude`.
3281
3283
  */
3282
- isKeywordIgnored(keyword) {
3284
+ isKeywordIgnored(keyword, matcher = (list, it) => list.includes(it)) {
3283
3285
  const { include, exclude } = this.options;
3284
3286
  /* ignore keyword if not present in "include" */
3285
- if (include && !include.includes(keyword)) {
3287
+ if (include && !matcher(include, keyword)) {
3286
3288
  return true;
3287
3289
  }
3288
3290
  /* ignore keyword if present in "excludes" */
3289
- if (exclude && exclude.includes(keyword)) {
3291
+ if (exclude && matcher(exclude, keyword)) {
3290
3292
  return true;
3291
3293
  }
3292
3294
  return false;
@@ -3434,7 +3436,7 @@ function ruleDocumentationUrl(filename) {
3434
3436
  return `${homepage}/rules/${normalized}.html`;
3435
3437
  }
3436
3438
 
3437
- const defaults$r = {
3439
+ const defaults$s = {
3438
3440
  allowExternal: true,
3439
3441
  allowRelative: true,
3440
3442
  allowAbsolute: true,
@@ -3478,7 +3480,7 @@ function matchList(value, list) {
3478
3480
  }
3479
3481
  class AllowedLinks extends Rule {
3480
3482
  constructor(options) {
3481
- super({ ...defaults$r, ...options });
3483
+ super({ ...defaults$s, ...options });
3482
3484
  this.allowExternal = parseAllow(this.options.allowExternal);
3483
3485
  this.allowRelative = parseAllow(this.options.allowRelative);
3484
3486
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3779,13 +3781,13 @@ class CaseStyle {
3779
3781
  }
3780
3782
  }
3781
3783
 
3782
- const defaults$q = {
3784
+ const defaults$r = {
3783
3785
  style: "lowercase",
3784
3786
  ignoreForeign: true,
3785
3787
  };
3786
3788
  class AttrCase extends Rule {
3787
3789
  constructor(options) {
3788
- super({ ...defaults$q, ...options });
3790
+ super({ ...defaults$r, ...options });
3789
3791
  this.style = new CaseStyle(this.options.style, "attr-case");
3790
3792
  }
3791
3793
  static schema() {
@@ -4125,7 +4127,7 @@ class AttrDelimiter extends Rule {
4125
4127
  }
4126
4128
 
4127
4129
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4128
- const defaults$p = {
4130
+ const defaults$q = {
4129
4131
  pattern: DEFAULT_PATTERN,
4130
4132
  ignoreForeign: true,
4131
4133
  };
@@ -4162,7 +4164,7 @@ function generateDescription(name, pattern) {
4162
4164
  }
4163
4165
  class AttrPattern extends Rule {
4164
4166
  constructor(options) {
4165
- super({ ...defaults$p, ...options });
4167
+ super({ ...defaults$q, ...options });
4166
4168
  this.pattern = generateRegexp(this.options.pattern);
4167
4169
  }
4168
4170
  static schema() {
@@ -4221,20 +4223,56 @@ var QuoteStyle;
4221
4223
  QuoteStyle["SINGLE_QUOTE"] = "'";
4222
4224
  QuoteStyle["DOUBLE_QUOTE"] = "\"";
4223
4225
  QuoteStyle["AUTO_QUOTE"] = "auto";
4226
+ QuoteStyle["ANY_QUOTE"] = "any";
4224
4227
  })(QuoteStyle || (QuoteStyle = {}));
4225
- const defaults$o = {
4228
+ const defaults$p = {
4226
4229
  style: "auto",
4227
4230
  unquoted: false,
4228
4231
  };
4232
+ function describeError(context) {
4233
+ if (context) {
4234
+ switch (context.error) {
4235
+ case "style":
4236
+ return `Attribute \`${context.attr}\` must use \`${context.expected}\` instead of \`${context.actual}\`.`;
4237
+ case "unquoted":
4238
+ return `Attribute \`${context.attr}\` must not be unquoted.`;
4239
+ }
4240
+ }
4241
+ else {
4242
+ return "This attribute is not quoted properly.";
4243
+ }
4244
+ }
4245
+ function describeStyle(style, unquoted) {
4246
+ const description = [];
4247
+ switch (style) {
4248
+ case QuoteStyle.AUTO_QUOTE:
4249
+ description.push("- quoted with double quotes `\"` unless the value contains double quotes in which case single quotes `'` should be used instead");
4250
+ break;
4251
+ case QuoteStyle.ANY_QUOTE:
4252
+ description.push("- quoted with single quotes `'`");
4253
+ description.push('- quoted with double quotes `"`');
4254
+ break;
4255
+ case QuoteStyle.SINGLE_QUOTE:
4256
+ case QuoteStyle.DOUBLE_QUOTE: {
4257
+ const name = style === QuoteStyle.SINGLE_QUOTE ? "single" : "double";
4258
+ description.push(`- quoted with ${name} quotes \`${style}\``);
4259
+ break;
4260
+ }
4261
+ }
4262
+ if (unquoted) {
4263
+ description.push("- unquoted (if applicable)");
4264
+ }
4265
+ return `${description.join(" or\n")}\n`;
4266
+ }
4229
4267
  class AttrQuotes extends Rule {
4230
4268
  constructor(options) {
4231
- super({ ...defaults$o, ...options });
4269
+ super({ ...defaults$p, ...options });
4232
4270
  this.style = parseStyle$4(this.options.style);
4233
4271
  }
4234
4272
  static schema() {
4235
4273
  return {
4236
4274
  style: {
4237
- enum: ["auto", "double", "single"],
4275
+ enum: ["auto", "double", "single", "any"],
4238
4276
  type: "string",
4239
4277
  },
4240
4278
  unquoted: {
@@ -4242,19 +4280,20 @@ class AttrQuotes extends Rule {
4242
4280
  },
4243
4281
  };
4244
4282
  }
4245
- documentation() {
4246
- if (this.options.style === "auto") {
4247
- return {
4248
- description: `Attribute values are required to be quoted with doublequotes unless the attribute value itself contains doublequotes in which case singlequotes should be used.`,
4249
- url: ruleDocumentationUrl("@/rules/attr-quotes.ts"),
4250
- };
4251
- }
4252
- else {
4253
- return {
4254
- description: `Attribute values are required to be quoted with ${this.options.style}quotes.`,
4255
- url: ruleDocumentationUrl("@/rules/attr-quotes.ts"),
4256
- };
4257
- }
4283
+ documentation(context) {
4284
+ const { style } = this;
4285
+ const { unquoted } = this.options;
4286
+ const description = [
4287
+ describeError(context),
4288
+ "",
4289
+ "Under the current configuration attributes must be:",
4290
+ "",
4291
+ describeStyle(style, unquoted),
4292
+ ];
4293
+ return {
4294
+ description: description.join("\n"),
4295
+ url: ruleDocumentationUrl("@/rules/attr-quotes.ts"),
4296
+ };
4258
4297
  }
4259
4298
  setup() {
4260
4299
  this.on("attr", (event) => {
@@ -4264,13 +4303,31 @@ class AttrQuotes extends Rule {
4264
4303
  }
4265
4304
  if (!event.quote) {
4266
4305
  if (this.options.unquoted === false) {
4267
- this.report(event.target, `Attribute "${event.key}" using unquoted value`);
4306
+ const message = `Attribute "${event.key}" using unquoted value`;
4307
+ const context = {
4308
+ error: "unquoted",
4309
+ attr: event.key,
4310
+ };
4311
+ this.report(event.target, message, null, context);
4268
4312
  }
4269
4313
  return;
4270
4314
  }
4315
+ /* if the style is set to any we skip the rest of the rule as the only
4316
+ * thing that matters is if the "unquoted" options triggers an error or
4317
+ * not */
4318
+ if (this.style === QuoteStyle.ANY_QUOTE) {
4319
+ return;
4320
+ }
4271
4321
  const expected = this.resolveQuotemark(event.value.toString(), this.style);
4272
4322
  if (event.quote !== expected) {
4273
- this.report(event.target, `Attribute "${event.key}" used ${event.quote} instead of expected ${expected}`);
4323
+ const message = `Attribute "${event.key}" used ${event.quote} instead of expected ${expected}`;
4324
+ const context = {
4325
+ error: "style",
4326
+ attr: event.key,
4327
+ actual: event.quote,
4328
+ expected,
4329
+ };
4330
+ this.report(event.target, message, null, context);
4274
4331
  }
4275
4332
  });
4276
4333
  }
@@ -4291,6 +4348,8 @@ function parseStyle$4(style) {
4291
4348
  return QuoteStyle.DOUBLE_QUOTE;
4292
4349
  case "single":
4293
4350
  return QuoteStyle.SINGLE_QUOTE;
4351
+ case "any":
4352
+ return QuoteStyle.ANY_QUOTE;
4294
4353
  /* istanbul ignore next: covered by schema validation */
4295
4354
  default:
4296
4355
  throw new ConfigError(`Invalid style "${style}" for "attr-quotes" rule`);
@@ -4393,12 +4452,12 @@ class AttributeAllowedValues extends Rule {
4393
4452
  }
4394
4453
  }
4395
4454
 
4396
- const defaults$n = {
4455
+ const defaults$o = {
4397
4456
  style: "omit",
4398
4457
  };
4399
4458
  class AttributeBooleanStyle extends Rule {
4400
4459
  constructor(options) {
4401
- super({ ...defaults$n, ...options });
4460
+ super({ ...defaults$o, ...options });
4402
4461
  this.hasInvalidStyle = parseStyle$3(this.options.style);
4403
4462
  }
4404
4463
  static schema() {
@@ -4474,12 +4533,12 @@ function reportMessage$1(attr, style) {
4474
4533
  return "";
4475
4534
  }
4476
4535
 
4477
- const defaults$m = {
4536
+ const defaults$n = {
4478
4537
  style: "omit",
4479
4538
  };
4480
4539
  class AttributeEmptyStyle extends Rule {
4481
4540
  constructor(options) {
4482
- super({ ...defaults$m, ...options });
4541
+ super({ ...defaults$n, ...options });
4483
4542
  this.hasInvalidStyle = parseStyle$2(this.options.style);
4484
4543
  }
4485
4544
  static schema() {
@@ -4587,12 +4646,12 @@ function describePattern(pattern) {
4587
4646
  }
4588
4647
  }
4589
4648
 
4590
- const defaults$l = {
4649
+ const defaults$m = {
4591
4650
  pattern: "kebabcase",
4592
4651
  };
4593
4652
  class ClassPattern extends Rule {
4594
4653
  constructor(options) {
4595
- super({ ...defaults$l, ...options });
4654
+ super({ ...defaults$m, ...options });
4596
4655
  this.pattern = parsePattern(this.options.pattern);
4597
4656
  }
4598
4657
  static schema() {
@@ -4701,13 +4760,13 @@ class CloseOrder extends Rule {
4701
4760
  }
4702
4761
  }
4703
4762
 
4704
- const defaults$k = {
4763
+ const defaults$l = {
4705
4764
  include: null,
4706
4765
  exclude: null,
4707
4766
  };
4708
4767
  class Deprecated extends Rule {
4709
4768
  constructor(options) {
4710
- super({ ...defaults$k, ...options });
4769
+ super({ ...defaults$l, ...options });
4711
4770
  }
4712
4771
  static schema() {
4713
4772
  return {
@@ -4870,12 +4929,12 @@ class NoStyleTag$1 extends Rule {
4870
4929
  }
4871
4930
  }
4872
4931
 
4873
- const defaults$j = {
4932
+ const defaults$k = {
4874
4933
  style: "uppercase",
4875
4934
  };
4876
4935
  class DoctypeStyle extends Rule {
4877
4936
  constructor(options) {
4878
- super({ ...defaults$j, ...options });
4937
+ super({ ...defaults$k, ...options });
4879
4938
  }
4880
4939
  static schema() {
4881
4940
  return {
@@ -4907,12 +4966,12 @@ class DoctypeStyle extends Rule {
4907
4966
  }
4908
4967
  }
4909
4968
 
4910
- const defaults$i = {
4969
+ const defaults$j = {
4911
4970
  style: "lowercase",
4912
4971
  };
4913
4972
  class ElementCase extends Rule {
4914
4973
  constructor(options) {
4915
- super({ ...defaults$i, ...options });
4974
+ super({ ...defaults$j, ...options });
4916
4975
  this.style = new CaseStyle(this.options.style, "element-case");
4917
4976
  }
4918
4977
  static schema() {
@@ -4978,14 +5037,14 @@ class ElementCase extends Rule {
4978
5037
  }
4979
5038
  }
4980
5039
 
4981
- const defaults$h = {
5040
+ const defaults$i = {
4982
5041
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
4983
5042
  whitelist: [],
4984
5043
  blacklist: [],
4985
5044
  };
4986
5045
  class ElementName extends Rule {
4987
5046
  constructor(options) {
4988
- super({ ...defaults$h, ...options });
5047
+ super({ ...defaults$i, ...options });
4989
5048
  // eslint-disable-next-line security/detect-non-literal-regexp
4990
5049
  this.pattern = new RegExp(this.options.pattern);
4991
5050
  }
@@ -5026,7 +5085,7 @@ class ElementName extends Rule {
5026
5085
  ...context.blacklist.map((cur) => `- ${cur}`),
5027
5086
  ];
5028
5087
  }
5029
- if (context.pattern !== defaults$h.pattern) {
5088
+ if (context.pattern !== defaults$i.pattern) {
5030
5089
  return [
5031
5090
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5032
5091
  "",
@@ -5428,7 +5487,7 @@ class EmptyTitle extends Rule {
5428
5487
  }
5429
5488
  }
5430
5489
 
5431
- const defaults$g = {
5490
+ const defaults$h = {
5432
5491
  allowMultipleH1: false,
5433
5492
  minInitialRank: "h1",
5434
5493
  sectioningRoots: ["dialog", '[role="dialog"]'],
@@ -5459,7 +5518,7 @@ function parseMaxInitial(value) {
5459
5518
  }
5460
5519
  class HeadingLevel extends Rule {
5461
5520
  constructor(options) {
5462
- super({ ...defaults$g, ...options });
5521
+ super({ ...defaults$h, ...options });
5463
5522
  this.stack = [];
5464
5523
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
5465
5524
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -5617,12 +5676,12 @@ class HeadingLevel extends Rule {
5617
5676
  }
5618
5677
  }
5619
5678
 
5620
- const defaults$f = {
5679
+ const defaults$g = {
5621
5680
  pattern: "kebabcase",
5622
5681
  };
5623
5682
  class IdPattern extends Rule {
5624
5683
  constructor(options) {
5625
- super({ ...defaults$f, ...options });
5684
+ super({ ...defaults$g, ...options });
5626
5685
  this.pattern = parsePattern(this.options.pattern);
5627
5686
  }
5628
5687
  static schema() {
@@ -5973,12 +6032,12 @@ function findLabelByParent(el) {
5973
6032
  return [];
5974
6033
  }
5975
6034
 
5976
- const defaults$e = {
6035
+ const defaults$f = {
5977
6036
  maxlength: 70,
5978
6037
  };
5979
6038
  class LongTitle extends Rule {
5980
6039
  constructor(options) {
5981
- super({ ...defaults$e, ...options });
6040
+ super({ ...defaults$f, ...options });
5982
6041
  this.maxlength = this.options.maxlength;
5983
6042
  }
5984
6043
  static schema() {
@@ -6125,13 +6184,13 @@ class MultipleLabeledControls extends Rule {
6125
6184
  }
6126
6185
  }
6127
6186
 
6128
- const defaults$d = {
6187
+ const defaults$e = {
6129
6188
  include: null,
6130
6189
  exclude: null,
6131
6190
  };
6132
6191
  class NoAutoplay extends Rule {
6133
6192
  constructor(options) {
6134
- super({ ...defaults$d, ...options });
6193
+ super({ ...defaults$e, ...options });
6135
6194
  }
6136
6195
  documentation(context) {
6137
6196
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -6372,14 +6431,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
6372
6431
  }
6373
6432
  }
6374
6433
 
6375
- const defaults$c = {
6434
+ const defaults$d = {
6376
6435
  include: null,
6377
6436
  exclude: null,
6378
6437
  allowedProperties: ["display"],
6379
6438
  };
6380
6439
  class NoInlineStyle extends Rule {
6381
6440
  constructor(options) {
6382
- super({ ...defaults$c, ...options });
6441
+ super({ ...defaults$d, ...options });
6383
6442
  }
6384
6443
  static schema() {
6385
6444
  return {
@@ -6581,7 +6640,7 @@ class NoMultipleMain extends Rule {
6581
6640
  }
6582
6641
  }
6583
6642
 
6584
- const defaults$b = {
6643
+ const defaults$c = {
6585
6644
  relaxed: false,
6586
6645
  };
6587
6646
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -6598,7 +6657,7 @@ const replacementTable = {
6598
6657
  };
6599
6658
  class NoRawCharacters extends Rule {
6600
6659
  constructor(options) {
6601
- super({ ...defaults$b, ...options });
6660
+ super({ ...defaults$c, ...options });
6602
6661
  this.relaxed = this.options.relaxed;
6603
6662
  }
6604
6663
  static schema() {
@@ -6776,13 +6835,13 @@ class NoRedundantRole extends Rule {
6776
6835
  }
6777
6836
 
6778
6837
  const xmlns = /^(.+):.+$/;
6779
- const defaults$a = {
6838
+ const defaults$b = {
6780
6839
  ignoreForeign: true,
6781
6840
  ignoreXML: true,
6782
6841
  };
6783
6842
  class NoSelfClosing extends Rule {
6784
6843
  constructor(options) {
6785
- super({ ...defaults$a, ...options });
6844
+ super({ ...defaults$b, ...options });
6786
6845
  }
6787
6846
  static schema() {
6788
6847
  return {
@@ -6915,13 +6974,13 @@ const replacement = {
6915
6974
  reset: '<button type="reset">',
6916
6975
  image: '<button type="button">',
6917
6976
  };
6918
- const defaults$9 = {
6977
+ const defaults$a = {
6919
6978
  include: null,
6920
6979
  exclude: null,
6921
6980
  };
6922
6981
  class PreferButton extends Rule {
6923
6982
  constructor(options) {
6924
- super({ ...defaults$9, ...options });
6983
+ super({ ...defaults$a, ...options });
6925
6984
  }
6926
6985
  static schema() {
6927
6986
  return {
@@ -6996,7 +7055,7 @@ class PreferButton extends Rule {
6996
7055
  }
6997
7056
  }
6998
7057
 
6999
- const defaults$8 = {
7058
+ const defaults$9 = {
7000
7059
  mapping: {
7001
7060
  article: "article",
7002
7061
  banner: "header",
@@ -7026,7 +7085,7 @@ const defaults$8 = {
7026
7085
  };
7027
7086
  class PreferNativeElement extends Rule {
7028
7087
  constructor(options) {
7029
- super({ ...defaults$8, ...options });
7088
+ super({ ...defaults$9, ...options });
7030
7089
  }
7031
7090
  static schema() {
7032
7091
  return {
@@ -7146,8 +7205,66 @@ class PreferTbody extends Rule {
7146
7205
  }
7147
7206
  }
7148
7207
 
7208
+ const defaults$8 = {
7209
+ tags: ["script", "style"],
7210
+ };
7211
+ class RequireCSPNonce extends Rule {
7212
+ constructor(options) {
7213
+ super({ ...defaults$8, ...options });
7214
+ }
7215
+ static schema() {
7216
+ return {
7217
+ tags: {
7218
+ type: "array",
7219
+ items: {
7220
+ enum: ["script", "style"],
7221
+ type: "string",
7222
+ },
7223
+ },
7224
+ };
7225
+ }
7226
+ documentation() {
7227
+ return {
7228
+ description: [
7229
+ "Required Content-Security-Policy (CSP) nonce is missing or empty.",
7230
+ "",
7231
+ "This is set by the `nonce` attribute and must match the `Content-Security-Policy` header.",
7232
+ "For instance, if the header contains `script-src 'nonce-r4nd0m'` the `nonce` attribute must be set to `nonce=\"r4nd0m\">`",
7233
+ "",
7234
+ "The nonce should be unique per each request and set to a cryptography secure random token.",
7235
+ "It is used to prevent cross site scripting (XSS) by preventing malicious actors from injecting scripts onto the page.",
7236
+ ].join("\n"),
7237
+ url: ruleDocumentationUrl("@/rules/require-csp-nonce.ts"),
7238
+ };
7239
+ }
7240
+ setup() {
7241
+ this.on("tag:end", (event) => {
7242
+ var _a;
7243
+ const { tags } = this.options;
7244
+ const node = event.previous;
7245
+ /* ignore other tags */
7246
+ if (!node || !tags.includes(node.tagName)) {
7247
+ return;
7248
+ }
7249
+ /* ignore if nonce is set to non-empty value (or dynamic) */
7250
+ const nonce = (_a = node.getAttribute("nonce")) === null || _a === void 0 ? void 0 : _a.value;
7251
+ if (nonce && nonce !== "") {
7252
+ return;
7253
+ }
7254
+ /* ignore <script src> */
7255
+ if (node.is("script") && node.hasAttribute("src")) {
7256
+ return;
7257
+ }
7258
+ const message = `required CSP nonce is missing`;
7259
+ this.report(node, message, node.location);
7260
+ });
7261
+ }
7262
+ }
7263
+
7149
7264
  const defaults$7 = {
7150
7265
  target: "all",
7266
+ include: null,
7267
+ exclude: null,
7151
7268
  };
7152
7269
  const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
7153
7270
  const supportSri = {
@@ -7165,6 +7282,32 @@ class RequireSri extends Rule {
7165
7282
  enum: ["all", "crossorigin"],
7166
7283
  type: "string",
7167
7284
  },
7285
+ include: {
7286
+ anyOf: [
7287
+ {
7288
+ items: {
7289
+ type: "string",
7290
+ },
7291
+ type: "array",
7292
+ },
7293
+ {
7294
+ type: "null",
7295
+ },
7296
+ ],
7297
+ },
7298
+ exclude: {
7299
+ anyOf: [
7300
+ {
7301
+ items: {
7302
+ type: "string",
7303
+ },
7304
+ type: "array",
7305
+ },
7306
+ {
7307
+ type: "null",
7308
+ },
7309
+ ],
7310
+ },
7168
7311
  };
7169
7312
  }
7170
7313
  documentation() {
@@ -7177,11 +7320,13 @@ class RequireSri extends Rule {
7177
7320
  this.on("tag:end", (event) => {
7178
7321
  /* only handle thats supporting and requires sri */
7179
7322
  const node = event.previous;
7180
- if (!(this.supportSri(node) && this.needSri(node)))
7323
+ if (!(this.supportSri(node) && this.needSri(node))) {
7181
7324
  return;
7325
+ }
7182
7326
  /* check if sri attribute is present */
7183
- if (node.hasAttribute("integrity"))
7327
+ if (node.hasAttribute("integrity")) {
7184
7328
  return;
7329
+ }
7185
7330
  this.report(node, `SRI "integrity" attribute is required on <${node.tagName}> element`, node.location);
7186
7331
  });
7187
7332
  }
@@ -7189,19 +7334,25 @@ class RequireSri extends Rule {
7189
7334
  return Object.keys(supportSri).includes(node.tagName);
7190
7335
  }
7191
7336
  needSri(node) {
7192
- if (this.target === "all")
7193
- return true;
7194
7337
  const attr = this.elementSourceAttr(node);
7195
- if (!attr || attr.value === null || attr.isDynamic) {
7338
+ if (!attr || attr.value === null || attr.value === "" || attr.isDynamic) {
7196
7339
  return false;
7197
7340
  }
7198
7341
  const url = attr.value.toString();
7199
- return crossorigin.test(url);
7342
+ if (this.target === "all" || crossorigin.test(url)) {
7343
+ return !this.isIgnored(url);
7344
+ }
7345
+ return false;
7200
7346
  }
7201
7347
  elementSourceAttr(node) {
7202
7348
  const key = supportSri[node.tagName];
7203
7349
  return node.getAttribute(key);
7204
7350
  }
7351
+ isIgnored(url) {
7352
+ return this.isKeywordIgnored(url, (list, it) => {
7353
+ return list.some((pattern) => it.includes(pattern));
7354
+ });
7355
+ }
7205
7356
  }
7206
7357
 
7207
7358
  class ScriptElement extends Rule {
@@ -9999,6 +10150,7 @@ const bundledRules = {
9999
10150
  "prefer-button": PreferButton,
10000
10151
  "prefer-native-element": PreferNativeElement,
10001
10152
  "prefer-tbody": PreferTbody,
10153
+ "require-csp-nonce": RequireCSPNonce,
10002
10154
  "require-sri": RequireSri,
10003
10155
  "script-element": ScriptElement,
10004
10156
  "script-type": ScriptType,