1import { defineRule, type ESTree } from "@oxlint/plugins";
2
3const GLOBAL_OBJECTS = new Set(["globalThis", "window", "self"]);
4
5function isResizeObserverConstructor(node: ESTree.Node): boolean {
6 if (node.type === "Identifier") return node.name === "ResizeObserver";
7 if (node.type !== "MemberExpression" || node.object.type !== "Identifier") return false;
8 if (!GLOBAL_OBJECTS.has(node.object.name)) return false;
9 const { property } = node;
10 if (!node.computed && property.type === "Identifier") return property.name === "ResizeObserver";
11 return property.type === "Literal" && property.value === "ResizeObserver";
12}
13
14export default defineRule({
15 meta: {
16 type: "problem",
17 docs: {
18 description: "Disallow constructing ResizeObserver directly in web code; use observeResize.",
19 },
20 },
21 create(context) {
22 return {
23 NewExpression(node) {
24 if (!isResizeObserverConstructor(node.callee)) return;
25 context.report({
26 node: node.callee,
27 message:
28 "Use observeResize from ~/lib/observeResize. React commits state set in a raw ResizeObserver callback after the paint, so layout derived from the observed size lands one frame late.",
29 });
30 },
31 };
32 },
33});