From 20c33381f949cd691feaa7fdb82bf5966c14c6a2 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Mon, 31 Oct 2022 10:17:20 -0400 Subject: [PATCH 1/3] Add async loading of react pages --- package-lock.json | 72 +++++++++++++++++++++++++++++++++++++++++--- package.json | 2 ++ src/routes/index.tsx | 65 +++++++++++++++++++++++++-------------- 3 files changed, 112 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98a90f4048..d2787f49c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@fontsource/noto-sans-tc": "4.5.12", "@jellyfin/libass-wasm": "4.1.1", "@jellyfin/sdk": "0.7.0", + "@loadable/component": "5.15.2", "blurhash": "2.0.3", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", "classnames": "2.3.2", @@ -63,6 +64,7 @@ "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "7.18.6", "@types/escape-html": "1.0.2", + "@types/loadable__component": "5.13.4", "@types/lodash-es": "4.17.6", "@types/react": "17.0.51", "@types/react-dom": "17.0.17", @@ -2479,6 +2481,26 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@loadable/component": { + "version": "5.15.2", + "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz", + "integrity": "sha512-ryFAZOX5P2vFkUdzaAtTG88IGnr9qxSdvLRvJySXcUA4B4xVWurUNADu3AnKPksxOZajljqTrDEDcYjeL4lvLw==", + "dependencies": { + "@babel/runtime": "^7.7.7", + "hoist-non-react-statics": "^3.3.1", + "react-is": "^16.12.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, "node_modules/@mdn/browser-compat-data": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-4.1.8.tgz", @@ -2780,6 +2802,15 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "node_modules/@types/loadable__component": { + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@types/loadable__component/-/loadable__component-5.13.4.tgz", + "integrity": "sha512-YhoCCxyuvP2XeZNbHbi8Wb9EMaUJuA2VGHxJffcQYrJKIKSkymJrhbzsf9y4zpTmr5pExAAEh5hbF628PAZ8Dg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/lodash": { "version": "4.14.178", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", @@ -8154,6 +8185,14 @@ "url-toolkit": "^2.1.6" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -12674,8 +12713,7 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-router": { "version": "6.4.2", @@ -20392,6 +20430,16 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "@loadable/component": { + "version": "5.15.2", + "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz", + "integrity": "sha512-ryFAZOX5P2vFkUdzaAtTG88IGnr9qxSdvLRvJySXcUA4B4xVWurUNADu3AnKPksxOZajljqTrDEDcYjeL4lvLw==", + "requires": { + "@babel/runtime": "^7.7.7", + "hoist-non-react-statics": "^3.3.1", + "react-is": "^16.12.0" + } + }, "@mdn/browser-compat-data": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-4.1.8.tgz", @@ -20649,6 +20697,15 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/loadable__component": { + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@types/loadable__component/-/loadable__component-5.13.4.tgz", + "integrity": "sha512-YhoCCxyuvP2XeZNbHbi8Wb9EMaUJuA2VGHxJffcQYrJKIKSkymJrhbzsf9y4zpTmr5pExAAEh5hbF628PAZ8Dg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/lodash": { "version": "4.14.178", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", @@ -24725,6 +24782,14 @@ "url-toolkit": "^2.1.6" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -27943,8 +28008,7 @@ "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "react-router": { "version": "6.4.2", diff --git a/package.json b/package.json index ae0d9beead..4b09b3b5a4 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "7.18.6", "@types/escape-html": "1.0.2", + "@types/loadable__component": "5.13.4", "@types/lodash-es": "4.17.6", "@types/react": "17.0.51", "@types/react-dom": "17.0.17", @@ -75,6 +76,7 @@ "@fontsource/noto-sans-tc": "4.5.12", "@jellyfin/libass-wasm": "4.1.1", "@jellyfin/sdk": "0.7.0", + "@loadable/component": "5.15.2", "blurhash": "2.0.3", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", "classnames": "2.3.2", diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 4c90fe2dd8..45b18447e3 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,47 +1,66 @@ +import loadable from '@loadable/component'; import React from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; import ConnectionRequired from '../components/ConnectionRequired'; import ServerContentPage from '../components/ServerContentPage'; import { LEGACY_ADMIN_ROUTES, LEGACY_USER_ROUTES, toViewManagerPageRoute } from './legacyRoutes'; -import UserNew from './user/usernew'; -import Search from './search'; -import UserEdit from './user/useredit'; -import UserLibraryAccess from './user/userlibraryaccess'; -import UserParentalControl from './user/userparentalcontrol'; -import UserPassword from './user/userpassword'; -import UserProfile from './user/userprofile'; -import UserProfiles from './user/userprofiles'; -import Home from './home'; -import Movies from './movies'; + +interface AsyncPageProps { + page: string +} + +const AsyncPage = loadable( + (props: AsyncPageProps) => import(/* webpackChunkName: "[request]" */ `./${props.page}`), + { cacheKey: (props: AsyncPageProps) => props.page } +); + +interface AsyncRoute { + path: string + page: string +} + +const toAsyncPageRoute = (route: AsyncRoute) => ( + } + /> +); + +const USER_ROUTES: AsyncRoute[] = [ + { path: 'search.html', page: 'search' }, + { path: 'userprofile.html', page: 'user/userprofile' }, + { path: 'home.html', page: 'home' }, + { path: 'movies.html', page: 'movies' } +]; + +const ADMIN_ROUTES: AsyncRoute[] = [ + { path: 'usernew.html', page: 'user/usernew' }, + { path: 'userprofiles.html', page: 'user/userprofiles' }, + { path: 'useredit.html', page: 'user/useredit' }, + { path: 'userlibraryaccess.html', page: 'user/userlibraryaccess' }, + { path: 'userparentalcontrol.html', page: 'user/userparentalcontrol' }, + { path: 'userpassword.html', page: 'user/userpassword' } +]; const AppRoutes = () => ( {/* User routes */} }> - } /> - } /> - } /> - } /> - + {USER_ROUTES.map(toAsyncPageRoute)} {LEGACY_USER_ROUTES.map(toViewManagerPageRoute)} {/* Admin routes */} }> - } /> - } /> - } /> - } /> - } /> - } /> + {ADMIN_ROUTES.map(toAsyncPageRoute)} + {LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)} } /> - - {LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)} {/* Public routes */} From 2bc3bc8a9358e1d5e597393692fc3fcb19c22cf7 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Tue, 1 Nov 2022 16:41:09 -0400 Subject: [PATCH 2/3] Restructure async route code --- src/components/AsyncPage.tsx | 17 ++++++++++++ src/routes/asyncRoutes/admin.ts | 10 +++++++ src/routes/asyncRoutes/index.tsx | 19 +++++++++++++ src/routes/asyncRoutes/user.ts | 8 ++++++ src/routes/index.tsx | 46 ++++---------------------------- 5 files changed, 59 insertions(+), 41 deletions(-) create mode 100644 src/components/AsyncPage.tsx create mode 100644 src/routes/asyncRoutes/admin.ts create mode 100644 src/routes/asyncRoutes/index.tsx create mode 100644 src/routes/asyncRoutes/user.ts diff --git a/src/components/AsyncPage.tsx b/src/components/AsyncPage.tsx new file mode 100644 index 0000000000..4c1e6fa7a2 --- /dev/null +++ b/src/components/AsyncPage.tsx @@ -0,0 +1,17 @@ +import loadable from '@loadable/component'; + +interface AsyncPageProps { + /** The relative path to the page component in the routes directory. */ + page: string +} + +/** + * Page component that uses the loadable component library to load pages defined in the routes directory asynchronously + * with code splitting. + */ +const AsyncPage = loadable( + (props: AsyncPageProps) => import(/* webpackChunkName: "[request]" */ `../routes/${props.page}`), + { cacheKey: (props: AsyncPageProps) => props.page } +); + +export default AsyncPage; diff --git a/src/routes/asyncRoutes/admin.ts b/src/routes/asyncRoutes/admin.ts new file mode 100644 index 0000000000..2d001ed1e4 --- /dev/null +++ b/src/routes/asyncRoutes/admin.ts @@ -0,0 +1,10 @@ +import { AsyncRoute } from '.'; + +export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [ + { path: 'usernew.html', page: 'user/usernew' }, + { path: 'userprofiles.html', page: 'user/userprofiles' }, + { path: 'useredit.html', page: 'user/useredit' }, + { path: 'userlibraryaccess.html', page: 'user/userlibraryaccess' }, + { path: 'userparentalcontrol.html', page: 'user/userparentalcontrol' }, + { path: 'userpassword.html', page: 'user/userpassword' } +]; diff --git a/src/routes/asyncRoutes/index.tsx b/src/routes/asyncRoutes/index.tsx new file mode 100644 index 0000000000..661a41d840 --- /dev/null +++ b/src/routes/asyncRoutes/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Route } from 'react-router-dom'; + +import AsyncPage from '../../components/AsyncPage'; + +export interface AsyncRoute { + /** The URL path for this route. */ + path: string + /** The relative path to the page component in the routes directory. */ + page: string +} + +export const toAsyncPageRoute = (route: AsyncRoute) => ( + } + /> +); diff --git a/src/routes/asyncRoutes/user.ts b/src/routes/asyncRoutes/user.ts new file mode 100644 index 0000000000..1abf687034 --- /dev/null +++ b/src/routes/asyncRoutes/user.ts @@ -0,0 +1,8 @@ +import { AsyncRoute } from '.'; + +export const ASYNC_USER_ROUTES: AsyncRoute[] = [ + { path: 'search.html', page: 'search' }, + { path: 'userprofile.html', page: 'user/userprofile' }, + { path: 'home.html', page: 'home' }, + { path: 'movies.html', page: 'movies' } +]; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 45b18447e3..231a18db11 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,61 +1,25 @@ -import loadable from '@loadable/component'; import React from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; +import { toAsyncPageRoute } from './asyncRoutes'; +import { ASYNC_ADMIN_ROUTES } from './asyncRoutes/admin'; +import { ASYNC_USER_ROUTES } from './asyncRoutes/user'; import ConnectionRequired from '../components/ConnectionRequired'; import ServerContentPage from '../components/ServerContentPage'; import { LEGACY_ADMIN_ROUTES, LEGACY_USER_ROUTES, toViewManagerPageRoute } from './legacyRoutes'; -interface AsyncPageProps { - page: string -} - -const AsyncPage = loadable( - (props: AsyncPageProps) => import(/* webpackChunkName: "[request]" */ `./${props.page}`), - { cacheKey: (props: AsyncPageProps) => props.page } -); - -interface AsyncRoute { - path: string - page: string -} - -const toAsyncPageRoute = (route: AsyncRoute) => ( - } - /> -); - -const USER_ROUTES: AsyncRoute[] = [ - { path: 'search.html', page: 'search' }, - { path: 'userprofile.html', page: 'user/userprofile' }, - { path: 'home.html', page: 'home' }, - { path: 'movies.html', page: 'movies' } -]; - -const ADMIN_ROUTES: AsyncRoute[] = [ - { path: 'usernew.html', page: 'user/usernew' }, - { path: 'userprofiles.html', page: 'user/userprofiles' }, - { path: 'useredit.html', page: 'user/useredit' }, - { path: 'userlibraryaccess.html', page: 'user/userlibraryaccess' }, - { path: 'userparentalcontrol.html', page: 'user/userparentalcontrol' }, - { path: 'userpassword.html', page: 'user/userpassword' } -]; - const AppRoutes = () => ( {/* User routes */} }> - {USER_ROUTES.map(toAsyncPageRoute)} + {ASYNC_USER_ROUTES.map(toAsyncPageRoute)} {LEGACY_USER_ROUTES.map(toViewManagerPageRoute)} {/* Admin routes */} }> - {ADMIN_ROUTES.map(toAsyncPageRoute)} + {ASYNC_ADMIN_ROUTES.map(toAsyncPageRoute)} {LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)} Date: Tue, 1 Nov 2022 16:47:09 -0400 Subject: [PATCH 3/3] Update async route exports --- src/routes/asyncRoutes/index.tsx | 3 +++ src/routes/index.tsx | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/routes/asyncRoutes/index.tsx b/src/routes/asyncRoutes/index.tsx index 661a41d840..9b9df27dd5 100644 --- a/src/routes/asyncRoutes/index.tsx +++ b/src/routes/asyncRoutes/index.tsx @@ -17,3 +17,6 @@ export const toAsyncPageRoute = (route: AsyncRoute) => ( element={} /> ); + +export * from './admin'; +export * from './user'; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 231a18db11..be24c811d8 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,9 +1,7 @@ import React from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; -import { toAsyncPageRoute } from './asyncRoutes'; -import { ASYNC_ADMIN_ROUTES } from './asyncRoutes/admin'; -import { ASYNC_USER_ROUTES } from './asyncRoutes/user'; +import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES, toAsyncPageRoute } from './asyncRoutes'; import ConnectionRequired from '../components/ConnectionRequired'; import ServerContentPage from '../components/ServerContentPage'; import { LEGACY_ADMIN_ROUTES, LEGACY_USER_ROUTES, toViewManagerPageRoute } from './legacyRoutes';