Skip to content

Commit 417571d

Browse files
committed
Add compat functions and update rules to pass tests across all tracked ESLint versions.
1 parent 0922c4a commit 417571d

20 files changed

+130
-87
lines changed

src/compat.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { type TSESLint, type TSESTree, ASTUtils } from "@typescript-eslint/utils";
2+
3+
export type CompatContext =
4+
| {
5+
sourceCode: Readonly<TSESLint.SourceCode>;
6+
getSourceCode: undefined;
7+
getScope: undefined;
8+
markVariableAsUsed: undefined;
9+
}
10+
| {
11+
sourceCode?: Readonly<TSESLint.SourceCode>;
12+
getSourceCode: () => Readonly<TSESLint.SourceCode>;
13+
getScope: () => TSESLint.Scope.Scope;
14+
markVariableAsUsed: (name: string) => void;
15+
};
16+
17+
export function getSourceCode(context: CompatContext) {
18+
if (typeof context.getSourceCode === "function") {
19+
return context.getSourceCode();
20+
}
21+
return context.sourceCode;
22+
}
23+
24+
export function getScope(context: CompatContext, node: TSESTree.Node): TSESLint.Scope.Scope {
25+
const sourceCode = getSourceCode(context);
26+
27+
if (typeof sourceCode.getScope === "function") {
28+
return sourceCode.getScope(node); // >= v8, I think
29+
}
30+
if (typeof context.getScope === "function") {
31+
return context.getScope();
32+
}
33+
return context.sourceCode.getScope(node);
34+
}
35+
36+
export function findVariable(context: CompatContext, node: TSESTree.Identifier) {
37+
return ASTUtils.findVariable(getScope(context, node), node);
38+
}
39+
40+
export function markVariableAsUsed(context: CompatContext, name: string, node: TSESTree.Node) {
41+
if (typeof context.markVariableAsUsed === "function") {
42+
context.markVariableAsUsed(name);
43+
}
44+
getSourceCode(context).markVariableAsUsed(name, node);
45+
}

src/rules/components-return-once.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { TSESLint } from "@typescript-eslint/utils";
88

99
import { TSESTree as T, ESLintUtils } from "@typescript-eslint/utils";
1010
import { getFunctionName, type FunctionNode } from "../utils";
11+
import { getSourceCode } from "../compat";
1112

1213
const createRule = ESLintUtils.RuleCreator.withoutDocs;
1314

