html-validate 7.18.0 → 8.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
@@ -1,47 +1,29 @@
1
1
  'use strict';
2
2
 
3
- var fs = require('fs');
4
- var espree = require('espree');
5
- var walk = require('acorn-walk');
6
3
  var path = require('path');
7
4
  var semver = require('semver');
8
5
  var kleur = require('kleur');
6
+ var fs$1 = require('node:fs');
7
+ var path$1 = require('node:path');
9
8
  var Ajv = require('ajv');
10
9
  var deepmerge = require('deepmerge');
11
10
  var elements = require('./elements.js');
12
11
  var rulesHelper = require('./rules-helper.js');
12
+ var fs = require('fs');
13
13
  var betterAjvErrors = require('@sidvind/better-ajv-errors');
14
14
  var codeFrame = require('@babel/code-frame');
15
15
  var stylish$2 = require('@html-validate/stylish');
16
16
 
17
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
18
 
19
- function _interopNamespace(e) {
20
- if (e && e.__esModule) return e;
21
- var n = Object.create(null);
22
- if (e) {
23
- Object.keys(e).forEach(function (k) {
24
- if (k !== 'default') {
25
- var d = Object.getOwnPropertyDescriptor(e, k);
26
- Object.defineProperty(n, k, d.get ? d : {
27
- enumerable: true,
28
- get: function () { return e[k]; }
29
- });
30
- }
31
- });
32
- }
33
- n.default = e;
34
- return Object.freeze(n);
35
- }
36
-
37
- var fs__default = /*#__PURE__*/_interopDefault(fs);
38
- var espree__namespace = /*#__PURE__*/_interopNamespace(espree);
39
- var walk__namespace = /*#__PURE__*/_interopNamespace(walk);
40
19
  var path__default = /*#__PURE__*/_interopDefault(path);
41
20
  var semver__default = /*#__PURE__*/_interopDefault(semver);
42
21
  var kleur__default = /*#__PURE__*/_interopDefault(kleur);
22
+ var fs__default$1 = /*#__PURE__*/_interopDefault(fs$1);
23
+ var path__default$1 = /*#__PURE__*/_interopDefault(path$1);
43
24
  var Ajv__default = /*#__PURE__*/_interopDefault(Ajv);
44
25
  var deepmerge__default = /*#__PURE__*/_interopDefault(deepmerge);
26
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
45
27
  var betterAjvErrors__default = /*#__PURE__*/_interopDefault(betterAjvErrors);
46
28
 
47
29
  const $schema$2 = "http://json-schema.org/draft-06/schema#";
@@ -3188,232 +3170,11 @@ var configurationSchema = {
3188
3170
  properties: properties
3189
3171
  };
3190
3172
 
3191
- /* eslint-disable @typescript-eslint/no-non-null-assertion -- declarations say
3192
- * location fields are optional but they are always present when `{loc: true}` */
3193
- function joinTemplateLiteral(nodes) {
3194
- let offset = nodes[0].start + 1;
3195
- let output = "";
3196
- for (const node of nodes) {
3197
- output += " ".repeat(node.start + 1 - offset);
3198
- output += node.value.raw;
3199
- offset = node.end - 2;
3200
- }
3201
- return output;
3202
- }
3203
- /**
3204
- * Compute source offset from line and column and the given markup.
3205
- *
3206
- * @param position - Line and column.
3207
- * @param data - Source markup.
3208
- * @returns The byte offset into the markup which line and column corresponds to.
3209
- */
3210
- function computeOffset(position, data) {
3211
- let line = position.line;
3212
- let column = position.column + 1;
3213
- for (let i = 0; i < data.length; i++) {
3214
- if (line > 1) {
3215
- /* not yet on the correct line */
3216
- if (data[i] === "\n") {
3217
- line--;
3218
- }
3219
- }
3220
- else if (column > 1) {
3221
- /* not yet on the correct column */
3222
- column--;
3223
- }
3224
- else {
3225
- /* line/column found, return current position */
3226
- return i;
3227
- }
3228
- }
3229
- /* istanbul ignore next: should never reach this line unless espree passes bad
3230
- * positions, no sane way to test */
3231
- throw new Error("Failed to compute location offset from position");
3232
- }
3233
- function extractLiteral(node, filename, data) {
3234
- switch (node.type) {
3235
- /* ignored nodes */
3236
- case "FunctionExpression":
3237
- case "Identifier":
3238
- return null;
3239
- case "Literal":
3240
- if (typeof node.value !== "string") {
3241
- return null;
3242
- }
3243
- return {
3244
- data: node.value.toString(),
3245
- filename,
3246
- line: node.loc.start.line,
3247
- column: node.loc.start.column + 1,
3248
- offset: computeOffset(node.loc.start, data) + 1,
3249
- };
3250
- case "TemplateLiteral":
3251
- return {
3252
- data: joinTemplateLiteral(node.quasis),
3253
- filename,
3254
- line: node.loc.start.line,
3255
- column: node.loc.start.column + 1,
3256
- offset: computeOffset(node.loc.start, data) + 1,
3257
- };
3258
- case "TaggedTemplateExpression":
3259
- return {
3260
- data: joinTemplateLiteral(node.quasi.quasis),
3261
- filename,
3262
- line: node.quasi.loc.start.line,
3263
- column: node.quasi.loc.start.column + 1,
3264
- offset: computeOffset(node.quasi.loc.start, data) + 1,
3265
- };
3266
- case "ArrowFunctionExpression": {
3267
- const whitelist = ["Literal", "TemplateLiteral"];
3268
- if (whitelist.includes(node.body.type)) {
3269
- return extractLiteral(node.body, filename, data);
3270
- }
3271
- else {
3272
- return null;
3273
- }
3274
- }
3275
- /* istanbul ignore next: this only provides a better error, all currently known nodes are tested */
3276
- default: {
3277
- const loc = node.loc.start;
3278
- const context = `${filename}:${loc.line}:${loc.column}`;
3279
- throw new Error(`Unhandled node type "${node.type}" at "${context}" in extractLiteral`);
3280
- }
3281
- }
3282
- }
3283
- function compareKey(node, key, filename) {
3284
- switch (node.type) {
3285
- case "Identifier":
3286
- return node.name === key;
3287
- case "Literal":
3288
- return node.value === key;
3289
- /* istanbul ignore next: this only provides a better error, all currently known nodes are tested */
3290
- default: {
3291
- const loc = node.loc.start;
3292
- const context = `${filename}:${loc.line}:${loc.column}`;
3293
- throw new Error(`Unhandled node type "${node.type}" at "${context}" in compareKey`);
3294
- }
3295
- }
3296
- }
3297
- /**
3298
- * @public
3299
- */
3300
- class TemplateExtractor {
3301
- constructor(ast, filename, data) {
3302
- this.ast = ast;
3303
- this.filename = filename;
3304
- this.data = data;
3305
- }
3306
- static fromFilename(filename) {
3307
- const source = fs__default.default.readFileSync(filename, "utf-8");
3308
- const ast = espree__namespace.parse(source, {
3309
- ecmaVersion: 2017,
3310
- sourceType: "module",
3311
- loc: true,
3312
- });
3313
- return new TemplateExtractor(ast, filename, source);
3314
- }
3315
- /**
3316
- * Create a new [[TemplateExtractor]] from javascript source code.
3317
- *
3318
- * `Source` offsets will be relative to the string, i.e. offset 0 is the first
3319
- * character of the string. If the string is only a subset of a larger string
3320
- * the offsets must be adjusted manually.
3321
- *
3322
- * @param source - Source code.
3323
- * @param filename - Optional filename to set in the resulting
3324
- * `Source`. Defauls to `"inline"`.
3325
- */
3326
- static fromString(source, filename) {
3327
- const ast = espree__namespace.parse(source, {
3328
- ecmaVersion: 2017,
3329
- sourceType: "module",
3330
- loc: true,
3331
- });
3332
- return new TemplateExtractor(ast, filename || "inline", source);
3333
- }
3334
- /**
3335
- * Convenience function to create a [[Source]] instance from an existing file.
3336
- *
3337
- * @param filename - Filename with javascript source code. The file must exist
3338
- * and be readable by the user.
3339
- * @returns An array of Source's suitable for passing to [[Engine]] linting
3340
- * functions.
3341
- */
3342
- static createSource(filename) {
3343
- const data = fs__default.default.readFileSync(filename, "utf-8");
3344
- return [
3345
- {
3346
- column: 1,
3347
- data,
3348
- filename,
3349
- line: 1,
3350
- offset: 0,
3351
- },
3352
- ];
3353
- }
3354
- /**
3355
- * Extract object properties.
3356
- *
3357
- * Given a key `"template"` this method finds all objects literals with a
3358
- * `"template"` property and creates a [[Source]] instance with proper offsets
3359
- * with the value of the property. For instance:
3360
- *
3361
- * ```
3362
- * const myObj = {
3363
- * foo: 'bar',
3364
- * };
3365
- * ```
3366
- *
3367
- * The above snippet would yield a `Source` with the content `bar`.
3368
- *
3369
- */
3370
- extractObjectProperty(key) {
3371
- const result = [];
3372
- const { filename, data } = this;
3373
- const node = this.ast;
3374
- walk__namespace.simple(node, {
3375
- Property(node) {
3376
- if (compareKey(node.key, key, filename)) {
3377
- const source = extractLiteral(node.value, filename, data);
3378
- if (source) {
3379
- source.filename = filename;
3380
- result.push(source);
3381
- }
3382
- }
3383
- },
3384
- });
3385
- return result;
3386
- }
3387
- }
3388
-
3389
3173
  var TRANSFORMER_API;
3390
3174
  (function (TRANSFORMER_API) {
3391
3175
  TRANSFORMER_API[TRANSFORMER_API["VERSION"] = 1] = "VERSION";
3392
3176
  })(TRANSFORMER_API || (TRANSFORMER_API = {}));
3393
3177
 
3394
- /**
3395
- * Similar to `require(..)` but removes the cached copy first.
3396
- */
3397
- function requireUncached(require, moduleId) {
3398
- const filename = require.resolve(moduleId);
3399
- /* remove references from the parent module to prevent memory leak */
3400
- const m = require.cache[filename];
3401
- if (m && m.parent) {
3402
- const { parent } = m;
3403
- for (let i = parent.children.length - 1; i >= 0; i--) {
3404
- if (parent.children[i].id === filename) {
3405
- parent.children.splice(i, 1);
3406
- }
3407
- }
3408
- }
3409
- /* remove old module from cache */
3410
- delete require.cache[filename];
3411
- /* eslint-disable-next-line import/no-dynamic-require, security/detect-non-literal-require -- as expected but should be moved to upcoming resolver class */
3412
- return require(filename);
3413
- }
3414
-
3415
- const legacyRequire = require;
3416
-
3417
3178
  /**
3418
3179
  * @public
3419
3180
  */
