Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,20 @@ ruleTester.run(RULE_NAME, rule, {
return <Context.Provider value={foo}></Context.Provider>
}
`,
tsx`
function App() {
"use memo";
const foo = {}
return <Context.Provider value={foo}></Context.Provider>;
}
`,
tsx`
"use memo";
function App() {
const foo = {}
return <Context.Provider value={foo}></Context.Provider>;
}
`,
tsx`
function App() {
const foo = useMemo(() => [], [])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export default createRule<[], MessageID>({
});

export function create(context: RuleContext<MessageID, []>): RuleListener {
// If "use memo" directive is present in the file, skip analysis
if (AST.getProgramDirectives(context.sourceCode.ast).some((d) => d.value === "use memo")) return {};

const { version } = getSettingsFromContext(context);
const isReact18OrBelow = compare(version, "19.0.0", "<");
const { ctx, visitor } = useComponentCollector(context);
Expand Down Expand Up @@ -69,8 +72,9 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
getOrElseUpdate(constructions, functionEntry.node, () => []).push(construction);
},
"Program:exit"(program) {
for (const { node: component } of ctx.getAllComponents(program)) {
for (const { node: component, directives } of ctx.getAllComponents(program)) {
for (const construction of constructions.get(component) ?? []) {
if (directives.some((d) => d.value === "use memo")) return;
const { kind, node: constructionNode } = construction;
const suggestion = kind === "function"
? "Consider wrapping it in a useCallback hook."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,24 @@ ruleTester.run(RULE_NAME, rule, {
],
valid: [
...allValid,
tsx`
"use memo";
const App = ({
a = {},
b = ['one', 'two'],
c = /regex/i,
d = () => {},
e = function() {},
f = class {},
g = new Thing(),
h = <Thing />,
i = Symbol('foo'),
j = unknownFunction(),
k = window.name
}) => {
return null
}
`,
{
code: tsx`
function MyComponent({ position = new Vector3(0, 0, 0) }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ function extractIdentifier(node: TSESTree.Node): string | null {
}

export function create(context: RuleContext<MessageID, Options>, [options]: Options): RuleListener {
// If "use memo" directive is present in the file, skip analysis
if (AST.getProgramDirectives(context.sourceCode.ast).some((d) => d.value === "use memo")) return {};

const { ctx, visitor } = useComponentCollector(context);
const declarators = new WeakMap<AST.TSESTreeFunction, AST.ObjectDestructuringVariableDeclarator[]>();
const { safeDefaultProps = [] } = options;
Expand Down
1 change: 1 addition & 0 deletions packages/utilities/ast/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from "./is";
export * from "./literal";
export * from "./misc";
export * from "./process-env-node-env";
export * from "./program-directives";
export * from "./property-name";
export * from "./selectors";
export * from "./traverse";
Expand Down
20 changes: 20 additions & 0 deletions packages/utilities/ast/src/program-directives.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AST_NODE_TYPES as T, type TSESTree } from "@typescript-eslint/types";

import { getUnderlyingExpression } from "./expression-base";
import { isLiteral } from "./literal";

/**
* Get all directive string literals from a program node
* @param node The program AST node
* @returns The array of directive string literals (e.g., "use strict")
*/
export function getProgramDirectives(node: TSESTree.Program): TSESTree.StringLiteral[] {
const directives: TSESTree.StringLiteral[] = [];
for (const stmt of node.body) {
if (stmt.type !== T.ExpressionStatement) continue;
const expr = getUnderlyingExpression(stmt.expression);
if (!isLiteral(expr, "string")) continue;
directives.push(expr);
}
return directives;
}
Loading