Skip to content
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
23 changes: 0 additions & 23 deletions .eslintrc.js

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 18.x, 20.x]
node-version: [20.x, 22.x]

steps:
- uses: actions/checkout@v4
Expand Down
29 changes: 29 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { defineConfig } from 'eslint/config'
import reactHooks from 'eslint-plugin-react-hooks'
import jest from 'eslint-plugin-jest'
import eslint from '@eslint/js'
import tseslint from 'typescript-eslint'

export default tseslint.config(
// This global ignore syntax is maximum wtf
{ ignores: ['node_modules/', 'coverage/'] },
{
extends: [
eslint.configs.recommended,
tseslint.configs.recommended,
// reactHooks.configs.recommended,
// jest.configs.recommended,
],
files: ['{src,test}/**/*.{ts,tsx}'],

plugins: {
jest,
'react-hooks': reactHooks,
},
rules: {
'react/display-name': 0,
'@typescript-eslint/no-use-before-define': ['error', { functions: false }],
'@typescript-eslint/ban-ts-comment': 0,
},
}
)
14 changes: 6 additions & 8 deletions jest.config.js → jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module.exports = {
import type { Config } from 'jest'

const config: Config = {
clearMocks: true,
collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/main.ts'],
coverageDirectory: 'coverage',
Expand All @@ -10,18 +12,14 @@ module.exports = {
statements: 50,
},
},
globals: {
'ts-jest': {
isolatedModules: true,
},
},
preset: 'ts-jest',
// setupFiles: ['./test/setup.ts'],
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
testEnvironment: 'jest-environment-jsdom',
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
'^.+.tsx?$': ['ts-jest', {}],
},
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
}

export default config
51 changes: 27 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
"dist"
],
"engines": {
"node": ">=12"
"node": ">=20"
},
"scripts": {
"style": "prettier --config package.json --write \"{src,test}/**/*.{ts,tsx}\"",
"style:ci": "prettier --config package.json --check \"{src,test}/**/*.{ts,tsx}\"",
"lint": "eslint \"{src,test}/**/*.{ts,tsx}\" --ext .js,.ts,.tsx --ignore-pattern node_modules/",
"lint": "eslint",
"check": "npm run style && npm run lint",
"check:ci": "npm run style:ci && npm run lint",
"build:clean": "rimraf dist && mkdirp dist",
Expand All @@ -50,7 +50,7 @@
"analyze": "size-limit --why"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"prettier": {
"tabWidth": 2,
Expand All @@ -75,35 +75,38 @@
"yarn": true
},
"devDependencies": {
"@size-limit/preset-small-lib": "^5.0.2",
"@eslint/js": "^9.22.0",
"@rollup/plugin-replace": "^6.0.2",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.2",
"@size-limit/preset-small-lib": "9.x",
"@size-limit/webpack": "9.x",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1",
"@testing-library/react": "15.x",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"eslint": "^8.10.0",
"eslint": "^9.22.0",
"eslint-plugin-import": "^2.24.0",
"eslint-plugin-jest": "^26.1.1",
"eslint-plugin-react": "^7.29.3",
"eslint-plugin-react-hooks": "^4.3.0",
"jest": "^27.2.4",
"jest-junit": "^12.2.0",
"jest-watch-typeahead": "^1.0.0",
"mkdirp": "^1.0.4",
"np": "^7.6.0",
"eslint-plugin-jest": "^28.11.0",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.2.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-junit": "^16.0.0",
"jest-watch-typeahead": "^2.2.2",
"mkdirp": "^3.0.1",
"np": "^10.2.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.5.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"rimraf": "^3.0.2",
"rollup": "^2.56.1",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.36.0",
"size-limit": "7.x",
"ts-jest": "^27.1.4",
"rimraf": "^6.0.1",
"rollup": "^4.35.0",
"size-limit": "9.x",
"ts-jest": "^29.2.6",
"ts-node": "^10.9.2",
"tslib": "^2.8.1",
"typescript": "^4.3.5"
"typescript": "^5.8.2",
"typescript-eslint": "^8.26.1"
}
}
12 changes: 7 additions & 5 deletions rollup.config.js → rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import replace from 'rollup-plugin-replace'
import packageJson from './package.json'
import { terser } from 'rollup-plugin-terser'
import typescript from 'rollup-plugin-typescript2'
import replace from '@rollup/plugin-replace'
import packageJson from './package.json' with { type: "json" }
import terser from '@rollup/plugin-terser'
import typescript from '@rollup/plugin-typescript'