@@ -3431,11 +3192,6 @@ function parseSeverity(value) {
3431
3192
  case 0:
3432
3193
  case "off":
3433
3194
  return exports.Severity.DISABLED;
3434
- /* istanbul ignore next: deprecated code which will be removed later */
3435
- case "disable":
3436
- // eslint-disable-next-line no-console -- expected to log
3437
- console.warn(`Deprecated alias "disabled" will be removed, replace with severity "off"`);
3438
- return exports.Severity.DISABLED;
3439
3195
  case 1:
3440
3196
  case "warn":
3441
3197
  return exports.Severity.WARN;
@@ -3770,6 +3526,8 @@ class Rule {
3770
3526
  * Called when requesting additional documentation for a rule. Some rules
3771
3527
  * provide additional context to provide context-aware suggestions.
3772
3528
  *
3529
+ * @public
3530
+ * @virtual
3773
3531
  * @param context - Error context given by a reported error.
3774
3532
  * @returns Rule documentation and url with additional details or `null` if no
3775
3533
  * additional documentation is available.
@@ -3780,15 +3538,15 @@ class Rule {
3780
3538
  }
3781
3539
  }
3782
3540
 
3783
- var Style$2;
3541
+ var Style$1;
3784
3542
  (function (Style) {
3785
3543
  Style["EXTERNAL"] = "external";
3786
3544
  Style["RELATIVE_BASE"] = "relative-base";
3787
3545
  Style["RELATIVE_PATH"] = "relative-path";
3788
3546
  Style["ABSOLUTE"] = "absolute";
3789
3547
  Style["ANCHOR"] = "anchor";
3790
- })(Style$2 || (Style$2 = {}));
3791
- const defaults$w = {
3548
+ })(Style$1 || (Style$1 = {}));
3549
+ const defaults$v = {
3792
3550
  allowExternal: true,
3793
3551
  allowRelative: true,
3794
3552
  allowAbsolute: true,
@@ -3801,11 +3559,11 @@ const mapping$1 = {
3801
3559
  script: "src",
3802
3560
  };
3803
3561
  const description = {
3804
- [Style$2.EXTERNAL]: "External links are not allowed by current configuration.",
3805
- [Style$2.RELATIVE_BASE]: "Links relative to <base> are not allowed by current configuration.",
3806
- [Style$2.RELATIVE_PATH]: "Relative links are not allowed by current configuration.",
3807
- [Style$2.ABSOLUTE]: "Absolute links are not allowed by current configuration.",
3808
- [Style$2.ANCHOR]: null,
3562
+ [Style$1.EXTERNAL]: "External links are not allowed by current configuration.",
3563
+ [Style$1.RELATIVE_BASE]: "Links relative to <base> are not allowed by current configuration.",
3564
+ [Style$1.RELATIVE_PATH]: "Relative links are not allowed by current configuration.",
3565
+ [Style$1.ABSOLUTE]: "Absolute links are not allowed by current configuration.",
3566
+ [Style$1.ANCHOR]: null,
3809
3567
  };
3810
3568
  function parseAllow(value) {
3811
3569
  if (typeof value === "boolean") {
@@ -3832,7 +3590,7 @@ function matchList(value, list) {
3832
3590
  }
3833
3591
  class AllowedLinks extends Rule {
3834
3592
  constructor(options) {
3835
- super({ ...defaults$w, ...options });
3593
+ super({ ...defaults$v, ...options });
3836
3594
  this.allowExternal = parseAllow(this.options.allowExternal);
3837
3595
  this.allowRelative = parseAllow(this.options.allowRelative);
3838
3596
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3878,19 +3636,19 @@ class AllowedLinks extends Rule {
3878
3636
  const link = event.value.toString();
3879
3637
  const style = this.getStyle(link);
3880
3638
  switch (style) {
3881
- case Style$2.ANCHOR:
3639
+ case Style$1.ANCHOR:
3882
3640
  /* anchor links are always allowed by this rule */
3883
3641
  break;
3884
- case Style$2.ABSOLUTE:
3642
+ case Style$1.ABSOLUTE:
3885
3643
  this.handleAbsolute(link, event, style);
3886
3644
  break;
3887
- case Style$2.EXTERNAL:
3645
+ case Style$1.EXTERNAL:
3888
3646
  this.handleExternal(link, event, style);
3889
3647
  break;
3890
- case Style$2.RELATIVE_BASE:
3648
+ case Style$1.RELATIVE_BASE:
3891
3649
  this.handleRelativeBase(link, event, style);
3892
3650
  break;
3893
- case Style$2.RELATIVE_PATH:
3651
+ case Style$1.RELATIVE_PATH:
3894
3652
  this.handleRelativePath(link, event, style);
3895
3653
  break;
3896
3654
  }
@@ -3908,21 +3666,21 @@ class AllowedLinks extends Rule {
3908
3666
  getStyle(value) {
3909
3667
  /* http://example.net or //example.net */
3910
3668
  if (value.match(/^([a-z]+:)?\/\//g)) {
3911
- return Style$2.EXTERNAL;
3669
+ return Style$1.EXTERNAL;
3912
3670
  }
3913
3671
  switch (value[0]) {
3914
3672
  /* /foo/bar */
3915
3673
  case "/":
3916
- return Style$2.ABSOLUTE;
3674
+ return Style$1.ABSOLUTE;
3917
3675
  /* ../foo/bar */
3918
3676
  case ".":
3919
- return Style$2.RELATIVE_PATH;
3677
+ return Style$1.RELATIVE_PATH;
3920
3678
  /* #foo */
3921
3679
  case "#":
3922
- return Style$2.ANCHOR;
3680
+ return Style$1.ANCHOR;
3923
3681
  /* foo/bar */
3924
3682
  default:
3925
- return Style$2.RELATIVE_BASE;
3683
+ return Style$1.RELATIVE_BASE;
3926
3684
  }
3927
3685
  }
3928
3686
  handleAbsolute(target, event, style) {
@@ -3980,7 +3738,7 @@ var RuleContext$1;
3980
3738
  RuleContext["MISSING_ALT"] = "missing-alt";
3981
3739
  RuleContext["MISSING_HREF"] = "missing-href";
3982
3740
  })(RuleContext$1 || (RuleContext$1 = {}));
3983
- const defaults$v = {
3741
+ const defaults$u = {
3984
3742
  accessible: true,
3985
3743
  };
3986
3744
  function findByTarget(target, siblings) {
@@ -4018,7 +3776,7 @@ function getDescription$1(context) {
4018
3776
  }
4019
3777
  class AreaAlt extends Rule {
4020
3778
  constructor(options) {
4021
- super({ ...defaults$v, ...options });
3779
+ super({ ...defaults$u, ...options });
4022
3780
  }
4023
3781
  static schema() {
4024
3782
  return {
@@ -4198,13 +3956,13 @@ class ConfigError extends UserError {
4198
3956
  }
4199
3957
  }
4200
3958
 
4201
- const defaults$u = {
3959
+ const defaults$t = {
4202
3960
  style: "lowercase",
4203
3961
  ignoreForeign: true,
4204
3962
  };
4205
3963
  class AttrCase extends Rule {
4206
3964
  constructor(options) {
4207
- super({ ...defaults$u, ...options });
3965
+ super({ ...defaults$t, ...options });
4208
3966
  this.style = new rulesHelper.CaseStyle(this.options.style, "attr-case");
4209
3967
  }
4210
3968
  static schema() {
@@ -4552,7 +4310,7 @@ class AttrDelimiter extends Rule {
4552
4310
  }
4553
4311
 
4554
4312
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4555
- const defaults$t = {
4313
+ const defaults$s = {
4556
4314
  pattern: DEFAULT_PATTERN,
4557
4315
  ignoreForeign: true,
4558
4316
  };
@@ -4589,7 +4347,7 @@ function generateDescription(name, pattern) {
4589
4347
  }
4590
4348
  class AttrPattern extends Rule {
4591
4349
  constructor(options) {
4592
- super({ ...defaults$t, ...options });
4350
+ super({ ...defaults$s, ...options });
4593
4351
  this.pattern = generateRegexp(this.options.pattern);
4594
4352
  }
4595
4353
  static schema() {
@@ -4650,7 +4408,7 @@ var QuoteStyle;
4650
4408
  QuoteStyle["AUTO_QUOTE"] = "auto";
4651
4409
  QuoteStyle["ANY_QUOTE"] = "any";
4652
4410
  })(QuoteStyle || (QuoteStyle = {}));
4653
- const defaults$s = {
4411
+ const defaults$r = {
4654
4412
  style: "auto",
4655
4413
  unquoted: false,
4656
4414
  };
@@ -4717,8 +4475,8 @@ class AttrQuotes extends Rule {
4717
4475
  };
4718
4476
  }
4719
4477
  constructor(options) {
4720
- super({ ...defaults$s, ...options });
4721
- this.style = parseStyle$4(this.options.style);
4478
+ super({ ...defaults$r, ...options });
4479
+ this.style = parseStyle$3(this.options.style);
4722
4480
  }
4723
4481
  setup() {
4724
4482
  this.on("attr", (event) => {
@@ -4765,7 +4523,7 @@ class AttrQuotes extends Rule {
4765
4523
  }
4766
4524
  }
4767
4525
  }
4768
- function parseStyle$4(style) {
4526
+ function parseStyle$3(style) {
4769
4527
  switch (style.toLowerCase()) {
4770
4528
  case "auto":
4771
4529
  return QuoteStyle.AUTO_QUOTE;
@@ -4887,13 +4645,13 @@ class AttributeAllowedValues extends Rule {
4887
4645
  }
4888
4646
  }
4889
4647
 
4890
- const defaults$r = {
4648
+ const defaults$q = {
4891
4649
  style: "omit",
4892
4650
  };
4893
4651
  class AttributeBooleanStyle extends Rule {
4894
4652
  constructor(options) {
4895
- super({ ...defaults$r, ...options });
4896
- this.hasInvalidStyle = parseStyle$3(this.options.style);
4653
+ super({ ...defaults$q, ...options });
4654
+ this.hasInvalidStyle = parseStyle$2(this.options.style);
4897
4655
  }
4898
4656
  static schema() {
4899
4657
  return {
@@ -4941,7 +4699,7 @@ class AttributeBooleanStyle extends Rule {
4941
4699
  return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.boolean);
4942
4700
  }
4943
4701
  }
4944
- function parseStyle$3(style) {
4702
+ function parseStyle$2(style) {
4945
4703
  switch (style.toLowerCase()) {
4946
4704
  case "omit":
4947
4705
  return (attr) => attr.value !== null;
@@ -4968,13 +4726,13 @@ function reportMessage$1(attr, style) {
4968
4726
  return "";
4969
4727
  }
4970
4728
 
4971
- const defaults$q = {
4729
+ const defaults$p = {
4972
4730
  style: "omit",
4973
4731
  };
4974
4732
  class AttributeEmptyStyle extends Rule {
4975
4733
  constructor(options) {
4976
- super({ ...defaults$q, ...options });
4977
- this.hasInvalidStyle = parseStyle$2(this.options.style);
4734
+ super({ ...defaults$p, ...options });
4735
+ this.hasInvalidStyle = parseStyle$1(this.options.style);
4978
4736
  }
4979
4737
  static schema() {
4980
4738
  return {
@@ -5032,7 +4790,7 @@ function isEmptyValue(attr) {
5032
4790
  }
5033
4791
  return attr.value === null || attr.value === "";
5034
4792
  }
5035
- function parseStyle$2(style) {
4793
+ function parseStyle$1(style) {
5036
4794
  switch (style.toLowerCase()) {
5037
4795
  case "omit":
5038
4796
  return (attr) => attr.value !== null;
@@ -5129,12 +4887,12 @@ function describePattern(pattern) {
5129
4887
  }
5130
4888
  }
5131
4889
 
5132
- const defaults$p = {
4890
+ const defaults$o = {
5133
4891
  pattern: "kebabcase",
5134
4892
  };
5135
4893
  class ClassPattern extends Rule {
5136
4894
  constructor(options) {
5137
- super({ ...defaults$p, ...options });
4895
+ super({ ...defaults$o, ...options });
5138
4896
  this.pattern = parsePattern(this.options.pattern);
5139
4897
  }
5140
4898
  static schema() {
@@ -5243,13 +5001,13 @@ class CloseOrder extends Rule {
5243
5001
  }
5244
5002
  }
5245
5003
 
5246
- const defaults$o = {
5004
+ const defaults$n = {
5247
5005
  include: null,
5248
5006
  exclude: null,
5249
5007
  };
5250
5008
  class Deprecated extends Rule {
5251
5009
  constructor(options) {
5252
- super({ ...defaults$o, ...options });
5010
+ super({ ...defaults$n, ...options });
5253
5011
  }
5254
5012
  static schema() {
5255
5013
  return {
@@ -5412,12 +5170,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
5412
5170
  }
5413
5171
  };
5414
5172
 
5415
- const defaults$n = {
5173
+ const defaults$m = {
5416
5174
  style: "uppercase",
5417
5175
  };
5418
5176
  class DoctypeStyle extends Rule {
5419
5177
  constructor(options) {
5420
- super({ ...defaults$n, ...options });
5178
+ super({ ...defaults$m, ...options });
5421
5179
  }
5422
5180
  static schema() {
5423
5181
  return {
@@ -5449,12 +5207,12 @@ class DoctypeStyle extends Rule {
5449
5207
  }
5450
5208
  }
5451
5209
 
5452
- const defaults$m = {
5210
+ const defaults$l = {
5453
5211
  style: "lowercase",
5454
5212
  };
5455
5213
  class ElementCase extends Rule {
5456
5214
  constructor(options) {
5457
- super({ ...defaults$m, ...options });
5215
+ super({ ...defaults$l, ...options });
5458
5216
  this.style = new rulesHelper.CaseStyle(this.options.style, "element-case");
5459
5217
  }
5460
5218
  static schema() {
@@ -5520,14 +5278,14 @@ class ElementCase extends Rule {
5520
5278
  }
5521
5279
  }
5522
5280
 
5523
- const defaults$l = {
5281
+ const defaults$k = {
5524
5282
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
5525
5283
  whitelist: [],
5526
5284
  blacklist: [],
5527
5285
  };
5528
5286
  class ElementName extends Rule {
5529
5287
  constructor(options) {
5530
- super({ ...defaults$l, ...options });
5288
+ super({ ...defaults$k, ...options });
5531
5289
  /* eslint-disable-next-line security/detect-non-literal-regexp -- expected to be a regexp */
5532
5290
  this.pattern = new RegExp(this.options.pattern);
5533
5291
  }
@@ -5568,7 +5326,7 @@ class ElementName extends Rule {
5568
5326
  ...context.blacklist.map((cur) => `- ${cur}`),
5569
5327
  ];
5570
5328
  }
5571
- if (context.pattern !== defaults$l.pattern) {
5329
+ if (context.pattern !== defaults$k.pattern) {
5572
5330
  return [
5573
5331
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5574
5332
  "",
@@ -6112,7 +5870,7 @@ class EmptyTitle extends Rule {
6112
5870
  }
6113
5871
  }
6114
5872
 
6115
- const defaults$k = {
5873
+ const defaults$j = {
6116
5874
  allowArrayBrackets: true,
6117
5875
  shared: ["radio", "button", "reset", "submit"],
6118
5876
  };
@@ -6145,7 +5903,7 @@ function getDocumentation(context) {
6145
5903
  }
6146
5904
  class FormDupName extends Rule {
6147
5905
  constructor(options) {
6148
- super({ ...defaults$k, ...options });
5906
+ super({ ...defaults$j, ...options });
6149
5907
  }
6150
5908
  static schema() {
6151
5909
  return {
@@ -6305,7 +6063,7 @@ class FormDupName extends Rule {
6305
6063
  }
6306
6064
  }
6307
6065
 
6308
- const defaults$j = {
6066
+ const defaults$i = {
6309
6067
  allowMultipleH1: false,
6310
6068
  minInitialRank: "h1",
6311
6069
  sectioningRoots: ["dialog", '[role="dialog"]', '[role="alertdialog"]'],
@@ -6336,7 +6094,7 @@ function parseMaxInitial(value) {
6336
6094
  }
6337
6095
  class HeadingLevel extends Rule {
6338
6096
  constructor(options) {
6339
- super({ ...defaults$j, ...options });
6097
+ super({ ...defaults$i, ...options });
6340
6098
  this.stack = [];
6341
6099
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
6342
6100
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -6494,12 +6252,12 @@ class HeadingLevel extends Rule {
6494
6252
  }
6495
6253
  }
6496
6254
 
6497
- const defaults$i = {
6255
+ const defaults$h = {
6498
6256
  pattern: "kebabcase",
6499
6257
  };
6500
6258
  class IdPattern extends Rule {
6501
6259
  constructor(options) {
6502
- super({ ...defaults$i, ...options });
6260
+ super({ ...defaults$h, ...options });
6503
6261
  this.pattern = parsePattern(this.options.pattern);
6504
6262
  }
6505
6263
  static schema() {
@@ -6781,12 +6539,12 @@ function findLabelByParent(el) {
6781
6539
  return [];
6782
6540
  }
6783
6541
 
6784
- const defaults$h = {
6542
+ const defaults$g = {
6785
6543
  maxlength: 70,
6786
6544
  };
6787
6545
  class LongTitle extends Rule {
6788
6546
  constructor(options) {
6789
- super({ ...defaults$h, ...options });
6547
+ super({ ...defaults$g, ...options });
6790
6548
  this.maxlength = this.options.maxlength;
6791
6549
  }
6792
6550
  static schema() {
@@ -7012,13 +6770,13 @@ class MultipleLabeledControls extends Rule {
7012
6770
  }
7013
6771
  }
7014
6772
 
7015
- const defaults$g = {
6773
+ const defaults$f = {
7016
6774
  include: null,
7017
6775
  exclude: null,
7018
6776
  };
7019
6777
  class NoAutoplay extends Rule {
7020
6778
  constructor(options) {
7021
- super({ ...defaults$g, ...options });
6779
+ super({ ...defaults$f, ...options });
7022
6780
  }
7023
6781
  documentation(context) {
7024
6782
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -7259,14 +7017,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
7259
7017
  }
7260
7018
  }
7261
7019
 
7262
- const defaults$f = {
7020
+ const defaults$e = {
7263
7021
  include: null,
7264
7022
  exclude: null,
7265
7023
  allowedProperties: ["display"],
7266
7024
  };
7267
7025
  class NoInlineStyle extends Rule {
7268
7026
  constructor(options) {
7269
- super({ ...defaults$f, ...options });
7027
+ super({ ...defaults$e, ...options });
7270
7028
  }
7271
7029
  static schema() {
7272
7030
  return {
@@ -7468,7 +7226,7 @@ class NoMultipleMain extends Rule {
7468
7226
  }
7469
7227
  }
7470
7228
 
7471
- const defaults$e = {
7229
+ const defaults$d = {
7472
7230
  relaxed: false,
7473
7231
  };
7474
7232
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -7485,7 +7243,7 @@ const replacementTable = {
7485
7243
  };
7486
7244
  class NoRawCharacters extends Rule {
7487
7245
  constructor(options) {
7488
- super({ ...defaults$e, ...options });
7246
+ super({ ...defaults$d, ...options });
7489
7247
  this.relaxed = this.options.relaxed;
7490
7248
  }
7491
7249
  static schema() {
@@ -7663,13 +7421,13 @@ class NoRedundantRole extends Rule {
7663
7421
  }
7664
7422
 
7665
7423
  const xmlns = /^(.+):.+$/;
7666
- const defaults$d = {
7424
+ const defaults$c = {
7667
7425
  ignoreForeign: true,
7668
7426
  ignoreXML: true,
7669
7427
  };
7670
7428
  class NoSelfClosing extends Rule {
7671
7429
  constructor(options) {
7672
- super({ ...defaults$d, ...options });
7430
+ super({ ...defaults$c, ...options });
7673
7431
  }
7674
7432
  static schema() {
7675
7433
  return {
@@ -7758,13 +7516,13 @@ class NoTrailingWhitespace extends Rule {
7758
7516
  }
7759
7517
  }
7760
7518
 
7761
- const defaults$c = {
7519
+ const defaults$b = {
7762
7520
  include: null,
7763
7521
  exclude: null,
7764
7522
  };
7765
7523
  class NoUnknownElements extends Rule {
7766
7524
  constructor(options) {
7767
- super({ ...defaults$c, ...options });
7525
+ super({ ...defaults$b, ...options });
7768
7526
  }
7769
7527
  static schema() {
7770
7528
  return {
@@ -7876,13 +7634,13 @@ const replacement = {
7876
7634
  reset: '<button type="reset">',
7877
7635
  image: '<button type="button">',
7878
7636
  };
7879
- const defaults$b = {
7637
+ const defaults$a = {
7880
7638
  include: null,
7881
7639
  exclude: null,
7882
7640
  };
7883
7641
  class PreferButton extends Rule {
7884
7642
  constructor(options) {
7885
- super({ ...defaults$b, ...options });
7643
+ super({ ...defaults$a, ...options });
7886
7644
  }
7887
7645
  static schema() {
7888
7646
  return {
@@ -7957,7 +7715,7 @@ class PreferButton extends Rule {
7957
7715
  }
7958
7716
  }
7959
7717
 
7960
- const defaults$a = {
7718
+ const defaults$9 = {
7961
7719
  mapping: {
7962
7720
  article: "article",
7963
7721
  banner: "header",
@@ -7987,7 +7745,7 @@ const defaults$a = {
7987
7745
  };
7988
7746
  class PreferNativeElement extends Rule {
7989
7747
  constructor(options) {
7990
- super({ ...defaults$a, ...options });
7748
+ super({ ...defaults$9, ...options });
7991
7749
  }
7992
7750
  static schema() {
7993
7751
  return {
@@ -8107,12 +7865,12 @@ class PreferTbody extends Rule {
8107
7865
  }
8108
7866
  }
8109
7867
 
8110
- const defaults$9 = {
7868
+ const defaults$8 = {
8111
7869
  tags: ["script", "style"],
8112
7870
  };
8113
7871
  class RequireCSPNonce extends Rule {
8114
7872
  constructor(options) {
8115
- super({ ...defaults$9, ...options });
7873
+ super({ ...defaults$8, ...options });
8116
7874
  }
8117
7875
  static schema() {
8118
7876
  return {
@@ -8163,7 +7921,7 @@ class RequireCSPNonce extends Rule {
8163
7921
  }
8164
7922
  }
8165
7923
 
8166
- const defaults$8 = {
7924
+ const defaults$7 = {
8167
7925
  target: "all",
8168
7926
  include: null,
8169
7927
  exclude: null,
@@ -8175,7 +7933,7 @@ const supportSri = {
8175
7933
  };
8176
7934
  class RequireSri extends Rule {
8177
7935
  constructor(options) {
8178
- super({ ...defaults$8, ...options });
7936
+ super({ ...defaults$7, ...options });
8179
7937
  this.target = this.options.target;
8180
7938
  }
8181
7939
  static schema() {
@@ -8337,7 +8095,7 @@ class SvgFocusable extends Rule {
8337
8095
  }
8338
8096
  }
8339
8097
 
8340
- const defaults$7 = {
8098
+ const defaults$6 = {
8341
8099
  characters: [
8342
8100
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
8343
8101
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" },
@@ -8376,7 +8134,7 @@ function matchAll(text, regexp) {
8376
8134
  }
8377
8135
  class TelNonBreaking extends Rule {
8378
8136
  constructor(options) {
8379
- super({ ...defaults$7, ...options });
8137
+ super({ ...defaults$6, ...options });
8380
8138
  this.regex = constructRegex(this.options.characters);
8381
8139
  }
8382
8140
  static schema() {
@@ -8664,7 +8422,7 @@ class TextContent extends Rule {
8664
8422
  }
8665
8423
  }
8666
8424
 
8667
- const defaults$6 = {
8425
+ const defaults$5 = {
8668
8426
  ignoreCase: false,
8669
8427
  requireSemicolon: true,
8670
8428
  };
@@ -8706,7 +8464,7 @@ function getDescription(context, options) {
8706
8464
  }
8707
8465
  class UnknownCharReference extends Rule {
8708
8466
  constructor(options) {
8709
- super({ ...defaults$6, ...options });
8467
+ super({ ...defaults$5, ...options });
8710
8468
  }
8711
8469
  static schema() {
8712
8470
  return {
@@ -8823,12 +8581,12 @@ var RuleContext;
8823
8581
  RuleContext[RuleContext["LEADING_CHARACTER"] = 3] = "LEADING_CHARACTER";
8824
8582
  RuleContext[RuleContext["DISALLOWED_CHARACTER"] = 4] = "DISALLOWED_CHARACTER";
8825
8583
  })(RuleContext || (RuleContext = {}));
8826
- const defaults$5 = {
8584
+ const defaults$4 = {
8827
8585
  relaxed: false,
8828
8586
  };
8829
8587
  class ValidID extends Rule {
8830
8588
  constructor(options) {
8831
- super({ ...defaults$5, ...options });
8589
+ super({ ...defaults$4, ...options });
8832
8590
  }
8833
8591
  static schema() {
8834
8592
  return {
@@ -8905,119 +8663,39 @@ class ValidID extends Rule {
8905
8663
  }
8906
8664
  }
8907
8665
 
8908
- var Style$1;
8666
+ class VoidContent extends Rule {
8667
+ documentation(tagName) {
8668
+ const doc = {
8669
+ description: "HTML void elements cannot have any content and must not have content or end tag.",
8670
+ url: "https://html-validate.org/rules/void-content.html",
8671
+ };
8672
+ if (tagName) {
8673
+ doc.description = `<${tagName}> is a void element and must not have content or end tag.`;
8674
+ }
8675
+ return doc;
8676
+ }
8677
+ setup() {
8678
+ this.on("tag:end", (event) => {
8679
+ const node = event.target; // The current element being closed.
8680
+ if (!node) {
8681
+ return;
8682
+ }
8683
+ if (!node.voidElement) {
8684
+ return;
8685
+ }
8686
+ if (node.closed === exports.NodeClosed.EndTag) {
8687
+ this.report(null, `End tag for <${node.tagName}> must be omitted`, node.location, node.tagName);
8688
+ }
8689
+ });
8690
+ }
8691
+ }
8692
+
8693
+ var Style;
8909
8694
  (function (Style) {
8910
- Style[Style["Any"] = 0] = "Any";
8911
8695
  Style[Style["AlwaysOmit"] = 1] = "AlwaysOmit";
8912
8696
  Style[Style["AlwaysSelfclose"] = 2] = "AlwaysSelfclose";
8913
- })(Style$1 || (Style$1 = {}));
8914
- const defaults$4 = {
8915
- style: "omit",
8916
- };
8917
- class Void extends Rule {
8918
- get deprecated() {
8919
- return true;
8920
- }
8921
- static schema() {
8922
- return {
8923
- style: {
8924
- enum: ["any", "omit", "selfclose", "selfclosing"],
8925
- type: "string",
8926
- },
8927
- };
8928
- }
8929
- documentation() {
8930
- return {
8931
- description: "HTML void elements cannot have any content and must not have an end tag.",
8932
- url: "https://html-validate.org/rules/void.html",
8933
- };
8934
- }
8935
- constructor(options) {
8936
- super({ ...defaults$4, ...options });
8937
- this.style = parseStyle$1(this.options.style);
8938
- }
8939
- setup() {
8940
- this.on("tag:end", (event) => {
8941
- const current = event.target; // The current element being closed
8942
- const active = event.previous; // The current active element (that is, the current element on the stack)
8943
- if (current && current.meta) {
8944
- this.validateCurrent(current);
8945
- }
8946
- if (active && active.meta) {
8947
- this.validateActive(active, active.meta);
8948
- }
8949
- });
8950
- }
8951
- validateCurrent(node) {
8952
- if (node.voidElement && node.closed === exports.NodeClosed.EndTag) {
8953
- this.report(null, `End tag for <${node.tagName}> must be omitted`, node.location);
8954
- }
8955
- }
8956
- validateActive(node, meta) {
8957
- /* ignore foreign elements, they may or may not be self-closed and both are
8958
- * valid */
8959
- if (meta.foreign) {
8960
- return;
8961
- }
8962
- const selfOrOmitted = node.closed === exports.NodeClosed.VoidOmitted || node.closed === exports.NodeClosed.VoidSelfClosed;
8963
- if (node.voidElement) {
8964
- if (this.style === Style$1.AlwaysOmit && node.closed === exports.NodeClosed.VoidSelfClosed) {
8965
- this.report(node, `Expected omitted end tag <${node.tagName}> instead of self-closing element <${node.tagName}/>`);
8966
- }
8967
- if (this.style === Style$1.AlwaysSelfclose && node.closed === exports.NodeClosed.VoidOmitted) {
8968
- this.report(node, `Expected self-closing element <${node.tagName}/> instead of omitted end-tag <${node.tagName}>`);
8969
- }
8970
- }
8971
- if (selfOrOmitted && node.voidElement === false) {
8972
- this.report(node, `End tag for <${node.tagName}> must not be omitted`);
8973
- }
8974
- }
8975
- }
8976
- function parseStyle$1(name) {
8977
- switch (name) {
8978
- case "any":
8979
- return Style$1.Any;
8980
- case "omit":
8981
- return Style$1.AlwaysOmit;
8982
- case "selfclose":
8983
- case "selfclosing":
8984
- return Style$1.AlwaysSelfclose;
8985
- }
8986
- }
8987
-
8988
- class VoidContent extends Rule {
8989
- documentation(tagName) {
8990
- const doc = {
8991
- description: "HTML void elements cannot have any content and must not have content or end tag.",
8992
- url: "https://html-validate.org/rules/void-content.html",
8993
- };
8994
- if (tagName) {
8995
- doc.description = `<${tagName}> is a void element and must not have content or end tag.`;
8996
- }
8997
- return doc;
8998
- }
8999
- setup() {
9000
- this.on("tag:end", (event) => {
9001
- const node = event.target; // The current element being closed.
9002
- if (!node) {
9003
- return;
9004
- }
9005
- if (!node.voidElement) {
9006
- return;
9007
- }
9008
- if (node.closed === exports.NodeClosed.EndTag) {
9009
- this.report(null, `End tag for <${node.tagName}> must be omitted`, node.location, node.tagName);
9010
- }
9011
- });
9012
- }
9013
- }
9014
-
9015
- var Style;
9016
- (function (Style) {
9017
- Style[Style["AlwaysOmit"] = 1] = "AlwaysOmit";
9018
- Style[Style["AlwaysSelfclose"] = 2] = "AlwaysSelfclose";
9019
- })(Style || (Style = {}));
9020
- const defaults$3 = {
8697
+ })(Style || (Style = {}));
8698
+ const defaults$3 = {
9021
8699
  style: "omit",
9022
8700
  };
9023
8701
  class VoidStyle extends Rule {
@@ -9486,7 +9164,6 @@ const bundledRules = {
9486
9164
  "text-content": TextContent,
9487
9165
  "unrecognized-char-ref": UnknownCharReference,
9488
9166
  "valid-id": ValidID,
9489
- void: Void,
9490
9167
  "void-content": VoidContent,
9491
9168
  "void-style": VoidStyle,
9492
9169
  ...WCAG,
@@ -9779,7 +9456,112 @@ class ResolvedConfig {
9779
9456
  }
9780
9457
  }
9781
9458
 
9782
- let rootDirCache = null;
9459
+ function haveResolver(key, value) {
9460
+ return key in value;
9461
+ }
9462
+ function haveConfigResolver(value) {
9463
+ return haveResolver("resolveConfig", value);
9464
+ }
9465
+ function haveElementsResolver(value) {
9466
+ return haveResolver("resolveElements", value);
9467
+ }
9468
+ function havePluginResolver(value) {
9469
+ return haveResolver("resolvePlugin", value);
9470
+ }
9471
+ function haveTransformerResolver(value) {
9472
+ return haveResolver("resolveTransformer", value);
9473
+ }
9474
+ /**
9475
+ * @internal
9476
+ */
9477
+ function resolveConfig(resolvers, id, options) {
9478
+ for (const resolver of resolvers.filter(haveConfigResolver)) {
9479
+ const config = resolver.resolveConfig(id, options);
9480
+ if (config) {
9481
+ return config;
9482
+ }
9483
+ }
9484
+ throw new UserError(`Failed to load configuration from "${id}"`);
9485
+ }
9486
+ /**
9487
+ * @internal
9488
+ */
9489
+ function resolveElements(resolvers, id, options) {
9490
+ for (const resolver of resolvers.filter(haveElementsResolver)) {
9491
+ const elements = resolver.resolveElements(id, options);
9492
+ if (elements) {
9493
+ return elements;
9494
+ }
9495
+ }
9496
+ throw new UserError(`Failed to load elements from "${id}"`);
9497
+ }
9498
+ /**
9499
+ * @internal
9500
+ */
9501
+ function resolvePlugin(resolvers, id, options) {
9502
+ for (const resolver of resolvers.filter(havePluginResolver)) {
9503
+ const plugin = resolver.resolvePlugin(id, options);
9504
+ if (plugin) {
9505
+ return plugin;
9506
+ }
9507
+ }
9508
+ throw new UserError(`Failed to load plugin from "${id}"`);
9509
+ }
9510
+ /**
9511
+ * @internal
9512
+ */
9513
+ function resolveTransformer(resolvers, id, options) {
9514
+ for (const resolver of resolvers.filter(haveTransformerResolver)) {
9515
+ const transformer = resolver.resolveTransformer(id, options);
9516
+ if (transformer) {
9517
+ return transformer;
9518
+ }
9519
+ }
9520
+ throw new UserError(`Failed to load transformer from "${id}"`);
9521
+ }
9522
+
9523
+ /**
9524
+ * Create a new resolver for static content, i.e. plugins or transformers known
9525
+ * at compile time.
9526
+ *
9527
+ * @public
9528
+ * @since 8.0.0
9529
+ */
9530
+ function staticResolver(map = {}) {
9531
+ const { elements = {}, configs = {}, plugins = {}, transformers = {} } = map;
9532
+ return {
9533
+ name: "static-qresolver",
9534
+ addElements(id, value) {
9535
+ elements[id] = value;
9536
+ },
9537
+ addConfig(id, value) {
9538
+ configs[id] = value;
9539
+ },
9540
+ addPlugin(id, value) {
9541
+ plugins[id] = value;
9542
+ },
9543
+ addTransformer(id, value) {
9544
+ transformers[id] = value;
9545
+ },
9546
+ resolveElements(id) {
9547
+ var _a;
9548
+ return (_a = elements[id]) !== null && _a !== void 0 ? _a : null;
9549
+ },
9550
+ resolveConfig(id) {
9551
+ var _a;
9552
+ return (_a = configs[id]) !== null && _a !== void 0 ? _a : null;
9553
+ },
9554
+ resolvePlugin(id) {
9555
+ var _a;
9556
+ return (_a = plugins[id]) !== null && _a !== void 0 ? _a : null;
9557
+ },
9558
+ resolveTransformer(id) {
9559
+ var _a;
9560
+ return (_a = transformers[id]) !== null && _a !== void 0 ? _a : null;
9561
+ },
9562
+ };
9563
+ }
9564
+
9783
9565
  const ajv = new Ajv__default.default({ strict: true, strictTuples: true, strictTypes: true });
9784
9566
  ajv.addMetaSchema(ajvSchemaDraft);
9785
9567
  const validator = ajv.compile(configurationSchema);
@@ -9802,30 +9584,13 @@ function mergeInternal(base, rhs) {
9802
9584
  }
9803
9585
  return dst;
9804
9586
  }
9805
- /**
9806
- * @internal
9807
- */
9808
- function configDataFromFile(filename) {
9809
- let json;
9810
- try {
9811
- /* load using require as it can process both js and json */
9812
- /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- technical debt, should be refactored into something more typesafe */
9813
- json = requireUncached(legacyRequire, filename);
9814
- }
9815
- catch (err) {
9816
- throw new ConfigError(`Failed to read configuration from "${filename}"`, ensureError(err));
9817
- }
9818
- /* expand any relative paths */
9819
- for (const key of ["extends", "elements", "plugins"]) {
9820
- /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- technical debt, should be refactored into something more typesafe */
9821
- const value = json[key];
9822
- if (!value)
9823
- continue;
9824
- json[key] = value.map((ref) => {
9825
- return Config.expandRelative(ref, path__default.default.dirname(filename));
9826
- });
9587
+ function toArray(value) {
9588
+ if (Array.isArray(value)) {
9589
+ return value;
9590
+ }
9591
+ else {
9592
+ return [value];
9827
9593
  }
9828
- return json;
9829
9594
  }
9830
9595
  /**
9831
9596
  * Configuration holder.
@@ -9839,7 +9604,7 @@ class Config {
9839
9604
  * Create a new blank configuration. See also `Config.defaultConfig()`.
9840
9605
  */
9841
9606
  static empty() {
9842
- return new Config({
9607
+ return new Config([], {
9843
9608
  extends: [],
9844
9609
  rules: {},
9845
9610
  plugins: [],
@@ -9849,9 +9614,9 @@ class Config {
9849
9614
  /**
9850
9615
  * Create configuration from object.
9851
9616
  */
9852
- static fromObject(options, filename = null) {
9617
+ static fromObject(resolvers, options, filename = null) {
9853
9618
  Config.validate(options, filename);
9854
- return new Config(options);
9619
+ return new Config(resolvers, options);
9855
9620
  }
9856
9621
  /**
9857
9622
  * Read configuration from filename.
@@ -9862,9 +9627,9 @@ class Config {
9862
9627
  * @internal
9863
9628
  * @param filename - The file to read from
9864
9629
  */
9865
- static fromFile(filename) {
9866
- const configdata = configDataFromFile(filename);
9867
- return Config.fromObject(configdata, filename);
9630
+ static fromFile(resolvers, filename) {
9631
+ const configData = resolveConfig(toArray(resolvers), filename, { cache: false });
9632
+ return Config.fromObject(resolvers, configData, filename);
9868
9633
  }
9869
9634
  /**
9870
9635
  * Validate configuration data.
@@ -9894,12 +9659,12 @@ class Config {
9894
9659
  * Load a default configuration object.
9895
9660
  */
9896
9661
  static defaultConfig() {
9897
- return new Config(defaultConfig);
9662
+ return new Config([], defaultConfig);
9898
9663
  }
9899
9664
  /**
9900
9665
  * @internal
9901
9666
  */
9902
- constructor(options) {
9667
+ constructor(resolvers, options) {
9903
9668
  var _a;
9904
9669
  this.transformers = [];
9905
9670
  const initial = {
@@ -9908,10 +9673,10 @@ class Config {
9908
9673
  rules: {},
9909
9674
  transform: {},
9910
9675
  };
9911
- this.config = mergeInternal(initial, options || {});
9676
+ this.config = mergeInternal(initial, options);
9912
9677
  this.metaTable = null;
9913
- this.rootDir = this.findRootDir();
9914
9678
  this.initialized = false;
9679
+ this.resolvers = toArray(resolvers);
9915
9680
  /* load plugins */
9916
9681
  this.plugins = this.loadPlugins(this.config.plugins || []);
9917
9682
  this.configurations = this.loadConfigurations(this.plugins);
@@ -9957,8 +9722,8 @@ class Config {
9957
9722
  * @public
9958
9723
  * @param rhs - Configuration to merge with this one.
9959
9724
  */
9960
- merge(rhs) {
9961
- return new Config(mergeInternal(this.config, rhs.config));
9725
+ merge(resolvers, rhs) {
9726
+ return new Config(resolvers, mergeInternal(this.config, rhs.config));
9962
9727
  }
9963
9728
  extendConfig(entries) {
9964
9729
  if (entries.length === 0) {
@@ -9972,7 +9737,7 @@ class Config {
9972
9737
  extended = this.configurations.get(entry);
9973
9738
  }
9974
9739
  else {
9975
- extended = Config.fromFile(entry).config;
9740
+ extended = Config.fromFile(this.resolvers, entry).config;
9976
9741
  }
9977
9742
  base = mergeInternal(base, extended);
9978
9743
  }
@@ -10009,30 +9774,20 @@ class Config {
10009
9774
  metaTable.loadFromObject(bundled);
10010
9775
  continue;
10011
9776
  }
10012
- /* assume it is loadable with require() */
10013
- const id = entry.replace("<rootDir>", this.rootDir);
9777
+ /* load with resolver */
10014
9778
  try {
10015
- const data = legacyRequire(id);
10016
- metaTable.loadFromObject(data, id);
9779
+ const data = resolveElements(this.resolvers, entry, { cache: false });
9780
+ metaTable.loadFromObject(data, entry);
10017
9781
  }
10018
9782
  catch (err) {
10019
9783
  /* istanbul ignore next: only used as a fallback */
10020
9784
  const message = err instanceof Error ? err.message : String(err);
10021
- throw new ConfigError(`Failed to load elements from "${id}": ${message}`, ensureError(err));
9785
+ throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, ensureError(err));
10022
9786
  }
10023
9787
  }
10024
9788
  metaTable.init();
10025
9789
  return (this.metaTable = metaTable);
10026
9790
  }
10027
- /**
10028
- * @internal exposed for testing only
10029
- */
10030
- static expandRelative(src, currentPath) {
10031
- if (src[0] === ".") {
10032
- return path__default.default.normalize(`${currentPath}/${src}`);
10033
- }
10034
- return src;
10035
- }
10036
9791
  /**
10037
9792
  * Get a copy of internal configuration data.
10038
9793
  *
@@ -10084,7 +9839,7 @@ class Config {
10084
9839
  return plugin;
10085
9840
  }
10086
9841
  try {
10087
- const plugin = legacyRequire(moduleName.replace("<rootDir>", this.rootDir));
9842
+ const plugin = resolvePlugin(this.resolvers, moduleName, { cache: true });
10088
9843
  plugin.name = plugin.name || moduleName;
10089
9844
  plugin.originalName = moduleName;
10090
9845
  return plugin;
@@ -10257,57 +10012,7 @@ class Config {
10257
10012
  return plugin.transformer;
10258
10013
  }
10259
10014
  getTransformerFromModule(name) {
10260
- /* expand <rootDir> */
10261
- const moduleName = name.replace("<rootDir>", this.rootDir);
10262
- /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- technical debt, the code kinda does the right thing but it should be reflected in the typing too */
10263
- const fn = legacyRequire(moduleName);
10264
- /* sanity check */
10265
- if (typeof fn !== "function") {
10266
- /* this is not a proper transformer, is it a plugin exposing a transformer? */
10267
- if (fn.transformer) {
10268
- throw new ConfigError(`Module is not a valid transformer. This looks like a plugin, did you forget to load the plugin first?`);
10269
- }
10270
- throw new ConfigError(`Module is not a valid transformer.`);
10271
- }
10272
- return fn;
10273
- }
10274
- /**
10275
- * @internal
10276
- */
10277
- get rootDirCache() {
10278
- /* return global instance */
10279
- return rootDirCache;
10280
- }
10281
- set rootDirCache(value) {
10282
- /* set global instance */
10283
- rootDirCache = value;
10284
- }
10285
- /**
10286
- * @internal
10287
- */
10288
- findRootDir() {
10289
- const cache = this.rootDirCache;
10290
- if (cache !== null) {
10291
- return cache;
10292
- }
10293
- /* try to locate package.json */
10294
- let current = process.cwd();
10295
- // eslint-disable-next-line no-constant-condition -- break outs when filesystem is traversed
10296
- while (true) {
10297
- const search = path__default.default.join(current, "package.json");
10298
- if (fs__default.default.existsSync(search)) {
10299
- return (this.rootDirCache = current);
10300
- }
10301
- /* get the parent directory */
10302
- const child = current;
10303
- current = path__default.default.dirname(current);
10304
- /* stop if this is the root directory */
10305
- if (current === child) {
10306
- break;
10307
- }
10308
- }
10309
- /* default to working directory if no package.json is found */
10310
- return (this.rootDirCache = process.cwd());
10015
+ return resolveTransformer(this.resolvers, name, { cache: true });
10311
10016
  }
10312
10017
  }
10313
10018
 
@@ -10320,19 +10025,25 @@ class Config {
10320
10025
  * @public
10321
10026
  */
10322
10027
  class ConfigLoader {
10323
- constructor(config, configFactory = Config) {
10324
- const defaults = configFactory.empty();
10325
- this.configFactory = configFactory;
10326
- this.globalConfig = defaults.merge(config ? this.loadFromObject(config) : this.defaultConfig());
10028
+ constructor(resolvers, config) {
10029
+ const defaults = Config.empty();
10030
+ this.resolvers = resolvers;
10031
+ this.globalConfig = defaults.merge(this.resolvers, config ? this.loadFromObject(config) : this.defaultConfig());
10032
+ }
10033
+ /**
10034
+ * @internal For testing only
10035
+ */
10036
+ _getGlobalConfig() {
10037
+ return this.globalConfig.get();
10327
10038
  }
10328
10039
  empty() {
10329
- return this.configFactory.empty();
10040
+ return Config.empty();
10330
10041
  }
10331
10042
  loadFromObject(options, filename) {
10332
- return this.configFactory.fromObject(options, filename);
10043
+ return Config.fromObject(this.resolvers, options, filename);
10333
10044
  }
10334
10045
  loadFromFile(filename) {
10335
- return this.configFactory.fromFile(filename);
10046
+ return Config.fromFile(this.resolvers, filename);
10336
10047
  }
10337
10048
  }
10338
10049
 
@@ -11320,7 +11031,7 @@ class Engine {
11320
11031
  /**
11321
11032
  * Get rule documentation.
11322
11033
  */
11323
- getRuleDocumentation(ruleId, context) {
11034
+ getRuleDocumentation({ ruleId, context, }) {
11324
11035
  const rules = this.config.getRules();
11325
11036
  const ruleData = rules.get(ruleId);
11326
11037
  if (ruleData) {
@@ -11557,6 +11268,10 @@ class Engine {
11557
11268
  }
11558
11269
  }
11559
11270
 
11271
+ const defaultResolvers$1 = [];
11272
+ function hasResolver$1(value) {
11273
+ return Array.isArray(value[0]);
11274
+ }
11560
11275
  /**
11561
11276
  * The static configuration loader does not do any per-handle lookup. Only the
11562
11277
  * global or per-call configuration is used.
@@ -11567,13 +11282,23 @@ class Engine {
11567
11282
  * @public
11568
11283
  */
11569
11284
  class StaticConfigLoader extends ConfigLoader {
11285
+ constructor(...args) {
11286
+ if (hasResolver$1(args)) {
11287
+ const [resolvers, config] = args;
11288
+ super(resolvers, config);
11289
+ }
11290
+ else {
11291
+ const [config] = args;
11292
+ super(defaultResolvers$1, config);
11293
+ }
11294
+ }
11570
11295
  getConfigFor(_handle, configOverride) {
11571
11296
  const override = this.loadFromObject(configOverride || {});
11572
11297
  if (override.isRootFound()) {
11573
11298
  override.init();
11574
11299
  return override.resolve();
11575
11300
  }
11576
- const merged = this.globalConfig.merge(override);
11301
+ const merged = this.globalConfig.merge(this.resolvers, override);
11577
11302
  merged.init();
11578
11303
  return merged.resolve();
11579
11304
  }
@@ -11626,6 +11351,33 @@ class HtmlValidate {
11626
11351
  };
11627
11352
  return this.validateSource(source, options);
11628
11353
  }
11354
+ validateStringSync(str, arg1, arg2, arg3) {
11355
+ const filename = typeof arg1 === "string" ? arg1 : "inline";
11356
+ const options = isConfigData(arg1) ? arg1 : isConfigData(arg2) ? arg2 : undefined;
11357
+ const hooks = isSourceHooks(arg1) ? arg1 : isSourceHooks(arg2) ? arg2 : arg3;
11358
+ const source = {
11359
+ data: str,
11360
+ filename,
11361
+ line: 1,
11362
+ column: 1,
11363
+ offset: 0,
11364
+ hooks,
11365
+ };
11366
+ return this.validateSourceSync(source, options);
11367
+ }
11368
+ /**
11369
+ * Parse and validate HTML from [[Source]].
11370
+ *
11371
+ * @public
11372
+ * @param input - Source to parse.
11373
+ * @returns Report output.
11374
+ */
11375
+ async validateSource(input, configOverride) {
11376
+ const config = await this.getConfigFor(input.filename, configOverride);
11377
+ const source = config.transformSource(input);
11378
+ const engine = new Engine(config, Parser);
11379
+ return engine.lint(source);
11380
+ }
11629
11381
  /**
11630
11382
  * Parse and validate HTML from [[Source]].
11631
11383
  *
@@ -11633,8 +11385,8 @@ class HtmlValidate {
11633
11385
  * @param input - Source to parse.
11634
11386
  * @returns Report output.
11635
11387
  */
11636
- validateSource(input, configOverride) {
11637
- const config = this.getConfigFor(input.filename, configOverride);
11388
+ validateSourceSync(input, configOverride) {
11389
+ const config = this.getConfigForSync(input.filename, configOverride);
11638
11390
  const source = config.transformSource(input);
11639
11391
  const engine = new Engine(config, Parser);
11640
11392
  return engine.lint(source);
@@ -11646,8 +11398,21 @@ class HtmlValidate {
11646
11398
  * @param filename - Filename to read and parse.
11647
11399
  * @returns Report output.
11648
11400
  */
11649
- validateFile(filename) {
11650
- const config = this.getConfigFor(filename);
11401
+ async validateFile(filename) {
11402
+ const config = await this.getConfigFor(filename);
11403
+ const source = config.transformFilename(filename);
11404
+ const engine = new Engine(config, Parser);
11405
+ return Promise.resolve(engine.lint(source));
11406
+ }
11407
+ /**
11408
+ * Parse and validate HTML from file.
11409
+ *
11410
+ * @public
11411
+ * @param filename - Filename to read and parse.
11412
+ * @returns Report output.
11413
+ */
11414
+ validateFileSync(filename) {
11415
+ const config = this.getConfigForSync(filename);
11651
11416
  const source = config.transformFilename(filename);
11652
11417
  const engine = new Engine(config, Parser);
11653
11418
  return engine.lint(source);
@@ -11659,8 +11424,19 @@ class HtmlValidate {
11659
11424
  * @param filenames - Filenames to read and parse.
11660
11425
  * @returns Report output.
11661
11426
  */
11662
- validateMultipleFiles(filenames) {
11663
- return Reporter.merge(filenames.map((filename) => this.validateFile(filename)));
11427
+ async validateMultipleFiles(filenames) {
11428
+ const report = Reporter.merge(filenames.map((filename) => this.validateFileSync(filename)));
11429
+ return Promise.resolve(report);
11430
+ }
11431
+ /**
11432
+ * Parse and validate HTML from multiple files. Result is merged together to a
11433
+ * single report.
11434
+ *
11435
+ * @param filenames - Filenames to read and parse.
11436
+ * @returns Report output.
11437
+ */
11438
+ validateMultipleFilesSync(filenames) {
11439
+ return Reporter.merge(filenames.map((filename) => this.validateFileSync(filename)));
11664
11440
  }
11665
11441
  /**
11666
11442
  * Returns true if the given filename can be validated.
@@ -11671,14 +11447,33 @@ class HtmlValidate {
11671
11447
  * This is mostly useful for tooling to determine whenever to validate the
11672
11448
  * file or not. CLI tools will run on all the given files anyway.
11673
11449
  */
11674
- canValidate(filename) {
11450
+ async canValidate(filename) {
11675
11451
  /* .html is always supported */
11676
11452
  const extension = path__default.default.extname(filename).toLowerCase();
11677
11453
  if (extension === ".html") {
11678
11454
  return true;
11679
11455
  }
11680
11456
  /* test if there is a matching transformer */
11681
- const config = this.getConfigFor(filename);
11457
+ const config = await this.getConfigFor(filename);
11458
+ return config.canTransform(filename);
11459
+ }
11460
+ /**
11461
+ * Returns true if the given filename can be validated.
11462
+ *
11463
+ * A file is considered to be validatable if the extension is `.html` or if a
11464
+ * transformer matches the filename.
11465
+ *
11466
+ * This is mostly useful for tooling to determine whenever to validate the
11467
+ * file or not. CLI tools will run on all the given files anyway.
11468
+ */
11469
+ canValidateSync(filename) {
11470
+ /* .html is always supported */
11471
+ const extension = path__default.default.extname(filename).toLowerCase();
11472
+ if (extension === ".html") {
11473
+ return true;
11474
+ }
11475
+ /* test if there is a matching transformer */
11476
+ const config = this.getConfigForSync(filename);
11682
11477
  return config.canTransform(filename);
11683
11478
  }
11684
11479
  /**
@@ -11691,7 +11486,7 @@ class HtmlValidate {
11691
11486
  * @param filename - Filename to tokenize.
11692
11487
  */
11693
11488
  dumpTokens(filename) {
11694
- const config = this.getConfigFor(filename);
11489
+ const config = this.getConfigForSync(filename);
11695
11490
  const source = config.transformFilename(filename);
11696
11491
  const engine = new Engine(config, Parser);
11697
11492
  return engine.dumpTokens(source);
@@ -11706,7 +11501,7 @@ class HtmlValidate {
11706
11501
  * @param filename - Filename to dump events from.
11707
11502
  */
11708
11503
  dumpEvents(filename) {
11709
- const config = this.getConfigFor(filename);
11504
+ const config = this.getConfigForSync(filename);
11710
11505
  const source = config.transformFilename(filename);
11711
11506
  const engine = new Engine(config, Parser);
11712
11507
  return engine.dumpEvents(source);
@@ -11721,7 +11516,7 @@ class HtmlValidate {
11721
11516
  * @param filename - Filename to dump DOM tree from.
11722
11517
  */
11723
11518
  dumpTree(filename) {
11724
- const config = this.getConfigFor(filename);
11519
+ const config = this.getConfigForSync(filename);
11725
11520
  const source = config.transformFilename(filename);
11726
11521
  const engine = new Engine(config, Parser);
11727
11522
  return engine.dumpTree(source);
@@ -11736,7 +11531,7 @@ class HtmlValidate {
11736
11531
  * @param filename - Filename to dump source from.
11737
11532
  */
11738
11533
  dumpSource(filename) {
11739
- const config = this.getConfigFor(filename);
11534
+ const config = this.getConfigForSync(filename);
11740
11535
  const sources = config.transformFilename(filename);
11741
11536
  return sources.reduce((result, source) => {
11742
11537
  result.push(`Source ${source.filename}@${source.line}:${source.column} (offset: ${source.offset})`);
@@ -11772,37 +11567,95 @@ class HtmlValidate {
11772
11567
  * handled by html-validate but the path will be used when resolving
11773
11568
  * configuration. As a rule-of-thumb, set it to the elements json file.
11774
11569
  */
11775
- getElementsSchema(filename) {
11776
- const config = this.getConfigFor(filename !== null && filename !== void 0 ? filename : "inline");
11570
+ async getElementsSchema(filename) {
11571
+ const config = await this.getConfigFor(filename !== null && filename !== void 0 ? filename : "inline");
11777
11572
  const metaTable = config.getMetaTable();
11778
11573
  return metaTable.getJSONSchema();
11779
11574
  }
11575
+ /**
11576
+ * Get effective metadata element schema.
11577
+ *
11578
+ * If a filename is given the configured plugins can extend the
11579
+ * schema. Filename must not be an existing file or a filetype normally
11580
+ * handled by html-validate but the path will be used when resolving
11581
+ * configuration. As a rule-of-thumb, set it to the elements json file.
11582
+ */
11583
+ getElementsSchemaSync(filename) {
11584
+ const config = this.getConfigForSync(filename !== null && filename !== void 0 ? filename : "inline");
11585
+ const metaTable = config.getMetaTable();
11586
+ return metaTable.getJSONSchema();
11587
+ }
11588
+ async getContextualDocumentation(message, filenameOrConfig = "inline") {
11589
+ const config = typeof filenameOrConfig === "string"
11590
+ ? await this.getConfigFor(filenameOrConfig)
11591
+ : await filenameOrConfig;
11592
+ const engine = new Engine(config, Parser);
11593
+ return engine.getRuleDocumentation(message);
11594
+ }
11595
+ getContextualDocumentationSync(message, filenameOrConfig = "inline") {
11596
+ const config = typeof filenameOrConfig === "string"
11597
+ ? this.getConfigForSync(filenameOrConfig)
11598
+ : filenameOrConfig;
11599
+ const engine = new Engine(config, Parser);
11600
+ return engine.getRuleDocumentation(message);
11601
+ }
11780
11602
  /**
11781
11603
  * Get contextual documentation for the given rule.
11782
11604
  *
11783
11605
  * Typical usage:
11784
11606
  *
11785
11607
  * ```js
11786
- * const report = htmlvalidate.validateFile("my-file.html");
11608
+ * const report = await htmlvalidate.validateFile("my-file.html");
11787
11609
  * for (const result of report.results){
11788
- * const config = htmlvalidate.getConfigFor(result.filePath);
11610
+ * const config = await htmlvalidate.getConfigFor(result.filePath);
11789
11611
  * for (const message of result.messages){
11790
- * const documentation = htmlvalidate.getRuleDocumentation(message.ruleId, config, message.context);
11612
+ * const documentation = await htmlvalidate.getRuleDocumentation(message.ruleId, config, message.context);
11791
11613
  * // do something with documentation
11792
11614
  * }
11793
11615
  * }
11794
11616
  * ```
11795
11617
  *
11618
+ * @public
11619
+ * @deprecated Deprecated since 8.0.0, use [[getContextualDocumentation]] instead.
11796
11620
  * @param ruleId - Rule to get documentation for.
11797
11621
  * @param config - If set it provides more accurate description by using the
11798
11622
  * correct configuration for the file.
11799
11623
  * @param context - If set to `Message.context` some rules can provide
11800
11624
  * contextual details and suggestions.
11801
11625
  */
11802
- getRuleDocumentation(ruleId, config = null, context = null) {
11626
+ async getRuleDocumentation(ruleId, config = null, context = null) {
11803
11627
  const c = config || this.getConfigFor("inline");
11628
+ const engine = new Engine(await c, Parser);
11629
+ return engine.getRuleDocumentation({ ruleId, context });
11630
+ }
11631
+ /**
11632
+ * Get contextual documentation for the given rule.
11633
+ *
11634
+ * Typical usage:
11635
+ *
11636
+ * ```js
11637
+ * const report = htmlvalidate.validateFileSync("my-file.html");
11638
+ * for (const result of report.results){
11639
+ * const config = htmlvalidate.getConfigForSync(result.filePath);
11640
+ * for (const message of result.messages){
11641
+ * const documentation = htmlvalidate.getRuleDocumentationSync(message.ruleId, config, message.context);
11642
+ * // do something with documentation
11643
+ * }
11644
+ * }
11645
+ * ```
11646
+ *
11647
+ * @public
11648
+ * @deprecated Deprecated since 8.0.0, use [[getContextualDocumentationSync]] instead.
11649
+ * @param ruleId - Rule to get documentation for.
11650
+ * @param config - If set it provides more accurate description by using the
11651
+ * correct configuration for the file.
11652
+ * @param context - If set to `Message.context` some rules can provide
11653
+ * contextual details and suggestions.
11654
+ */
11655
+ getRuleDocumentationSync(ruleId, config = null, context = null) {
11656
+ const c = config || this.getConfigForSync("inline");
11804
11657
  const engine = new Engine(c, Parser);
11805
- return engine.getRuleDocumentation(ruleId, context);
11658
+ return engine.getRuleDocumentation({ ruleId, context });
11806
11659
  }
11807
11660
  /**
11808
11661
  * Create a parser configured for given filename.
@@ -11810,8 +11663,8 @@ class HtmlValidate {
11810
11663
  * @internal
11811
11664
  * @param source - Source to use.
11812
11665
  */
11813
- getParserFor(source) {
11814
- const config = this.getConfigFor(source.filename);
11666
+ async getParserFor(source) {
11667
+ const config = await this.getConfigFor(source.filename);
11815
11668
  return new Parser(config);
11816
11669
  }
11817
11670
  /**
@@ -11825,11 +11678,19 @@ class HtmlValidate {
11825
11678
  */
11826
11679
  getConfigFor(filename, configOverride) {
11827
11680
  const config = this.configLoader.getConfigFor(filename, configOverride);
11828
- /* for backwards compatibility only */
11829
- if (config instanceof Config) {
11830
- return config.resolve();
11831
- }
11832
- return config;
11681
+ return Promise.resolve(config);
11682
+ }
11683
+ /**
11684
+ * Get configuration for given filename.
11685
+ *
11686
+ * See [[FileSystemConfigLoader]] for details.
11687
+ *
11688
+ * @public
11689
+ * @param filename - Filename to get configuration for.
11690
+ * @param configOverride - Configuration to apply last.
11691
+ */
11692
+ getConfigForSync(filename, configOverride) {
11693
+ return this.configLoader.getConfigFor(filename, configOverride);
11833
11694
  }
11834
11695
  /**
11835
11696
  * Flush configuration cache. Clears full cache unless a filename is given.
@@ -11848,7 +11709,7 @@ class HtmlValidate {
11848
11709
  /** @public */
11849
11710
  const name = "html-validate";
11850
11711
  /** @public */
11851
- const version = "7.18.0";
11712
+ const version = "8.0.0";
11852
11713
  /** @public */
11853
11714
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11854
11715
 
@@ -11874,6 +11735,7 @@ const defaults$1 = {
11874
11735
  * option is used a warning is displayed on the console.
11875
11736
  *
11876
11737
  * @public
11738
+ * @since v5.0.0
11877
11739
  * @param name - Name of plugin
11878
11740
  * @param declared - What library versions the plugin support (e.g. declared peerDependencies)
11879
11741
  * @returns - `true` if version is compatible
@@ -11894,6 +11756,27 @@ function compatibilityCheck(name, declared, options) {
11894
11756
  return false;
11895
11757
  }
11896
11758
 
11759
+ /**
11760
+ * Similar to `require(..)` but removes the cached copy first.
11761
+ */
11762
+ function requireUncached(require, moduleId) {
11763
+ const filename = require.resolve(moduleId);
11764
+ /* remove references from the parent module to prevent memory leak */
11765
+ const m = require.cache[filename];
11766
+ if (m && m.parent) {
11767
+ const { parent } = m;
11768
+ for (let i = parent.children.length - 1; i >= 0; i--) {
11769
+ if (parent.children[i].id === filename) {
11770
+ parent.children.splice(i, 1);
11771
+ }
11772
+ }
11773
+ }
11774
+ /* remove old module from cache */
11775
+ delete require.cache[filename];
11776
+ /* eslint-disable-next-line import/no-dynamic-require, security/detect-non-literal-require -- as expected but should be moved to upcoming resolver class */
11777
+ return require(filename);
11778
+ }
11779
+
11897
11780
  const ruleIds = new Set(Object.keys(rules));
11898
11781
  /**
11899
11782
  * Returns true if given ruleId is an existing builtin rule. It does not handle
@@ -11910,13 +11793,148 @@ function ruleExists(ruleId) {
11910
11793
  return ruleIds.has(ruleId);
11911
11794
  }
11912
11795
 
11796
+ const legacyRequire = require;
11797
+
11798
+ let cachedRootDir = null;
11799
+ /**
11800
+ * @internal
11801
+ */
11802
+ function determineRootDirImpl(intial, fs) {
11803
+ /* try to locate package.json */
11804
+ let current = intial;
11805
+ // eslint-disable-next-line no-constant-condition -- break outs when filesystem is traversed
11806
+ while (true) {
11807
+ const search = path__default$1.default.join(current, "package.json");
11808
+ if (fs.existsSync(search)) {
11809
+ return current;
11810
+ }
11811
+ /* get the parent directory */
11812
+ const child = current;
11813
+ current = path__default$1.default.dirname(current);
11814
+ /* stop if this is the root directory */
11815
+ if (current === child) {
11816
+ break;
11817
+ }
11818
+ }
11819
+ /* default to working directory if no package.json is found */
11820
+ return intial;
11821
+ }
11913
11822
  /**
11823
+ * Try to determine root directory based on the location of the closest
11824
+ * `package.json`. Fallbacks on `process.cwd()` if no package.json was found.
11825
+ *
11914
11826
  * @internal
11915
11827
  */
11916
- function findConfigurationFiles(directory) {
11828
+ /* istanbul ignore next: cached version of determineRootDirImpl, no need to test */
11829
+ function determineRootDir() {
11830
+ if (cachedRootDir === null) {
11831
+ cachedRootDir = determineRootDirImpl(process.cwd(), fs__default$1.default);
11832
+ }
11833
+ return cachedRootDir;
11834
+ }
11835
+
11836
+ /**
11837
+ * @internal
11838
+ */
11839
+ function expandRelativePath(value, { cwd }) {
11840
+ if (typeof value === "string" && value[0] === ".") {
11841
+ return path__default$1.default.normalize(path__default$1.default.join(cwd, value));
11842
+ }
11843
+ else {
11844
+ return value;
11845
+ }
11846
+ }
11847
+
11848
+ function isRequireError(error) {
11849
+ return Boolean(error && typeof error === "object" && "code" in error);
11850
+ }
11851
+ function isTransformer(value) {
11852
+ return typeof value === "function";
11853
+ }
11854
+ /**
11855
+ * Create a new resolver for NodeJS packages using `require(..)`.
11856
+ *
11857
+ * If the module name contains `<rootDir>` (e.g. `<rootDir/foo`) it will be
11858
+ * expanded relative to the root directory either explicitly set by the
11859
+ * `rootDir` parameter or determined automatically by the closest `package.json`
11860
+ * file (starting at the current working directory).
11861
+ *
11862
+ * @public
11863
+ * @since 8.0.0
11864
+ */
11865
+ function nodejsResolver(options = {}) {
11866
+ var _a;
11867
+ const rootDir = (_a = options.rootDir) !== null && _a !== void 0 ? _a : determineRootDir();
11868
+ function internalRequire(id, { cache }) {
11869
+ const moduleName = id.replace("<rootDir>", rootDir);
11870
+ try {
11871
+ /* istanbul ignore else: the tests only runs the cached versions to get
11872
+ * unmodified access to `require`, the implementation of `requireUncached`
11873
+ * is assumed to be tested elsewhere */
11874
+ if (cache) {
11875
+ return legacyRequire(moduleName);
11876
+ }
11877
+ else {
11878
+ return requireUncached(legacyRequire, moduleName);
11879
+ }
11880
+ }
11881
+ catch (err) {
11882
+ if (isRequireError(err) && err.code === "MODULE_NOT_FOUND") {
11883
+ return null;
11884
+ }
11885
+ throw err;
11886
+ }
11887
+ }
11888
+ return {
11889
+ name: "nodejs-resolver",
11890
+ resolveElements(id, options) {
11891
+ return internalRequire(id, options);
11892
+ },
11893
+ resolveConfig(id, options) {
11894
+ var _a, _b, _c;
11895
+ const configData = internalRequire(id, options);
11896
+ if (!configData) {
11897
+ return null;
11898
+ }
11899
+ /* expand any relative paths */
11900
+ const cwd = path__default$1.default.dirname(id);
11901
+ const expand = (value) => expandRelativePath(value, { cwd });
11902
+ configData.elements = (_a = configData.elements) === null || _a === void 0 ? void 0 : _a.map(expand);
11903
+ configData.extends = (_b = configData.extends) === null || _b === void 0 ? void 0 : _b.map(expand);
11904
+ configData.plugins = (_c = configData.plugins) === null || _c === void 0 ? void 0 : _c.map(expand);
11905
+ return configData;
11906
+ },
11907
+ resolvePlugin(id, options) {
11908
+ return internalRequire(id, options);
11909
+ },
11910
+ resolveTransformer(id, options) {
11911
+ const mod = internalRequire(id, options);
11912
+ if (!mod) {
11913
+ return null;
11914
+ }
11915
+ if (isTransformer(mod)) {
11916
+ return mod;
11917
+ }
11918
+ /* this is not a proper transformer, is it a plugin exposing a transformer? */
11919
+ if (mod.transformer) {
11920
+ throw new ConfigError(`Module "${id}" is not a valid transformer. This looks like a plugin, did you forget to load the plugin first?`);
11921
+ }
11922
+ throw new ConfigError(`Module "${id}" is not a valid transformer.`);
11923
+ },
11924
+ };
11925
+ }
11926
+
11927
+ /**
11928
+ * @internal
11929
+ */
11930
+ function findConfigurationFiles(fs, directory) {
11917
11931
  return ["json", "cjs", "js"]
11918
- .map((extension) => path__default.default.join(directory, `.htmlvalidate.${extension}`))
11919
- .filter((filePath) => fs__default.default.existsSync(filePath));
11932
+ .map((extension) => path__default$1.default.join(directory, `.htmlvalidate.${extension}`))
11933
+ .filter((filePath) => fs.existsSync(filePath));
11934
+ }
11935
+ const defaultResolvers = [nodejsResolver()];
11936
+ function hasResolver(value) {
11937
+ return Array.isArray(value[0]);
11920
11938
  }
11921
11939
  /**
11922
11940
  * Loads configuration by traversing filesystem.
@@ -11946,12 +11964,20 @@ function findConfigurationFiles(directory) {
11946
11964
  * @public
11947
11965
  */
11948
11966
  class FileSystemConfigLoader extends ConfigLoader {
11949
- /**
11950
- * @param config - Global configuration
11951
- * @param configFactory - Optional configuration factory
11952
- */
11953
- constructor(config, configFactory = Config) {
11954
- super(config, configFactory);
11967
+ constructor(...args) {
11968
+ var _a, _b;
11969
+ if (hasResolver(args)) {
11970
+ /* istanbul ignore next */
11971
+ const [resolvers, config, options = {}] = args;
11972
+ super(resolvers, config);
11973
+ this.fs = /* istanbul ignore next */ (_a = options.fs) !== null && _a !== void 0 ? _a : fs__default$1.default;
11974
+ }
11975
+ else {
11976
+ /* istanbul ignore next */
11977
+ const [config, options = {}] = args;
11978
+ super(defaultResolvers, config);
11979
+ this.fs = /* istanbul ignore next */ (_b = options.fs) !== null && _b !== void 0 ? _b : fs__default$1.default;
11980
+ }
11955
11981
  this.cache = new Map();
11956
11982
  }
11957
11983
  /**
@@ -11971,12 +11997,14 @@ class FileSystemConfigLoader extends ConfigLoader {
11971
11997
  /* special case when the global configuration is marked as root, should not
11972
11998
  * try to load and more configuration files */
11973
11999
  if (this.globalConfig.isRootFound()) {
11974
- const merged = this.globalConfig.merge(override);
12000
+ const merged = this.globalConfig.merge(this.resolvers, override);
11975
12001
  merged.init();
11976
12002
  return merged.resolve();
11977
12003
  }
11978
12004
  const config = this.fromFilename(filename);
11979
- const merged = config ? config.merge(override) : this.globalConfig.merge(override);
12005
+ const merged = config
12006
+ ? config.merge(this.resolvers, override)
12007
+ : this.globalConfig.merge(this.resolvers, override);
11980
12008
  merged.init();
11981
12009
  return merged.resolve();
11982
12010
  }
@@ -12008,15 +12036,15 @@ class FileSystemConfigLoader extends ConfigLoader {
12008
12036
  return cache;
12009
12037
  }
12010
12038
  let found = false;
12011
- let current = path__default.default.resolve(path__default.default.dirname(filename));
12039
+ let current = path__default$1.default.resolve(path__default$1.default.dirname(filename));
12012
12040
  let config = this.empty();
12013
12041
  // eslint-disable-next-line no-constant-condition -- it will break out when filesystem is traversed
12014
12042
  while (true) {
12015
12043
  /* search configuration files in current directory */
12016
- for (const configFile of findConfigurationFiles(current)) {
12044
+ for (const configFile of findConfigurationFiles(this.fs, current)) {
12017
12045
  const local = this.loadFromFile(configFile);
12018
12046
  found = true;
12019
- config = local.merge(config);
12047
+ config = local.merge(this.resolvers, config);
12020
12048
  }
12021
12049
  /* stop if a configuration with "root" is set to true */
12022
12050
  if (config.isRootFound()) {
@@ -12024,7 +12052,7 @@ class FileSystemConfigLoader extends ConfigLoader {
12024
12052
  }
12025
12053
  /* get the parent directory */
12026
12054
  const child = current;
12027
- current = path__default.default.dirname(current);
12055
+ current = path__default$1.default.dirname(current);
12028
12056
  /* stop if this is the root directory */
12029
12057
  if (current === child) {
12030
12058
  break;
@@ -12038,8 +12066,14 @@ class FileSystemConfigLoader extends ConfigLoader {
12038
12066
  this.cache.set(filename, config);
12039
12067
  return config;
12040
12068
  }
12069
+ /**
12070
+ * @internal For testing only
12071
+ */
12072
+ _getInternalCache() {
12073
+ return this.cache;
12074
+ }
12041
12075
  defaultConfig() {
12042
- return this.configFactory.defaultConfig();
12076
+ return Config.defaultConfig();
12043
12077
  }
12044
12078
  }
12045
12079
 
@@ -12316,7 +12350,6 @@ exports.ResolvedConfig = ResolvedConfig;
12316
12350
  exports.Rule = Rule;
12317
12351
  exports.SchemaValidationError = SchemaValidationError;
12318
12352
  exports.StaticConfigLoader = StaticConfigLoader;
12319
- exports.TemplateExtractor = TemplateExtractor;
12320
12353
  exports.TextNode = TextNode;
12321
12354
  exports.UserError = UserError;
12322
12355
  exports.Validator = Validator;
@@ -12324,7 +12357,6 @@ exports.WrappedError = WrappedError;
12324
12357
  exports.bugs = bugs;
12325
12358
  exports.codeframe = codeframe;
12326
12359
  exports.compatibilityCheck = compatibilityCheck;
12327
- exports.configDataFromFile = configDataFromFile;
12328
12360
  exports.definePlugin = definePlugin;
12329
12361
  exports.ensureError = ensureError;
12330
12362
  exports.generateIdSelector = generateIdSelector;
@@ -12333,7 +12365,9 @@ exports.isElementNode = isElementNode;
12333
12365
  exports.isTextNode = isTextNode;
12334
12366
  exports.legacyRequire = legacyRequire;
12335
12367
  exports.name = name;
12368
+ exports.nodejsResolver = nodejsResolver;
12336
12369
  exports.ruleExists = ruleExists;
12337
12370
  exports.sliceLocation = sliceLocation;
12371
+ exports.staticResolver = staticResolver;
12338
12372
  exports.version = version;
12339
12373
  //# sourceMappingURL=core.js.map