eslint-plugin-baseline-js 0.4.0 → 0.4.2

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/README.md CHANGED
@@ -141,7 +141,7 @@ and shows which ones are currently mapped by this plugin.
141
141
  | Option | Type | Default | Description |
142
142
  | ---------------------- | ------------------------- | ---------- | ----------- |
143
143
  | `baseline` | `widely`, `newly`, `number`, `widely` | Baseline level or year (alias: `available`). |
144
- | `ignoreFeatures` | `string[]` | — | Skip specific web‑features by ID (supports regex `/.../`). |
144
+ | `ignoreFeatures` | `string[]` | — | Skip specific web‑features by ID (supports regex `/.../`) across syntax delegates and Web API/JS builtin detection. |
145
145
  | `ignoreNodeTypes` | `string[]` | — | Suppress reports by ESTree `node.type` (supports regex `/.../`). |
146
146
 
147
147
  ## Baseline for HTML and CSS
@@ -169,4 +169,4 @@ MIT
169
169
  [jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1ea446
170
170
  [jsdocs-href]: https://www.jsdocs.io/package/eslint-plugin-baseline-js
171
171
  [license-src]: https://img.shields.io/github/license/3ru/eslint-plugin-baseline-js.svg?style=flat&colorA=080f12&colorB=1ea446
172
- [license-href]: https://github.com/3ru/eslint-plugin-baseline-js/blob/main/LICENSE
172
+ [license-href]: https://github.com/3ru/eslint-plugin-baseline-js/blob/main/LICENSE
package/dist/index.cjs CHANGED
@@ -56297,6 +56297,33 @@ function isBeyondBaseline(featureId, baseline) {
56297
56297
  return false;
56298
56298
  }
56299
56299
 
56300
+ //#endregion
56301
+ //#region src/utils/listeners.ts
56302
+ /**
56303
+ * Merge one or more source listener maps into an existing map.
56304
+ * This preserves execution order (existing listeners run before new ones)
56305
+ * while keeping the original object reference intact.
56306
+ */
56307
+ function mergeRuleListeners(target, ...sources) {
56308
+ const dest = target;
56309
+ for (const source of sources) {
56310
+ if (!source) continue;
56311
+ for (const [event, handler] of Object.entries(source)) {
56312
+ const prev = target[event];
56313
+ if (!prev) {
56314
+ dest[event] = handler;
56315
+ continue;
56316
+ }
56317
+ const next = handler;
56318
+ dest[event] = function merged(...args) {
56319
+ prev.apply(this, args);
56320
+ next.apply(this, args);
56321
+ };
56322
+ }
56323
+ }
56324
+ return target;
56325
+ }
56326
+
56300
56327
  //#endregion
56301
56328
  //#region src/util/ast.ts
56302
56329
  function isGlobalBase(_context, node, baseName) {
@@ -56468,6 +56495,15 @@ function addTypedInstanceMemberDetector(context, d, report) {
56468
56495
  if (!checker || !services.esTreeNodeToTSNodeMap) return {};
56469
56496
  const checkerOk = checker;
56470
56497
  const tsMap = services.esTreeNodeToTSNodeMap;
56498
+ const tsMapGet = tsMap.get;
56499
+ if (typeof tsMapGet !== "function") return {};
56500
+ const getTsNode = tsMapGet.bind(tsMap);
56501
+ const typeAtLocation = checkerOk.getTypeAtLocation;
56502
+ if (typeof typeAtLocation !== "function") return {};
56503
+ const getTypeAtLocation = typeAtLocation.bind(checkerOk);
56504
+ const propertyOfType = checkerOk.getPropertyOfType;
56505
+ if (typeof propertyOfType !== "function") return {};
56506
+ const getPropertyOfType = propertyOfType.bind(checkerOk);
56471
56507
  function typeMatches(t) {
56472
56508
  if (!t) return false;
56473
56509
  const withTypes = t;
@@ -56488,15 +56524,13 @@ function addTypedInstanceMemberDetector(context, d, report) {
56488
56524
  }
56489
56525
  const handler = (node) => {
56490
56526
  if (!isMemberWithProperty(node, prop)) return;
56491
- try {
56492
- const obj = node.object;
56493
- const tsNode = tsMap.get(obj);
56494
- if (!tsNode) return;
56495
- const type = checkerOk.getTypeAtLocation?.(tsNode);
56496
- if (!type) return;
56497
- if (!!!checkerOk.getPropertyOfType?.(type, prop)) return;
56498
- if (typeMatches(type)) report(node.property, featureId);
56499
- } catch {}
56527
+ const obj = node.object;
56528
+ const tsNode = getTsNode(obj);
56529
+ if (!tsNode) return;
56530
+ const type = getTypeAtLocation(tsNode);
56531
+ if (!type) return;
56532
+ if (!!!getPropertyOfType(type, prop)) return;
56533
+ if (typeMatches(type)) report(node.property, featureId);
56500
56534
  };
56501
56535
  return { MemberExpression: handler };
56502
56536
  }
@@ -56505,20 +56539,6 @@ function addTypedInstanceMemberDetector(context, d, report) {
56505
56539
  //#region src/rules/feature-usage/builder.ts
56506
56540
  function buildListeners(context, opt) {
56507
56541
  const listeners = {};
56508
- function merge(target, add) {
56509
- const tgt = target;
56510
- for (const [event, handler] of Object.entries(add)) {
56511
- const prev = target[event];
56512
- if (!prev) tgt[event] = handler;
56513
- else {
56514
- const next = handler;
56515
- tgt[event] = function merged(...args) {
56516
- prev.apply(this, args);
56517
- next.apply(this, args);
56518
- };
56519
- }
56520
- }
56521
- }
56522
56542
  function report(node, featureId) {
56523
56543
  const msg = opt.messages[featureId] ?? `Feature '${featureId}' exceeds configured Baseline.`;
56524
56544
  context.report({
@@ -56532,25 +56552,25 @@ function buildListeners(context, opt) {
56532
56552
  const useTyped = !!opt.typed && !!checker && !!services.esTreeNodeToTSNodeMap;
56533
56553
  for (const d of opt.descriptors) switch (d.kind) {
56534
56554
  case "newIdent":
56535
- merge(listeners, addNewIdentDetector(context, d, report));
56555
+ mergeRuleListeners(listeners, addNewIdentDetector(context, d, report));
56536
56556
  break;
56537
56557
  case "newMember":
56538
- merge(listeners, addNewMemberDetector(context, d, report));
56558
+ mergeRuleListeners(listeners, addNewMemberDetector(context, d, report));
56539
56559
  break;
56540
56560
  case "callStatic":
56541
- merge(listeners, addCallStaticDetector(context, d, report));
56561
+ mergeRuleListeners(listeners, addCallStaticDetector(context, d, report));
56542
56562
  break;
56543
56563
  case "member":
56544
- merge(listeners, addMemberDetector(context, d, report));
56564
+ mergeRuleListeners(listeners, addMemberDetector(context, d, report));
56545
56565
  break;
56546
56566
  case "instanceMember":
56547
- if (useTyped) merge(listeners, addTypedInstanceMemberDetector(context, d, report));
56567
+ if (useTyped) mergeRuleListeners(listeners, addTypedInstanceMemberDetector(context, d, report));
56548
56568
  break;
56549
56569
  case "callMemberWithArgs":
56550
- merge(listeners, addCallMemberWithArgsDetector(context, d, report));
56570
+ mergeRuleListeners(listeners, addCallMemberWithArgsDetector(context, d, report));
56551
56571
  break;
56552
56572
  case "newWithOptions":
56553
- merge(listeners, addNewWithOptionsDetector(context, d, report));
56573
+ mergeRuleListeners(listeners, addNewWithOptionsDetector(context, d, report));
56554
56574
  break;
56555
56575
  default: break;
56556
56576
  }
@@ -56718,6 +56738,31 @@ const rule$1 = {
56718
56738
  };
56719
56739
  var no_temporal_default = rule$1;
56720
56740
 
56741
+ //#endregion
56742
+ //#region src/utils/optional-module.ts
56743
+ const requireFromHere = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
56744
+ function isModuleNotFound(err, specifier) {
56745
+ if (!(err instanceof Error)) return false;
56746
+ const anyErr = err;
56747
+ if (anyErr.code === "MODULE_NOT_FOUND") {
56748
+ const msg = typeof anyErr.message === "string" ? anyErr.message : "";
56749
+ return msg.includes(`'${specifier}'`) || msg.includes(`"${specifier}"`) || msg.endsWith(specifier);
56750
+ }
56751
+ return false;
56752
+ }
56753
+ /**
56754
+ * Attempt to load a CommonJS module; return undefined when the specifier cannot be resolved.
56755
+ * Other failures bubble so unexpected issues remain visible.
56756
+ */
56757
+ function requireOptional(specifier) {
56758
+ try {
56759
+ return requireFromHere(specifier);
56760
+ } catch (error) {
56761
+ if (isModuleNotFound(error, specifier)) return void 0;
56762
+ throw error;
56763
+ }
56764
+ }
56765
+
56721
56766
  //#endregion
56722
56767
  //#region src/utils/rule-resolver.ts
56723
56768
  /**
@@ -56731,19 +56776,12 @@ function resolvePluginRules(mod) {
56731
56776
  const rules$1 = m.rules ?? m.default?.rules;
56732
56777
  if (rules$1 && typeof rules$1 === "object") return rules$1;
56733
56778
  }
56734
- /**
56735
- * Resolve rules for eslint-plugin-es-x in a robust way.
56736
- * Why: In CI (Node 22 / ESLint 9), ESM interop may attach rules under
56737
- * `default.rules` instead of `rules`. Only checking `esx.rules` meant no
56738
- * delegates were created and tests failed.
56739
- * Approach: Prefer loading the CommonJS entry via `createRequire` to avoid
56740
- * interop variance, then normalize the export with `resolvePluginRules()`.
56741
- */
56779
+ /** Resolve eslint-plugin-es-x rules, handling ESM/CJS export variance. */
56742
56780
  function resolveEsxRules() {
56743
- try {
56744
- const rules$1 = resolvePluginRules((0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("eslint-plugin-es-x"));
56745
- if (rules$1) return rules$1;
56746
- } catch {}
56781
+ const mod = requireOptional("eslint-plugin-es-x");
56782
+ if (mod === void 0) return void 0;
56783
+ const rules$1 = resolvePluginRules(mod);
56784
+ if (rules$1) return rules$1;
56747
56785
  }
56748
56786
  /**
56749
56787
  * Try resolving es-x rules from a provided module first (ESM import),
@@ -56798,20 +56836,6 @@ registerSelfRules({
56798
56836
  "no-math-sum-precise": no_math_sum_precise_default,
56799
56837
  "no-temporal": no_temporal_default
56800
56838
  });
56801
- function mergeListeners(target, add) {
56802
- const tgt = target;
56803
- for (const [event, handler] of Object.entries(add)) {
56804
- const prev = target[event];
56805
- if (!prev) tgt[event] = handler;
56806
- else {
56807
- const next = handler;
56808
- tgt[event] = function merged(...args) {
56809
- prev.apply(this, args);
56810
- next.apply(this, args);
56811
- };
56812
- }
56813
- }
56814
- }
56815
56839
  function baselineMessage(featureId, baseline) {
56816
56840
  const rec = getFeatureRecord(featureId);
56817
56841
  const label = rec?.name || featureId;
@@ -56910,14 +56934,8 @@ const rule = {
56910
56934
  },
56911
56935
  create(ctx) {
56912
56936
  const _DEBUG = process.env.BASELINE_DEBUG === "1";
56913
- const getFilename = ctx.getFilename;
56914
- let filename = null;
56915
- if (typeof getFilename === "function") try {
56916
- filename = getFilename();
56917
- } catch {}
56918
- if (filename) {
56919
- if (!/\.(?:[mc]?[jt]s|[jt]sx)$/i.test(filename)) return {};
56920
- }
56937
+ const ast = (ctx.getSourceCode?.())?.ast;
56938
+ if (ast && ast.type && ast.type !== "Program") return {};
56921
56939
  const opt = ctx.options[0] ?? {};
56922
56940
  const baseline = getBaselineValue(opt);
56923
56941
  const ignoreFeaturePatterns = opt.ignoreFeatures ?? [];
@@ -56927,10 +56945,9 @@ const rule = {
56927
56945
  function makeMatcher(patterns) {
56928
56946
  if (!patterns.length) return null;
56929
56947
  const compiled = patterns.map((p) => {
56930
- if (p.startsWith("/") && p.endsWith("/") && p.length >= 2) try {
56931
- return new RegExp(p.slice(1, -1));
56932
- } catch {
56933
- return p;
56948
+ if (p.startsWith("/") && p.endsWith("/") && p.length >= 2) {
56949
+ const expr = p.slice(1, -1);
56950
+ return new RegExp(expr);
56934
56951
  }
56935
56952
  return p;
56936
56953
  });
@@ -56955,20 +56972,16 @@ const rule = {
56955
56972
  }
56956
56973
  const listeners = {};
56957
56974
  const mappedFeatureIds = new Set(entries.map((e) => e.featureId));
56958
- if (_DEBUG) try {
56959
- console.log(`[baseline-js] resolvers=${listResolverPlugins().join(",")} entries=${entries.length}`);
56960
- } catch {}
56975
+ if (_DEBUG) console.log(`[baseline-js] resolvers=${listResolverPlugins().join(",")} entries=${entries.length}`);
56961
56976
  for (const { featureId, delegateRule } of entries) {
56962
- let impl;
56977
+ const { plugin, name } = parseDelegateRuleKey(delegateRule);
56963
56978
  let delegateOptions = [];
56964
56979
  const mappingEntry = map[featureId];
56965
56980
  if (mappingEntry) {
56966
- const { plugin: plugin$1 } = parseDelegateRuleKey(delegateRule);
56967
- const d = mappingEntry.delegates.find((x) => x.plugin === plugin$1);
56981
+ const d = mappingEntry.delegates.find((x) => x.plugin === plugin);
56968
56982
  if (d?.options) delegateOptions = Array.isArray(d.options) ? d.options : [d.options];
56969
56983
  }
56970
- const { plugin, name } = parseDelegateRuleKey(delegateRule);
56971
- impl = resolveDelegateRule(plugin, name);
56984
+ const impl = resolveDelegateRule(plugin, name);
56972
56985
  if (!impl?.create) continue;
56973
56986
  const delegateCtx = {};
56974
56987
  for (const k of [
@@ -57010,12 +57023,13 @@ const rule = {
57010
57023
  message: baselineMessage(featureId, baseline)
57011
57024
  });
57012
57025
  };
57013
- mergeListeners(listeners, impl.create(delegateCtx));
57026
+ mergeRuleListeners(listeners, impl.create(delegateCtx));
57014
57027
  }
57015
57028
  let descriptors$2 = getIncludedDescriptors({
57016
57029
  webApis: includeWebApis,
57017
57030
  jsBuiltins: includeJsBuiltins
57018
57031
  });
57032
+ if (matchIgnoreFeature) descriptors$2 = descriptors$2.filter((d) => !matchIgnoreFeature(d.featureId));
57019
57033
  descriptors$2 = descriptors$2.filter((d) => isBeyondBaseline(d.featureId, baseline)).filter((d) => !mappedFeatureIds.has(d.featureId));
57020
57034
  function resolveUseTypes(opt$1) {
57021
57035
  if (!opt$1) return "off";
@@ -57086,7 +57100,7 @@ const rule = {
57086
57100
  message: msg
57087
57101
  });
57088
57102
  };
57089
- mergeListeners(listeners, feature_usage_default.create(delegateCtx));
57103
+ mergeRuleListeners(listeners, feature_usage_default.create(delegateCtx));
57090
57104
  }
57091
57105
  return listeners;
57092
57106
  }
package/dist/index.d.cts CHANGED
@@ -16,6 +16,7 @@ type BaselineName = (typeof BASELINE)[keyof typeof BASELINE];
16
16
  type BaselineOption = BaselineName | number;
17
17
  //#endregion
18
18
  //#region src/configs/baseline.d.ts
19
+
19
20
  /**
20
21
  * Recommended config: enable Baseline with Web APIs and JS builtins detection on auto preset.
21
22
  * - baseline: defaults to 'widely'
package/dist/index.d.mts CHANGED
@@ -16,6 +16,7 @@ type BaselineName = (typeof BASELINE)[keyof typeof BASELINE];
16
16
  type BaselineOption = BaselineName | number;
17
17
  //#endregion
18
18
  //#region src/configs/baseline.d.ts
19
+
19
20
  /**
20
21
  * Recommended config: enable Baseline with Web APIs and JS builtins detection on auto preset.
21
22
  * - baseline: defaults to 'widely'
package/dist/index.mjs CHANGED
@@ -56270,6 +56270,33 @@ function isBeyondBaseline(featureId, baseline) {
56270
56270
  return false;
56271
56271
  }
56272
56272
 
56273
+ //#endregion
56274
+ //#region src/utils/listeners.ts
56275
+ /**
56276
+ * Merge one or more source listener maps into an existing map.
56277
+ * This preserves execution order (existing listeners run before new ones)
56278
+ * while keeping the original object reference intact.
56279
+ */
56280
+ function mergeRuleListeners(target, ...sources) {
56281
+ const dest = target;
56282
+ for (const source of sources) {
56283
+ if (!source) continue;
56284
+ for (const [event, handler] of Object.entries(source)) {
56285
+ const prev = target[event];
56286
+ if (!prev) {
56287
+ dest[event] = handler;
56288
+ continue;
56289
+ }
56290
+ const next = handler;
56291
+ dest[event] = function merged(...args) {
56292
+ prev.apply(this, args);
56293
+ next.apply(this, args);
56294
+ };
56295
+ }
56296
+ }
56297
+ return target;
56298
+ }
56299
+
56273
56300
  //#endregion
56274
56301
  //#region src/util/ast.ts
56275
56302
  function isGlobalBase(_context, node, baseName) {
@@ -56441,6 +56468,15 @@ function addTypedInstanceMemberDetector(context, d, report) {
56441
56468
  if (!checker || !services.esTreeNodeToTSNodeMap) return {};
56442
56469
  const checkerOk = checker;
56443
56470
  const tsMap = services.esTreeNodeToTSNodeMap;
56471
+ const tsMapGet = tsMap.get;
56472
+ if (typeof tsMapGet !== "function") return {};
56473
+ const getTsNode = tsMapGet.bind(tsMap);
56474
+ const typeAtLocation = checkerOk.getTypeAtLocation;
56475
+ if (typeof typeAtLocation !== "function") return {};
56476
+ const getTypeAtLocation = typeAtLocation.bind(checkerOk);
56477
+ const propertyOfType = checkerOk.getPropertyOfType;
56478
+ if (typeof propertyOfType !== "function") return {};
56479
+ const getPropertyOfType = propertyOfType.bind(checkerOk);
56444
56480
  function typeMatches(t) {
56445
56481
  if (!t) return false;
56446
56482
  const withTypes = t;
@@ -56461,15 +56497,13 @@ function addTypedInstanceMemberDetector(context, d, report) {
56461
56497
  }
56462
56498
  const handler = (node) => {
56463
56499
  if (!isMemberWithProperty(node, prop)) return;
56464
- try {
56465
- const obj = node.object;
56466
- const tsNode = tsMap.get(obj);
56467
- if (!tsNode) return;
56468
- const type = checkerOk.getTypeAtLocation?.(tsNode);
56469
- if (!type) return;
56470
- if (!!!checkerOk.getPropertyOfType?.(type, prop)) return;
56471
- if (typeMatches(type)) report(node.property, featureId);
56472
- } catch {}
56500
+ const obj = node.object;
56501
+ const tsNode = getTsNode(obj);
56502
+ if (!tsNode) return;
56503
+ const type = getTypeAtLocation(tsNode);
56504
+ if (!type) return;
56505
+ if (!!!getPropertyOfType(type, prop)) return;
56506
+ if (typeMatches(type)) report(node.property, featureId);
56473
56507
  };
56474
56508
  return { MemberExpression: handler };
56475
56509
  }
@@ -56478,20 +56512,6 @@ function addTypedInstanceMemberDetector(context, d, report) {
56478
56512
  //#region src/rules/feature-usage/builder.ts
56479
56513
  function buildListeners(context, opt) {
56480
56514
  const listeners = {};
56481
- function merge(target, add) {
56482
- const tgt = target;
56483
- for (const [event, handler] of Object.entries(add)) {
56484
- const prev = target[event];
56485
- if (!prev) tgt[event] = handler;
56486
- else {
56487
- const next = handler;
56488
- tgt[event] = function merged(...args) {
56489
- prev.apply(this, args);
56490
- next.apply(this, args);
56491
- };
56492
- }
56493
- }
56494
- }
56495
56515
  function report(node, featureId) {
56496
56516
  const msg = opt.messages[featureId] ?? `Feature '${featureId}' exceeds configured Baseline.`;
56497
56517
  context.report({
@@ -56505,25 +56525,25 @@ function buildListeners(context, opt) {
56505
56525
  const useTyped = !!opt.typed && !!checker && !!services.esTreeNodeToTSNodeMap;
56506
56526
  for (const d of opt.descriptors) switch (d.kind) {
56507
56527
  case "newIdent":
56508
- merge(listeners, addNewIdentDetector(context, d, report));
56528
+ mergeRuleListeners(listeners, addNewIdentDetector(context, d, report));
56509
56529
  break;
56510
56530
  case "newMember":
56511
- merge(listeners, addNewMemberDetector(context, d, report));
56531
+ mergeRuleListeners(listeners, addNewMemberDetector(context, d, report));
56512
56532
  break;
56513
56533
  case "callStatic":
56514
- merge(listeners, addCallStaticDetector(context, d, report));
56534
+ mergeRuleListeners(listeners, addCallStaticDetector(context, d, report));
56515
56535
  break;
56516
56536
  case "member":
56517
- merge(listeners, addMemberDetector(context, d, report));
56537
+ mergeRuleListeners(listeners, addMemberDetector(context, d, report));
56518
56538
  break;
56519
56539
  case "instanceMember":
56520
- if (useTyped) merge(listeners, addTypedInstanceMemberDetector(context, d, report));
56540
+ if (useTyped) mergeRuleListeners(listeners, addTypedInstanceMemberDetector(context, d, report));
56521
56541
  break;
56522
56542
  case "callMemberWithArgs":
56523
- merge(listeners, addCallMemberWithArgsDetector(context, d, report));
56543
+ mergeRuleListeners(listeners, addCallMemberWithArgsDetector(context, d, report));
56524
56544
  break;
56525
56545
  case "newWithOptions":
56526
- merge(listeners, addNewWithOptionsDetector(context, d, report));
56546
+ mergeRuleListeners(listeners, addNewWithOptionsDetector(context, d, report));
56527
56547
  break;
56528
56548
  default: break;
56529
56549
  }
@@ -56691,6 +56711,31 @@ const rule$1 = {
56691
56711
  };
56692
56712
  var no_temporal_default = rule$1;
56693
56713
 
56714
+ //#endregion
56715
+ //#region src/utils/optional-module.ts
56716
+ const requireFromHere = createRequire(import.meta.url);
56717
+ function isModuleNotFound(err, specifier) {
56718
+ if (!(err instanceof Error)) return false;
56719
+ const anyErr = err;
56720
+ if (anyErr.code === "MODULE_NOT_FOUND") {
56721
+ const msg = typeof anyErr.message === "string" ? anyErr.message : "";
56722
+ return msg.includes(`'${specifier}'`) || msg.includes(`"${specifier}"`) || msg.endsWith(specifier);
56723
+ }
56724
+ return false;
56725
+ }
56726
+ /**
56727
+ * Attempt to load a CommonJS module; return undefined when the specifier cannot be resolved.
56728
+ * Other failures bubble so unexpected issues remain visible.
56729
+ */
56730
+ function requireOptional(specifier) {
56731
+ try {
56732
+ return requireFromHere(specifier);
56733
+ } catch (error) {
56734
+ if (isModuleNotFound(error, specifier)) return void 0;
56735
+ throw error;
56736
+ }
56737
+ }
56738
+
56694
56739
  //#endregion
56695
56740
  //#region src/utils/rule-resolver.ts
56696
56741
  /**
@@ -56704,19 +56749,12 @@ function resolvePluginRules(mod) {
56704
56749
  const rules$1 = m.rules ?? m.default?.rules;
56705
56750
  if (rules$1 && typeof rules$1 === "object") return rules$1;
56706
56751
  }
56707
- /**
56708
- * Resolve rules for eslint-plugin-es-x in a robust way.
56709
- * Why: In CI (Node 22 / ESLint 9), ESM interop may attach rules under
56710
- * `default.rules` instead of `rules`. Only checking `esx.rules` meant no
56711
- * delegates were created and tests failed.
56712
- * Approach: Prefer loading the CommonJS entry via `createRequire` to avoid
56713
- * interop variance, then normalize the export with `resolvePluginRules()`.
56714
- */
56752
+ /** Resolve eslint-plugin-es-x rules, handling ESM/CJS export variance. */
56715
56753
  function resolveEsxRules() {
56716
- try {
56717
- const rules$1 = resolvePluginRules(createRequire(import.meta.url)("eslint-plugin-es-x"));
56718
- if (rules$1) return rules$1;
56719
- } catch {}
56754
+ const mod = requireOptional("eslint-plugin-es-x");
56755
+ if (mod === void 0) return void 0;
56756
+ const rules$1 = resolvePluginRules(mod);
56757
+ if (rules$1) return rules$1;
56720
56758
  }
56721
56759
  /**
56722
56760
  * Try resolving es-x rules from a provided module first (ESM import),
@@ -56771,20 +56809,6 @@ registerSelfRules({
56771
56809
  "no-math-sum-precise": no_math_sum_precise_default,
56772
56810
  "no-temporal": no_temporal_default
56773
56811
  });
56774
- function mergeListeners(target, add) {
56775
- const tgt = target;
56776
- for (const [event, handler] of Object.entries(add)) {
56777
- const prev = target[event];
56778
- if (!prev) tgt[event] = handler;
56779
- else {
56780
- const next = handler;
56781
- tgt[event] = function merged(...args) {
56782
- prev.apply(this, args);
56783
- next.apply(this, args);
56784
- };
56785
- }
56786
- }
56787
- }
56788
56812
  function baselineMessage(featureId, baseline) {
56789
56813
  const rec = getFeatureRecord(featureId);
56790
56814
  const label = rec?.name || featureId;
@@ -56883,14 +56907,8 @@ const rule = {
56883
56907
  },
56884
56908
  create(ctx) {
56885
56909
  const _DEBUG = process.env.BASELINE_DEBUG === "1";
56886
- const getFilename = ctx.getFilename;
56887
- let filename = null;
56888
- if (typeof getFilename === "function") try {
56889
- filename = getFilename();
56890
- } catch {}
56891
- if (filename) {
56892
- if (!/\.(?:[mc]?[jt]s|[jt]sx)$/i.test(filename)) return {};
56893
- }
56910
+ const ast = (ctx.getSourceCode?.())?.ast;
56911
+ if (ast && ast.type && ast.type !== "Program") return {};
56894
56912
  const opt = ctx.options[0] ?? {};
56895
56913
  const baseline = getBaselineValue(opt);
56896
56914
  const ignoreFeaturePatterns = opt.ignoreFeatures ?? [];
@@ -56900,10 +56918,9 @@ const rule = {
56900
56918
  function makeMatcher(patterns) {
56901
56919
  if (!patterns.length) return null;
56902
56920
  const compiled = patterns.map((p) => {
56903
- if (p.startsWith("/") && p.endsWith("/") && p.length >= 2) try {
56904
- return new RegExp(p.slice(1, -1));
56905
- } catch {
56906
- return p;
56921
+ if (p.startsWith("/") && p.endsWith("/") && p.length >= 2) {
56922
+ const expr = p.slice(1, -1);
56923
+ return new RegExp(expr);
56907
56924
  }
56908
56925
  return p;
56909
56926
  });
@@ -56928,20 +56945,16 @@ const rule = {
56928
56945
  }
56929
56946
  const listeners = {};
56930
56947
  const mappedFeatureIds = new Set(entries.map((e) => e.featureId));
56931
- if (_DEBUG) try {
56932
- console.log(`[baseline-js] resolvers=${listResolverPlugins().join(",")} entries=${entries.length}`);
56933
- } catch {}
56948
+ if (_DEBUG) console.log(`[baseline-js] resolvers=${listResolverPlugins().join(",")} entries=${entries.length}`);
56934
56949
  for (const { featureId, delegateRule } of entries) {
56935
- let impl;
56950
+ const { plugin, name } = parseDelegateRuleKey(delegateRule);
56936
56951
  let delegateOptions = [];
56937
56952
  const mappingEntry = map[featureId];
56938
56953
  if (mappingEntry) {
56939
- const { plugin: plugin$1 } = parseDelegateRuleKey(delegateRule);
56940
- const d = mappingEntry.delegates.find((x) => x.plugin === plugin$1);
56954
+ const d = mappingEntry.delegates.find((x) => x.plugin === plugin);
56941
56955
  if (d?.options) delegateOptions = Array.isArray(d.options) ? d.options : [d.options];
56942
56956
  }
56943
- const { plugin, name } = parseDelegateRuleKey(delegateRule);
56944
- impl = resolveDelegateRule(plugin, name);
56957
+ const impl = resolveDelegateRule(plugin, name);
56945
56958
  if (!impl?.create) continue;
56946
56959
  const delegateCtx = {};
56947
56960
  for (const k of [
@@ -56983,12 +56996,13 @@ const rule = {
56983
56996
  message: baselineMessage(featureId, baseline)
56984
56997
  });
56985
56998
  };
56986
- mergeListeners(listeners, impl.create(delegateCtx));
56999
+ mergeRuleListeners(listeners, impl.create(delegateCtx));
56987
57000
  }
56988
57001
  let descriptors$2 = getIncludedDescriptors({
56989
57002
  webApis: includeWebApis,
56990
57003
  jsBuiltins: includeJsBuiltins
56991
57004
  });
57005
+ if (matchIgnoreFeature) descriptors$2 = descriptors$2.filter((d) => !matchIgnoreFeature(d.featureId));
56992
57006
  descriptors$2 = descriptors$2.filter((d) => isBeyondBaseline(d.featureId, baseline)).filter((d) => !mappedFeatureIds.has(d.featureId));
56993
57007
  function resolveUseTypes(opt$1) {
56994
57008
  if (!opt$1) return "off";
@@ -57059,7 +57073,7 @@ const rule = {
57059
57073
  message: msg
57060
57074
  });
57061
57075
  };
57062
- mergeListeners(listeners, feature_usage_default.create(delegateCtx));
57076
+ mergeRuleListeners(listeners, feature_usage_default.create(delegateCtx));
57063
57077
  }
57064
57078
  return listeners;
57065
57079
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-baseline-js",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Enforce JavaScript Baseline (widely/newly/year) browser compatibility with a single ESLint rule. Prevent runtime errors by catching unsupported JavaScript features during development.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -40,21 +40,22 @@
40
40
  "verify:features": "node scripts/data/verify-features.mjs"
41
41
  },
42
42
  "dependencies": {
43
- "eslint-plugin-es-x": "^9.1.1"
43
+ "eslint-plugin-es-x": "^9.1.2"
44
44
  },
45
45
  "devDependencies": {
46
- "@biomejs/biome": "^2.2.6",
46
+ "@biomejs/biome": "^2.3.2",
47
47
  "@semantic-release/changelog": "^6.0.3",
48
48
  "@semantic-release/git": "^10.0.1",
49
49
  "@types/eslint": "^9.6.1",
50
- "@types/node": "^24.8.1",
51
- "@typescript-eslint/parser": "^8.46.1",
52
- "eslint": "^9.37.0",
50
+ "@types/node": "^24.9.2",
51
+ "@typescript-eslint/parser": "^8.46.2",
52
+ "eslint": "^9.39.1",
53
53
  "semantic-release": "^24.2.9",
54
- "tsdown": "0.15.1",
54
+ "tsdown": "0.15.12",
55
55
  "typescript": "^5.9.3",
56
- "vitest": "^3.2.4",
57
- "web-features": "3.5.0"
56
+ "vitest": "^4.0.13",
57
+ "vue-eslint-parser": "^9.4.3",
58
+ "web-features": "3.6.0"
58
59
  },
59
60
  "peerDependencies": {
60
61
  "eslint": ">=8.57.0 <10"