Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tests] use parsers.all + features to maximally repeat test cases across parsers, plus fixes #3108

Merged
merged 4 commits into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel

### Fixed
* [`propTypes`]: add `VoidFunctionComponent` to react generic list ([#3092][] @vedadeepta)
* [`jsx-fragments`], [`jsx-no-useless-fragment`]: avoid a crash on fragment syntax in `typescript-eslint` parser (@ljharb)
* [`jsx-props-no-multi-spaces`]: avoid a crash on long member chains in tag names in `typescript-eslint` parser (@ljharb)
* [`no-unused-prop-types`], `usedPropTypes`: avoid crash with typescript-eslint parser (@ljharb)

[#3092]: https://github.com/yannickcr/eslint-plugin-react/pull/3092
[#2166]: https://github.com/yannickcr/eslint-plugin-react/pull/2166
Expand Down
5 changes: 5 additions & 0 deletions lib/rules/jsx-fragments.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ module.exports = {

function getFixerToLong(jsxFragment) {
const sourceCode = context.getSourceCode();
if (!jsxFragment.closingFragment || !jsxFragment.openingFragment) {
// the old TS parser crashes here
// TODO: FIXME: can we fake these two descriptors?
return null;
}
return function fix(fixer) {
let source = sourceCode.getText();
source = replaceNode(source, jsxFragment.closingFragment, closeFragLong);
Expand Down
5 changes: 5 additions & 0 deletions lib/rules/jsx-no-useless-fragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ module.exports = {
return false;
}

// old TS parser can't handle this one
if (node.type === 'JSXFragment' && (!node.openingFragment || !node.closingFragment)) {
return false;
}

return true;
}

Expand Down
4 changes: 3 additions & 1 deletion lib/rules/jsx-props-no-multi-spaces.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ module.exports = {
case 'JSXMemberExpression':
return `${getPropName(propNode.object)}.${propNode.property.name}`;
default:
return propNode.name.name;
return propNode.name
? propNode.name.name
: `${context.getSourceCode().getText(propNode.object)}.${propNode.property.name}`; // needed for typescript-eslint parser
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/util/usedPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ function isInLifeCycleMethod(node, checkAsyncSafeLifeCycles) {
* @return {boolean}
*/
function isSetStateUpdater(node) {
const unwrappedParentCalleeNode = node.parent.type === 'CallExpression'
const unwrappedParentCalleeNode = node.parent && node.parent.type === 'CallExpression'
&& ast.unwrapTSAsExpression(node.parent.callee);

return unwrappedParentCalleeNode
Expand Down
71 changes: 56 additions & 15 deletions tests/helpers/parsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,67 @@
const path = require('path');
const semver = require('semver');
const version = require('eslint/package.json').version;
const flatMap = require('array.prototype.flatmap');

const NODE_MODULES = '../../node_modules';

module.exports = {
const parsers = {
BABEL_ESLINT: path.join(__dirname, NODE_MODULES, 'babel-eslint'),
TYPESCRIPT_ESLINT: path.join(__dirname, NODE_MODULES, 'typescript-eslint-parser'),
'@TYPESCRIPT_ESLINT': path.join(__dirname, NODE_MODULES, '@typescript-eslint/parser'),
TS: function TS(tests) {
if (!Array.isArray(tests) || arguments.length > 1) {
throw new SyntaxError('parsers.TS() takes a single array argument');
}
if (semver.satisfies(version, '>= 5')) {
return tests;
}
return [];
},
ES2020: function ES2020(tests) {
if (semver.satisfies(version, '>= 6')) {
return tests;
}
return [];
all: function all(tests) {
const t = flatMap(tests, (test) => {
if (typeof test === 'string') {
test = { code: test };
}
if ('parser' in test) {
delete test.features;
return test;
}
const features = new Set([].concat(test.features || []));
delete test.features;
const es = test.parserOptions && test.parserOptions.ecmaVersion;

function addComment(testObject, parser) {
const extraComment = `\n// features: [${Array.from(features).join(',')}], parser: ${parser}`;
return Object.assign(
{},
testObject,
{ code: testObject.code + extraComment },
testObject.output && { output: testObject.output + extraComment }
);
}

const skipBase = (features.has('class fields') && semver.satisfies(version, '< 8'))
|| (es >= 2020 && semver.satisfies(version, '< 6'))
|| features.has('no-default')
|| features.has('bind operator')
|| features.has('do expressions')
|| features.has('decorators')
|| features.has('flow')
|| features.has('ts')
|| features.has('types')
|| (features.has('fragment') && semver.satisfies(version, '< 5'));

const skipBabel = features.has('no-babel');
const skipTS = semver.satisfies(version, '< 5')
|| features.has('flow')
|| features.has('no-ts')
|| features.has('jsx namespace')
|| features.has('bind operator')
|| features.has('do expressions');
const tsOld = !skipTS && !features.has('no-ts-old');
const tsNew = !skipTS && !features.has('no-ts-new');

return [].concat(
skipBase ? [] : addComment(test, 'default'),
skipBabel ? [] : addComment(Object.assign({}, test, { parser: parsers.BABEL_ESLINT }), 'babel-eslint'),
tsOld ? addComment(Object.assign({}, test, { parser: parsers.TYPESCRIPT_ESLINT }), 'typescript-eslint') : [],
tsNew ? addComment(Object.assign({}, test, { parser: parsers['@TYPESCRIPT_ESLINT'] }), '@typescript/eslint') : []
);
});
return t;
},
};

module.exports = parsers;
Loading