Skip to content

sanaei-integration: add 3x-ui dependency, theme-provider conditional … - #14

Open
AliiRezaTabatabaiee wants to merge 3 commits into
PasarGuard:mainfrom
AliiRezaTabatabaiee:sanaei-integration
Open

sanaei-integration: add 3x-ui dependency, theme-provider conditional …#14
AliiRezaTabatabaiee wants to merge 3 commits into
PasarGuard:mainfrom
AliiRezaTabatabaiee:sanaei-integration

Conversation

@AliiRezaTabatabaiee

@AliiRezaTabatabaiee AliiRezaTabatabaiee commented Jul 28, 2026

Copy link
Copy Markdown

…wrapper, and .env.example

Summary by CodeRabbit

  • New Features

    • Added support for configuring the application with a subscription-template integration.
    • Added optional 3x-ui integration with configurable theme support.
    • Added environment-based API endpoint configuration.
  • Improvements

    • Improved API requests across user information, usage, and app data.
    • Added explicit light and dark theme color configuration and border-radius settings.
    • Improved server-side compatibility when constructing API request paths.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AliiRezaTabatabaiee, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a02211-da77-4fff-9fe8-dc87c040c552

📥 Commits

Reviewing files that changed from the base of the PR and between 16cb46b and ad2f306.

📒 Files selected for processing (7)
  • README.md
  • src/components/ui/Badge.tsx
  • src/components/ui/Button.tsx
  • src/components/ui/Card.tsx
  • src/components/ui/Input.tsx
  • src/components/ui/Modal.tsx
  • src/components/ui/Toaster.tsx

Walkthrough

Adds optional 3X UI theme-provider integration, new environment configuration, and centralized API base URL handling for user, chart, and app requests with SSR-safe paths.

Changes

Theme Provider and API Configuration

Layer / File(s) Summary
3X UI configuration and dependency
.env.example, package.json
Adds the 3X UI dependency and documents its enablement flag, API base URL, and updated design-token defaults.
Conditional theme-provider selection
src/components/theme-provider.tsx
Selects the 3X UI theme provider when enabled and otherwise retains the Next Themes provider.
Environment-based API request routing
src/hooks/useUserData.ts, src/lib/fetcher.ts
Uses VITE_API_BASE for API requests with SSR-safe paths, updates chart revalidation behavior, and preserves response header extraction.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: x0sina

Poem

A rabbit hopped through themes so bright,
Then routed APIs left and right.
Three-X colors joined the play,
Safe paths guide requests each day.
“Hop hooray!” the bunny sings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main changes: Sanaei integration, 3x-ui dependency, and conditional theme-provider behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Around line 36-37: Update the `@MHSanaei/3x-ui` dependency in package.json so it
resolves to the installable frontend package defined by frontend/package.json,
rather than the repository root; use the scoped frontend package, a private
installable copy, or a local workspace that exports ThemeProvider, Toaster,
Card, and the other required symbols.

In `@src/components/theme-provider.tsx`:
- Around line 13-14: Update the wrapper rendering SanaeiThemeProvider to remove
the `@ts-ignore` and any cast, using its actual prop type or explicitly forwarding
only the compatible next-themes props attribute, defaultTheme, enableSystem, and
enableColorScheme while preserving children.
- Around line 12-17: Update the enable3x branch in the theme provider so
next-themes consumers such as theme-toggle.tsx and sonner.tsx still receive a
valid NextThemesProvider context, or migrate those consumers to
SanaeiThemeProvider’s context while preserving their existing behavior; keep the
current provider selection for non-3x mode unchanged.

In `@src/hooks/useUserData.ts`:
- Around line 109-117: Update useApps so useSWRImmutable is called
unconditionally, removing the early return based on initialAppsArray. Use
window.__INITIAL_DATA__?.apps || null as the SWR key, and select
window.__INITIAL_DATA__?.apps in the returned apps value while preserving
fetched data and loading/error behavior.
- Around line 7-10: Update resolveBaseUrl so it never calls getBaseUrl when
window is unavailable during SSR. Preserve the trimmed VITE_API_BASE behavior,
and otherwise defer origin resolution until typeof window !== 'undefined' or
obtain it from the server request context, ensuring useUserInfo, useChartData,
and useApps do not access window during initialization.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c1963b35-5435-4a25-830c-157119bcd183

📥 Commits

Reviewing files that changed from the base of the PR and between ba2d786 and 16cb46b.

