+
diff --git a/src/router/Router.tsx b/src/router/Router.tsx
index 97ed8f9..44c517d 100644
--- a/src/router/Router.tsx
+++ b/src/router/Router.tsx
@@ -1,4 +1,4 @@
-import { RouteObject } from 'react-router-dom'
+import type { RouteObject } from 'react-router-dom'
import { ROUTE_CONSTANTS } from 'constants/routeConstants'
import { About, Fetch, Home, NotFound } from 'pages'
@@ -6,30 +6,30 @@ import { About, Fetch, Home, NotFound } from 'pages'
const routes: RouteObject[] = [
{
path: '*',
- element:
+ element:
,
},
{
path: ROUTE_CONSTANTS.HOME,
- element:
+ element:
,
},
{
path: ROUTE_CONSTANTS.FETCH,
- element:
+ element:
,
},
{
path: ROUTE_CONSTANTS.ABOUT,
- element:
+ element:
,
},
{
path: ROUTE_CONSTANTS.NOT_FOUND,
- element:
+ element:
,
},
{
path: 'sw.js',
loader: async () => {
return await fetch('sw.js')
- }
- }
+ },
+ },
]
export { routes }
diff --git a/src/server/middlewares/apiRequest.ts b/src/server/middlewares/apiRequest.ts
index 7edf810..2ad97b7 100644
--- a/src/server/middlewares/apiRequest.ts
+++ b/src/server/middlewares/apiRequest.ts
@@ -1,13 +1,16 @@
-import { EnhancedStore } from '@reduxjs/toolkit'
+import type { EnhancedStore } from '@reduxjs/toolkit'
import { pokemonApi } from 'api'
const apiRequest = async (
- store: EnhancedStore
+ // biome-ignore lint/suspicious/noExplicitAny: Created for complex sample API
+ store: EnhancedStore,
+ // biome-ignore lint/suspicious/noExplicitAny: Created for complex sample API
): Promise => {
store.dispatch(pokemonApi.endpoints.getPokemonSpriteById.initiate(1))
+ // biome-ignore lint/suspicious/noExplicitAny: Created for complex sample API
return await Promise.all(
- store.dispatch(pokemonApi.util.getRunningQueriesThunk())
+ store.dispatch(pokemonApi.util.getRunningQueriesThunk()),
)
}
diff --git a/src/server/middlewares/csp.ts b/src/server/middlewares/csp.ts
index 0e678f3..0fdf75c 100644
--- a/src/server/middlewares/csp.ts
+++ b/src/server/middlewares/csp.ts
@@ -1,6 +1,6 @@
+import { randomUUID } from 'node:crypto'
+import type { NextFunction, Request, Response } from 'express'
import helmet from 'helmet'
-import { randomUUID } from 'crypto'
-import { Response, Request, NextFunction } from 'express'
import { IS_DEV } from 'rspack/constants'
const nonce = (_req: Request, res: Response, next: NextFunction): void => {
@@ -8,7 +8,7 @@ const nonce = (_req: Request, res: Response, next: NextFunction): void => {
next()
}
-const csp = (req: Request, res: Response, next: NextFunction): void => {
+const csp = (req: Request, res: Response, next: NextFunction) => {
const middleware = helmet({
contentSecurityPolicy: {
useDefaults: true,
@@ -18,13 +18,13 @@ const csp = (req: Request, res: Response, next: NextFunction): void => {
scriptSrc: [
"'self'",
`'nonce-${String(res.locals.cspNonce)}'`,
- IS_DEV ? "'unsafe-eval'" : ''
- ]
- }
+ IS_DEV ? "'unsafe-eval'" : '',
+ ],
+ },
},
crossOriginEmbedderPolicy: { policy: 'credentialless' },
noSniff: false,
- originAgentCluster: false
+ originAgentCluster: false,
})
return middleware(req, res, next)
diff --git a/src/server/middlewares/hotReload.ts b/src/server/middlewares/hotReload.ts
index 6744be9..d2cbff4 100644
--- a/src/server/middlewares/hotReload.ts
+++ b/src/server/middlewares/hotReload.ts
@@ -1,5 +1,5 @@
import { rspack } from '@rspack/core'
-import { RequestHandler } from 'express'
+import type { RequestHandler } from 'express'
import devMiddleware from 'webpack-dev-middleware'
import hotMiddleware from 'webpack-hot-middleware'
@@ -7,6 +7,7 @@ import { clientConfig as config } from 'rspack/client.config'
const compiler = rspack({ ...config, mode: 'development' })
+// biome-ignore lint/suspicious/noExplicitAny: Rspack types incompatibility
export const devMiddlewareInstance = devMiddleware(compiler as any, {
serverSideRender: true,
/*
@@ -15,10 +16,11 @@ export const devMiddlewareInstance = devMiddleware(compiler as any, {
*/
writeToDisk: false,
publicPath:
- config.output?.publicPath != null ? String(config.output.publicPath) : '/'
+ config.output?.publicPath != null ? String(config.output.publicPath) : '/',
})
-export function hotReload (): RequestHandler[] {
+export function hotReload(): RequestHandler[] {
+ // biome-ignore lint/suspicious/noExplicitAny: Rspack types incompatibility
return [devMiddlewareInstance, hotMiddleware(compiler as any)]
}
diff --git a/src/server/middlewares/serverRenderer.tsx b/src/server/middlewares/serverRenderer.tsx
index 21daa41..2bca28b 100644
--- a/src/server/middlewares/serverRenderer.tsx
+++ b/src/server/middlewares/serverRenderer.tsx
@@ -1,52 +1,52 @@
-import { HelmetProvider, HelmetServerState } from 'react-helmet-async'
+import { getDataFromTree } from '@apollo/react-ssr'
+import type { ChunkExtractor } from '@loadable/server'
+import type { Request, RequestHandler, Response } from 'express'
import { renderToPipeableStream, renderToString } from 'react-dom/server'
-import { Request, Response, RequestHandler } from 'express'
+import { HelmetProvider, type HelmetServerState } from 'react-helmet-async'
import { StaticRouter } from 'react-router-dom/server'
-import { ChunkExtractor } from '@loadable/server'
-import { getDataFromTree } from '@apollo/react-ssr'
-import { initStore, RootState } from 'store/store'
import { Provider } from 'react-redux'
+import { type RootState, initStore } from 'store/store'
-import { App } from 'src/App'
+import { THEME_NAMES } from 'constants/commonConstants'
import { ROUTE_CONSTANTS } from 'constants/routeConstants'
+import { IS_RENDER_TO_STREAM } from 'server/constants'
import { apiRequest, setTranslations } from 'server/middlewares'
import { getHtmlTemplate } from 'server/template'
-import { IS_RENDER_TO_STREAM } from 'server/constants'
-import { THEME_NAMES } from 'constants/commonConstants'
+import { App } from 'src/App'
const serverRenderer =
(chunkExtractor: ChunkExtractor): RequestHandler =>
- async (req: Request, res: Response) => {
- const isPageAvailable = Object.values(ROUTE_CONSTANTS).includes(req.path)
+ async (req: Request, res: Response) => {
+ const isPageAvailable = Object.values(ROUTE_CONSTANTS).includes(req.path)
- if (!isPageAvailable) {
- req.url = ROUTE_CONSTANTS.NOT_FOUND
- }
+ if (!isPageAvailable) {
+ req.url = ROUTE_CONSTANTS.NOT_FOUND
+ }
- const location: string = req.url
+ const location: string = req.url
- /*
+ /*
Check user accepted languages. If it is different from the default
set it in the root state initial state including all needed translation.
As a result rendering will be done with this language.
*/
- const i18nState = await setTranslations(req)
+ const i18nState = await setTranslations(req)
- let preloadedState: Partial = {
- counter: { value: 42 },
- theme: {
- theme:
+ let preloadedState: Partial = {
+ counter: { value: 42 },
+ theme: {
+ theme:
req.headers['sec-ch-prefers-color-scheme'] === 'dark'
? THEME_NAMES.DARK
- : THEME_NAMES.LIGHT
- },
- ...i18nState
- }
+ : THEME_NAMES.LIGHT,
+ },
+ ...i18nState,
+ }
- const store = initStore(preloadedState)
+ const store = initStore(preloadedState)
- /*
+ /*
Prefetching with RTK Query:
- Get data;
- Put it into the store;
@@ -56,73 +56,73 @@ const serverRenderer =
Note: Why not just get data during SSR?
Because rendering will be done before resolving the request Promise.
*/
- await apiRequest(store)
- preloadedState = { ...store.getState() }
+ await apiRequest(store)
+ preloadedState = { ...store.getState() }
- const helmetContext = {}
+ const helmetContext = {}
- /*
+ /*
react-helmet-async forgot to export this interface after migrating to TypeScript in v2+.
*/
- interface HelmetDataContext {
- helmet: HelmetServerState
- }
-
- const jsx = (
-
-
-
-
-
-
-
- )
-
- if (IS_RENDER_TO_STREAM) {
- await getDataFromTree(jsx)
-
- const { helmet } = helmetContext as HelmetDataContext
-
- const { header, footer } = getHtmlTemplate({
- preloadedState,
- helmetData: helmet,
- scriptTags: chunkExtractor.getScriptTags({
- nonce: res.locals.cspNonce
- }),
- styleTags: chunkExtractor.getStyleTags(),
+ interface HelmetDataContext {
+ helmet: HelmetServerState
+ }
+
+ const jsx = (
+
+
+
+
+
+
+
+ )
+
+ if (IS_RENDER_TO_STREAM) {
+ await getDataFromTree(jsx)
+
+ const { helmet } = helmetContext as HelmetDataContext
+
+ const { header, footer } = getHtmlTemplate({
+ preloadedState,
+ helmetData: helmet,
+ scriptTags: chunkExtractor.getScriptTags({
nonce: res.locals.cspNonce,
- lang: i18nState.i18n.lang
- })
-
- res.write(header)
- let didError = false
- const stream = renderToPipeableStream(jsx, {
- onShellReady () {
- res.statusCode = didError ? 500 : 200
- stream.pipe(res)
- },
- onAllReady () {
- res.end(footer)
- },
- onError (err) {
- didError = true
- console.error(err)
- }
- })
- } else {
- const reactHtml = renderToString(jsx)
- const { helmet } = helmetContext as HelmetDataContext
-
- const { header, footer } = getHtmlTemplate({
- preloadedState,
- helmetData: helmet,
- scriptTags: chunkExtractor.getScriptTags(),
- styleTags: chunkExtractor.getStyleTags(),
- nonce: res.locals.cspNonce
- })
-
- res.send(header + reactHtml + footer)
- }
+ }),
+ styleTags: chunkExtractor.getStyleTags(),
+ nonce: res.locals.cspNonce,
+ lang: i18nState.i18n.lang,
+ })
+
+ res.write(header)
+ let didError = false
+ const stream = renderToPipeableStream(jsx, {
+ onShellReady() {
+ res.statusCode = didError ? 500 : 200
+ stream.pipe(res)
+ },
+ onAllReady() {
+ res.end(footer)
+ },
+ onError(err) {
+ didError = true
+ console.error(err)
+ },
+ })
+ } else {
+ const reactHtml = renderToString(jsx)
+ const { helmet } = helmetContext as HelmetDataContext
+
+ const { header, footer } = getHtmlTemplate({
+ preloadedState,
+ helmetData: helmet,
+ scriptTags: chunkExtractor.getScriptTags(),
+ styleTags: chunkExtractor.getStyleTags(),
+ nonce: res.locals.cspNonce,
+ })
+
+ res.send(header + reactHtml + footer)
}
+ }
export { serverRenderer }
diff --git a/src/server/middlewares/setTranslations.ts b/src/server/middlewares/setTranslations.ts
index 93dce74..388340d 100644
--- a/src/server/middlewares/setTranslations.ts
+++ b/src/server/middlewares/setTranslations.ts
@@ -1,13 +1,13 @@
-import { Request } from 'express'
+import type { Request } from 'express'
-import { RootState } from 'store/store'
import {
+ type TSupportedLanguages,
+ type TTranslations,
defaultLang,
supportedLangs,
- TSupportedLanguages,
- TTranslations
} from 'i18n/i18nConfig'
import { initialState } from 'i18n/i18nSlice'
+import type { RootState } from 'store/store'
interface ITranslations {
[key: string]: string | ITranslations
@@ -15,23 +15,25 @@ interface ITranslations {
const loadTranslations = async (lang: string): Promise => {
const json = await import(`i18n/translations/${lang}.json`, {
- assert: { type: 'json' }
+ assert: { type: 'json' },
})
return json.default
}
export const setTranslations = async (
- req: Request
+ req: Request,
): Promise> => {
const i18nState = { ...initialState }
const userAcceptsLanguages = req.acceptsLanguages(
- Object.keys(supportedLangs)
+ Object.keys(supportedLangs),
) as keyof TSupportedLanguages | boolean
let lang = defaultLang
- userAcceptsLanguages === false
- ? (lang = defaultLang)
- : (lang = userAcceptsLanguages as keyof TSupportedLanguages)
+ if (userAcceptsLanguages === false) {
+ lang = defaultLang
+ } else {
+ lang = userAcceptsLanguages as keyof TSupportedLanguages
+ }
if (userAcceptsLanguages === defaultLang) return { i18n: i18nState }
diff --git a/src/server/server.ts b/src/server/server.ts
index f820a5e..48d00c3 100644
--- a/src/server/server.ts
+++ b/src/server/server.ts
@@ -1,12 +1,12 @@
-import path from 'path'
-import express, { RequestHandler } from 'express'
+import path from 'node:path'
+import { ChunkExtractor } from '@loadable/server'
import compression from 'compression'
import cookieParser from 'cookie-parser'
-import { ChunkExtractor } from '@loadable/server'
+import express, { type RequestHandler } from 'express'
-import { csp, serverRenderer, nonce } from 'server/middlewares'
-import { IS_RENDER_TO_STREAM, SERVER_PORT } from 'server/constants'
import { DIST_DIR, IS_DEV, SRC_DIR } from 'rspack/constants'
+import { IS_RENDER_TO_STREAM, SERVER_PORT } from 'server/constants'
+import { csp, nonce, serverRenderer } from 'server/middlewares'
const { PORT = SERVER_PORT } = process.env
@@ -39,7 +39,7 @@ const runServer = (hotReload?: () => RequestHandler[]): void => {
console.log(
`App listening on port ${PORT}! (render to ${
IS_RENDER_TO_STREAM ? 'stream' : 'string'
- })`
+ })`,
)
})
}
diff --git a/src/server/template.ts b/src/server/template.ts
index f1150c0..4e477e1 100644
--- a/src/server/template.ts
+++ b/src/server/template.ts
@@ -1,6 +1,6 @@
-import { HelmetServerState } from 'react-helmet-async'
+import type { HelmetServerState } from 'react-helmet-async'
import serialize from 'serialize-javascript'
-import { RootState } from 'store/store'
+import type { RootState } from 'store/store'
interface TTemplate {
header: string
@@ -35,10 +35,10 @@ export const getHtmlTemplate = (props: {
${props.scriptTags}