mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-07 04:26:32 +08:00
Bug: chromium:1229172 Change-Id: I80d1fcb1432a79a0682ed92ff6335571a62bba55 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3122649 Auto-Submit: Jack Franklin <jacktfranklin@chromium.org> Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org> Commit-Queue: Jack Franklin <jacktfranklin@chromium.org>
161 lines
5.7 KiB
JavaScript
161 lines
5.7 KiB
JavaScript
// Copyright 2021 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
'use strict';
|
|
module.exports = {
|
|
meta: {
|
|
type: 'problem',
|
|
|
|
docs: {
|
|
description: 'Enforce event naming convention',
|
|
category: 'Possible Errors',
|
|
},
|
|
fixable: 'code',
|
|
messages: {
|
|
missingEventName:
|
|
'Event is missing a static readonly eventName property containing the event name, or the eventName property was not found as the first node in the class body.',
|
|
eventNameNotReadonly: 'The eventName property must be marked as readonly.',
|
|
eventNameNotStatic: 'The eventName property must be a static property.',
|
|
superEventNameWrong: 'The first argument to super() must be EventClass.eventName.',
|
|
noSuperCallFound: 'Could not find a super() call in the constructor.',
|
|
noConstructorFound: 'Could not find a constructor for the custom event.',
|
|
},
|
|
schema: [] // no options
|
|
},
|
|
create: function(context) {
|
|
function findConstructorAndSuperCallAndFirstArgumentToSuper(node) {
|
|
const foundNodes = {
|
|
constructor: undefined,
|
|
superExpression: undefined,
|
|
firstArgumentToSuper: undefined,
|
|
};
|
|
const constructor = node.body.body.find(bodyNode => {
|
|
return bodyNode.type === 'MethodDefinition' && bodyNode.key?.name === 'constructor';
|
|
});
|
|
if (!constructor) {
|
|
return foundNodes;
|
|
}
|
|
|
|
foundNodes.constructor = constructor;
|
|
|
|
const superExpression = constructor.value.body.body.find(bodyNode => {
|
|
const isExpression = bodyNode.type === 'ExpressionStatement';
|
|
if (!isExpression) {
|
|
return false;
|
|
}
|
|
return isExpression && bodyNode.expression.callee?.type === 'Super';
|
|
});
|
|
|
|
if (!superExpression) {
|
|
return foundNodes;
|
|
}
|
|
|
|
foundNodes.superExpression = superExpression;
|
|
|
|
const firstArgumentToSuper = superExpression.expression.arguments[0];
|
|
if (!firstArgumentToSuper) {
|
|
// This is invalid so bail.
|
|
return foundNodes;
|
|
}
|
|
foundNodes.firstArgumentToSuper = firstArgumentToSuper;
|
|
return foundNodes;
|
|
}
|
|
|
|
function tryToAutoFixIfWeCan(fixer, node) {
|
|
// We can autofix nodes when either:
|
|
// 1. They do not have the static declaration, and there is a super('fooevent') call
|
|
// => in this case, we can define static readonly eventName = 'fooevent' and update the super() call's first argument.
|
|
// 2. There is a staticEventName declared, but the super() call is using the literal. In this case, we can update the super() call's first argument.
|
|
|
|
// Note: if we cannot fix, we return an empty array, which signifies to
|
|
// ESLint that there's no fixes we'd like to apply.
|
|
|
|
const nodeIsMissingEventName = node.body.body[0]?.key?.name !== 'eventName';
|
|
const className = node.id.name;
|
|
|
|
const {firstArgumentToSuper} = findConstructorAndSuperCallAndFirstArgumentToSuper(node);
|
|
if (firstArgumentToSuper.type !== 'Literal') {
|
|
// Either it's OK, or it's some value that isn't a string literal, so we should bail.
|
|
return [];
|
|
}
|
|
const eventNameFromSuper = firstArgumentToSuper.value;
|
|
const fixes = [];
|
|
if (nodeIsMissingEventName) {
|
|
fixes.push(fixer.insertTextBefore(node.body.body[0], `static readonly eventName = '${eventNameFromSuper}';`));
|
|
}
|
|
|
|
fixes.push(fixer.replaceText(firstArgumentToSuper, `${className}.eventName`));
|
|
return fixes;
|
|
}
|
|
|
|
return {
|
|
ClassDeclaration(node) {
|
|
if (!node.superClass) {
|
|
return;
|
|
}
|
|
if (node.superClass.name !== 'Event') {
|
|
return;
|
|
}
|
|
|
|
const bodyMembersOfClass = node.body.body;
|
|
// Look for the static readonly eventName line.
|
|
// We purposefully look at the first body node as it should be defined first
|
|
const firstBodyNode = bodyMembersOfClass[0];
|
|
if (!firstBodyNode || firstBodyNode.key.name !== 'eventName') {
|
|
context.report({
|
|
node,
|
|
messageId: 'missingEventName',
|
|
fix(fixer) {
|
|
return tryToAutoFixIfWeCan(fixer, node);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
if (!firstBodyNode.readonly) {
|
|
context.report({node, messageId: 'eventNameNotReadonly'});
|
|
}
|
|
if (!firstBodyNode.static) {
|
|
context.report({node, messageId: 'eventNameNotStatic'});
|
|
return;
|
|
}
|
|
|
|
// Now we know the static readonly eventName is defined, we check for
|
|
// the constructor and the super() call.
|
|
const {constructor, superExpression, firstArgumentToSuper} =
|
|
findConstructorAndSuperCallAndFirstArgumentToSuper(node);
|
|
|
|
if (!constructor) {
|
|
context.report({node, messageId: 'noConstructorFound'});
|
|
return;
|
|
}
|
|
if (!superExpression) {
|
|
context.report({node, messageId: 'noSuperCallFound'});
|
|
return;
|
|
}
|
|
if (!firstArgumentToSuper) {
|
|
context.report({node, messageId: 'superEventNameWrong'});
|
|
return;
|
|
}
|
|
|
|
const customEventClassName = node.id.name;
|
|
if (firstArgumentToSuper.type !== 'MemberExpression') {
|
|
context.report({
|
|
node,
|
|
messageId: 'superEventNameWrong',
|
|
fix(fixer) {
|
|
return tryToAutoFixIfWeCan(fixer, node);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
if (firstArgumentToSuper.object.name !== customEventClassName ||
|
|
firstArgumentToSuper.property.name !== 'eventName') {
|
|
context.report({node, messageId: 'superEventNameWrong'});
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
};
|