📒 Files selected for processing (5)
  • .env.example
  • package.json
  • src/components/theme-provider.tsx
  • src/hooks/useUserData.ts
  • src/lib/fetcher.ts

Comment thread package.json
Comment on lines +36 to +37
"tailwindcss": "^4.2.2",
"@MHSanaei/3x-ui": "github:MHSanaei/3x-ui"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package.json relevant lines =="
if [ -f package.json ]; then
  cat -n package.json | sed -n '1,90p'
else
  fd -a '^package\.json$' .
fi

echo
echo "== imports/usages of ThemeProvider and `@MHSanaei/3x-ui` =="
rg -n "ThemeProvider|`@MHSanaei/3x-ui`|3x-ui|`@MHSanaei`" . --glob '!node_modules' --glob '!dist' --glob '!build' || true

echo
echo "== files containing "3x-ui-frontend" or similar frontend clues =="
rg -n "3x-ui-frontend|MHSanaei/3x-ui|frontend|package.json" . --glob '!node_modules' --glob '!dist' --glob '!build' | head -200

Repository: PasarGuard/subscription-template

Length of output: 6462


🌐 Web query:

github.com MHSanaei 3x-ui package.json 3x-ui-frontend

💡 Result:

In the MHSanaei/3x-ui repository, the directory named frontend (often referred to as 3x-ui-frontend in developer documentation) contains the source code for the project's modern React-based Single Page Application (SPA) web management panel [1][2][3]. The frontend directory includes a package.json file that defines the project's dependencies and build scripts [4][3]. As of July 2026, the frontend is built using a modern stack including: - Framework: React 19 [4][3] - UI Library: Ant Design 6 [4][3] - Build Tool: Vite 8 [4][3] - State Management: TanStack Query v5 [4] - Routing: React Router v7 [4] - Validation: Zod [4][5] - HTTP Client: Axios [4] The frontend code is organized to mirror the Go backend's structure and utilizes automated code generation to maintain type safety [1][6]. Specifically, tools in the repo generate TypeScript types and Zod schemas from Go structs, which are then placed in the frontend/src/generated/ directory to keep the UI synchronized with backend API changes [2][6]. During the build process, the React SPA is compiled into static assets and embedded into the main Go binary using Go's embed.FS feature, allowing the entire application to be served from a single binary [1][2].

Citations:


🌐 Web query:

MHSanaei 3x-ui root package.json 3x-ui-frontend

💡 Result:

In the MHSanaei/3x-ui repository, the frontend component is located in the dedicated frontend/ directory [1][2]. The file named package.json located in the root of the repository is for the Go backend project, while the frontend configuration file, named package.json (identifying the project as 3x-ui-frontend) [3][4], resides at frontend/package.json [1][5]. Key details regarding this architecture include: - Repository Structure: The repository follows a clear separation where the Go backend source code is primarily housed in the root and internal/ directories [1][6][7], and the React-based Single Page Application (SPA) is maintained in the frontend/ directory [2][5][8]. - Frontend package.json: The file located at frontend/package.json defines the project as 3x-ui-frontend [3][4]. It manages the dependencies and scripts for the React 19, Ant Design 6, and Vite 8 stack [9][10][8]. - Build Integration: The frontend is not deployed separately; rather, the npm run build command triggers the build process (including API schema generation from Go models) and outputs the assets into internal/web/dist/ [1][9][8]. These assets are then embedded directly into the final Go binary using the go:embed directive [1][7]. - Code Generation: The frontend package includes scripts such as gen:zod and gen:api [9][8]. These scripts utilize Go-based tools (specifically in tools/openapigen/) to parse backend Go models and generate type-safe TypeScript interfaces and Zod validation schemas, ensuring consistency between the backend API and frontend data structures [7][8].

Citations:


Point npm at the installable frontend package instead of the 3X-UI app repository.

