Skip to content

Commit 473000d

Browse files
Merge branch 'main' into AddPolymorphicSetting
2 parents 1375338 + 3d1d26d commit 473000d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+560
-288
lines changed

.github/workflows/node-4+.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ jobs:
8888
eslint: 3
8989

9090
steps:
91-
- uses: actions/checkout@v2
91+
- uses: actions/checkout@v3
9292
with:
9393
fetch-depth: 0
9494
- uses: ljharb/actions/node/install@main
@@ -102,7 +102,7 @@ jobs:
102102
- run: rm __tests__/src/util/getComputedRole-test.js
103103
if: ${{ matrix.node-version < 7 }}
104104
- run: npm run test:ci
105-
- uses: codecov/codecov-action@v2
105+
- uses: codecov/codecov-action@v3
106106

107107
node:
108108
name: 'node 4+'

.github/workflows/node-pretest.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
runs-on: ubuntu-latest
88

99
steps:
10-
- uses: actions/checkout@v2
10+
- uses: actions/checkout@v3
1111
- uses: ljharb/actions/node/install@main
1212
name: 'nvm install lts/* && npm install'
1313
with:
@@ -19,7 +19,7 @@ jobs:
1919
runs-on: ubuntu-latest
2020

2121
steps:
22-
- uses: actions/checkout@v2
22+
- uses: actions/checkout@v3
2323
- uses: ljharb/actions/node/install@main
2424
name: 'nvm install lts/* && npm install'
2525
with:
@@ -31,7 +31,7 @@ jobs:
3131
runs-on: ubuntu-latest
3232

3333
steps:
34-
- uses: actions/checkout@v2
34+
- uses: actions/checkout@v3
3535
- uses: ljharb/actions/node/install@main
3636
name: 'nvm install lts/* && npm install'
3737
with:

.github/workflows/readme.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
runs-on: ubuntu-latest
88

99
steps:
10-
- uses: actions/checkout@v2
10+
- uses: actions/checkout@v3
1111
- uses: ljharb/actions/node/install@main
1212
name: 'nvm install lts/* && npm install'
1313
with:

.github/workflows/rebase.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010

1111
steps:
12-
- uses: actions/checkout@v2
12+
- uses: actions/checkout@v3
1313
- uses: ljharb/rebase@master
1414
env:
1515
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/require-allow-edits.yml

+5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ name: Require “Allow Edits”
22

33
on: [pull_request_target]
44

5+
permissions:
6+
contents: read
7+
58
jobs:
69
_:
10+
permissions:
11+
pull-requests: read # for ljharb/require-allow-edits to check 'allow edits' on PR
712
name: "Require “Allow Edits”"
813

914
runs-on: ubuntu-latest
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { version } from 'eslint/package.json';
2+
import semver from 'semver';
3+
4+
const isESLintV8 = semver.major(version) >= 8;
5+
6+
// eslint-disable-next-line global-require, import/no-dynamic-require, import/no-unresolved
7+
const getESLintCoreRule = (ruleId) => (isESLintV8 ? require('eslint/use-at-your-own-risk').builtinRules.get(ruleId) : require(`eslint/lib/rules/${ruleId}`));
8+
9+
export default getESLintCoreRule;

