From 94e47d716aa5cbc1b2d33242b70b951c5bfc1fb5 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Fri, 11 Apr 2025 14:28:55 +0800 Subject: [PATCH 01/11] fix(repl): improve deepUpdate logic for array item matching --- packages/docs/src/repl/repl-output-update.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/docs/src/repl/repl-output-update.ts b/packages/docs/src/repl/repl-output-update.ts index 408f785c58b..d8e544d61a9 100644 --- a/packages/docs/src/repl/repl-output-update.ts +++ b/packages/docs/src/repl/repl-output-update.ts @@ -13,7 +13,10 @@ const deepUpdate = (prev: any, next: any) => { } if (Array.isArray(prev)) { for (const item of prev) { - if (!next.includes(item)) { + // can't use Object as a matcher + // because it will be a different object + // so we need to use the path or code + if (!next.map((item: any) => item.path || item.code).includes(item.path || item.code)) { prev.splice(prev.indexOf(item), 1); } } @@ -33,6 +36,7 @@ export const updateReplOutput = async (store: ReplStore, result: ReplResult) => if (store.html !== result.html) { store.html = result.html; } + deepUpdate(store.transformedModules, result.transformedModules); deepUpdate(store.clientBundles, result.clientBundles); deepUpdate(store.ssrModules, result.ssrModules); From 69dd8fb148cf4889c57cbf4aab0738094ee29ca5 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Tue, 22 Apr 2025 19:36:12 +0800 Subject: [PATCH 02/11] fix it --- packages/docs/src/repl/repl-output-update.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/docs/src/repl/repl-output-update.ts b/packages/docs/src/repl/repl-output-update.ts index d8e544d61a9..4f288d71881 100644 --- a/packages/docs/src/repl/repl-output-update.ts +++ b/packages/docs/src/repl/repl-output-update.ts @@ -16,7 +16,10 @@ const deepUpdate = (prev: any, next: any) => { // can't use Object as a matcher // because it will be a different object // so we need to use the path or code - if (!next.map((item: any) => item.path || item.code).includes(item.path || item.code)) { + + if ( + next.some((nextItem: any) => (nextItem.path || nextItem.code) === (item.path || item.code)) + ) { prev.splice(prev.indexOf(item), 1); } } From e729fdb71ba0cb1415b835672e43a9c08fec9d31 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Fri, 6 Jun 2025 17:31:20 +0800 Subject: [PATCH 03/11] chore: update devDependencies and configurations --- starters/apps/base/package.json | 2 +- starters/apps/library/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/starters/apps/base/package.json b/starters/apps/base/package.json index 9c896261445..aabd4267aa1 100644 --- a/starters/apps/base/package.json +++ b/starters/apps/base/package.json @@ -27,7 +27,7 @@ "prettier": "latest", "typescript": "latest", "typescript-plugin-css-modules": "latest", - "vite": "^4.5.2", + "vite": "^6.2.6", "vite-tsconfig-paths": "^4.2.1" }, "engines": { diff --git a/starters/apps/library/package.json b/starters/apps/library/package.json index a33862e07fc..1836f4ab2b8 100644 --- a/starters/apps/library/package.json +++ b/starters/apps/library/package.json @@ -43,7 +43,7 @@ "np": "^8.0.4", "prettier": "latest", "typescript": "latest", - "vite": "^4.5.2", + "vite": "6.2.6", "vite-tsconfig-paths": "^4.2.1" }, "__qwik__": { From bfd176bc8deb65fb433206bb2b281b6a04b1d07e Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Fri, 6 Jun 2025 18:27:03 +0800 Subject: [PATCH 04/11] fix: init a test --- .../valid-scope-use-return-object.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx new file mode 100644 index 00000000000..7e00e4e1e6a --- /dev/null +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx @@ -0,0 +1,25 @@ +import { component$, useTask$, isBrowser, useSignal } from '@qwik.dev/core'; + +export default component$(() => { + const state = useSignal(true); + process.env; + useTask$(({ track }) => { + if (isBrowser) { + track(() => { + if (state.value) { + const values = [ + { + relativePath: '', + name: 'index', + type: '', + path: '', + isSymbolicLink: false, + children: undefined, + }, + ]; + } + }); + } + }); + return <>; +}); From d206cf60cf434fc3b237107a7ed5eca490dc4635 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 9 Jun 2025 15:45:03 +0800 Subject: [PATCH 05/11] =?UTF-8?q?FIX=EF=BC=9A=20fix=20edge=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/eslint-plugin-qwik/src/scope-use-task.ts | 12 ++++++++++-- .../tests/scope-use-task/invalid-scope-use-track.tsx | 3 ++- .../scope-use-task/valid-scope-use-function.tsx | 1 + .../scope-use-task/valid-scope-use-return-object.tsx | 1 - 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin-qwik/src/scope-use-task.ts b/packages/eslint-plugin-qwik/src/scope-use-task.ts index 9e616f29d80..8977e3e434c 100644 --- a/packages/eslint-plugin-qwik/src/scope-use-task.ts +++ b/packages/eslint-plugin-qwik/src/scope-use-task.ts @@ -1,6 +1,7 @@ import { Rule } from 'eslint'; import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; // For Scope types +import type { Identifier } from 'estree'; const ISSERVER = 'isServer'; // Helper function: checks if a node is a descendant of another node function isNodeDescendantOf(descendantNode, ancestorNode): boolean { @@ -93,7 +94,6 @@ export const scopeUseTask: Rule.RuleModule = { */ function isApiUsageGuarded(apiOrCallNode, functionContextNode): boolean { let currentParentNode: TSESTree.Node | undefined = apiOrCallNode.parent; - while ( currentParentNode && currentParentNode !== functionContextNode.body && @@ -322,7 +322,15 @@ export const scopeUseTask: Rule.RuleModule = { } if (forbiddenApis.has(node.name)) { - if (isIdentifierShadowedByDeclaration(node)) { + const isDirectIdentifier = + node.parent.type === 'VariableDeclarator' && + (node?.parent?.init as Identifier)?.name === node.name; + // node'api usually be invoked as a member expression (e.g., process.env) + // or as a direct identifier (e.g., process). + if ( + isIdentifierShadowedByDeclaration(node) || + (node.parent.type !== 'MemberExpression' && node.parent.type !== 'VariableDeclarator') + ) { return; } if (!isApiUsageGuarded(node, currentUseTaskFunction)) { diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-track.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-track.tsx index e6827caa40e..2870f2738ec 100644 --- a/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-track.tsx +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-track.tsx @@ -1,5 +1,5 @@ // Expect error: { "messageId": "unsafeApiUsage" } - +// Expect error: { "messageId": "unsafeApiUsage" } import { component$, useSignal, useTask$ } from '@qwik.dev/core'; export default component$(() => { @@ -7,6 +7,7 @@ export default component$(() => { useTask$(({ track }) => { track(() => { process.env; + const m = process; return s.value; }); }); diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-function.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-function.tsx index d10e15de7d8..66562db2228 100644 --- a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-function.tsx +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-function.tsx @@ -5,6 +5,7 @@ export default component$(() => { function foo() { if (isServer) { process.env; + const m = process; } } foo(); diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx index 7e00e4e1e6a..c0e4a98f926 100644 --- a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx @@ -2,7 +2,6 @@ import { component$, useTask$, isBrowser, useSignal } from '@qwik.dev/core'; export default component$(() => { const state = useSignal(true); - process.env; useTask$(({ track }) => { if (isBrowser) { track(() => { From 07740ac1e2dcb10ac76cd7c34e2f143bfab5cefb Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 9 Jun 2025 15:47:21 +0800 Subject: [PATCH 06/11] revert code --- starters/apps/base/package.json | 2 +- starters/apps/library/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/starters/apps/base/package.json b/starters/apps/base/package.json index aabd4267aa1..9c896261445 100644 --- a/starters/apps/base/package.json +++ b/starters/apps/base/package.json @@ -27,7 +27,7 @@ "prettier": "latest", "typescript": "latest", "typescript-plugin-css-modules": "latest", - "vite": "^6.2.6", + "vite": "^4.5.2", "vite-tsconfig-paths": "^4.2.1" }, "engines": { diff --git a/starters/apps/library/package.json b/starters/apps/library/package.json index 1836f4ab2b8..a33862e07fc 100644 --- a/starters/apps/library/package.json +++ b/starters/apps/library/package.json @@ -43,7 +43,7 @@ "np": "^8.0.4", "prettier": "latest", "typescript": "latest", - "vite": "6.2.6", + "vite": "^4.5.2", "vite-tsconfig-paths": "^4.2.1" }, "__qwik__": { From 63cdda07ace99de1af603cb71a593c1db0f75fac Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Thu, 12 Jun 2025 14:45:17 +0800 Subject: [PATCH 07/11] add change --- .changeset/loud-mammals-dress.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/loud-mammals-dress.md diff --git a/.changeset/loud-mammals-dress.md b/.changeset/loud-mammals-dress.md new file mode 100644 index 00000000000..7a5ffeb2ce4 --- /dev/null +++ b/.changeset/loud-mammals-dress.md @@ -0,0 +1,5 @@ +--- +'eslint-plugin-qwik': patch +--- + +FIX: cover more qwik eslint's edge case From 275ed8a11fa8412917868be3ca6357ba3badec27 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Thu, 26 Jun 2025 20:52:17 +0800 Subject: [PATCH 08/11] fix: enhance eslint-plugin to accurately detect Node API usage and improve edge case handling --- .changeset/loud-mammals-dress.md | 2 +- .../eslint-plugin-qwik/src/scope-use-task.ts | 21 ++++++------------- .../invalid-scope-use-function.tsx | 3 ++- .../invalid-scope-use-track.tsx | 8 ++++++- .../valid-scope-use-function.tsx | 13 +++++++++++- .../valid-scope-use-return-object.tsx | 7 +------ 6 files changed, 29 insertions(+), 25 deletions(-) diff --git a/.changeset/loud-mammals-dress.md b/.changeset/loud-mammals-dress.md index 7a5ffeb2ce4..b2f9fafab28 100644 --- a/.changeset/loud-mammals-dress.md +++ b/.changeset/loud-mammals-dress.md @@ -2,4 +2,4 @@ 'eslint-plugin-qwik': patch --- -FIX: cover more qwik eslint's edge case +FIX: eslint-plugin: detect node API usage more accurately diff --git a/packages/eslint-plugin-qwik/src/scope-use-task.ts b/packages/eslint-plugin-qwik/src/scope-use-task.ts index 8977e3e434c..9fcf6975c64 100644 --- a/packages/eslint-plugin-qwik/src/scope-use-task.ts +++ b/packages/eslint-plugin-qwik/src/scope-use-task.ts @@ -1,8 +1,8 @@ import { Rule } from 'eslint'; import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; // For Scope types -import type { Identifier } from 'estree'; const ISSERVER = 'isServer'; +const GLOBALAPIS = new Set(['process', '__dirname', '__filename', 'module']); // Helper function: checks if a node is a descendant of another node function isNodeDescendantOf(descendantNode, ancestorNode): boolean { if (!ancestorNode) { @@ -178,13 +178,13 @@ export const scopeUseTask: Rule.RuleModule = { currentScopeForSearch = currentScopeForSearch.upper; } - if (!variable) { + if (!GLOBALAPIS.has(identifierNode.name)) { // Cannot find variable, assume it's not a shadowed global for safety, // though this state implies an undeclared variable (another ESLint rule should catch this). - return false; + return true; } - if (variable.defs.length === 0) { + if (variable?.defs.length === 0) { // No definitions usually means it's an implicit global (e.g., 'process' in Node.js environment). // Such a variable is NOT considered "shadowed by a user declaration". return false; @@ -192,7 +192,7 @@ export const scopeUseTask: Rule.RuleModule = { // If there are definitions, check if any of them are standard declaration types. // This means the identifier refers to a user-declared variable, parameter, function, class, or an import. - return variable.defs.some((def) => { + return variable?.defs.some((def) => { return ( def.type === 'Variable' || def.type === 'Parameter' || @@ -322,15 +322,7 @@ export const scopeUseTask: Rule.RuleModule = { } if (forbiddenApis.has(node.name)) { - const isDirectIdentifier = - node.parent.type === 'VariableDeclarator' && - (node?.parent?.init as Identifier)?.name === node.name; - // node'api usually be invoked as a member expression (e.g., process.env) - // or as a direct identifier (e.g., process). - if ( - isIdentifierShadowedByDeclaration(node) || - (node.parent.type !== 'MemberExpression' && node.parent.type !== 'VariableDeclarator') - ) { + if (isIdentifierShadowedByDeclaration(node)) { return; } if (!isApiUsageGuarded(node, currentUseTaskFunction)) { @@ -426,7 +418,6 @@ export const scopeUseTask: Rule.RuleModule = { targetFunctionNode.body.type === AST_NODE_TYPES.BlockStatement ? targetFunctionNode.body : targetFunctionNode.body; - analyzeNodeContent(nodeToAnalyze, targetFunctionNode, callNode); } } diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-function.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-function.tsx index a5c39b24cb7..d1816bb940d 100644 --- a/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-function.tsx +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-function.tsx @@ -4,8 +4,8 @@ // Expect error: { "messageId": "unsafeApiUsageInCalledFunction" } import { component$, useTask$ } from '@qwik.dev/core'; - export default component$(() => { + function child_process() {} useTask$(() => { function foo() { process.env; @@ -13,6 +13,7 @@ export default component$(() => { const foo2 = () => { process.env; }; + child_process(); foo(); foo2(); }); diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-track.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-track.tsx index 2870f2738ec..feb391180e1 100644 --- a/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-track.tsx +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-track.tsx @@ -1,11 +1,17 @@ // Expect error: { "messageId": "unsafeApiUsage" } // Expect error: { "messageId": "unsafeApiUsage" } -import { component$, useSignal, useTask$ } from '@qwik.dev/core'; +// Expect error: { "messageId": "unsafeApiUsage" } +// Expect error: { "messageId": "unsafeApiUsage" } +import { component$, isBrowser, useSignal, useTask$ } from '@qwik.dev/core'; export default component$(() => { const s = useSignal(0); useTask$(({ track }) => { track(() => { + if (isBrowser) { + process.env; + const m = process; + } process.env; const m = process; return s.value; diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-function.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-function.tsx index 66562db2228..e03ac6170cc 100644 --- a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-function.tsx +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-function.tsx @@ -1,14 +1,25 @@ import { component$, isServer, useTask$ } from '@qwik.dev/core'; - +import path from 'path'; export default component$(() => { useTask$(() => { + function child_process() {} function foo() { if (isServer) { process.env; const m = process; + const _path = path; + const pathJoin = path.join('foo', 'bar'); } } + child_process(); + const foo2 = () => { + if (isServer) { + process.env; + const m = process; + } + }; foo(); + foo2(); }); return <>; }); diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx index c0e4a98f926..e4c042471ee 100644 --- a/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/valid-scope-use-return-object.tsx @@ -8,12 +8,7 @@ export default component$(() => { if (state.value) { const values = [ { - relativePath: '', - name: 'index', - type: '', - path: '', - isSymbolicLink: false, - children: undefined, + path: '1', }, ]; } From cde161b5341dd299724361259c22097df996705d Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Thu, 26 Jun 2025 20:59:02 +0800 Subject: [PATCH 09/11] fix: refactor GLOBALAPIS to use array instead of Set and streamline default forbidden APIs --- .../eslint-plugin-qwik/src/scope-use-task.ts | 33 ++++--------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/packages/eslint-plugin-qwik/src/scope-use-task.ts b/packages/eslint-plugin-qwik/src/scope-use-task.ts index 9fcf6975c64..378a7ece681 100644 --- a/packages/eslint-plugin-qwik/src/scope-use-task.ts +++ b/packages/eslint-plugin-qwik/src/scope-use-task.ts @@ -2,7 +2,8 @@ import { Rule } from 'eslint'; import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; // For Scope types const ISSERVER = 'isServer'; -const GLOBALAPIS = new Set(['process', '__dirname', '__filename', 'module']); +const GLOBALAPIS = ['process', '__dirname', '__filename', 'module']; +const PRESETNODEAPIS = ['fs', 'os', 'path', 'child_process', 'http', 'https', 'Buffer']; // Helper function: checks if a node is a descendant of another node function isNodeDescendantOf(descendantNode, ancestorNode): boolean { if (!ancestorNode) { @@ -36,18 +37,7 @@ export const scopeUseTask: Rule.RuleModule = { forbiddenApis: { type: 'array', items: { type: 'string' }, - default: [ - 'process', - 'fs', - 'os', - 'path', - 'child_process', - 'http', - 'https', - 'Buffer', - '__dirname', - '__filename', - ], + default: PRESETNODEAPIS, }, }, additionalProperties: false, @@ -63,18 +53,7 @@ export const scopeUseTask: Rule.RuleModule = { create(context: Rule.RuleContext): Rule.RuleListener { const options = context.options[0] || {}; const forbiddenApis = new Set( - options.forbiddenApis || [ - 'process', - 'fs', - 'os', - 'path', - 'child_process', - 'http', - 'https', - 'Buffer', - '__dirname', - '__filename', - ] + options.forbiddenApis || PRESETNODEAPIS.concat(GLOBALAPIS) ); const serverGuardIdentifier: string = ISSERVER; const sourceCode = context.sourceCode; @@ -178,7 +157,7 @@ export const scopeUseTask: Rule.RuleModule = { currentScopeForSearch = currentScopeForSearch.upper; } - if (!GLOBALAPIS.has(identifierNode.name)) { + if (!GLOBALAPIS.includes(identifierNode.name)) { // Cannot find variable, assume it's not a shadowed global for safety, // though this state implies an undeclared variable (another ESLint rule should catch this). return true; @@ -200,7 +179,7 @@ export const scopeUseTask: Rule.RuleModule = { def.type === 'ClassName' || def.type === 'ImportBinding' ); - }); + }) as boolean; } /** From 3e304c35f24ffd54333103b73c54c3bf58cbb764 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Thu, 26 Jun 2025 21:04:25 +0800 Subject: [PATCH 10/11] fix: improve handling of undeclared variables in scopeUseTask rule --- packages/eslint-plugin-qwik/src/scope-use-task.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/eslint-plugin-qwik/src/scope-use-task.ts b/packages/eslint-plugin-qwik/src/scope-use-task.ts index 378a7ece681..b5ffb428f65 100644 --- a/packages/eslint-plugin-qwik/src/scope-use-task.ts +++ b/packages/eslint-plugin-qwik/src/scope-use-task.ts @@ -157,9 +157,8 @@ export const scopeUseTask: Rule.RuleModule = { currentScopeForSearch = currentScopeForSearch.upper; } + // If we didn't find a variable, it might be a global API or an undeclared variable. if (!GLOBALAPIS.includes(identifierNode.name)) { - // Cannot find variable, assume it's not a shadowed global for safety, - // though this state implies an undeclared variable (another ESLint rule should catch this). return true; } From 9c25caa7d2cf8cca4042622550fbabfbac142c09 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Tue, 29 Jul 2025 16:54:29 +0800 Subject: [PATCH 11/11] fix: improve variable scope handling in scopeUseTask rule --- packages/eslint-plugin-qwik/src/scope-use-task.ts | 10 ++++++---- .../scope-use-task/invalid-scope-use-function.tsx | 2 -- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin-qwik/src/scope-use-task.ts b/packages/eslint-plugin-qwik/src/scope-use-task.ts index b5ffb428f65..aeb55958fd4 100644 --- a/packages/eslint-plugin-qwik/src/scope-use-task.ts +++ b/packages/eslint-plugin-qwik/src/scope-use-task.ts @@ -140,6 +140,11 @@ export const scopeUseTask: Rule.RuleModule = { // Try to find the variable starting from the current scope and going upwards let currentScopeForSearch: eslint.Scope.Scope | null = scope; + + if (!GLOBALAPIS.includes(identifierNode.name)) { + return true; + } + while (currentScopeForSearch) { const foundVar = currentScopeForSearch.variables.find( (v) => v.name === identifierNode.name @@ -158,11 +163,8 @@ export const scopeUseTask: Rule.RuleModule = { } // If we didn't find a variable, it might be a global API or an undeclared variable. - if (!GLOBALAPIS.includes(identifierNode.name)) { - return true; - } - if (variable?.defs.length === 0) { + if (!variable || variable.defs.length === 0) { // No definitions usually means it's an implicit global (e.g., 'process' in Node.js environment). // Such a variable is NOT considered "shadowed by a user declaration". return false; diff --git a/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-function.tsx b/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-function.tsx index d1816bb940d..f2cabd3de47 100644 --- a/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-function.tsx +++ b/packages/eslint-plugin-qwik/tests/scope-use-task/invalid-scope-use-function.tsx @@ -5,7 +5,6 @@ import { component$, useTask$ } from '@qwik.dev/core'; export default component$(() => { - function child_process() {} useTask$(() => { function foo() { process.env; @@ -13,7 +12,6 @@ export default component$(() => { const foo2 = () => { process.env; }; - child_process(); foo(); foo2(); });