@MHSanaei/3x-ui currently points to the repository root, whose frontend manifest is frontend/package.json with package name 3x-ui-frontend, not a root npm package exporting ThemeProvider, Toaster, Card, etc. The eager theme-provider.tsx import will fail package resolution on install/build. Use the scoped frontend package, a private installable copy, or a local workspace that exports these symbols.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` around lines 36 - 37, Update the `@MHSanaei/3x-ui` dependency in
package.json so it resolves to the installable frontend package defined by
frontend/package.json, rather than the repository root; use the scoped frontend
package, a private installable copy, or a local workspace that exports
ThemeProvider, Toaster, Card, and the other required symbols.

Source: MCP tools

Comment on lines +12 to 17
if (enable3x) {
// @ts-ignore - accept any props for the external provider
return <SanaeiThemeProvider {...(props as any)}>{children}</SanaeiThemeProvider>;
}

return <NextThemesProvider {...props}>{children}</NextThemesProvider>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

rg -n -P 'from ["'\''"]next-themes["'\''"]|\buseTheme\s*\(' src

Repository: PasarGuard/subscription-template

Length of output: 650


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files containing next-themes consumers:\n'
git ls-files | rg 'theme-provider|theme-toggle|sonner|next-themes|package.json' || true

printf '\nRelevant file excerpts:\n'
for f in src/components/theme-provider.tsx src/components/theme-toggle.tsx src/components/ui/sonner.tsx; do
  if [ -f "$f" ]; then
    echo "--- $f"
    nl -ba "$f" | sed -n '1,80p'
  fi
done

printf '\nPackage dependency references:\n'
for f in package.json src/components/theme-provider.tsx; do
  if [ -f "$f" ]; then
    echo "--- $f"
    rg -n '"[^"]*next-themes[^"]*"|next-themes|packageManager|dependencies|devDependencies' "$f" || true
  fi
done

printf '\nProvider imports/usages in src:\n'
rg -n -P 'NextThemesProvider|SanaeiThemeProvider|theme-provider|ThemeProvider|useTheme' src

Repository: PasarGuard/subscription-template

Length of output: 422


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Relevant file excerpts:\n'
for f in src/components/theme-provider.tsx src/components/theme-toggle.tsx src/components/ui/sonner.tsx package.json; do
  if [ -f "$f" ]; then
    echo "--- $f"
    awk '{printf "%5d\t%s\n", NR, $0}' "$f" | sed -n '1,120p'
  fi
done

printf '\nProvider imports/usages in src:\n'
rg -n -P 'NextThemesProvider|SanaeiThemeProvider|theme-provider|ThemeProvider|useTheme' src

printf '\nPackage dependency references:\n'
rg -n -P '"[^"]*next-themes[^"]*"|\bnext-themes\b|packageManager|dependencies|devDependencies' package.json src/components/theme-provider.tsx || true

Repository: PasarGuard/subscription-template

Length of output: 7945


Bridge or migrate the next-themes context for 3x UI mode.

theme-toggle.tsx and src/components/ui/sonner.tsx both call useTheme() from next-themes, but when VITE_ENABLE_3X_UI is true this route only renders SanaeiThemeProvider. Either keep NextThemesProvider wrapped/bridged in that path, or migrate those consumers to the provider being used by 3x UI.

🧰 Tools
🪛 ESLint

[error] 13-13: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.

(@typescript-eslint/ban-ts-comment)


[error] 14-14: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/theme-provider.tsx` around lines 12 - 17, Update the enable3x
branch in the theme provider so next-themes consumers such as theme-toggle.tsx
and sonner.tsx still receive a valid NextThemesProvider context, or migrate
those consumers to SanaeiThemeProvider’s context while preserving their existing
behavior; keep the current provider selection for non-3x mode unchanged.

Comment on lines +13 to +14
// @ts-ignore - accept any props for the external provider
return <SanaeiThemeProvider {...(props as any)}>{children}</SanaeiThemeProvider>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)(package\.json|eslint.*\.(js|cjs|mjs|json)$|src/components/theme-provider\.tsx|.*sanaei.*|.*theme.*provider.*)' || true

echo
echo "== theme-provider =="
if [ -f src/components/theme-provider.tsx ]; then
  cat -n src/components/theme-provider.tsx
fi

echo
echo "== package and deps =="
if [ -f package.json ]; then
  cat package.json | jq '{dependencies, devDependencies, scripts}' // true
fi

echo
echo "== eslint snippets =="
for f in $(git ls-files | rg '(^|/)(eslint\.config\.(js|cjs|mjs)|\.eslintrc(\..*)?|package\.json)$' || true); do
  echo "--- $f"
  sed -n '1,220p' "$f"
done

echo
echo "== search suppressions/rules =="
rg -n "`@ts-ignore`|typescript-eslint|no-explicit-any|ban-ts-comment|typescript|next-themes|SanaeiThemeProvider" .

Repository: PasarGuard/subscription-template

Length of output: 1337


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== package.json =="
cat package.json

echo
echo "== eslint.config.js outlines and contents =="
wc -l eslint.config.js
sed -n '1,220p' eslint.config.js

