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 +2 -2
- package/dist/index.cjs +92 -78
- package/dist/index.d.cts +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +92 -78
- package/package.json +10 -9
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
|
-
|
|
56492
|
-
|
|
56493
|
-
|
|
56494
|
-
|
|
56495
|
-
|
|
56496
|
-
|
|
56497
|
-
|
|
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
|
-
|
|
56555
|
+
mergeRuleListeners(listeners, addNewIdentDetector(context, d, report));
|
|
56536
56556
|
break;
|
|
56537
56557
|
case "newMember":
|
|
56538
|
-
|
|
56558
|
+
mergeRuleListeners(listeners, addNewMemberDetector(context, d, report));
|
|
56539
56559
|
break;
|
|
56540
56560
|
case "callStatic":
|
|
56541
|
-
|
|
56561
|
+
mergeRuleListeners(listeners, addCallStaticDetector(context, d, report));
|
|
56542
56562
|
break;
|
|
56543
56563
|
case "member":
|
|
56544
|
-
|
|
56564
|
+
mergeRuleListeners(listeners, addMemberDetector(context, d, report));
|
|
56545
56565
|
break;
|
|
56546
56566
|
case "instanceMember":
|
|
56547
|
-
if (useTyped)
|
|
56567
|
+
if (useTyped) mergeRuleListeners(listeners, addTypedInstanceMemberDetector(context, d, report));
|
|
56548
56568
|
break;
|
|
56549
56569
|
case "callMemberWithArgs":
|
|
56550
|
-
|
|
56570
|
+
mergeRuleListeners(listeners, addCallMemberWithArgsDetector(context, d, report));
|
|
56551
56571
|
break;
|
|
56552
56572
|
case "newWithOptions":
|
|
56553
|
-
|
|
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
|
-
|
|
56744
|
-
|
|
56745
|
-
|
|
56746
|
-
|
|
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
|
|
56914
|
-
|
|
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)
|
|
56931
|
-
|
|
56932
|
-
|
|
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)
|
|
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
|
-
|
|
56977
|
+
const { plugin, name } = parseDelegateRuleKey(delegateRule);
|
|
56963
56978
|
let delegateOptions = [];
|
|
56964
56979
|
const mappingEntry = map[featureId];
|
|
56965
56980
|
if (mappingEntry) {
|
|
56966
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56465
|
-
|
|
56466
|
-
|
|
56467
|
-
|
|
56468
|
-
|
|
56469
|
-
|
|
56470
|
-
|
|
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
|
-
|
|
56528
|
+
mergeRuleListeners(listeners, addNewIdentDetector(context, d, report));
|
|
56509
56529
|
break;
|
|
56510
56530
|
case "newMember":
|
|
56511
|
-
|
|
56531
|
+
mergeRuleListeners(listeners, addNewMemberDetector(context, d, report));
|
|
56512
56532
|
break;
|
|
56513
56533
|
case "callStatic":
|
|
56514
|
-
|
|
56534
|
+
mergeRuleListeners(listeners, addCallStaticDetector(context, d, report));
|
|
56515
56535
|
break;
|
|
56516
56536
|
case "member":
|
|
56517
|
-
|
|
56537
|
+
mergeRuleListeners(listeners, addMemberDetector(context, d, report));
|
|
56518
56538
|
break;
|
|
56519
56539
|
case "instanceMember":
|
|
56520
|
-
if (useTyped)
|
|
56540
|
+
if (useTyped) mergeRuleListeners(listeners, addTypedInstanceMemberDetector(context, d, report));
|
|
56521
56541
|
break;
|
|
56522
56542
|
case "callMemberWithArgs":
|
|
56523
|
-
|
|
56543
|
+
mergeRuleListeners(listeners, addCallMemberWithArgsDetector(context, d, report));
|
|
56524
56544
|
break;
|
|
56525
56545
|
case "newWithOptions":
|
|
56526
|
-
|
|
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
|
-
|
|
56717
|
-
|
|
56718
|
-
|
|
56719
|
-
|
|
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
|
|
56887
|
-
|
|
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)
|
|
56904
|
-
|
|
56905
|
-
|
|
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)
|
|
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
|
-
|
|
56950
|
+
const { plugin, name } = parseDelegateRuleKey(delegateRule);
|
|
56936
56951
|
let delegateOptions = [];
|
|
56937
56952
|
const mappingEntry = map[featureId];
|
|
56938
56953
|
if (mappingEntry) {
|
|
56939
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
43
|
+
"eslint-plugin-es-x": "^9.1.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@biomejs/biome": "^2.2
|
|
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.
|
|
51
|
-
"@typescript-eslint/parser": "^8.46.
|
|
52
|
-
"eslint": "^9.
|
|
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.
|
|
54
|
+
"tsdown": "0.15.12",
|
|
55
55
|
"typescript": "^5.9.3",
|
|
56
|
-
"vitest": "^
|
|
57
|
-
"
|
|
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"
|