Skip to content

Commit

Permalink
chore: update eslint config and fix errors
Browse files Browse the repository at this point in the history
  • Loading branch information
andrepolischuk committed Jan 24, 2024
1 parent d1cb7c6 commit 34de471
Show file tree
Hide file tree
Showing 29 changed files with 797 additions and 383 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"extends": [
"@rambler-tech/eslint-config",
"@rambler-tech/eslint-config/ts",
"@rambler-tech/eslint-config/react",
"@rambler-tech/eslint-config/ts",
"prettier"
]
}
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports = {
moduleDirectories: ['packages', 'node_modules'],
collectCoverage: true,
coverageReporters: ['text'],
transformIgnorePatterns: ['node_modules/(?!' + ['superjson'].join('|') + ')'],
transformIgnorePatterns: [`node_modules/(?!${['superjson'].join('|')})`],
transform: {
'^.+\\.jsx?$': ['ts-jest', {useESM: true}]
}
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,20 @@
"devDependencies": {
"@commitlint/cli": "^17.4.1",
"@commitlint/config-conventional": "^17.4.0",
"@rambler-tech/eslint-config": "^0.1.0",
"@rambler-tech/eslint-config": "^0.9.1",
"@rambler-tech/licenselint": "^1.3.0",
"@rambler-tech/licenselint-config": "^0.0.2",
"@rambler-tech/prettier-config": "^0.1.0",
"@rambler-tech/ts-config": "^0.0.2",
"@rambler-tech/ts-config": "^0.1.0",
"@rambler-tech/typedoc-config": "^0.3.0",
"@size-limit/preset-small-lib": "^9.0.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.2.5",
"@types/node": "^20.8.2",
"@types/react": "^18.2.24",
"@types/react-dom": "^18.2.8",
"eslint": "^8.31.0",
"eslint-config-prettier": "^9.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"express": "^4.18.2",
"history": "^5.3.0",
"husky": "^8.0.3",
Expand Down
1 change: 1 addition & 0 deletions src/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable import/no-unused-modules */
export {type DocumentProps, Document} from '../components/document'
export {type LayoutProps, Layout} from '../components/layout'
export {type ComponentFactory, lazy} from '../components/lazy'
Expand Down
3 changes: 2 additions & 1 deletion src/client/stream.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import {hydrateRoot} from 'react-dom/client'
import {BrowserRouter} from 'react-router-dom'
import {RenderOptions, TransitionMode} from '../common/types'
import {type RenderOptions, TransitionMode} from '../common/types'
import {getState} from '../components/state'
import {AppContextProvider} from '../components/context'
import {matchRoute} from '../components/loader'
Expand Down Expand Up @@ -45,6 +45,7 @@ export const hydrateFromStream = async (
...rest
} = options

// eslint-disable-next-line @arthurgeron/react-usememo/require-usememo
const appContext = {
...state,
...rest
Expand Down
24 changes: 11 additions & 13 deletions src/common/json.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import superjson from 'superjson'

export const stringify = <T extends Record<string, any>>(data: T): string =>
escape(superjson.stringify(data))

export const parse = <T extends Record<string, any>>(string: string): T =>
superjson.parse(string)
import {parse as parseJson, stringify as stringifyJson} from 'superjson'

/**
* NOTE:
Expand All @@ -13,12 +7,6 @@ export const parse = <T extends Record<string, any>>(string: string): T =>
* More info: http://timelessrepo.com/json-isnt-a-javascript-subset
*/

const escape = (value: string): string => value.replace(ESCAPE_REGEX, escaper)

const escaper = (match: string): string => {
return ESCAPE_LOOKUP[match]
}

const ESCAPE_LOOKUP: Record<string, string> = {
'&': '\\u0026',
'>': '\\u003e',
Expand All @@ -28,3 +16,13 @@ const ESCAPE_LOOKUP: Record<string, string> = {
}

const ESCAPE_REGEX = /[&><\u2028\u2029]/g

const escape = (value: string): string => value.replace(ESCAPE_REGEX, escaper)

const escaper = (match: string): string => ESCAPE_LOOKUP[match]

export const stringify = <T extends Record<string, any>>(data: T): string =>
escape(stringifyJson(data))

export const parse = <T extends Record<string, any>>(string: string): T =>
parseJson(string)
6 changes: 3 additions & 3 deletions src/components/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {Request, Response} from 'express'
import type {InitialData, MetaData} from '../common/types'

/** App context interface */
export interface AppContextValue extends Record<string, any> {
interface AppContextValue extends Record<string, any> {
/** Express.js [request](https://expressjs.com/en/4x/api.html#req) object (server-only) */
req?: Request
/** Express [response](https://expressjs.com/en/4x/api.html#res) object (server-only) */
Expand All @@ -20,9 +20,9 @@ export interface AppContextValue extends Record<string, any> {
onChangeMetaData: (meta: MetaData) => void
}

export const AppContext = createContext({} as AppContextValue)
const AppContext = createContext({} as AppContextValue)

export interface AppContextProviderProps {
interface AppContextProviderProps {
value: Omit<AppContextValue, 'onChangeMetaData'>
children: React.ReactNode
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {
} from '../common/types'

/** Page component factory */
export interface ComponentModule {
interface ComponentModule {
default: PageComponent
}

Expand All @@ -19,7 +19,7 @@ export interface ComponentFactory {
}

/** Data factory */
export interface DataFactory<T, C = any> {
interface DataFactory<T, C = any> {
(component: PageComponent, context: Context & C): ReturnType<Loader<T, C>>
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/loader.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {loadRouteData} from './loader'
import {routes} from '../test/routes'
import {loadRouteData} from './loader'

let context: any

Expand Down
8 changes: 4 additions & 4 deletions src/components/loader.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {matchRoutes, type RouteMatch} from 'react-router-dom'
import {Context, PageRoute, InitialData, MetaData} from '../common/types'
import type {Context, PageRoute, InitialData, MetaData} from '../common/types'

/** Match route options */
export interface MatchRouteOptions {
interface MatchRouteOptions {
pathname: string
routes: PageRoute[]
}
Expand All @@ -18,12 +18,12 @@ export const matchRoute = ({
}

/** Route loader options */
export interface LoadRouteDataOptions extends MatchRouteOptions {
interface LoadRouteDataOptions extends MatchRouteOptions {
context: Context
}

/** Route data */
export interface RouteData {
interface RouteData {
data?: InitialData
meta?: MetaData
match?: RouteMatch
Expand Down
46 changes: 25 additions & 21 deletions src/components/meta.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,6 @@
import React from 'react'
import {useAppContext} from './context'

/** Default meta data component */
export const Meta: React.FC = () => {
const {meta = {}} = useAppContext()

return (
<>
{Object.entries(meta).map(([name, content = '']) =>
name === 'title' ? (
<title key={name}>{content}</title>
) : HEAD_LINKS_RELS.includes(name) ? (
<link key={`${name}${content}`} rel={name} href={content} />
) : name.startsWith('og:') ? (
<meta key={name} property={name} content={content} />
) : (
<meta key={name} name={name} content={content} />
)
)}
</>
)
}

const HEAD_LINKS_RELS = [
'canonical',
'alternate',
Expand All @@ -34,3 +13,28 @@ const HEAD_LINKS_RELS = [
'preload',
'stylesheet'
]

/** Default meta data component */
export const Meta: React.FC = () => {
const {meta = {}} = useAppContext()

return (
<>
{Object.entries(meta).map(([name, content = '']) => {
if (name === 'title') {
return <title key={name}>{content}</title>
}

if (HEAD_LINKS_RELS.includes(name)) {
return <link key={`${name}${content}`} rel={name} href={content} />
}

if (name.startsWith('og:')) {
return <meta key={name} property={name} content={content} />
}

return <meta key={name} name={name} content={content} />
})}
</>
)
}
34 changes: 19 additions & 15 deletions src/components/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {useAppContext} from './context'
import {loadRouteData, matchRoute} from './loader'

/** Routing props */
export interface RoutesProps {
interface RoutesProps {
routes: PageRoute[]
scrollToTop?: boolean
transition?: TransitionMode
Expand Down Expand Up @@ -46,7 +46,7 @@ export const Routes: React.FC<RoutesProps> = ({
const isBlockedMode = !isWaitingMode && transition !== TransitionMode.INSTANT

useEffect(() => {
if (location !== currentLocation) {
const onNavigate = async (): Promise<void> => {
const {pathname} = location

const context = {
Expand All @@ -57,26 +57,30 @@ export const Routes: React.FC<RoutesProps> = ({
...rest
}

setRouteData((prevState) => ({...prevState, isLoading: true}))
setRouteData((previousState) => ({...previousState, isLoading: true}))

if (scrollToTop && !isBlockedMode) {
window.scrollTo(0, 0)
}

loadRouteData({pathname, routes, context}).then((routeData) => {
if (scrollToTop && isBlockedMode) {
window.scrollTo(0, 0)
}
const routeData = await loadRouteData({pathname, routes, context})

if (scrollToTop && isBlockedMode) {
window.scrollTo(0, 0)
}

setRouteData((prevState) => ({
...prevState,
...routeData,
isLoading: false
}))
setRouteData((previousState) => ({
...previousState,
...routeData,
isLoading: false
}))

setCurrentLocation(location)
onChangeMetaData?.(routeData.meta ?? {})
})
setCurrentLocation(location)
onChangeMetaData?.(routeData.meta ?? {})
}

if (location !== currentLocation) {
onNavigate()
}
}, [location, currentLocation, scrollToTop]) // eslint-disable-line react-hooks/exhaustive-deps

Expand Down
4 changes: 3 additions & 1 deletion src/components/scripts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ import {useAppContext} from './context'
export const Scripts: React.FC = () => {
const {scripts} = useAppContext()

return <>{scripts?.map((src) => <script key={src} src={src} defer />)}</>
return (
<>{scripts?.map((source) => <script key={source} src={source} defer />)}</>
)
}
1 change: 1 addition & 0 deletions src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/* eslint-disable import/no-unused-modules */
export {type RenderToStreamOptions, renderToStream} from './stream'
2 changes: 1 addition & 1 deletion src/server/stream.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Writable} from 'stream'
import {renderToStream} from './stream'
import {routes} from '../test/routes'
import {Layout} from '../test/components/layout'
import {Document} from '../test/components/document'
import {renderToStream} from './stream'

class MockResponse extends Writable {
data = ''
Expand Down
10 changes: 7 additions & 3 deletions src/server/stream.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import {renderToPipeableStream} from 'react-dom/server'
import {StaticRouter} from 'react-router-dom/server'
import type {Location} from 'history'
import type {Request, Response} from 'express'
import {RenderOptions} from '../common/types'
import type {RenderOptions} from '../common/types'
import {AppContextProvider} from '../components/context'
import {loadRouteData} from '../components/loader'
import {Routes} from '../components/routes'
import {Layout as BaseLayout} from '../components/layout'
import {Document as BaseDocument} from '../components/document'

const OK = 200
const FOUND = 302

/** Render to stream options */
export interface RenderToStreamOptions extends RenderOptions {
/** Express.js [request](https://expressjs.com/en/4x/api.html#req) object (server-only) */
Expand Down Expand Up @@ -86,11 +89,12 @@ export const renderToStream = async (
const {data, meta} = await loadRouteData({pathname, routes, context})

if (data?.redirect) {
return res.redirect(data?.statusCode ?? 302, data.redirect)
return res.redirect(data?.statusCode ?? FOUND, data.redirect)
}

res.status(data?.statusCode ?? 200)
res.status(data?.statusCode ?? OK)

// eslint-disable-next-line @arthurgeron/react-usememo/require-usememo
const appContext = {
req,
res,
Expand Down
9 changes: 5 additions & 4 deletions src/test/components/custom-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ const CustomContext: PageComponent<CustomContextProps> = ({store}) => (
</>
)

CustomContext.getMetaData = async () => ({
title: 'Custom context'
})
CustomContext.getMetaData = () =>
Promise.resolve({
title: 'Custom context'
})

CustomContext.getInitialData = async ({store}) => {
store.dispatch({message: 'Hello'})
await store.dispatch({message: 'Hello'})
}

export default CustomContext
8 changes: 4 additions & 4 deletions src/test/components/document.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, {FC, ReactNode} from 'react'
import React from 'react'
import {Meta, Preloads, Styles, Scripts, State} from '../../client'

export interface DocumentProps {
children: ReactNode
interface DocumentProps {
children: React.ReactNode
}

export const Document: FC<DocumentProps> = ({children}) => (
export const Document: React.FC<DocumentProps> = ({children}) => (
<html lang="ru">
<head>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
Expand Down
Loading

0 comments on commit 34de471

Please sign in to comment.