__tests__/__util__/helpers/parsers.js

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import path from 'path';
2+
import semver from 'semver';
3+
import entries from 'object.entries';
4+
import { version } from 'eslint/package.json';
5+
import flatMap from 'array.prototype.flatmap';
6+
7+
let tsParserVersion;
8+
try {
9+
// eslint-disable-next-line import/no-unresolved, global-require
10+
tsParserVersion = require('@typescript-eslint/parser/package.json').version;
11+
} catch (e) { /**/ }
12+
13+
const disableNewTS = semver.satisfies(tsParserVersion, '>= 4.1') // this rule is not useful on v4.1+ of the TS parser
14+
? (x) => ({ ...x, features: [].concat(x.features, 'no-ts-new') })
15+
: (x) => x;
16+
17+
function minEcmaVersion(features, parserOptions) {
18+
const minEcmaVersionForFeatures = {
19+
'class fields': 2022,
20+
'optional chaining': 2020,
21+
'nullish coalescing': 2020,
22+
};
23+
const result = Math.max(
24+
...[].concat(
25+
(parserOptions && parserOptions.ecmaVersion) || [],
26+
flatMap(entries(minEcmaVersionForFeatures), (entry) => {
27+
const f = entry[0];
28+
const y = entry[1];
29+
return features.has(f) ? y : [];
30+
}),
31+
).map((y) => (y > 5 && y < 2015 ? y + 2009 : y)), // normalize editions to years
32+
);
33+
return Number.isFinite(result) ? result : undefined;
34+
}
35+
36+
const NODE_MODULES = '../../node_modules';
37+
38+
const parsers = {
39+
BABEL_ESLINT: path.join(__dirname, NODE_MODULES, 'babel-eslint'),
40+
'@BABEL_ESLINT': path.join(__dirname, NODE_MODULES, '@babel/eslint-parser'),
41+
TYPESCRIPT_ESLINT: path.join(__dirname, NODE_MODULES, 'typescript-eslint-parser'),
42+
'@TYPESCRIPT_ESLINT': path.join(__dirname, NODE_MODULES, '@typescript-eslint/parser'),
43+
disableNewTS,
44+
babelParserOptions: function parserOptions(test, features) {
45+
return {
46+
...test.parserOptions,
47+
requireConfigFile: false,
48+
babelOptions: {
49+
presets: [
50+
'@babel/preset-react',
51+
],
52+
plugins: [
53+
'@babel/plugin-syntax-do-expressions',
54+
'@babel/plugin-syntax-function-bind',
55+
['@babel/plugin-syntax-decorators', { legacy: true }],
56+
],
57+
parserOpts: {
58+
allowSuperOutsideMethod: false,
59+
allowReturnOutsideFunction: false,
60+
},
61+
},
62+
ecmaFeatures: {
63+
64+
...test.parserOptions && test.parserOptions.ecmaFeatures,
65+
jsx: true,
66+
modules: true,
67+
legacyDecorators: features.has('decorators'),
68+
},
69+
};
70+
},
71+
all: function all(tests) {
72+
const t = flatMap(tests, (test) => {
73+
/* eslint no-param-reassign: 0 */
74+
if (typeof test === 'string') {
75+
test = { code: test };
76+
}
77+
if ('parser' in test) {
78+
delete test.features;
79+
return test;
80+
}
81+
const features = new Set([].concat(test.features || []));
82+
delete test.features;
83+
84+
const es = minEcmaVersion(features, test.parserOptions);
85+
86+
function addComment(testObject, parser) {
87+
const extras = [].concat(
88+
`features: [${Array.from(features).join(',')}]`,
89+
`parser: ${parser}`,
90+
testObject.parserOptions ? `parserOptions: ${JSON.stringify(testObject.parserOptions)}` : [],
91+
testObject.options ? `options: ${JSON.stringify(testObject.options)}` : [],
92+
testObject.settings ? `settings: ${JSON.stringify(testObject.settings)}` : [],
93+
);
94+
95+
const extraComment = `\n// ${extras.join(', ')}`;
96+
97+
// Augment expected fix code output with extraComment
98+
const nextCode = { code: testObject.code + extraComment };
99+
const nextOutput = testObject.output && { output: testObject.output + extraComment };
100+
101+
// Augment expected suggestion outputs with extraComment
102+
// `errors` may be a number (expected number of errors) or an array of
103+
// error objects.
104+
const nextErrors = testObject.errors
105+
&& typeof testObject.errors !== 'number'
106+
&& {
107+
errors: testObject.errors.map(
108+
(errorObject) => {
109+
const nextSuggestions = errorObject.suggestions && {
110+
suggestions: errorObject.suggestions.map((suggestion) => ({ ...suggestion, output: suggestion.output + extraComment })),
111+
};
112+
113+
return { ...errorObject, ...nextSuggestions };
114+
},
115+
),
116+
};
117+
118+
return {
119+
120+
...testObject,
121+
...nextCode,
122+
...nextOutput,
123+
...nextErrors,
124+
};
125+
}
126+
127+
const skipBase = (features.has('class fields') && semver.satisfies(version, '< 8'))
128+
|| (es >= 2020 && semver.satisfies(version, '< 6'))
129+
|| features.has('no-default')
130+
|| features.has('bind operator')
131+
|| features.has('do expressions')
132+
|| features.has('decorators')
133+
|| features.has('flow')
134+
|| features.has('ts')
135+
|| features.has('types')
136+
|| (features.has('fragment') && semver.satisfies(version, '< 5'));
137+
138+
const skipBabel = features.has('no-babel');
139+
const skipOldBabel = skipBabel
140+
|| features.has('no-babel-old')
141+
|| features.has('optional chaining')
142+
|| semver.satisfies(version, '>= 8');
143+
const skipNewBabel = skipBabel
144+
|| features.has('no-babel-new')
145+
|| !semver.satisfies(version, '^7.5.0') // require('@babel/eslint-parser/package.json').peerDependencies.eslint
146+
|| features.has('flow')
147+
|| features.has('types')
148+
|| features.has('ts');
149+
const skipTS = semver.satisfies(version, '<= 5') // TODO: make these pass on eslint 5
150+
|| features.has('no-ts')
151+
|| features.has('flow')
152+
|| features.has('jsx namespace')
153+
|| features.has('bind operator')
154+
|| features.has('do expressions');
155+
const tsOld = !skipTS && !features.has('no-ts-old');
156+
const tsNew = !skipTS && !features.has('no-ts-new');
157+
158+
return [].concat(
159+
skipBase ? [] : addComment(
160+
{
161+
...test,
162+
...typeof es === 'number' && {
163+
parserOptions: { ...test.parserOptions, ecmaVersion: es },
164+
},
165+
},
166+
'default',
167+
),
168+
skipOldBabel ? [] : addComment({
169+
...test,
170+
parser: parsers.BABEL_ESLINT,
171+
parserOptions: parsers.babelParserOptions(test, features),
172+
}, 'babel-eslint'),
173+
skipNewBabel ? [] : addComment({
174+
...test,
175+
parser: parsers['@BABEL_ESLINT'],
176+
parserOptions: parsers.babelParserOptions(test, features),
177+
}, '@babel/eslint-parser'),
178+
tsOld ? addComment({ ...test, parser: parsers.TYPESCRIPT_ESLINT }, 'typescript-eslint') : [],
179+
tsNew ? addComment({ ...test, parser: parsers['@TYPESCRIPT_ESLINT'] }, '@typescript-eslint/parser') : [],
180+
);
181+
});
182+
return t;
183+
},
184+
};
185+
186+
export default parsers;

