FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

View File

@@ -0,0 +1,831 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const estraverse_1 = require("estraverse");
const utils_2 = require("../utils");
const { findVariable, getFunctionHeadLocation } = utils_1.ASTUtils;
const createRule = utils_1.ESLintUtils.RuleCreator.withoutDocs;
class ScopeStackItem {
node;
trackedScopes = [];
unnamedDerivedSignals = new Set();
hasJSX = false;
constructor(node) {
this.node = node;
}
}
class ScopeStack extends Array {
currentScope = () => this[this.length - 1];
parentScope = () => this[this.length - 2];
pushSignal(variable, declarationScope = this.currentScope().node) {
this.signals.push({
references: variable.references.filter((reference) => !reference.init),
variable,
declarationScope,
});
}
pushUniqueSignal(variable, declarationScope) {
const foundSignal = this.signals.find((s) => s.variable === variable);
if (!foundSignal) {
this.pushSignal(variable, declarationScope);
}
else {
foundSignal.declarationScope = this.findDeepestDeclarationScope(foundSignal.declarationScope, declarationScope);
}
}
pushProps(variable, declarationScope = this.currentScope().node) {
this.props.push({
references: variable.references.filter((reference) => !reference.init),
variable,
declarationScope,
});
}
syncCallbacks = new Set();
*consumeSignalReferencesInScope() {
yield* this.consumeReferencesInScope(this.signals);
this.signals = this.signals.filter((variable) => variable.references.length !== 0);
}
*consumePropsReferencesInScope() {
yield* this.consumeReferencesInScope(this.props);
this.props = this.props.filter((variable) => variable.references.length !== 0);
}
*consumeReferencesInScope(variables) {
for (const variable of variables) {
const { references } = variable;
const inScope = [], notInScope = [];
references.forEach((reference) => {
if (this.isReferenceInCurrentScope(reference)) {
inScope.push(reference);
}
else {
notInScope.push(reference);
}
});
yield* inScope.map((reference) => ({
reference,
declarationScope: variable.declarationScope,
}));
variable.references = notInScope;
}
}
findDeepestDeclarationScope = (a, b) => {
if (a === b)
return a;
for (let i = this.length - 1; i >= 0; i -= 1) {
const { node } = this[i];
if (a === node || b === node) {
return node;
}
}
throw new Error("This should never happen");
};
isReferenceInCurrentScope(reference) {
let parentFunction = (0, utils_2.findParent)(reference.identifier, utils_2.isProgramOrFunctionNode);
while ((0, utils_2.isFunctionNode)(parentFunction) && this.syncCallbacks.has(parentFunction)) {
parentFunction = (0, utils_2.findParent)(parentFunction, utils_2.isProgramOrFunctionNode);
}
return parentFunction === this.currentScope().node;
}
signals = [];
props = [];
}
const getNthDestructuredVar = (id, n, scope) => {
if (id?.type === "ArrayPattern") {
const el = id.elements[n];
if (el?.type === "Identifier") {
return findVariable(scope, el.name);
}
}
return null;
};
const getReturnedVar = (id, scope) => {
if (id.type === "Identifier") {
return findVariable(scope, id.name);
}
return null;
};
exports.default = createRule({
meta: {
type: "problem",
docs: {
description: "Enforce that reactivity (props, signals, memos, etc.) is properly used, so changes in those values will be tracked and update the view as expected.",
url: "https://github.com/solidjs-community/eslint-plugin-solid/blob/main/docs/reactivity.md",
},
schema: [
{
type: "object",
properties: {
customReactiveFunctions: {
description: "List of function names to consider as reactive functions (allow signals to be safely passed as arguments). In addition, any create* or use* functions are automatically included.",
type: "array",
items: {
type: "string",
},
default: [],
},
},
additionalProperties: false,
},
],
messages: {
noWrite: "The reactive variable '{{name}}' should not be reassigned or altered directly.",
untrackedReactive: "The reactive variable '{{name}}' should be used within JSX, a tracked scope (like createEffect), or inside an event handler function, or else changes will be ignored.",
expectedFunctionGotExpression: "The reactive variable '{{name}}' should be wrapped in a function for reactivity. This includes event handler bindings on native elements, which are not reactive like other JSX props.",
badSignal: "The reactive variable '{{name}}' should be called as a function when used in {{where}}.",
badUnnamedDerivedSignal: "This function should be passed to a tracked scope (like createEffect) or an event handler because it contains reactivity, or else changes will be ignored.",
shouldDestructure: "For proper analysis, array destructuring should be used to capture the {{nth}}result of this function call.",
shouldAssign: "For proper analysis, a variable should be used to capture the result of this function call.",
noAsyncTrackedScope: "This tracked scope should not be async. Solid's reactivity only tracks synchronously.",
},
},
defaultOptions: [
{
customReactiveFunctions: [],
},
],
create(context, [options]) {
const warnShouldDestructure = (node, nth) => context.report({
node,
messageId: "shouldDestructure",
data: nth ? { nth: nth + " " } : undefined,
});
const warnShouldAssign = (node) => context.report({ node, messageId: "shouldAssign" });
const sourceCode = context.getSourceCode();
const scopeStack = new ScopeStack();
const { currentScope, parentScope } = scopeStack;
const { matchImport, handleImportDeclaration } = (0, utils_2.trackImports)();
const markPropsOnCondition = (node, cb) => {
if (node.params.length === 1 &&
node.params[0].type === "Identifier" &&
node.parent?.type !== "JSXExpressionContainer" &&
node.parent?.type !== "TemplateLiteral" &&
cb(node.params[0])) {
const propsParam = findVariable(context.getScope(), node.params[0]);
if (propsParam) {
scopeStack.pushProps(propsParam, node);
}
}
};
const onFunctionEnter = (node) => {
if ((0, utils_2.isFunctionNode)(node)) {
if (scopeStack.syncCallbacks.has(node)) {
return;
}
markPropsOnCondition(node, (props) => (0, utils_2.isPropsByName)(props.name));
}
scopeStack.push(new ScopeStackItem(node));
};
const matchTrackedScope = (trackedScope, node) => {
switch (trackedScope.expect) {
case "function":
case "called-function":
return node === trackedScope.node;
case "expression":
return Boolean((0, utils_2.findInScope)(node, currentScope().node, (node) => node === trackedScope.node));
}
};
const handleTrackedScopes = (identifier, declarationScope) => {
const currentScopeNode = currentScope().node;
if (!currentScope().trackedScopes.find((trackedScope) => matchTrackedScope(trackedScope, identifier))) {
const matchedExpression = currentScope().trackedScopes.find((trackedScope) => matchTrackedScope({ ...trackedScope, expect: "expression" }, identifier));
if (declarationScope === currentScopeNode) {
let parentMemberExpression = null;
if (identifier.parent?.type === "MemberExpression") {
parentMemberExpression = identifier.parent;
while (parentMemberExpression.parent?.type === "MemberExpression") {
parentMemberExpression = parentMemberExpression.parent;
}
}
const parentCallExpression = identifier.parent?.type === "CallExpression" ? identifier.parent : null;
context.report({
node: parentMemberExpression ?? parentCallExpression ?? identifier,
messageId: matchedExpression ? "expectedFunctionGotExpression" : "untrackedReactive",
data: {
name: parentMemberExpression
? sourceCode.getText(parentMemberExpression)
: identifier.name,
},
});
}
else {
if (!parentScope() || !(0, utils_2.isFunctionNode)(currentScopeNode)) {
throw new Error("this shouldn't happen!");
}
const pushUnnamedDerivedSignal = () => (parentScope().unnamedDerivedSignals ??= new Set()).add(currentScopeNode);
if (currentScopeNode.type === "FunctionDeclaration") {
const functionVariable = sourceCode.scopeManager?.getDeclaredVariables(currentScopeNode)?.[0];
if (functionVariable) {
scopeStack.pushUniqueSignal(functionVariable, declarationScope);
}
else {
pushUnnamedDerivedSignal();
}
}
else if (currentScopeNode.parent?.type === "VariableDeclarator") {
const declarator = currentScopeNode.parent;
const functionVariable = sourceCode.scopeManager?.getDeclaredVariables(declarator)?.[0];
if (functionVariable) {
scopeStack.pushUniqueSignal(functionVariable, declarationScope);
}
else {
pushUnnamedDerivedSignal();
}
}
else if (currentScopeNode.parent?.type === "Property") {
}
else {
pushUnnamedDerivedSignal();
}
}
}
};
const onFunctionExit = (currentScopeNode) => {
if ((0, utils_2.isFunctionNode)(currentScopeNode)) {
markPropsOnCondition(currentScopeNode, (props) => {
if (!(0, utils_2.isPropsByName)(props.name) &&
currentScope().hasJSX) {
const functionName = (0, utils_2.getFunctionName)(currentScopeNode);
if (functionName && !/^[a-z]/.test(functionName))
return true;
}
return false;
});
}
if ((0, utils_2.isFunctionNode)(currentScopeNode) && scopeStack.syncCallbacks.has(currentScopeNode)) {
return;
}
for (const { reference, declarationScope } of scopeStack.consumeSignalReferencesInScope()) {
const identifier = reference.identifier;
if (reference.isWrite()) {
context.report({
node: identifier,
messageId: "noWrite",
data: {
name: identifier.name,
},
});
}
else if (identifier.type === "Identifier") {
const reportBadSignal = (where) => context.report({
node: identifier,
messageId: "badSignal",
data: { name: identifier.name, where },
});
if (identifier.parent?.type === "CallExpression" ||
(identifier.parent?.type === "ArrayExpression" &&
identifier.parent.parent?.type === "CallExpression")) {
handleTrackedScopes(identifier, declarationScope);
}
else if (identifier.parent?.type === "TemplateLiteral") {
reportBadSignal("template literals");
}
else if (identifier.parent?.type === "BinaryExpression" &&
[
"<",
"<=",
">",
">=",
"<<",
">>",
">>>",
"+",
"-",
"*",
"/",
"%",
"**",
"|",
"^",
"&",
"in",
].includes(identifier.parent.operator)) {
reportBadSignal("arithmetic or comparisons");
}
else if (identifier.parent?.type === "UnaryExpression" &&
["-", "+", "~"].includes(identifier.parent.operator)) {
reportBadSignal("unary expressions");
}
else if (identifier.parent?.type === "MemberExpression" &&
identifier.parent.computed &&
identifier.parent.property === identifier) {
reportBadSignal("property accesses");
}
else if (identifier.parent?.type === "JSXExpressionContainer" &&
!currentScope().trackedScopes.find((trackedScope) => trackedScope.node === identifier &&
(trackedScope.expect === "function" || trackedScope.expect === "called-function"))) {
const elementOrAttribute = identifier.parent.parent;
if ((0, utils_2.isJSXElementOrFragment)(elementOrAttribute) ||
(elementOrAttribute?.type === "JSXAttribute" &&
elementOrAttribute.parent?.type === "JSXOpeningElement" &&
elementOrAttribute.parent.name.type === "JSXIdentifier" &&
(0, utils_2.isDOMElementName)(elementOrAttribute.parent.name.name))) {
reportBadSignal("JSX");
}
}
}
}
for (const { reference, declarationScope } of scopeStack.consumePropsReferencesInScope()) {
const identifier = reference.identifier;
if (reference.isWrite()) {
context.report({
node: identifier,
messageId: "noWrite",
data: {
name: identifier.name,
},
});
}
else if (identifier.parent?.type === "MemberExpression" &&
identifier.parent.object === identifier) {
const { parent } = identifier;
if (parent.parent?.type === "AssignmentExpression" && parent.parent.left === parent) {
context.report({
node: identifier,
messageId: "noWrite",
data: {
name: identifier.name,
},
});
}
else if (parent.property.type === "Identifier" &&
/^(?:initial|default|static)[A-Z]/.test(parent.property.name)) {
}
else {
handleTrackedScopes(identifier, declarationScope);
}
}
else if (identifier.parent?.type === "AssignmentExpression" ||
identifier.parent?.type === "VariableDeclarator") {
context.report({
node: identifier,
messageId: "untrackedReactive",
data: { name: identifier.name },
});
}
}
const { unnamedDerivedSignals } = currentScope();
if (unnamedDerivedSignals) {
for (const node of unnamedDerivedSignals) {
if (!currentScope().trackedScopes.find((trackedScope) => matchTrackedScope(trackedScope, node))) {
context.report({
loc: getFunctionHeadLocation(node, sourceCode),
messageId: "badUnnamedDerivedSignal",
});
}
}
}
scopeStack.pop();
};
const checkForSyncCallbacks = (node) => {
if (node.arguments.length === 1 &&
(0, utils_2.isFunctionNode)(node.arguments[0]) &&
!node.arguments[0].async) {
if (node.callee.type === "Identifier" &&
matchImport(["batch", "produce"], node.callee.name)) {
scopeStack.syncCallbacks.add(node.arguments[0]);
}
else if (node.callee.type === "MemberExpression" &&
!node.callee.computed &&
node.callee.object.type !== "ObjectExpression" &&
/^(?:forEach|map|flatMap|reduce|reduceRight|find|findIndex|filter|every|some)$/.test(node.callee.property.name)) {
scopeStack.syncCallbacks.add(node.arguments[0]);
}
}
if (node.callee.type === "Identifier") {
if (matchImport(["createSignal", "createStore"], node.callee.name) &&
node.parent?.type === "VariableDeclarator") {
const setter = getNthDestructuredVar(node.parent.id, 1, context.getScope());
if (setter) {
for (const reference of setter.references) {
const { identifier } = reference;
if (!reference.init &&
reference.isRead() &&
identifier.parent?.type === "CallExpression") {
for (const arg of identifier.parent.arguments) {
if ((0, utils_2.isFunctionNode)(arg) && !arg.async) {
scopeStack.syncCallbacks.add(arg);
}
}
}
}
}
}
else if (matchImport(["mapArray", "indexArray"], node.callee.name)) {
const arg1 = node.arguments[1];
if ((0, utils_2.isFunctionNode)(arg1)) {
scopeStack.syncCallbacks.add(arg1);
}
}
}
if ((0, utils_2.isFunctionNode)(node.callee)) {
scopeStack.syncCallbacks.add(node.callee);
}
};
const checkForReactiveAssignment = (id, init) => {
init = (0, utils_2.ignoreTransparentWrappers)(init);
if (init.type === "CallExpression" && init.callee.type === "Identifier") {
const { callee } = init;
if (matchImport(["createSignal", "useTransition"], callee.name)) {
const signal = id && getNthDestructuredVar(id, 0, context.getScope());
if (signal) {
scopeStack.pushSignal(signal, currentScope().node);
}
else {
warnShouldDestructure(id ?? init, "first");
}
}
else if (matchImport(["createMemo", "createSelector"], callee.name)) {
const memo = id && getReturnedVar(id, context.getScope());
if (memo) {
scopeStack.pushSignal(memo, currentScope().node);
}
else {
warnShouldAssign(id ?? init);
}
}
else if (matchImport("createStore", callee.name)) {
const store = id && getNthDestructuredVar(id, 0, context.getScope());
if (store) {
scopeStack.pushProps(store, currentScope().node);
}
else {
warnShouldDestructure(id ?? init, "first");
}
}
else if (matchImport("mergeProps", callee.name)) {
const merged = id && getReturnedVar(id, context.getScope());
if (merged) {
scopeStack.pushProps(merged, currentScope().node);
}
else {
warnShouldAssign(id ?? init);
}
}
else if (matchImport("splitProps", callee.name)) {
if (id?.type === "ArrayPattern") {
const vars = id.elements
.map((_, i) => getNthDestructuredVar(id, i, context.getScope()))
.filter(Boolean);
if (vars.length === 0) {
warnShouldDestructure(id);
}
else {
vars.forEach((variable) => {
scopeStack.pushProps(variable, currentScope().node);
});
}
}
else {
const vars = id && getReturnedVar(id, context.getScope());
if (vars) {
scopeStack.pushProps(vars, currentScope().node);
}
}
}
else if (matchImport("createResource", callee.name)) {
const resourceReturn = id && getNthDestructuredVar(id, 0, context.getScope());
if (resourceReturn) {
scopeStack.pushProps(resourceReturn, currentScope().node);
}
}
else if (matchImport("createMutable", callee.name)) {
const mutable = id && getReturnedVar(id, context.getScope());
if (mutable) {
scopeStack.pushProps(mutable, currentScope().node);
}
}
else if (matchImport("mapArray", callee.name)) {
const arg1 = init.arguments[1];
if ((0, utils_2.isFunctionNode)(arg1) &&
arg1.params.length >= 2 &&
arg1.params[1].type === "Identifier") {
const indexSignal = findVariable(context.getScope(), arg1.params[1]);
if (indexSignal) {
scopeStack.pushSignal(indexSignal);
}
}
}
else if (matchImport("indexArray", callee.name)) {
const arg1 = init.arguments[1];
if ((0, utils_2.isFunctionNode)(arg1) &&
arg1.params.length >= 1 &&
arg1.params[0].type === "Identifier") {
const valueSignal = findVariable(context.getScope(), arg1.params[0]);
if (valueSignal) {
scopeStack.pushSignal(valueSignal);
}
}
}
}
};
const checkForTrackedScopes = (node) => {
const pushTrackedScope = (node, expect) => {
currentScope().trackedScopes.push({ node, expect });
if (expect !== "called-function" && (0, utils_2.isFunctionNode)(node) && node.async) {
context.report({
node,
messageId: "noAsyncTrackedScope",
});
}
};
const permissivelyTrackNode = (node) => {
(0, estraverse_1.traverse)(node, {
enter(cn) {
const childNode = cn;
const traced = (0, utils_2.trace)(childNode, context.getScope());
if ((0, utils_2.isFunctionNode)(traced) ||
(traced.type === "Identifier" &&
traced.parent.type !== "MemberExpression" &&
!(traced.parent.type === "CallExpression" && traced.parent.callee === traced))) {
pushTrackedScope(childNode, "called-function");
this.skip();
}
},
});
};
if (node.type === "JSXExpressionContainer") {
if (node.parent?.type === "JSXAttribute" &&
/^on[:A-Z]/.test(sourceCode.getText(node.parent.name)) &&
node.parent.parent?.type === "JSXOpeningElement" &&
node.parent.parent.name.type === "JSXIdentifier" &&
(0, utils_2.isDOMElementName)(node.parent.parent.name.name)) {
pushTrackedScope(node.expression, "called-function");
}
else if (node.parent?.type === "JSXAttribute" &&
node.parent.name.type === "JSXNamespacedName" &&
node.parent.name.namespace.name === "use" &&
(0, utils_2.isFunctionNode)(node.expression)) {
pushTrackedScope(node.expression, "called-function");
}
else if (node.parent?.type === "JSXAttribute" &&
node.parent.name.name === "value" &&
node.parent.parent?.type === "JSXOpeningElement" &&
((node.parent.parent.name.type === "JSXIdentifier" &&
node.parent.parent.name.name.endsWith("Provider")) ||
(node.parent.parent.name.type === "JSXMemberExpression" &&
node.parent.parent.name.property.name === "Provider"))) {
}
else if (node.parent?.type === "JSXAttribute" &&
node.parent.name?.type === "JSXIdentifier" &&
/^static[A-Z]/.test(node.parent.name.name) &&
node.parent.parent?.type === "JSXOpeningElement" &&
node.parent.parent.name.type === "JSXIdentifier" &&
!(0, utils_2.isDOMElementName)(node.parent.parent.name.name)) {
}
else if (node.parent?.type === "JSXAttribute" &&
node.parent.name.name === "ref" &&
(0, utils_2.isFunctionNode)(node.expression)) {
pushTrackedScope(node.expression, "called-function");
}
else if ((0, utils_2.isJSXElementOrFragment)(node.parent) && (0, utils_2.isFunctionNode)(node.expression)) {
pushTrackedScope(node.expression, "function");
}
else {
pushTrackedScope(node.expression, "expression");
}
}
else if (node.type === "JSXSpreadAttribute") {
pushTrackedScope(node.argument, "expression");
}
else if (node.type === "NewExpression") {
const { callee, arguments: { 0: arg0 }, } = node;
if (callee.type === "Identifier" &&
arg0 &&
[
"IntersectionObserver",
"MutationObserver",
"PerformanceObserver",
"ReportingObserver",
"ResizeObserver",
].includes(callee.name)) {
pushTrackedScope(arg0, "called-function");
}
}
else if (node.type === "CallExpression") {
if (node.callee.type === "Identifier") {
const { callee, arguments: { 0: arg0, 1: arg1 }, } = node;
if (matchImport([
"createMemo",
"children",
"createEffect",
"createRenderEffect",
"createDeferred",
"createComputed",
"createSelector",
"untrack",
"mapArray",
"indexArray",
"observable",
], callee.name) ||
(matchImport("createResource", callee.name) && node.arguments.length >= 2)) {
pushTrackedScope(arg0, "function");
}
else if (matchImport(["onMount", "onCleanup", "onError"], callee.name) ||
[
"setInterval",
"setTimeout",
"setImmediate",
"requestAnimationFrame",
"requestIdleCallback",
].includes(callee.name)) {
pushTrackedScope(arg0, "called-function");
}
else if (matchImport("on", callee.name)) {
if (arg0) {
if (arg0.type === "ArrayExpression") {
arg0.elements.forEach((element) => {
if (element && element?.type !== "SpreadElement") {
pushTrackedScope(element, "function");
}
});
}
else {
pushTrackedScope(arg0, "function");
}
}
if (arg1) {
pushTrackedScope(arg1, "called-function");
}
}
else if (matchImport("createStore", callee.name) && arg0?.type === "ObjectExpression") {
for (const property of arg0.properties) {
if (property.type === "Property" &&
property.kind === "get" &&
(0, utils_2.isFunctionNode)(property.value)) {
pushTrackedScope(property.value, "function");
}
}
}
else if (matchImport("runWithOwner", callee.name)) {
if (arg1) {
let isTrackedScope = true;
const owner = arg0.type === "Identifier" && findVariable(context.getScope(), arg0);
if (owner) {
const decl = owner.defs[0];
if (decl &&
decl.node.type === "VariableDeclarator" &&
decl.node.init?.type === "CallExpression" &&
decl.node.init.callee.type === "Identifier" &&
matchImport("getOwner", decl.node.init.callee.name)) {
const ownerFunction = (0, utils_2.findParent)(decl.node, utils_2.isProgramOrFunctionNode);
const scopeStackIndex = scopeStack.findIndex(({ node }) => ownerFunction === node);
if ((scopeStackIndex >= 1 &&
!scopeStack[scopeStackIndex - 1].trackedScopes.some((trackedScope) => trackedScope.expect === "function" && trackedScope.node === ownerFunction)) ||
scopeStackIndex === 0) {
isTrackedScope = false;
}
}
}
if (isTrackedScope) {
pushTrackedScope(arg1, "function");
}
}
}
else if (/^(?:use|create)[A-Z]/.test(callee.name) ||
options.customReactiveFunctions.includes(callee.name)) {
for (const arg of node.arguments) {
permissivelyTrackNode(arg);
}
}
}
else if (node.callee.type === "MemberExpression") {
const { property } = node.callee;
if (property.type === "Identifier" &&
property.name === "addEventListener" &&
node.arguments.length >= 2) {
pushTrackedScope(node.arguments[1], "called-function");
}
else if (property.type === "Identifier" &&
(/^(?:use|create)[A-Z]/.test(property.name) ||
options.customReactiveFunctions.includes(property.name))) {
for (const arg of node.arguments) {
permissivelyTrackNode(arg);
}
}
}
}
else if (node.type === "VariableDeclarator") {
if (node.init?.type === "CallExpression" && node.init.callee.type === "Identifier") {
if (matchImport(["createReactive", "createReaction"], node.init.callee.name)) {
const track = getReturnedVar(node.id, context.getScope());
if (track) {
for (const reference of track.references) {
if (!reference.init &&
reference.isReadOnly() &&
reference.identifier.parent?.type === "CallExpression" &&
reference.identifier.parent.callee === reference.identifier) {
const arg0 = reference.identifier.parent.arguments[0];
arg0 && pushTrackedScope(arg0, "function");
}
}
}
if ((0, utils_2.isFunctionNode)(node.init.arguments[0])) {
pushTrackedScope(node.init.arguments[0], "called-function");
}
}
}
}
else if (node.type === "AssignmentExpression") {
if (node.left.type === "MemberExpression" &&
node.left.property.type === "Identifier" &&
(0, utils_2.isFunctionNode)(node.right) &&
/^on[a-z]+$/.test(node.left.property.name)) {
pushTrackedScope(node.right, "called-function");
}
}
else if (node.type === "TaggedTemplateExpression") {
for (const expression of node.quasi.expressions) {
if ((0, utils_2.isFunctionNode)(expression)) {
pushTrackedScope(expression, "called-function");
for (const param of expression.params) {
if (param.type === "Identifier" && (0, utils_2.isPropsByName)(param.name)) {
const variable = findVariable(context.getScope(), param);
if (variable)
scopeStack.pushProps(variable, currentScope().node);
}
}
}
}
}
};
return {
ImportDeclaration: handleImportDeclaration,
JSXExpressionContainer(node) {
checkForTrackedScopes(node);
},
JSXSpreadAttribute(node) {
checkForTrackedScopes(node);
},
CallExpression(node) {
checkForTrackedScopes(node);
checkForSyncCallbacks(node);
const parent = node.parent && (0, utils_2.ignoreTransparentWrappers)(node.parent, true);
if (parent?.type !== "AssignmentExpression" && parent?.type !== "VariableDeclarator") {
checkForReactiveAssignment(null, node);
}
},
NewExpression(node) {
checkForTrackedScopes(node);
},
VariableDeclarator(node) {
if (node.init) {
checkForReactiveAssignment(node.id, node.init);
checkForTrackedScopes(node);
}
},
AssignmentExpression(node) {
if (node.left.type !== "MemberExpression") {
checkForReactiveAssignment(node.left, node.right);
}
checkForTrackedScopes(node);
},
TaggedTemplateExpression(node) {
checkForTrackedScopes(node);
},
"JSXElement > JSXExpressionContainer > :function"(node) {
if ((0, utils_2.isFunctionNode)(node) &&
node.parent?.type === "JSXExpressionContainer" &&
node.parent.parent?.type === "JSXElement") {
const element = node.parent.parent;
if (element.openingElement.name.type === "JSXIdentifier") {
const tagName = element.openingElement.name.name;
if (matchImport("For", tagName) &&
node.params.length === 2 &&
node.params[1].type === "Identifier") {
const index = findVariable(context.getScope(), node.params[1]);
if (index) {
scopeStack.pushSignal(index, currentScope().node);
}
}
else if (matchImport("Index", tagName) &&
node.params.length >= 1 &&
node.params[0].type === "Identifier") {
const item = findVariable(context.getScope(), node.params[0]);
if (item) {
scopeStack.pushSignal(item, currentScope().node);
}
}
}
}
},
FunctionExpression: onFunctionEnter,
ArrowFunctionExpression: onFunctionEnter,
FunctionDeclaration: onFunctionEnter,
Program: onFunctionEnter,
"FunctionExpression:exit": onFunctionExit,
"ArrowFunctionExpression:exit": onFunctionExit,
"FunctionDeclaration:exit": onFunctionExit,
"Program:exit": onFunctionExit,
JSXElement() {
if (scopeStack.length) {
currentScope().hasJSX = true;
}
},
JSXFragment() {
if (scopeStack.length) {
currentScope().hasJSX = true;
}
},
};
},
});