es-check 9.7.2 → 9.8.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.
@@ -12,6 +12,7 @@ const { ES16_FEATURES } = require("./16");
12
12
  const { ES17_FEATURES } = require("./17");
13
13
  const { ES_GLOBAL_MIN_VERSION, ES_GLOBALS_SOURCE } = require("./globals");
14
14
  const { POLYFILL_PATTERNS, IMPORT_PATTERNS, FEATURE_TO_POLYFILL_MAP } = require("./polyfills");
15
+ const { FEATURE_BROWSER_SUPPORT } = require("./browserSupport");
15
16
 
16
17
  const ES_FEATURES = Object.assign(
17
18
  {},
@@ -36,4 +37,5 @@ module.exports = {
36
37
  POLYFILL_PATTERNS,
37
38
  IMPORT_PATTERNS,
38
39
  FEATURE_TO_POLYFILL_MAP,
40
+ FEATURE_BROWSER_SUPPORT,
39
41
  };
@@ -20,14 +20,19 @@ function getParserVersionForFeature(featureName, targetEsVersion) {
20
20
  return Math.max(targetEsVersion, requiredParser);
21
21
  }
22
22
 
23
- function getMinimumParserVersion(targetEsVersion, enableFeatureDetection = false) {
23
+ function getMinimumParserVersion(
24
+ targetEsVersion,
25
+ enableFeatureDetection = false,
26
+ ) {
24
27
  if (!enableFeatureDetection) {
25
28
  return targetEsVersion;
26
29
  }
27
30
 
28
31
  let minParserVersion = targetEsVersion;
29
32
 
30
- for (const [_feature, requiredParser] of Object.entries(FEATURE_PARSER_REQUIREMENTS)) {
33
+ for (const [_feature, requiredParser] of Object.entries(
34
+ FEATURE_PARSER_REQUIREMENTS,
35
+ )) {
31
36
  if (targetEsVersion >= requiredParser) {
32
37
  minParserVersion = Math.max(minParserVersion, requiredParser);
33
38
  }
@@ -1,14 +1,7 @@
1
- /**
2
- * Generated by scripts/generate/polyfills/core.mjs — do not edit manually.
3
- *
4
- * These values use es-check's internal feature names (e.g. "ArrayToSorted"),
5
- * not core-js module names (e.g. "es.array.to-sorted"). Core-js module names
6
- * cannot be used directly because es-check matches features against its own
7
- * ES_FEATURES keys, which follow PascalCase naming derived from acorn AST
8
- * detection. The generation script bridges the two naming schemes by converting
9
- * core-js modules to es-check feature names and intersecting with ES_FEATURES.
10
- */
1
+ // Generated from core-js-compat 3.50.0; run pnpm scripts:generate:polyfills.
2
+ // Feature names are mapped explicitly in scripts/constants.mjs.
11
3
  const coreJsPolyfillable = new Set([
4
+ "AggregateError",
12
5
  "ArrayBufferTransfer",
13
6
  "ArrayBufferTransferToFixedLength",
14
7
  "ArrayFindLast",
@@ -22,11 +15,15 @@ const coreJsPolyfillable = new Set([
22
15
  "ArrayToSorted",
23
16
  "ArrayToSpliced",
24
17
  "ArrayWith",
18
+ "DataView",
25
19
  "DataViewGetFloat16",
26
20
  "DataViewSetFloat16",
27
21
  "ErrorCause",
28
22
  "ErrorIsError",
23
+ "Iterator",
29
24
  "IteratorConcat",
25
+ "JSONIsRawJSON",
26
+ "JSONRawJSON",
30
27
  "Map",
31
28
  "MapGetOrInsert",
32
29
  "MapGetOrInsertComputed",
@@ -225,10 +225,28 @@ const VERSION_ORDER = [
225
225
 
226
226
  const LATEST_PARSER_VERSION = 2026;
227
227
 
228
+ const BROWSERSLIST_TO_BCD = {
229
+ and_chr: "chrome_android",
230
+ and_ff: "firefox_android",
231
+ android: "webview_android",
232
+ chrome: "chrome",
233
+ deno: "deno",
234
+ edge: "edge",
235
+ firefox: "firefox",
236
+ ie: "ie",
237
+ ios_saf: "safari_ios",
238
+ node: "nodejs",
239
+ op_mob: "opera_android",
240
+ opera: "opera",
241
+ safari: "safari",
242
+ samsung: "samsunginternet_android",
243
+ };
244
+
228
245
  module.exports = {
229
246
  ECMA_VERSION_MAP,
230
247
  ECMA_VERSION_TO_NUMBER,
231
248
  BROWSER_TO_ES_VERSION,
249
+ BROWSERSLIST_TO_BCD,
232
250
  JS_VERSIONS,
233
251
  VERSION_ORDER,
234
252
  LATEST_PARSER_VERSION,
@@ -7,6 +7,7 @@ const {
7
7
  const { ES_GLOBAL_MIN_VERSION } = require("./constants/es-features/globals");
8
8
  const { POLYFILLABLE_FEATURES, CORE_JS_POLYFILLABLE } = require("./constants/polyfillableFeatures");
9
9
  const { detectFeaturesFromAST } = require("./helpers/astDetector");
10
+ const { getUnsupportedBrowsers, formatFeatureWithBrowsers } = require("./helpers/browserSupport");
10
11
 
11
12
  function getPolyfillableFeatures(library) {
12
13
  if (!library) {
@@ -50,7 +51,7 @@ const detectPolyfills = (
50
51
  };
51
52
 
52
53
  const detectFeatures = (code, ecmaVersion, sourceType, ignoreList = new Set(), options = {}) => {
53
- const { checkForPolyfills, ast, ignorePolyfillable } = options;
54
+ const { checkForPolyfills, ast, ignorePolyfillable, targetBrowsers } = options;
54
55
 
55
56
  if (!ast) {
56
57
  const error = new Error("AST is required for feature detection");
@@ -65,17 +66,26 @@ const detectFeatures = (code, ecmaVersion, sourceType, ignoreList = new Set(), o
65
66
 
66
67
  const polyfillableSet = ignorePolyfillable ? getPolyfillableFeatures(ignorePolyfillable) : null;
67
68
 
69
+ const browserDetails = {};
70
+
68
71
  function isUnsupportedFeature(name, minVersion) {
69
72
  const wasFound = foundFeatures[name];
73
+ if (!wasFound) return false;
74
+
75
+ const unsupportedBrowsers = getUnsupportedBrowsers(name, targetBrowsers);
76
+ const lacksBrowserSupport = unsupportedBrowsers.length > 0;
70
77
  const requiresHigherVersion = minVersion > ecmaVersion;
78
+ const shouldReport = requiresHigherVersion || lacksBrowserSupport;
79
+ if (!shouldReport) return false;
80
+
71
81
  const isIgnored = ignoreList.has(name);
72
82
  const isPolyfilled = checkForPolyfills && polyfills.has(name);
73
83
  const isPolyfillableIgnored = polyfillableSet && polyfillableSet.has(name);
74
-
75
84
  const isAllowed = isIgnored || isPolyfilled || isPolyfillableIgnored;
76
- const shouldReport = wasFound && requiresHigherVersion;
77
- if (!shouldReport) return false;
78
- return !isAllowed;
85
+ if (isAllowed) return false;
86
+
87
+ if (lacksBrowserSupport) browserDetails[name] = unsupportedBrowsers;
88
+ return true;
79
89
  }
80
90
 
81
91
  const unsupportedStandardFeatures = Object.entries(ES_FEATURES)
@@ -90,12 +100,17 @@ const detectFeatures = (code, ecmaVersion, sourceType, ignoreList = new Set(), o
90
100
  const unsupportedFeatures = unsupportedStandardFeatures.concat(unsupportedGlobalFeatures);
91
101
 
92
102
  if (unsupportedFeatures.length > 0) {
93
- const error = new Error(
94
- `Unsupported features detected: ${unsupportedFeatures.join(", ")}. ` +
95
- `These require a higher ES version than ${ecmaVersion}.`,
96
- );
103
+ const hasBrowserDetails = Object.keys(browserDetails).length > 0;
104
+ const featureList = unsupportedFeatures
105
+ .map((name) => formatFeatureWithBrowsers(name, browserDetails[name]))
106
+ .join(", ");
107
+ const reason = hasBrowserDetails
108
+ ? `These require a higher ES version than ${ecmaVersion} or are not supported by the target browsers.`
109
+ : `These require a higher ES version than ${ecmaVersion}.`;
110
+ const error = new Error(`Unsupported features detected: ${featureList}. ${reason}`);
97
111
  error.type = "ES-Check";
98
112
  error.features = unsupportedFeatures;
113
+ error.browserDetails = browserDetails;
99
114
  error.ecmaVersion = ecmaVersion;
100
115
  throw error;
101
116
  }
@@ -103,6 +118,7 @@ const detectFeatures = (code, ecmaVersion, sourceType, ignoreList = new Set(), o
103
118
  return {
104
119
  foundFeatures,
105
120
  unsupportedFeatures,
121
+ browserDetails,
106
122
  };
107
123
  };
108
124
 
@@ -0,0 +1,89 @@
1
+ const { FEATURE_BROWSER_SUPPORT } = require("../constants/es-features/browserSupport");
2
+
3
+ function compareVersions(a, b) {
4
+ const aParts = String(a).split(".").map(Number);
5
+ const bParts = String(b).split(".").map(Number);
6
+ const length = Math.max(aParts.length, bParts.length);
7
+
8
+ let index = 0;
9
+ while (index < length) {
10
+ const diff = (aParts[index] || 0) - (bParts[index] || 0);
11
+ if (diff !== 0) return diff;
12
+ index += 1;
13
+ }
14
+
15
+ return 0;
16
+ }
17
+
18
+ function isVersionSupported(targetVersion, versionAdded) {
19
+ const isSupportedWithoutVersion = versionAdded === true;
20
+ if (isSupportedWithoutVersion) return true;
21
+
22
+ const isNeverSupported = versionAdded === false;
23
+ if (isNeverSupported) return false;
24
+
25
+ return compareVersions(targetVersion, versionAdded) >= 0;
26
+ }
27
+
28
+ function keepLowestVersionPerBrowser(browsers) {
29
+ const lowestByName = new Map();
30
+
31
+ browsers.forEach((browser) => {
32
+ const current = lowestByName.get(browser.name);
33
+ const isLower = !current || compareVersions(browser.version, current.version) < 0;
34
+ if (isLower) lowestByName.set(browser.name, browser);
35
+ });
36
+
37
+ return Array.from(lowestByName.values());
38
+ }
39
+
40
+ function getUnsupportedBrowsers(
41
+ featureName,
42
+ targetBrowsers,
43
+ browserSupport = FEATURE_BROWSER_SUPPORT,
44
+ ) {
45
+ const support = browserSupport[featureName];
46
+ const hasNoTargets = !targetBrowsers || targetBrowsers.length === 0;
47
+ const hasNoData = !support || hasNoTargets;
48
+ if (hasNoData) return [];
49
+
50
+ const unsupported = targetBrowsers.filter((browser) => {
51
+ const versionAdded = support[browser.name];
52
+ const hasBrowserData = versionAdded !== undefined;
53
+ if (!hasBrowserData) return false;
54
+ return !isVersionSupported(browser.version, versionAdded);
55
+ });
56
+
57
+ return keepLowestVersionPerBrowser(unsupported).map((browser) => ({
58
+ name: browser.name,
59
+ version: browser.version,
60
+ requiredVersion: support[browser.name],
61
+ }));
62
+ }
63
+
64
+ function formatBrowserRequirement({ name, version, requiredVersion }) {
65
+ const isNeverSupported = requiredVersion === false;
66
+ if (isNeverSupported) return `${name} ${version} has no support`;
67
+ return `${name} ${version} requires ${requiredVersion}`;
68
+ }
69
+
70
+ function formatFeatureWithBrowsers(featureName, unsupportedBrowsers) {
71
+ const hasNoBrowsers = !unsupportedBrowsers || unsupportedBrowsers.length === 0;
72
+ if (hasNoBrowsers) return featureName;
73
+
74
+ const requirements = unsupportedBrowsers.map(formatBrowserRequirement).join(", ");
75
+ return `${featureName} (${requirements})`;
76
+ }
77
+
78
+ function formatTargetBrowsers(targetBrowsers) {
79
+ return targetBrowsers.map((browser) => `${browser.name} ${browser.version}`).join(", ");
80
+ }
81
+
82
+ module.exports = {
83
+ compareVersions,
84
+ isVersionSupported,
85
+ getUnsupportedBrowsers,
86
+ formatBrowserRequirement,
87
+ formatFeatureWithBrowsers,
88
+ formatTargetBrowsers,
89
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "es-check",
3
- "version": "9.7.2",
3
+ "version": "9.8.1",
4
4
  "description": "Checks the ECMAScript version of .js glob against a specified version of ECMAScript with a shell command",
5
5
  "keywords": [
6
6
  "check for es6",
@@ -75,9 +75,10 @@
75
75
  "release:tag:dry": "node scripts/release-tag.mjs --dry-run",
76
76
  "release:dry": "release-it --dry-run --no-git.requireUpstream",
77
77
  "report:coverage": "mkdir -p coverage && node tests/scripts/test-runner.js unit --coverage --reporter=lcov --reporter-destination=coverage/lcov.info",
78
- "scripts:generate": "pnpm run scripts:generate:requirements && pnpm run scripts:generate:polyfills",
78
+ "scripts:generate": "pnpm run scripts:generate:requirements && pnpm run scripts:generate:polyfills && pnpm run scripts:generate:browser-support",
79
79
  "scripts:generate:requirements": "node scripts/generate/requirements/parser.mjs",
80
80
  "scripts:generate:polyfills": "node scripts/generate/polyfills/core.mjs",
81
+ "scripts:generate:browser-support": "node scripts/generate/browser-support/index.mjs",
81
82
  "test": "node tests/scripts/test-runner.js unit && npm run test:e2e",
82
83
  "test:unit": "node tests/scripts/test-runner.js unit",
83
84
  "test:unit:verbose": "node tests/scripts/test-runner.js unit --verbose",
@@ -96,17 +97,17 @@
96
97
  },
97
98
  "dependencies": {
98
99
  "acorn": "8.18.0",
99
- "browserslist": "4.28.9",
100
+ "browserslist": "4.29.0",
100
101
  "fast-glob": "^3.3.3"
101
102
  },
102
103
  "devDependencies": {
103
- "@mdn/browser-compat-data": "^8.1.0",
104
+ "@mdn/browser-compat-data": "^8.1.2",
104
105
  "codependence": "1.0.15",
105
106
  "core-js-compat": "^3.50.0",
106
- "oxfmt": "^0.67.0",
107
- "oxlint": "^1.82.0",
108
- "pastoralist": "1.13.2",
109
- "release-it": "21.0.2"
107
+ "oxfmt": "^0.68.0",
108
+ "oxlint": "^1.83.0",
109
+ "pastoralist": "1.13.4",
110
+ "release-it": "21.1.0"
110
111
  },
111
112
  "peerDependencies": {},
112
113
  "optionalDependencies": {},
@@ -134,20 +135,6 @@
134
135
  "site/package.json"
135
136
  ]
136
137
  },
137
- "pastoralist": {
138
- "depPaths": [
139
- "package.json",
140
- "site/package.json"
141
- ],
142
- "security": {
143
- "enabled": true,
144
- "provider": "github",
145
- "autoFix": true,
146
- "interactive": false,
147
- "severityThreshold": "low",
148
- "hasWorkspaceSecurityChecks": true
149
- }
150
- },
151
138
  "release-it": {
152
139
  "git": {
153
140
  "commitMessage": "chore(release): ${version}",
@@ -172,5 +159,19 @@
172
159
  "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
173
160
  }
174
161
  },
175
- "website": "https://jeffry.in/es-check"
162
+ "website": "https://jeffry.in/es-check",
163
+ "pastoralist": {
164
+ "depPaths": [
165
+ "package.json",
166
+ "site/package.json"
167
+ ],
168
+ "security": {
169
+ "enabled": true,
170
+ "provider": "github",
171
+ "autoFix": true,
172
+ "interactive": false,
173
+ "severityThreshold": "low",
174
+ "hasWorkspaceSecurityChecks": true
175
+ }
176
+ }
176
177
  }