__tests__/src/rules/accessible-emoji-test.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import { RuleTester } from 'eslint';
1111
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
1212
import rule from '../../../src/rules/accessible-emoji';
13+
import parsers from '../../__util__/helpers/parsers';
1314

1415
// -----------------------------------------------------------------------------
1516
// Tests
@@ -23,7 +24,7 @@ const expectedError = {
2324
};
2425

2526
ruleTester.run('accessible-emoji', rule, {
26-
valid: [
27+
valid: parsers.all([].concat(
2728
{ code: '<div />;' },
2829
{ code: '<span />' },
2930
{ code: '<span>No emoji here!</span>' },
@@ -46,8 +47,8 @@ ruleTester.run('accessible-emoji', rule, {
4647
code: '<Box as="input" type="hidden">🐼</Box>',
4748
settings: { 'jsx-a11y': { polymorphicPropName: 'as' } },
4849
},
49-
].map(parserOptionsMapper),
50-
invalid: [
50+
)).map(parserOptionsMapper),
51+
invalid: parsers.all([].concat(
5152
{ code: '<span>🐼</span>', errors: [expectedError] },
5253
{ code: '<span>foo🐼bar</span>', errors: [expectedError] },
5354
{ code: '<span>foo 🐼 bar</span>', errors: [expectedError] },
@@ -61,5 +62,5 @@ ruleTester.run('accessible-emoji', rule, {
6162
settings: { 'jsx-a11y': { polymorphicPropName: 'as' } },
6263
errors: [expectedError],
6364
},
64-
].map(parserOptionsMapper),
65+
)).map(parserOptionsMapper),
6566
});

__tests__/src/rules/alt-text-test.js

+20-9
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import { RuleTester } from 'eslint';
1111
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
12+
import parsers from '../../__util__/helpers/parsers';
1213
import rule from '../../../src/rules/alt-text';
1314

1415
// -----------------------------------------------------------------------------
@@ -28,19 +29,29 @@ Use alt="" for presentational images.`,
2829
type: 'JSXOpeningElement',
2930
});
3031

31-
const ariaLabelValueError = 'The aria-label attribute must have a value. The alt attribute is preferred over aria-label for images.';
32-
const ariaLabelledbyValueError = 'The aria-labelledby attribute must have a value. The alt attribute is preferred over aria-labelledby for images.';
32+
const ariaLabelValueError = {
33+
message: 'The aria-label attribute must have a value. The alt attribute is preferred over aria-label for images.',
34+
};
35+
const ariaLabelledbyValueError = {
36+
message: 'The aria-labelledby attribute must have a value. The alt attribute is preferred over aria-labelledby for images.',
37+
};
3338

3439
const preferAltError = () => ({
3540
message: 'Prefer alt="" over a presentational role. First rule of aria is to not use aria if it can be achieved via native HTML.',
3641
type: 'JSXOpeningElement',
3742
});
3843

39-
const objectError = 'Embedded <object> elements must have alternative text by providing inner text, aria-label or aria-labelledby props.';
44+
const objectError = {
45+
message: 'Embedded <object> elements must have alternative text by providing inner text, aria-label or aria-labelledby props.',
46+
};
4047

41-
const areaError = 'Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.';
48+
const areaError = {
49+
message: 'Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.',
50+
};
4251

43-
const inputImageError = '<input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.';
52+
const inputImageError = {
53+
message: '<input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.',
54+
};
4455

4556
const componentsSettings = {
4657
'jsx-a11y': {
@@ -59,7 +70,7 @@ const array = [{
5970
}];
6071

6172
ruleTester.run('alt-text', rule, {
62-
valid: [
73+
valid: parsers.all([].concat(
6374
// DEFAULT ELEMENT 'img' TESTS
6475
{ code: '<img alt="foo" />;' },
6576
{ code: '<img alt={"foo"} />;' },
@@ -168,8 +179,8 @@ ruleTester.run('alt-text', rule, {
168179
{ code: '<InputImage alt="" />', options: array },
169180
{ code: '<InputImage alt="This is descriptive!" />', options: array },
170181
{ code: '<InputImage alt={altText} />', options: array },
171-
].map(parserOptionsMapper),
172-
invalid: [
182+
)).map(parserOptionsMapper),
183+
invalid: parsers.all([].concat(
173184
// DEFAULT ELEMENT 'img' TESTS
174185
{ code: '<img />;', errors: [missingPropError('img')] },
175186
{ code: '<img alt />;', errors: [altValueError('img')] },
@@ -276,5 +287,5 @@ ruleTester.run('alt-text', rule, {
276287
{ code: '<InputImage>Foo</InputImage>', errors: [inputImageError], options: array },
277288
{ code: '<InputImage {...this.props} />', errors: [inputImageError], options: array },
278289
{ code: '<Input type="image" />', errors: [inputImageError], settings: componentsSettings },
279-
].map(parserOptionsMapper),
290+
)).map(parserOptionsMapper),
280291
});

0 commit comments

Comments
 (0)