const deps = Object.keys(packageJson.dependencies || []).concat(
Object.keys(packageJson.peerDependencies)
Expand Down Expand Up @@ -34,12 +34,13 @@ export default [
globals: {
react: 'React',
},
interop: false,
// interop: false,
sourcemap: true,
},
external: deps,
plugins: [
replace({
preventAssignment: false,
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
typescript(),
Expand All @@ -60,6 +61,7 @@ export default [
external: deps,
plugins: [
replace({
preventAssignment: false,
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
typescript(),
Expand Down
1 change: 0 additions & 1 deletion src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ export function useMountedLayout(
useLayoutEffect(() => {
if (!hasMounted.current) hasMounted.current = true
else fn()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps)
}
2 changes: 1 addition & 1 deletion src/intercept.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function shouldCancelNavigation(): boolean {
if (!prompt) return false

// cancel navigation if user declines
hasUserCancelled = !window.confirm(prompt) // eslint-disable-line no-alert
hasUserCancelled = !window.confirm(prompt)

// track user response so that multiple interceptors don't prompt
hasIntercepted = true
Expand Down
1 change: 0 additions & 1 deletion src/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ export function useLocationChange(
if (isNode) return

// All hooks after this are conditional, but the runtime can't actually change
/* eslint-disable react-hooks/rules-of-hooks */

const routerBasePath = useBasePath()
if (inheritBasePath && routerBasePath) basePath = routerBasePath
Expand Down
2 changes: 0 additions & 2 deletions src/navigate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export function navigate(url: string, options?: NavigateOptions): void {
export function useNavigationPrompt(predicate = true, prompt: string = defaultPrompt): void {
if (isNode) return

// eslint-disable-next-line react-hooks/rules-of-hooks
useLayoutEffect(() => {
const onPopStateNavigation = () => {
if (shouldCancelNavigation()) {
Expand All @@ -71,7 +70,6 @@ export function useNavigationPrompt(predicate = true, prompt: string = defaultPr
return () => window.removeEventListener('popstate', onPopStateNavigation)
}, [])

// eslint-disable-next-line react-hooks/rules-of-hooks
useLayoutEffect(() => {
const handler = (e?: BeforeUnloadEvent): string | void => {
if (predicate) {
Expand Down
2 changes: 1 addition & 1 deletion src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ let ssrPath = '/'
let isNode = true
try {
isNode = window === undefined
} catch (e) {} // eslint-disable-line no-empty
} catch {} // eslint-disable-line no-empty

export { isNode }
export function getSsrPath(): string {
Expand Down
14 changes: 8 additions & 6 deletions src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export function usePathParams<Path extends string>(
route: Path,
options?: PathParamOptions
): NonEmptyRecord<ExtractPathParams<Path extends `${infer P1}*` ? P1 : Path>> | null

export function usePathParams<Path extends string>(
routes: ReadonlyArray<Path>,
options?: PathParamOptions
Expand All @@ -113,6 +114,7 @@ export function usePathParams<Path extends string>(
]
}>
| [null, null]

export function usePathParams<Params extends ReadonlyArray<string> | string>(
routes: Params,
options: PathParamOptions = {}
Expand All @@ -138,10 +140,12 @@ export function usePathParams<Params extends ReadonlyArray<string> | string>(
// @ts-expect-error inference is not carried forward and I don't know how to resolve this type
if (!routeMatch) return isSingle ? null : emptyPathResult

// @ts-expect-error inference is not carried forward and I don't know how to resolve this type
// @ts-ignore inference is not carried forward and I don't know how to resolve this type
return isSingle
? props
: ([routeMatch.path, props] as ValueOf<{
? // @ts-ignore
props
: // @ts-ignore
([routeMatch.path, props] as ValueOf<{
[P in (typeof routes)[number]]: [
P,
NonEmptyRecord<ExtractPathParams<P extends `${infer P1}*` ? P1 : P>>
Expand All @@ -167,7 +171,6 @@ function usePathOptions(
}

function useMatchers(routes: string[]): RouteMatcher[] {
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(() => routes.map(createRouteMatcher), [hashParams(routes)])
}

Expand All @@ -187,7 +190,6 @@ function getMatchParams(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const props = routeMatch.props.reduce((props: any, prop, i) => {
// The following `match` can't be null because the above return asserts it
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
props[prop] = pathParams![i + 1]
return props
}, {})
Expand All @@ -212,7 +214,7 @@ export function setPath(path: string): void {
if (!isNode) {
throw new Error('This method should only be used in NodeJS environments')
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/no-require-imports
const url = require('url')
setSsrPath(url.resolve(getSsrPath(), path))
}
Expand Down
2 changes: 1 addition & 1 deletion src/typeChecks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export function isFunction(obj: unknown): obj is Function {
return !!obj && typeof obj === 'function'
}
4 changes: 2 additions & 2 deletions test/Link.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useRef } from 'react'
import { render, act, fireEvent } from '@testing-library/react'
import React, { useRef, act } from 'react'
import { render, fireEvent } from '@testing-library/react'

import { useRoutes, navigate, Link, ActiveLink } from '../src/main'

Expand Down
10 changes: 5 additions & 5 deletions test/context.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react'
import { render, act } from '@testing-library/react'
import React, { act } from 'react'
import { render } from '@testing-library/react'

import { useRoutes, navigate, RouteOptionParams, RouteParams } from '../src/main'
import { useRoutes, navigate, RouteOptionParams, Routes } from '../src/main'
import { useRouter } from '../src/context'

beforeEach(() => {
act(() => navigate('/'))
})

describe('useRouter', () => {
function Harness({ routes, options }: { routes: RouteParams; options?: RouteOptionParams }) {
function Harness({ routes, options }: { routes: Routes<string>; options?: RouteOptionParams }) {
const route = useRoutes(routes, options) || <span data-testid="label">not found</span>
return route
}
Expand All @@ -24,7 +24,7 @@ describe('useRouter', () => {
</div>
)
}
const routes: RouteParams = {
const routes: Routes<string> = {
'/': () => <Route label="home" />,
'/about': () => <Route label="about" />,
}
Expand Down
Loading
Loading