@@ -53,7 +54,7 @@ export default createRule({
5354
earlyReturns: Array<T.ReturnStatement>;
5455
}> = [];
5556
const putIntoJSX = (node: T.Node): string => {
56-
const text = context.getSourceCode().getText(node);
57+
const text = getSourceCode(context).getText(node);
5758
return node.type === "JSXElement" || node.type === "JSXFragment" ? text : `{${text}}`;
5859
};
5960
const currentFunction = () => functionStack[functionStack.length - 1];
@@ -92,7 +93,7 @@ export default createRule({
9293

9394
const argument = currentFunction().lastReturn?.argument;
9495
if (argument?.type === "ConditionalExpression") {
95-
const sourceCode = context.getSourceCode();
96+
const sourceCode = getSourceCode(context);
9697
context.report({
9798
node: argument.parent!,
9899
messageId: "noConditionalReturn",
@@ -154,7 +155,7 @@ export default createRule({
154155
});
155156
} else if (argument?.type === "LogicalExpression")
156157
if (argument.operator === "&&") {
157-
const sourceCode = context.getSourceCode();
158+
const sourceCode = getSourceCode(context);
158159
// we have a `return condition && expression`--put that in a <Show />
159160
context.report({
160161
node: argument,

src/rules/event-handlers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { TSESLint } from "@typescript-eslint/utils";
88

99
import { TSESTree as T, ESLintUtils, ASTUtils } from "@typescript-eslint/utils";
1010
import { isDOMElementName } from "../utils";
11+
import { getScope, getSourceCode } from "../compat";
1112

1213
const createRule = ESLintUtils.RuleCreator.withoutDocs;
1314
const { getStaticValue } = ASTUtils;
@@ -155,7 +156,7 @@ export default createRule<Options, MessageIds>({
155156
},
156157
defaultOptions: [],
157158
create(context) {
158-
const sourceCode = context.getSourceCode();
159+
const sourceCode = getSourceCode(context);
159160

160161
return {
161162
JSXAttribute(node) {
@@ -183,7 +184,7 @@ export default createRule<Options, MessageIds>({
183184
node.value?.type === "JSXExpressionContainer" &&
184185
node.value.expression.type !== "JSXEmptyExpression" &&
185186
node.value.expression.type !== "ArrayExpression" && // array syntax prevents inlining
186-
(staticValue = getStaticValue(node.value.expression, context.getScope())) !== null &&
187+
(staticValue = getStaticValue(node.value.expression, getScope(context, node))) !== null &&
187188
(typeof staticValue.value === "string" || typeof staticValue.value === "number")
188189
) {
189190
// One of the first things Solid (actually babel-plugin-dom-expressions) does with an

src/rules/imports.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TSESTree as T, TSESLint, ESLintUtils } from "@typescript-eslint/utils";
22
import { appendImports, insertImports, removeSpecifier } from "../utils";
3+
import { getSourceCode } from "../compat";
34

45
const createRule = ESLintUtils.RuleCreator.withoutDocs;
56

@@ -158,7 +159,7 @@ export default createRule({
158159
source: correctSource,
159160
},
160161
fix(fixer) {
161-
const sourceCode = context.getSourceCode();
162+
const sourceCode = getSourceCode(context);
162163
const program: T.Program = sourceCode.ast;
163164
const correctDeclaration = program.body.find(
164165
(node) =>

src/rules/jsx-no-script-url.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import type { TSESLint } from "@typescript-eslint/utils";
88

99
import { ESLintUtils, ASTUtils } from "@typescript-eslint/utils";
10+
import { getScope } from "../compat";
1011

1112
const createRule = ESLintUtils.RuleCreator.withoutDocs;
1213
const { getStaticValue }: { getStaticValue: any } = ASTUtils;
@@ -45,7 +46,7 @@ export default createRule({
4546
if (node.name.type === "JSXIdentifier" && node.value) {
4647
const link: { value: unknown } | null = getStaticValue(
4748
node.value.type === "JSXExpressionContainer" ? node.value.expression : node.value,
48-
context.getScope()
49+
getScope(context, node)
4950
);
5051
if (link && typeof link.value === "string" && isJavaScriptProtocol.test(link.value)) {
5152
context.report({

src/rules/jsx-no-undef.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { TSESLint } from "@typescript-eslint/utils";
88

99
import { TSESTree as T, ESLintUtils } from "@typescript-eslint/utils";
1010
import { isDOMElementName, formatList, appendImports, insertImports } from "../utils";
11+
import { getScope, getSourceCode } from "../compat";
1112

1213
const createRule = ESLintUtils.RuleCreator.withoutDocs;
1314

@@ -86,8 +87,8 @@ export default createRule<Options, MessageIds>({
8687
isCustomDirective,
8788
}: { isComponent?: boolean; isCustomDirective?: boolean } = {}
8889
) {
89-
let scope = context.getScope();
90-
const sourceCode = context.getSourceCode();
90+
let scope = getScope(context, node);
91+
const sourceCode = getSourceCode(context);
9192
const sourceType = sourceCode.ast.sourceType;
9293
const scopeUpperBound = !allowGlobals && sourceType === "module" ? "module" : "global";
9394
const variables = [...scope.variables];
@@ -192,7 +193,7 @@ export default createRule<Options, MessageIds>({
192193
source: SOURCE_MODULE,
193194
},
194195
fix: (fixer) => {
195-
return appendImports(fixer, context.getSourceCode(), importNode, missingComponents);
196+
return appendImports(fixer, getSourceCode(context), importNode, missingComponents);
196197
},
197198
});
198199
} else {
@@ -205,7 +206,7 @@ export default createRule<Options, MessageIds>({
205206
},
206207
fix: (fixer) => {
207208
// insert `import { missing, identifiers } from "solid-js"` at top of module
208-
return insertImports(fixer, context.getSourceCode(), "solid-js", missingComponents);
209+
return insertImports(fixer, getSourceCode(context), "solid-js", missingComponents);
209210
},
210211
});
211212
}

src/rules/jsx-uses-vars.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import type { TSESLint } from "@typescript-eslint/utils";
88

99
import { TSESTree as T, ESLintUtils } from "@typescript-eslint/utils";
10+
import { markVariableAsUsed } from "../compat";
1011

1112
const createRule = ESLintUtils.RuleCreator.withoutDocs;
1213

@@ -37,15 +38,15 @@ export default createRule({
3738
case "JSXNamespacedName": // <Foo:Bar>
3839
return;
3940
case "JSXIdentifier": // <Foo>
40-
context.markVariableAsUsed(node.name.name);
41+
markVariableAsUsed(context, node.name.name, node.name);
4142
break;
4243
case "JSXMemberExpression": // <Foo...Bar>
4344
parent = node.name.object;
4445
while (parent?.type === "JSXMemberExpression") {
4546
parent = parent.object;
4647
}
4748
if (parent.type === "JSXIdentifier") {
48-
context.markVariableAsUsed(parent.name);
49+
markVariableAsUsed(context, parent.name, parent);
4950
}
5051
break;
5152
}
@@ -57,7 +58,7 @@ export default createRule({
5758
node.namespace.name === "use" &&
5859
node.name?.type === "JSXIdentifier"
5960
) {
60-
context.markVariableAsUsed(node.name.name);
61+
markVariableAsUsed(context, node.name.name, node.name);
6162
}
6263
},
6364
};

src/rules/no-array-handlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default createRule({
4343
if (
4444
(isNamespacedHandler || isNormalEventHandler) &&
4545
node.value?.type === "JSXExpressionContainer" &&
46-
trace(node.value.expression, context.getScope()).type === "ArrayExpression"
46+
trace(node.value.expression, context).type === "ArrayExpression"
4747
) {
4848
// Warn if passed an array
4949
context.report({

src/rules/no-destructure.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TSESTree as T, TSESLint, ESLintUtils, ASTUtils } from "@typescript-eslint/utils";
22
import type { FunctionNode } from "../utils";
3+
import { getSourceCode } from "../compat";
34

45
const createRule = ESLintUtils.RuleCreator.withoutDocs;
56
const { getStringIfConstant } = ASTUtils;
@@ -123,7 +124,7 @@ export default createRule({
123124
yield fixer.replaceText(props, origProps);
124125
}
125126

126-
const sourceCode = context.getSourceCode();
127+
const sourceCode = getSourceCode(context);
127128

128129
const defaultsObjectString = () =>
129130
propertyInfo

src/rules/no-proxy-apis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default createRule({
5959
node.arguments
6060
.filter((arg) => {
6161
if (arg.type === "SpreadElement") return true;
62-
const traced = trace(arg, context.getScope());
62+
const traced = trace(arg, context);
6363
return (
6464
(traced.type === "Identifier" && !isPropsByName(traced.name)) ||
6565
isFunctionNode(traced)

0 commit comments

Comments
 (0)