Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(console): safely lazy load pages #6332

Merged
merged 2 commits into from
Jul 26, 2024
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
9 changes: 3 additions & 6 deletions packages/console/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
unnamedComponents: 'arrow-function',
},
],
'react/jsx-pascal-case': ['error', { ignore: ['__Internal__*'] }],
'import/no-unused-modules': [
'error',
{
Expand All @@ -30,6 +31,8 @@ module.exports = {
'**/assets/docs/guides/*/index.ts',
'**/assets/docs/guides/*/components/**/*.tsx',
'**/mdx-components*/*/index.tsx',
'*.config.js',
'*.config.ts',
],
rules: {
'import/no-unused-modules': 'off',
Expand All @@ -49,12 +52,6 @@ module.exports = {
],
},
},
{
files: ['*.config.js', '*.config.ts', '*.d.ts'],
rules: {
'import/no-unused-modules': 'off',
},
},
{
files: ['*.d.ts'],
rules: {
Expand Down
1 change: 1 addition & 0 deletions packages/console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"react-modal": "^3.15.1",
"react-paginate": "^8.1.3",
"react-router-dom": "^6.25.1",
"react-safe-lazy": "^0.1.0",
"react-syntax-highlighter": "^15.5.0",
"react-timer-hook": "^3.0.5",
"recharts": "^2.1.13",
Expand Down
5 changes: 3 additions & 2 deletions packages/console/src/containers/ConsoleContent/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { lazy, Suspense } from 'react';
import { Suspense } from 'react';
import { useOutletContext, useRoutes } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { isDevFeaturesEnabled } from '@/consts/env';
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
Expand All @@ -14,7 +15,7 @@ import { Skeleton } from './Sidebar';
import useTenantScopeListener from './hooks';
import styles from './index.module.scss';

const Sidebar = lazy(async () => import('./Sidebar'));
const Sidebar = safeLazy(async () => import('./Sidebar'));

function ConsoleContent() {
const { scrollableContent } = useOutletContext<AppContentOutletContext>();
Expand Down
65 changes: 38 additions & 27 deletions packages/console/src/containers/ConsoleRoutes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { ossConsolePath } from '@logto/schemas';
import { Suspense } from 'react';
import { Navigate, Outlet, Route, Routes } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';
import { SWRConfig } from 'swr';

import { isCloud } from '@/consts/env';
import AppLoading from '@/components/AppLoading';
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
import AppBoundary from '@/containers/AppBoundary';
import AppContent, { RedirectToFirstItem } from '@/containers/AppContent';
import ConsoleContent from '@/containers/ConsoleContent';
Expand All @@ -12,10 +15,13 @@ import { GlobalRoute } from '@/contexts/TenantsProvider';
import useSwrOptions from '@/hooks/use-swr-options';
import Callback from '@/pages/Callback';
import CheckoutSuccessCallback from '@/pages/CheckoutSuccessCallback';
import Profile from '@/pages/Profile';
import Welcome from '@/pages/Welcome';
import { dropLeadingSlash } from '@/utils/url';

import { __Internal__ImportError } from './internal';

const Welcome = safeLazy(async () => import('@/pages/Welcome'));
const Profile = safeLazy(async () => import('@/pages/Profile'));

function Layout() {
const swrOptions = useSwrOptions();

Expand All @@ -30,32 +36,37 @@ function Layout() {

export function ConsoleRoutes() {
return (
<Routes>
{/**
* OSS doesn't have a tenant concept nor root path handling component, but it may
* navigate to the root path in frontend. In this case, we redirect it to the OSS
* console path to trigger the console routes.
*/}
{!isCloud && <Route path="/" element={<Navigate to={ossConsolePath} />} />}
<Route path="/:tenantId" element={<Layout />}>
<Route path="callback" element={<Callback />} />
<Route path="welcome" element={<Welcome />} />
<Route element={<ProtectedRoutes />}>
<Route path={dropLeadingSlash(GlobalRoute.Profile) + '/*'} element={<Profile />} />
<Route element={<TenantAccess />}>
{isCloud && (
<Route
path={dropLeadingSlash(GlobalRoute.CheckoutSuccessCallback)}
element={<CheckoutSuccessCallback />}
/>
)}
<Route element={<AppContent />}>
<Route index element={<RedirectToFirstItem />} />
<Route path="*" element={<ConsoleContent />} />
<Suspense fallback={<AppLoading />}>
<Routes>
{/**
* OSS doesn't have a tenant concept nor root path handling component, but it may
* navigate to the root path in frontend. In this case, we redirect it to the OSS
* console path to trigger the console routes.
*/}
{!isCloud && <Route path="/" element={<Navigate to={ossConsolePath} />} />}
<Route path="/:tenantId" element={<Layout />}>
<Route path="callback" element={<Callback />} />
<Route path="welcome" element={<Welcome />} />
{isDevFeaturesEnabled && (
<Route path="__internal__/import-error" element={<__Internal__ImportError />} />
)}
<Route element={<ProtectedRoutes />}>
<Route path={dropLeadingSlash(GlobalRoute.Profile) + '/*'} element={<Profile />} />
<Route element={<TenantAccess />}>
{isCloud && (
<Route
path={dropLeadingSlash(GlobalRoute.CheckoutSuccessCallback)}
element={<CheckoutSuccessCallback />}
/>
)}
<Route element={<AppContent />}>
<Route index element={<RedirectToFirstItem />} />
<Route path="*" element={<ConsoleContent />} />
</Route>
</Route>
</Route>
</Route>
</Route>
</Routes>
</Routes>
</Suspense>
);
}
14 changes: 14 additions & 0 deletions packages/console/src/containers/ConsoleRoutes/internal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { safeLazy } from 'react-safe-lazy';

/**
* An internal module that is used to test the lazy loading failure in the console. Normally, this
* module should not involve any production code.
*/
export const __Internal__ImportError = safeLazy(async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const module = await import(
/* @vite-ignore */ `${window.location.origin}/some-non-existing-path`
);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return module;
});
gao-sun marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 5 additions & 4 deletions packages/console/src/hooks/use-console-routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { condArray } from '@silverhand/essentials';
import { lazy, useMemo } from 'react';
import { useMemo } from 'react';
import { type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { isCloud } from '@/consts/env';
import NotFound from '@/pages/NotFound';
Expand All @@ -20,9 +21,9 @@ import { useTenantSettings } from './routes/tenant-settings';
import { users } from './routes/users';
import { webhooks } from './routes/webhooks';

const Dashboard = lazy(async () => import('@/pages/Dashboard'));
const GetStarted = lazy(async () => import('@/pages/GetStarted'));
const SigningKeys = lazy(async () => import('@/pages/SigningKeys'));
const Dashboard = safeLazy(async () => import('@/pages/Dashboard'));
const GetStarted = safeLazy(async () => import('@/pages/GetStarted'));
const SigningKeys = safeLazy(async () => import('@/pages/SigningKeys'));

export const useConsoleRoutes = () => {
const tenantSettings = useTenantSettings();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { lazy } from 'react';
import { Navigate, type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { ApiResourceDetailsTabs } from '@/consts';

const ApiResources = lazy(async () => import('@/pages/ApiResources'));
const ApiResourceDetails = lazy(async () => import('@/pages/ApiResourceDetails'));
const ApiResourcePermissions = lazy(
const ApiResources = safeLazy(async () => import('@/pages/ApiResources'));
const ApiResourceDetails = safeLazy(async () => import('@/pages/ApiResourceDetails'));
const ApiResourcePermissions = safeLazy(
async () => import('@/pages/ApiResourceDetails/ApiResourcePermissions')
);
const ApiResourceSettings = lazy(
const ApiResourceSettings = safeLazy(
async () => import('@/pages/ApiResourceDetails/ApiResourceSettings')
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { lazy } from 'react';
import { Navigate, type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { ApplicationDetailsTabs } from '@/consts';

const Applications = lazy(async () => import('@/pages/Applications'));
const ApplicationDetails = lazy(async () => import('@/pages/ApplicationDetails'));
const AuditLogDetails = lazy(async () => import('@/pages/AuditLogDetails'));
const Applications = safeLazy(async () => import('@/pages/Applications'));
const ApplicationDetails = safeLazy(async () => import('@/pages/ApplicationDetails'));
const AuditLogDetails = safeLazy(async () => import('@/pages/AuditLogDetails'));

export const applications: RouteObject = {
path: 'applications',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { lazy } from 'react';
import { type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

const AuditLogs = lazy(async () => import('@/pages/AuditLogs'));
const AuditLogDetails = lazy(async () => import('@/pages/AuditLogDetails'));
const AuditLogs = safeLazy(async () => import('@/pages/AuditLogs'));
const AuditLogDetails = safeLazy(async () => import('@/pages/AuditLogDetails'));

export const auditLogs: RouteObject = {
path: 'audit-logs',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { lazy } from 'react';
import { Navigate } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { ConnectorsTabs } from '@/consts';

const Connectors = lazy(async () => import('@/pages/Connectors'));
const ConnectorDetails = lazy(async () => import('@/pages/ConnectorDetails'));
const Connectors = safeLazy(async () => import('@/pages/Connectors'));
const ConnectorDetails = safeLazy(async () => import('@/pages/ConnectorDetails'));

export const connectors = {
path: 'connectors',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { lazy } from 'react';
import { type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

const CustomizeJwt = lazy(async () => import('@/pages/CustomizeJwt'));
const CustomizeJwtDetails = lazy(async () => import('@/pages/CustomizeJwtDetails'));
const CustomizeJwt = safeLazy(async () => import('@/pages/CustomizeJwt'));
const CustomizeJwtDetails = safeLazy(async () => import('@/pages/CustomizeJwtDetails'));

export const customizeJwt: RouteObject = {
path: 'customize-jwt',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { lazy } from 'react';
import { Navigate, type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { EnterpriseSsoDetailsTabs } from '@/consts/page-tabs';

const EnterpriseSso = lazy(async () => import('@/pages/EnterpriseSso'));
const EnterpriseSsoDetails = lazy(async () => import('@/pages/EnterpriseSsoDetails'));
const EnterpriseSso = safeLazy(async () => import('@/pages/EnterpriseSso'));
const EnterpriseSsoDetails = safeLazy(async () => import('@/pages/EnterpriseSsoDetails'));

export const enterpriseSso: RouteObject = {
path: 'enterprise-sso',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { lazy } from 'react';
import { type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

const Mfa = lazy(async () => import('@/pages/Mfa'));
const Mfa = safeLazy(async () => import('@/pages/Mfa'));

export const mfa: RouteObject = { path: 'mfa', element: <Mfa /> };
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { lazy } from 'react';
import { Navigate, type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { OrganizationRoleDetailsTabs, OrganizationTemplateTabs } from '@/consts';

const OrganizationTemplate = lazy(async () => import('@/pages/OrganizationTemplate'));
const OrganizationRoles = lazy(
const OrganizationTemplate = safeLazy(async () => import('@/pages/OrganizationTemplate'));
const OrganizationRoles = safeLazy(
async () => import('@/pages/OrganizationTemplate/OrganizationRoles')
);
const OrganizationPermissions = lazy(
const OrganizationPermissions = safeLazy(
async () => import('@/pages/OrganizationTemplate/OrganizationPermissions')
);
const OrganizationRoleDetails = lazy(async () => import('@/pages/OrganizationRoleDetails'));
const Permissions = lazy(async () => import('@/pages/OrganizationRoleDetails/Permissions'));
const Settings = lazy(async () => import('@/pages/OrganizationRoleDetails/Settings'));
const OrganizationRoleDetails = safeLazy(async () => import('@/pages/OrganizationRoleDetails'));
const Permissions = safeLazy(async () => import('@/pages/OrganizationRoleDetails/Permissions'));
const Settings = safeLazy(async () => import('@/pages/OrganizationRoleDetails/Settings'));

export const organizationTemplate: RouteObject[] = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { condArray } from '@silverhand/essentials';
import { lazy } from 'react';
import { Navigate, type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { OrganizationDetailsTabs } from '@/pages/OrganizationDetails/types';

const Organizations = lazy(async () => import('@/pages/Organizations'));
const OrganizationDetails = lazy(async () => import('@/pages/OrganizationDetails'));
const MachineToMachine = lazy(async () => import('@/pages/OrganizationDetails/MachineToMachine'));
const Members = lazy(async () => import('@/pages/OrganizationDetails/Members'));
const Settings = lazy(async () => import('@/pages/OrganizationDetails/Settings'));
const Organizations = safeLazy(async () => import('@/pages/Organizations'));
const OrganizationDetails = safeLazy(async () => import('@/pages/OrganizationDetails'));
const MachineToMachine = safeLazy(
async () => import('@/pages/OrganizationDetails/MachineToMachine')
);
const Members = safeLazy(async () => import('@/pages/OrganizationDetails/Members'));
const Settings = safeLazy(async () => import('@/pages/OrganizationDetails/Settings'));

export const organizations: RouteObject = {
path: 'organizations',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { lazy } from 'react';
import { type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

const ChangePasswordModal = lazy(
const ChangePasswordModal = safeLazy(
async () => import('@/pages/Profile/containers/ChangePasswordModal')
);
const LinkEmailModal = lazy(async () => import('@/pages/Profile/containers/LinkEmailModal'));
const VerificationCodeModal = lazy(
const LinkEmailModal = safeLazy(async () => import('@/pages/Profile/containers/LinkEmailModal'));
const VerificationCodeModal = safeLazy(
async () => import('@/pages/Profile/containers/VerificationCodeModal')
);
const VerifyPasswordModal = lazy(
const VerifyPasswordModal = safeLazy(
async () => import('@/pages/Profile/containers/VerifyPasswordModal')
);

Expand Down
14 changes: 7 additions & 7 deletions packages/console/src/hooks/use-console-routes/routes/roles.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { lazy } from 'react';
import { Navigate, type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { RoleDetailsTabs } from '@/consts/page-tabs';

const Roles = lazy(async () => import('@/pages/Roles'));
const RoleDetails = lazy(async () => import('@/pages/RoleDetails'));
const RolePermissions = lazy(async () => import('@/pages/RoleDetails/RolePermissions'));
const RoleSettings = lazy(async () => import('@/pages/RoleDetails/RoleSettings'));
const RoleUsers = lazy(async () => import('@/pages/RoleDetails/RoleUsers'));
const RoleApplications = lazy(async () => import('@/pages/RoleDetails/RoleApplications'));
const Roles = safeLazy(async () => import('@/pages/Roles'));
const RoleDetails = safeLazy(async () => import('@/pages/RoleDetails'));
const RolePermissions = safeLazy(async () => import('@/pages/RoleDetails/RolePermissions'));
const RoleSettings = safeLazy(async () => import('@/pages/RoleDetails/RoleSettings'));
const RoleUsers = safeLazy(async () => import('@/pages/RoleDetails/RoleUsers'));
const RoleApplications = safeLazy(async () => import('@/pages/RoleDetails/RoleApplications'));

export const roles: RouteObject = {
path: 'roles',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { lazy } from 'react';
import { Navigate, type RouteObject } from 'react-router-dom';
import { safeLazy } from 'react-safe-lazy';

import { SignInExperienceTab } from '@/pages/SignInExperience/types';

const SignInExperience = lazy(async () => import('@/pages/SignInExperience'));
const SignInExperience = safeLazy(async () => import('@/pages/SignInExperience'));

export const signInExperience: RouteObject = {
path: 'sign-in-experience',
Expand Down
Loading
Loading