Skip to content

Commit ca540d8

Browse files
Lightning00BladeDevtools-frontend LUCI CQ
authored andcommitted
[eslint] Migrate rules to TypeScript
Bug: 397586315 Change-Id: Iffe4bb260d3198dae106b61dcc97493583df8622 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6451308 Auto-Submit: Nikolay Vitkov <[email protected]> Reviewed-by: Danil Somsikov <[email protected]> Commit-Queue: Danil Somsikov <[email protected]>
1 parent d1b2a89 commit ca540d8

11 files changed

+395
-319
lines changed

scripts/eslint_rules/lib/enforce-custom-event-names.js

Lines changed: 0 additions & 144 deletions
This file was deleted.
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright 2020 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import type {TSESTree} from '@typescript-eslint/utils';
6+
7+
import {createRule} from './tsUtils.ts';
8+
9+
// Define specific node types for clarity
10+
type ClassDeclaration = TSESTree.ClassDeclaration;
11+
type MethodDefinition = TSESTree.MethodDefinition;
12+
type ExpressionStatement = TSESTree.ExpressionStatement;
13+
type PropertyDefinition = TSESTree.PropertyDefinition;
14+
15+
/**
16+
* Match all lowercase letters from the start to the end.
17+
*/
18+
const VALID_EVENT_NAME_REGEX = /^([a-z]+)$/;
19+
20+
export default createRule({
21+
name: 'enforce-custom-event-names',
22+
meta: {
23+
type: 'problem',
24+
docs: {
25+
description: 'check for event naming',
26+
category: 'Possible Errors',
27+
},
28+
messages: {
29+
invalidEventName: 'Custom events must be named in all lower case with no punctuation.',
30+
invalidEventNameReference: 'When referencing a custom event name, it must be accessed as ClassName.eventName.',
31+
},
32+
schema: [], // no options
33+
},
34+
defaultOptions: [],
35+
create: function(context) {
36+
let foundLocalEventClassDeclaration = false;
37+
const classDeclarationsToLint: ClassDeclaration[] = [];
38+
39+
function lintClassNode(node: ClassDeclaration) {
40+
const constructor = node.body.body.find((bodyNode): bodyNode is MethodDefinition => {
41+
return bodyNode.type === 'MethodDefinition' && bodyNode.kind === 'constructor';
42+
});
43+
44+
if (!constructor?.value.body) {
45+
return;
46+
}
47+
48+
// Find the super() call within the constructor
49+
const superCallExpressionStatement =
50+
constructor.value.body.body.find((bodyNode): bodyNode is ExpressionStatement => {
51+
return bodyNode.type === 'ExpressionStatement' && bodyNode.expression.type === 'CallExpression' &&
52+
bodyNode.expression.callee?.type === 'Super';
53+
});
54+
55+
if (!superCallExpressionStatement || superCallExpressionStatement.expression.type !== 'CallExpression') {
56+
// Should not happen if the type guard worked, but good for safety
57+
return;
58+
}
59+
const superCall = superCallExpressionStatement.expression;
60+
61+
const firstArgToSuper = superCall.arguments[0];
62+
if (!firstArgToSuper) {
63+
// This is invalid, but TypeScript will catch this for us so no need to
64+
// error in ESLint land as well.
65+
return;
66+
}
67+
68+
// Case 1: super('eventname')
69+
if (firstArgToSuper.type === 'Literal') {
70+
// Ensure it's a string literal before checking value
71+
if (typeof firstArgToSuper.value === 'string') {
72+
const firstArgLiteralValue = firstArgToSuper.value;
73+
if (!firstArgLiteralValue.match(VALID_EVENT_NAME_REGEX)) {
74+
// Report on the literal value itself for better location
75+
context.report({node: firstArgToSuper, messageId: 'invalidEventName'});
76+
}
77+
} else {
78+
context.report({node: firstArgToSuper, messageId: 'invalidEventName'});
79+
}
80+
return;
81+
}
82+
83+
// Case 2: super(SomeClass.eventName)
84+
if (firstArgToSuper.type === 'MemberExpression') {
85+
// Ensure the class has a name to compare against
86+
const eventClassName = node.id?.name;
87+
if (!eventClassName) {
88+
// Cannot validate anonymous classes this way
89+
return;
90+
}
91+
92+
// Check if the member expression structure is correct (Identifier.Identifier)
93+
if (firstArgToSuper.object.type !== 'Identifier' || firstArgToSuper.property.type !== 'Identifier') {
94+
context.report({node: firstArgToSuper, messageId: 'invalidEventNameReference'});
95+
return;
96+
}
97+
98+
const objectName: string = firstArgToSuper.object.name;
99+
const propertyName: string = firstArgToSuper.property.name;
100+
101+
// Check if it matches ClassName.eventName
102+
if (objectName !== eventClassName || propertyName !== 'eventName') {
103+
context.report({node: firstArgToSuper, messageId: 'invalidEventNameReference'});
104+
return;
105+
}
106+
107+
// If the reference is right (ClassName.eventName), find the static property definition
108+
const eventNameProperty = node.body.body.find((classBodyPart): classBodyPart is PropertyDefinition => {
109+
return classBodyPart.type === 'PropertyDefinition' && classBodyPart.static === true && // Ensure it's static
110+
classBodyPart.key?.type === 'Identifier' && classBodyPart.key.name === 'eventName';
111+
});
112+
113+
// This rule depends on the static-custom-event-names rule to enforce existence/static/readonly.
114+
// If it exists, check its value.
115+
if (eventNameProperty) {
116+
// We only allow static eventName = 'literalvalue';
117+
if (!eventNameProperty.value || eventNameProperty.value.type !== 'Literal' ||
118+
typeof eventNameProperty.value.value !== 'string') {
119+
// Report on the property value if it's not a string literal
120+
context.report(
121+
{node: eventNameProperty.value ?? eventNameProperty, messageId: 'invalidEventNameReference'});
122+
return;
123+
}
124+
125+
// Grab the value of static eventName and confirm it follows the required conventions.
126+
const valueOfEventName = eventNameProperty.value.value;
127+
if (!valueOfEventName.match(VALID_EVENT_NAME_REGEX)) {
128+
// Report on the property value literal
129+
context.report({node: eventNameProperty.value, messageId: 'invalidEventName'});
130+
}
131+
}
132+
133+
return;
134+
}
135+
136+
// Case 3: super(someOtherVariable) or invalid structure
137+
// This means it's neither a Literal nor the specific MemberExpression ClassName.eventName
138+
context.report({node: firstArgToSuper, messageId: 'invalidEventNameReference'});
139+
}
140+
141+
return {
142+
ClassDeclaration(node) {
143+
// If we find a local class defined called Event, we do not apply this
144+
// check, as we have some instances where a local Event class is used
145+
// which is not the builtin Event class that represents DOM emitted
146+
// events.
147+
if (node.id?.name === 'Event') {
148+
foundLocalEventClassDeclaration = true;
149+
return;
150+
}
151+
152+
// Check if the class extends the global 'Event'
153+
if (!node.superClass || node.superClass.type !== 'Identifier' || node.superClass.name !== 'Event') {
154+
return;
155+
}
156+
157+
if (!node.superClass || node.superClass.type !== 'Identifier' || node.superClass.name !== 'Event') {
158+
return;
159+
}
160+
161+
classDeclarationsToLint.push(node);
162+
},
163+
'Program:exit'(): void {
164+
if (foundLocalEventClassDeclaration) {
165+
return;
166+
}
167+
168+
classDeclarationsToLint.forEach((node: ClassDeclaration) => {
169+
lintClassNode(node);
170+
});
171+
},
172+
};
173+
},
174+
});

scripts/eslint_rules/lib/enforce-default-import-name.js

Lines changed: 0 additions & 73 deletions
This file was deleted.

0 commit comments

Comments
 (0)