@automattic/eslint-config-target-es 2.0.0 → 2.1.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/CHANGELOG.md CHANGED
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.1.0] - 2024-02-07
9
+ ### Added
10
+ - All versions indicated by browserslist are now checked, not just the lowest. Added `getAllBrowsers` function to support this. [#31658]
11
+ - Support for more complex MDN data:
12
+ * Multiple support statements are now all checked. Previously only the first (most recent) was, which may have missed cases where support was backported.
13
+ * `version_removed` is now checked.
14
+ * Ranged versions (≤) are now handled.
15
+ * `prefix`, `alternative_name`, and `flags` now indicate (possible) lack of support. [#31658]
16
+
17
+ ### Changed
18
+ - Updated package dependencies.
19
+
20
+ ### Deprecated
21
+ - Deprecated `getBrowsers` function in favor of the new `getAllBrowsers`. [#31658]
22
+
23
+ ### Fixed
24
+ - Apparently MDN data considers "preview" a version, but didn't think that worth documenting. Handle it. [#31816]
25
+
8
26
  ## [2.0.0] - 2023-06-26
9
27
  ### Changed
10
28
  - As `eslint-plugin-es` appears to be abandoned, change to using `eslint-plugin-es-x`. [#31556]
@@ -41,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
41
59
  ### Added
42
60
  - Initial release.
43
61
 
62
+ [2.1.0]: https://github.com/Automattic/eslint-config-target-es/compare/2.0.0...2.1.0
44
63
  [2.0.0]: https://github.com/Automattic/eslint-config-target-es/compare/1.0.6...2.0.0
45
64
  [1.0.6]: https://github.com/Automattic/eslint-config-target-es/compare/1.0.5...1.0.6
46
65
  [1.0.5]: https://github.com/Automattic/eslint-config-target-es/compare/1.0.4...1.0.5
package/README.md CHANGED
@@ -61,10 +61,14 @@ to avoid your standard eslintrc and eslintignore and to avoid errors from any in
61
61
 
62
62
  You can import or require `@automattic/eslint-config-target-es/functions` to gain access to some functions that can be used to build your own configuration.
63
63
 
64
- As browserslist and MDN use different browser codes, `getBrowsers( { query: } )` will take a browserslist query and return an object with the MDN browser codes and the minimum matched version for each.
64
+ As browserslist and MDN use different browser codes, `getAllBrowsers( { query: } )` will take a browserslist query and return an object with the MDN browser codes and the matched versions for each.
65
65
 
66
66
  `getRules( { query:, builtins: } )` will return the rules config. Set `builtins` true for "builtins", false for "language", or null/undefined for "all".
67
67
 
68
+ ### Caveats
69
+
70
+ Some browsers supported by browserslist are not availble in the MDN data (e.g. Opera Mini) or are no longer being updated (e.g. Internet Explorer). In cases like these where no data is available, features are assumed to be supported. Set the environment variable `DEBUG=@automattic/eslint-config-target-es:warn` to generate messages when this happens.
71
+
68
72
  ## Security
69
73
 
70
74
  Need to report a security vulnerability? Go to [https://automattic.com/security/](https://automattic.com/security/) or directly to our security bug bounty site [https://hackerone.com/automattic](https://hackerone.com/automattic).
package/SECURITY.md CHANGED
@@ -4,11 +4,20 @@ Full details of the Automattic Security Policy can be found on [automattic.com](
4
4
 
5
5
  ## Supported Versions
6
6
 
7
- Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
7
+ Generally, only the latest version of Jetpack and its associated plugins have continued support. If a critical vulnerability is found in the current version of a plugin, we may opt to backport any patches to previous versions.
8
8
 
9
9
  ## Reporting a Vulnerability
10
10
 
11
- [Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
11
+ Our HackerOne program covers the below plugin software, as well as a variety of related projects and infrastructure:
12
+
13
+ * [Jetpack](https://jetpack.com/)
14
+ * Jetpack Backup
15
+ * Jetpack Boost
16
+ * Jetpack CRM
17
+ * Jetpack Protect
18
+ * Jetpack Search
19
+ * Jetpack Social
20
+ * Jetpack VideoPress
12
21
 
13
22
  **For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
14
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/eslint-config-target-es",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "ESLint sharable config to activate eslint-plugin-es checks based on browserslist targets.",
5
5
  "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/eslint-config-target-es/README.md#readme",
6
6
  "bugs": {
@@ -17,16 +17,16 @@
17
17
  "test": "jest tests"
18
18
  },
19
19
  "dependencies": {
20
- "@mdn/browser-compat-data": "5.2.66",
20
+ "@mdn/browser-compat-data": "5.3.28",
21
21
  "browserslist": "^4.17.6",
22
22
  "debug": "^4.3.2",
23
23
  "semver": "^7.3.5"
24
24
  },
25
25
  "devDependencies": {
26
- "@wordpress/browserslist-config": "5.18.0",
27
- "eslint": "8.39.0",
28
- "eslint-plugin-es-x": "7.1.0",
29
- "jest": "29.4.3"
26
+ "@wordpress/browserslist-config": "5.33.0",
27
+ "eslint": "8.51.0",
28
+ "eslint-plugin-es-x": "7.2.0",
29
+ "jest": "29.7.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "eslint": ">=4.19.1",
package/src/funcs.js CHANGED
@@ -1,22 +1,20 @@
1
- const mdn = require( '@mdn/browser-compat-data' );
2
1
  const browserslist = require( 'browserslist' );
3
2
  const debug = require( 'debug' );
4
3
  const { rules: esRules } = require( 'eslint-plugin-es-x' );
5
4
  const semver = require( 'semver' );
6
5
  const browsersMap = require( './browsersMap.js' );
7
- const rulesMap = require( './rulesMap.js' );
6
+ const { needsCheck } = require( './needsCheck.js' );
8
7
 
9
8
  const warn = debug( '@automattic/eslint-config-target-es:warn' );
10
- const debuglog = debug( '@automattic/eslint-config-target-es:debug' );
11
9
 
12
10
  /**
13
11
  * Get the list of supported browsers.
14
12
  *
15
13
  * @param {object} options - Options.
16
14
  * @param {string} options.query - Browserslist query.
17
- * @returns {object} Browsers mapped to versions.
15
+ * @returns {object} Browsers mapped to arrays of versions.
18
16
  */
19
- function getBrowsers( options = {} ) {
17
+ function getAllBrowsers( options = {} ) {
20
18
  const browsers = {};
21
19
  for ( const b of browserslist( options.query ) ) {
22
20
  const m = b.match( /^([a-z_]+) (all|[0-9.]+)(-.*)?$/ );
@@ -33,92 +31,29 @@ function getBrowsers( options = {} ) {
33
31
  continue;
34
32
  }
35
33
  const ver = m[ 2 ] === 'all' ? '0.0.0' : semver.coerce( m[ 2 ] ).version;
36
- if ( typeof browsers[ browser ] === 'undefined' || semver.lt( ver, browsers[ browser ] ) ) {
37
- browsers[ browser ] = ver;
38
- }
34
+ browsers[ browser ] ||= [];
35
+ browsers[ browser ].push( ver );
36
+ browsers[ browser ].sort( semver.compare );
39
37
  }
40
38
  return browsers;
41
39
  }
42
40
 
43
41
  /**
44
- * Test if a rule needs to be checked.
42
+ * Get the list of supported browsers.
45
43
  *
46
- * @param {string} rule - Rule.
47
- * @param {object} browsers - Browsers targeted.
44
+ * @deprecated since 2.1.0. Use getAllBrowsers instead.
48
45
  * @param {object} options - Options.
49
- * @param {boolean|null} options.builtins - If true, only rules with "javascript.builtins" paths are checked. If false, such rules are not checked. If null/undefined, all may be checked.
50
- * @returns {boolean} Whether the rule needs to be checked.
46
+ * @param {string} options.query - Browserslist query.
47
+ * @returns {object} Browsers mapped to minimum versions.
51
48
  */
52
- // eslint-disable-next-line no-unused-vars
53
- function needsCheck( rule, browsers, options = {} ) {
54
- let paths = rulesMap[ rule ];
55
- if ( paths === true || paths === false ) {
56
- return paths;
57
- }
58
- if ( ! Array.isArray( paths ) ) {
59
- paths = [ paths ];
60
- }
61
-
62
- if (
63
- options.builtins === false &&
64
- paths.some( path => path.startsWith( 'javascript.builtins.' ) )
65
- ) {
66
- return false;
67
- }
68
- if (
69
- options.builtins === true &&
70
- ! paths.some( path => path.startsWith( 'javascript.builtins.' ) )
71
- ) {
72
- return false;
73
- }
74
-
75
- path: for ( const path of paths ) {
76
- let data = mdn;
77
- for ( const k of path.split( '.' ) ) {
78
- data = data[ k ];
79
- if ( ! data ) {
80
- warn( `Invalid feature map for rule ${ rule }: ${ path } does not exist` );
81
- continue path;
82
- }
83
- }
84
- if ( ! data.__compat || ! data.__compat.support ) {
85
- warn( `Invalid feature map for rule ${ rule }: No data at ${ path }` );
86
- continue path;
87
- }
88
-
89
- browser: for ( const browser of Object.keys( browsers ) ) {
90
- if ( ! data.__compat.support[ browser ] ) {
91
- debuglog( `No support data for ${ browser } for rule ${ rule } (${ path }), skipping` );
92
- continue browser;
93
- }
94
- let support = data.__compat.support[ browser ];
95
- if ( Array.isArray( support ) ) {
96
- support = support[ 0 ];
97
- }
98
-
99
- if ( support.version_added === null ) {
100
- debuglog( `No support data for ${ browser } for rule ${ rule } (${ path }), skipping` );
101
- continue browser;
102
- } else if ( support.version_added === false ) {
103
- debuglog( `${ browser } needs check for ${ rule } (${ path })` );
104
- return true;
105
- } else if ( support.version_added === true ) {
106
- continue browser;
107
- } else if ( semver.gt( semver.coerce( support.version_added ), browsers[ browser ] ) ) {
108
- debuglog(
109
- `${ browser } < ${ support.version_added } needs check for ${ rule } (${ path }); we have ${ browsers[ browser ] }`
110
- );
111
- return true;
112
- } else if ( support.partial_implementation ) {
113
- debuglog(
114
- `${ browser } needs check for ${ rule } (${ path }) due to partial implementation`
115
- );
116
- return true;
117
- }
118
- }
49
+ function getBrowsers( options = {} ) {
50
+ warn( 'getBrowsers is deprecated. Use getAllBrowsers instead.' );
51
+ const browsers = getAllBrowsers( options );
52
+ const ret = {};
53
+ for ( const k of Object.keys( browsers ) ) {
54
+ ret[ k ] = browsers[ k ][ 0 ];
119
55
  }
120
-
121
- return false;
56
+ return ret;
122
57
  }
123
58
 
124
59
  /**
@@ -130,7 +65,7 @@ function needsCheck( rule, browsers, options = {} ) {
130
65
  * @returns {object} Rules configuration.
131
66
  */
132
67
  function getRules( options = {} ) {
133
- const browsers = getBrowsers( options );
68
+ const browsers = getAllBrowsers( options );
134
69
  const ret = {};
135
70
  for ( const rule of Object.keys( esRules ) ) {
136
71
  ret[ `es-x/${ rule }` ] = needsCheck( rule, browsers, options ) ? 2 : 0;
@@ -139,6 +74,7 @@ function getRules( options = {} ) {
139
74
  }
140
75
 
141
76
  module.exports = {
77
+ getAllBrowsers,
142
78
  getBrowsers,
143
79
  getRules,
144
80
  };
@@ -0,0 +1,151 @@
1
+ const mdn = require( '@mdn/browser-compat-data' );
2
+ const debug = require( 'debug' );
3
+ const semver = require( 'semver' );
4
+ const rulesMap = require( './rulesMap.js' );
5
+
6
+ const warn = debug( '@automattic/eslint-config-target-es:warn' );
7
+ const debuglog = debug( '@automattic/eslint-config-target-es:debug' );
8
+
9
+ /**
10
+ * Test if a rule needs to be checked.
11
+ *
12
+ * @param {string} rule - Rule.
13
+ * @param {object} browsers - Browsers targeted.
14
+ * @param {object} options - Options.
15
+ * @param {boolean|null} options.builtins - If true, only rules with "javascript.builtins" paths are checked. If false, such rules are not checked. If null/undefined, all may be checked.
16
+ * @returns {boolean} Whether the rule needs to be checked.
17
+ */
18
+ function needsCheck( rule, browsers, options = {} ) {
19
+ let paths = rulesMap[ rule ];
20
+ if ( paths === true || paths === false ) {
21
+ return paths;
22
+ }
23
+ if ( paths === undefined ) {
24
+ warn( `No feature map for rule ${ rule }` );
25
+ return false;
26
+ }
27
+ if ( ! Array.isArray( paths ) ) {
28
+ paths = [ paths ];
29
+ }
30
+
31
+ if (
32
+ options.builtins === false &&
33
+ paths.some( path => path.startsWith( 'javascript.builtins.' ) )
34
+ ) {
35
+ return false;
36
+ }
37
+ if (
38
+ options.builtins === true &&
39
+ ! paths.some( path => path.startsWith( 'javascript.builtins.' ) )
40
+ ) {
41
+ return false;
42
+ }
43
+
44
+ const semver000 = semver.coerce( '0.0.0' );
45
+
46
+ path: for ( const path of paths ) {
47
+ let data = mdn;
48
+ for ( const k of path.split( '.' ) ) {
49
+ data = data[ k ];
50
+ if ( ! data ) {
51
+ warn( `Invalid feature map for rule ${ rule }: ${ path } does not exist` );
52
+ continue path;
53
+ }
54
+ }
55
+ if ( ! data.__compat || ! data.__compat.support ) {
56
+ warn( `Invalid feature map for rule ${ rule }: No data at ${ path }` );
57
+ continue path;
58
+ }
59
+
60
+ browser: for ( const browser of Object.keys( browsers ) ) {
61
+ if ( ! data.__compat.support[ browser ] ) {
62
+ warn( `No support data for ${ browser } for rule ${ rule } (${ path }), skipping` );
63
+ continue browser;
64
+ }
65
+
66
+ const supports = Array.isArray( data.__compat.support[ browser ] )
67
+ ? data.__compat.support[ browser ]
68
+ : [ data.__compat.support[ browser ] ];
69
+ const vers = Array.isArray( browsers[ browser ] )
70
+ ? browsers[ browser ]
71
+ : [ browsers[ browser ] ];
72
+
73
+ version: for ( const ver of vers ) {
74
+ const results = [];
75
+ support: for ( const support of supports ) {
76
+ if ( support.version_added === null ) {
77
+ continue support;
78
+ }
79
+
80
+ if ( support.version_added === false ) {
81
+ results.push( 'no support' );
82
+ continue support;
83
+ }
84
+ if ( support.version_added === 'preview' ) {
85
+ results.push( 'added version is "preview"' );
86
+ continue support;
87
+ }
88
+
89
+ let added, removed;
90
+ if ( support.version_added === true ) {
91
+ added = semver000;
92
+ } else {
93
+ added = support.version_added.startsWith( '≤' )
94
+ ? semver000
95
+ : semver.coerce( support.version_added );
96
+ }
97
+ if ( support.version_removed === true ) {
98
+ removed = semver000;
99
+ } else if (
100
+ typeof support.version_removed === 'string' &&
101
+ support.version_removed !== 'preview'
102
+ ) {
103
+ removed = support.version_removed.startsWith( '≤' )
104
+ ? semver000
105
+ : semver.coerce( support.version_removed );
106
+ }
107
+
108
+ const range = removed ? `${ added } – <${ removed }` : `>= ${ added }`;
109
+
110
+ if ( semver.gt( added, ver ) || ( removed && semver.lte( removed, ver ) ) ) {
111
+ results.push( `outside range ${ range }` );
112
+ } else if ( support.partial_implementation ) {
113
+ results.push( `partial implementation for ${ range }` );
114
+ } else if ( support.prefix ) {
115
+ results.push( `prefixed implementation for ${ range }` );
116
+ } else if ( support.alternative_name ) {
117
+ results.push( `alternatively named implementation for ${ range }` );
118
+ } else if ( support.flags ) {
119
+ results.push( `flagged implementation for ${ range }` );
120
+ } else {
121
+ // It's good! Check the next version.
122
+ continue version;
123
+ }
124
+ }
125
+
126
+ // If there was no support data at all...
127
+ if ( results.length === 0 ) {
128
+ warn( `No support data for ${ browser } for rule ${ rule } (${ path }), skipping` );
129
+ continue browser;
130
+ }
131
+
132
+ // If no support entry hit the "It's good" case, it's no good. Log the reasons.
133
+ // If there are reasons other than "outside range", skip any "outside range" as irrelevant.
134
+ let results2 = results.filter( v => ! v.startsWith( 'outside range' ) );
135
+ if ( results2.length === 0 ) {
136
+ results2 = results;
137
+ }
138
+ debuglog(
139
+ `${ browser } ${ ver } needs check for ${ rule } (${ path }); ${ results2.join( '; ' ) }`
140
+ );
141
+ return true;
142
+ }
143
+ }
144
+ }
145
+
146
+ return false;
147
+ }
148
+
149
+ module.exports = {
150
+ needsCheck,
151
+ };
package/src/rulesMap.js CHANGED
@@ -3,6 +3,7 @@
3
3
  module.exports = {
4
4
  // ?
5
5
  'no-atomics-waitasync': 'javascript.builtins.Atomics.waitAsync',
6
+ 'no-regexp-v-flag': 'javascript.builtins.RegExp.unicodeSets',
6
7
  'no-string-prototype-iswellformed-towellformed': [
7
8
  'javascript.builtins.String.isWellFormed',
8
9
  'javascript.builtins.String.toWellFormed',