echo
echo "== relevant source/config/search =="
rg -n -C 3 '`@ts-ignore`|typescript-eslint|no-explicit-any|ban-ts-comment|next-themes|SanaeiThemeProvider|ThemeProvider' .

Repository: PasarGuard/subscription-template

Length of output: 20857


Remove the @ts-ignore and any escape hatch.

eslint.config.js enables typescript-eslint recommended rules, so @ts-ignore/no-explicit-any will fail lint here. Replace the cast with the Sanaei provider’s actual prop type or explicitly map only the compatible next-themes props (attribute, defaultTheme, enableSystem, enableColorScheme) to avoid hiding prop incompatibilities.

🧰 Tools
🪛 ESLint

[error] 13-13: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.

(@typescript-eslint/ban-ts-comment)


[error] 14-14: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/theme-provider.tsx` around lines 13 - 14, Update the wrapper
rendering SanaeiThemeProvider to remove the `@ts-ignore` and any cast, using its
actual prop type or explicitly forwarding only the compatible next-themes props
attribute, defaultTheme, enableSystem, and enableColorScheme while preserving
children.

Source: Linters/SAST tools

Comment thread src/hooks/useUserData.ts
Comment on lines +7 to +10
const resolveBaseUrl = () => {
const envBase = import.meta.env.VITE_API_BASE?.trim();
if (envBase && envBase.length > 0) return envBase.replace(/\/$/, '');
return getBaseUrl();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## files"
git ls-files | rg '(^|/)useUserData\.ts$|(^|/)fetcher\.ts$|package\.json|vite\.config' || true

echo "## useUserData outline"
ast-grep outline src/hooks/useUserData.ts --view expanded || true

echo "## useUserData relevant lines"
cat -n src/hooks/useUserData.ts | sed -n '1,120p'

echo "## fetcher relevant lines"
cat -n src/lib/fetcher.ts | sed -n '1,110p'

echo "## imports/calls to useUserData and VITE_API_BASE"
rg -n "useUserData|VITE_API_BASE|getBaseUrl|window\.location\.origin" -S . || true

echo "## package deps/scripts"
if [ -f package.json ]; then cat package.json | jq '.scripts,.dependencies,.devDependencies' 2>/dev/null || cat package.json; fi
if [ -f vite.config.* ]; then cat vite.config.*; fi

Repository: PasarGuard/subscription-template

Length of output: 11447


🌐 Web query:

vite-plugin-singlefile SSR window is not defined window.location.origin production behavior

💡 Result:

The "window is not defined" error in Vite SSR (Server-Side Rendering) is a common issue that occurs when your application code tries to access browser-specific globals (like window, document, or window.location) during the server-side execution phase [1][2][3]. Vite's SSR runs your code in a Node.js environment, which does not have a global window object. While vite-plugin-singlefile is designed to bundle your final output into a single HTML file for client-side use [4][5], it is a build-time plugin and does not inherently solve SSR compatibility issues. Key reasons and solutions for this behavior include: 1. Understanding the Error: If your code executes at the module level (top-level code) or within server-side lifecycle hooks, it will attempt to access window immediately [2]. Because SSR is executed in Node.js, this throws a ReferenceError [1][6]. 2. SSR Compatibility: Third-party libraries or your own code using window.location.origin or other DOM APIs are often incompatible with SSR by default [1]. To fix this, you should ensure such code is wrapped in checks or executed only on the client side [1]: - Use conditional checks: Ensure the code only runs if the window is defined (e.g., if (typeof window!== 'undefined') {... }) [2]. - Use Lifecycle Hooks: In frameworks like Vue or React, move DOM-reliant logic into lifecycle hooks that only trigger on the client, such as onMounted or useEffect [6]. - Configure Vite SSR Defines: If you are using plugins that globally define window or global as a workaround, ensure they are correctly scoped to the client build and not the SSR build [2]. 3. vite-plugin-singlefile and SSR: This plugin is intended to consolidate build assets for offline/file-protocol usage [4][7]. It is not a tool for SSR. If you are building a Single Page Application (SPA) with routing, note that Web History API routing is not supported by single-file deployments because they lack a server to handle route fallbacks [4][8]. If you require SSR for your application, ensure your SSR build configuration is separate from the single-file packaging process [9]. If you are encountering this during a build process, it is likely that your application code is being evaluated by Vite's SSR module runner while generating your static assets [1][3]. Check your imports and top-level code to ensure DOM dependencies are guarded against the server-side environment [1][2].

Citations:


Make resolveBaseUrl() avoid accessing window during SSR.

When VITE_API_BASE is unset, getBaseUrl() falls back to window.location.origin, so server-side rendering with useUserInfo/useChartData/useApps can throw before the hooks’ own window guards. Resolve the origin from server request context, or defer this lookup until typeof window !== 'undefined'.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useUserData.ts` around lines 7 - 10, Update resolveBaseUrl so it
never calls getBaseUrl when window is unavailable during SSR. Preserve the
trimmed VITE_API_BASE behavior, and otherwise defer origin resolution until
typeof window !== 'undefined' or obtain it from the server request context,
ensuring useUserInfo, useChartData, and useApps do not access window during
initialization.

