@markuplint/file-resolver 5.5.0 → 5.6.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [5.6.1](https://github.com/markuplint/markuplint/compare/v5.6.0...v5.6.1) (2026-11-13)
7
+
8
+ **Note:** Version bump only for package @markuplint/file-resolver
9
+
10
+
11
+
12
+
13
+
14
+ # [5.6.0](https://github.com/markuplint/markuplint/compare/v5.5.0...v5.6.0) (2026-11-12)
15
+
16
+ ### Bug Fixes
17
+
18
+ * **file-resolver:** validate every selector of every configuration before they are merged ([be2611c](https://github.com/markuplint/markuplint/commit/be2611ca18243bee59676c81e409a070ce9af7ab))
19
+
20
+
6
21
  # [5.5.0](https://github.com/markuplint/markuplint/compare/v5.4.0...v5.5.0) (2026-11-10)
7
22
 
8
23
  ### Bug Fixes
@@ -17,6 +17,9 @@ import { cacheClear, resolvePlugins } from './resolve-plugins.js';
17
17
  import { fileExists, uuid } from './utils.js';
18
18
  const cpLog = log.extend('config-provider');
19
19
  const KEY_SEPARATOR = '__ML_CONFIG_MERGE__';
20
+ function isPretenderList(pretenders) {
21
+ return Array.isArray(pretenders);
22
+ }
20
23
  /**
21
24
  * Manages loading, caching, and resolving markuplint configuration files.
22
25
  *
@@ -231,10 +234,7 @@ export class ConfigProvider {
231
234
  filePath: referrer,
232
235
  });
233
236
  }
234
- const errors = this.#validateConfig(configSet.config, filePath);
235
- configSet.errs.push(...errors);
236
237
  const { plugins, errors: pluginErrors } = await resolvePlugins(configSet.config.plugins);
237
- configSet.errs.push(...pluginErrors);
238
238
  if (this.#held.size > 0) {
239
239
  const extendHelds = [...this.#held.values()];
240
240
  for (const held of extendHelds) {
@@ -253,6 +253,9 @@ export class ConfigProvider {
253
253
  configSet = await this.#mergeConfigs([...keys, ...extendHelds], cache, referrer);
254
254
  this.#held.clear();
255
255
  }
256
+ // After the plugins' configurations are merged in: that merge makes a new `errs`, which would lose
257
+ // these.
258
+ configSet.errs.push(...pluginErrors);
256
259
  return {
257
260
  ...configSet,
258
261
  plugins,
@@ -422,13 +425,19 @@ export class ConfigProvider {
422
425
  }
423
426
  errs.push(...keySet.errs);
424
427
  }
425
- const configs = [...resolvedKeys].map(name => this.#store.get(name)).filter(nonNullableFilter);
426
428
  let resultConfig = {};
427
- for (const config of configs) {
429
+ for (const name of resolvedKeys) {
430
+ const config = this.#store.get(name);
431
+ if (!config) {
432
+ continue;
433
+ }
428
434
  if (config instanceof ConfigLoadError) {
429
435
  errs.push(config);
430
436
  continue;
431
437
  }
438
+ // Each configuration is validated as it is, before the merge loses which one holds a selector,
439
+ // so that an error names the file (or the plugin configuration) that the user has to fix.
440
+ errs.push(...this.#validateConfig(config, name));
432
441
  resultConfig = mergeConfig(resultConfig, config);
433
442
  }
434
443
  return {
@@ -451,28 +460,56 @@ export class ConfigProvider {
451
460
  : undefined,
452
461
  };
453
462
  }
463
+ /**
464
+ * Creates every selector that one configuration holds, so that an invalid one is in
465
+ * {@link ConfigSet.errs} wherever it is written: the top level, and the value of each `overrides` entry
466
+ * (whether or not its glob matches the target file, as the result is cached for every file of `names`).
467
+ * It runs on each configuration before they are merged, because the merge loses which one holds a selector.
468
+ * The selectors are those of `nodeRules`, `childNodeRules`, and the pretenders written in the
469
+ * configuration itself (`pretenders.data`; the merge has turned an array into it), including those
470
+ * that a plugin provides. Those that a pretender scan or a file produces are not known here;
471
+ * `MLDocument` reports them when it meets them. An error names the file of the pretender
472
+ * (`Pretender#filePath`) when it has one, and `filePath`, the key of the configuration (its file, or
473
+ * `plugin:<name>/<config>`), otherwise.
474
+ */
454
475
  #validateConfig(config, filePath) {
455
476
  const errors = [];
456
- if (config.nodeRules)
457
- for (const rule of config.nodeRules) {
458
- if (rule.selector) {
459
- try {
460
- createSelector(rule.selector);
461
- }
462
- catch (error) {
463
- // Anything but an `InvalidSelectorError` is not a fault of the selector string the user wrote
464
- // (a bug, or an argument that only an extended pseudo-class reads), so it must propagate
465
- // instead of being reported as a configuration error.
466
- if (isFatalError(error) || !(error instanceof InvalidSelectorError)) {
467
- throw error;
468
- }
469
- errors.push(new ConfigParserError(error.message, {
470
- filePath,
471
- raw: rule.selector,
472
- }));
473
- }
477
+ const check = (selector, from = filePath) => {
478
+ if (!selector) {
479
+ return;
480
+ }
481
+ try {
482
+ createSelector(selector);
483
+ }
484
+ catch (error) {
485
+ // Anything but an `InvalidSelectorError` is not a fault of the selector string the user wrote
486
+ // (a bug), so it must propagate instead of being reported as a configuration error.
487
+ if (isFatalError(error) || !(error instanceof InvalidSelectorError)) {
488
+ throw error;
474
489
  }
490
+ errors.push(new ConfigParserError(error.message, {
491
+ filePath: from,
492
+ raw: selector,
493
+ }));
494
+ }
495
+ };
496
+ const checkSelectors = (holder) => {
497
+ for (const rule of holder.nodeRules ?? []) {
498
+ check(rule.selector);
475
499
  }
500
+ for (const rule of holder.childNodeRules ?? []) {
501
+ check(rule.selector);
502
+ }
503
+ const pretenders = holder.pretenders;
504
+ for (const pretender of isPretenderList(pretenders) ? pretenders : (pretenders?.data ?? [])) {
505
+ // The file that declares the component is where the user fixes the entry
506
+ check(pretender.selector, pretender.filePath ?? filePath);
507
+ }
508
+ };
509
+ checkSelectors(config);
510
+ for (const override of Object.values(config.overrides ?? {})) {
511
+ checkSelectors(override);
512
+ }
476
513
  return errors;
477
514
  }
478
515
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/file-resolver",
3
- "version": "5.5.0",
3
+ "version": "5.6.1",
4
4
  "description": "The file resolver of markuplint",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,16 +22,13 @@
22
22
  "publishConfig": {
23
23
  "access": "public"
24
24
  },
25
- "typedoc": {
26
- "entryPoint": "./src/index.ts"
27
- },
28
25
  "scripts": {
29
26
  "build": "tsc --project tsconfig.build.json",
30
27
  "dev": "tsc --watch --project tsconfig.build.json",
31
28
  "clean": "tsc --build --clean"
32
29
  },
33
30
  "devDependencies": {
34
- "@markuplint/pretenders": "5.5.0",
31
+ "@markuplint/pretenders": "5.6.1",
35
32
  "@types/node": "24.19.2"
36
33
  },
37
34
  "peerDependencies": {
@@ -43,14 +40,14 @@
43
40
  }
44
41
  },
45
42
  "dependencies": {
46
- "@markuplint/html-parser": "5.5.0",
47
- "@markuplint/ml-ast": "5.5.0",
48
- "@markuplint/ml-config": "5.5.0",
49
- "@markuplint/ml-core": "5.5.0",
50
- "@markuplint/ml-spec": "5.5.0",
51
- "@markuplint/parser-utils": "5.5.0",
52
- "@markuplint/selector": "5.5.0",
53
- "@markuplint/shared": "5.5.0",
43
+ "@markuplint/html-parser": "5.6.1",
44
+ "@markuplint/ml-ast": "5.6.0",
45
+ "@markuplint/ml-config": "5.6.1",
46
+ "@markuplint/ml-core": "5.6.1",
47
+ "@markuplint/ml-spec": "5.6.1",
48
+ "@markuplint/parser-utils": "5.6.1",
49
+ "@markuplint/selector": "5.6.1",
50
+ "@markuplint/shared": "5.6.0",
54
51
  "cosmiconfig": "^10.0.1",
55
52
  "debug": "^4.4.3",
56
53
  "glob": "^14.0.3",
@@ -59,5 +56,5 @@
59
56
  "jsonc": "^3.0.0",
60
57
  "minimatch": "^11.0.4"
61
58
  },
62
- "gitHead": "fedebcfd56eaec4642e68643b2f9e10750d810e1"
59
+ "gitHead": "a9245162df3699c4880469f493aaaf6103343d83"
63
60
  }