Comment thread src/hooks/useUserData.ts
Comment on lines 109 to +117
if (initialAppsArray && initialAppsArray.length > 0) {
return { apps: initialAppsArray, appsError: null, appsLoading: false };
}

// Only make network request if no initial data is available
const path = typeof window !== 'undefined' ? `${window.location.pathname}/apps` : '/apps';
const base = resolveBaseUrl();

const { data, error, isLoading } = useSWRImmutable<AppClient[]>(
`${getBaseUrl()}${window.location.pathname}/apps`,
`${base}${path}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching useUserData:"
fd -a 'useUserData\.ts$' . || true

file="$(fd 'useUserData\.ts$' . | head -n 1 || true)"
if [ -n "$file" ]; then
  echo "--- outline ---"
  ast-grep outline "$file" --view expanded || true
  echo "--- relevant content ---"
  wc -l "$file"
  cat -n "$file" | sed -n '1,180p'
fi

echo "--- eslint hook-order rules / config ---"
rg -n "eslint|hooks|react-hooks|hook|condition|order" -S . --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' | head -n 200 || true

Repository: PasarGuard/subscription-template

Length of output: 34029


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- eslint config ---"
cat -n eslint.config.js | sed -n '1,160p'

echo "--- package scripts/deps relevant ---"
node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
console.log(JSON.stringify({
  scripts: pkg.scripts,
  deps: Object.fromEntries(Object.entries(pkg.dependencies || {}).filter(([k]) => k.includes('react') || k.includes('swr'))),
  devDeps: Object.fromEntries(Object.entries(pkg.devDependencies || {}).filter(([k]) => k.includes('eslint') || k.includes('react') || k.includes('swr') || k.includes('typescript/eslint')))
}, null, 2));
JS

echo "--- useApps usages ---"
rg -n "useApps\\(|__INITIAL_DATA__\\?\\.apps|initialAppsArray" src package.json eslint.config.js -S || true

echo "--- React hooks invariant shape ---"
node - <<'JS'
const fn = ([initial, render]) => {
  const calls = [];
  const state = [undefined, undefined];
  const useState = (fn) => {
    calls.push('useState');
    const v = state.shift();
    return [v, (nv) => state.unshift(nv)];
  };
  const useSWR = () => calls.push('useSWR');
  const ReactHook = 0;

  const useUserData = render => {
    if (render) return { value: 'initial' };
    useSWR();
    useSWR();
    return { value: 'api' };
  };
  try {
    useUserData(initial);
    useUserData(0);
    console.log('ok initial api', calls.join(','));
  } catch (e) {
    console.log('crash initial api', e.message);
  }
  try {
    useUserData(0);
    useUserData(initial);
    console.log('ok api initial', calls.join(','));
  } catch (e) {
    console.log('crash api initial', e.message);
  }
};
fn(true);
fn(false);
JS

Repository: PasarGuard/subscription-template

Length of output: 3046


Make useSWRImmutable unconditional for useApps.

useApps can skip the hook on the conditional initialAppsArray path, which violates React’s hook call order. Pass window.__INITIAL_DATA__?.apps || null as the SWR key, then select window.__INITIAL_DATA__?.apps in the returned apps value.

🧰 Tools
🪛 ESLint

[error] 116-116: React Hook "useSWRImmutable" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?

(react-hooks/rules-of-hooks)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useUserData.ts` around lines 109 - 117, Update useApps so
useSWRImmutable is called unconditionally, removing the early return based on
initialAppsArray. Use window.__INITIAL_DATA__?.apps || null as the SWR key, and
select window.__INITIAL_DATA__?.apps in the returned apps value while preserving
fetched data and loading/error behavior.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant