From 63a44a633159d6e16161e407fa4d6869fdb70623 Mon Sep 17 00:00:00 2001 From: Szymon Wiszczuk <37072867+golota60@users.noreply.github.com> Date: Tue, 13 Dec 2022 17:00:12 +0100 Subject: [PATCH] feat: more types (#1014) * chore: setup tests * update lock * switch to babel * hooks types * next batch * couplea globals * createRender * fixes * fix ts build * fix redirect file * more fixes * fixes * rm comment * lint --- package.json | 2 +- src/{Redirect.js => Redirect.tsx} | 16 +- src/RouterContext.ts | 3 +- ...{StaticContainer.js => StaticContainer.ts} | 8 +- src/additional.d.ts | 12 +- src/{createRender.js => createRender.tsx} | 12 +- ...erObject.js => createStoreRouterObject.ts} | 9 +- src/{foundReducer.js => foundReducer.ts} | 25 +- ...oreRenderArgs.js => getStoreRenderArgs.ts} | 6 +- src/{hotRouteConfig.js => hotRouteConfig.ts} | 4 +- ...{makeRouteConfig.js => makeRouteConfig.ts} | 28 +- src/typeUtils.ts | 550 ++++++++++++++++++ src/useMatch.js | 5 - src/useMatch.ts | 7 + src/useParams.js | 5 - src/useParams.ts | 7 + src/useRouter.js | 7 - src/useRouter.ts | 11 + yarn.lock | 39 +- 19 files changed, 670 insertions(+), 86 deletions(-) rename src/{Redirect.js => Redirect.tsx} (61%) rename src/{StaticContainer.js => StaticContainer.ts} (65%) rename src/{createRender.js => createRender.tsx} (61%) rename src/{createStoreRouterObject.js => createStoreRouterObject.ts} (66%) rename src/{foundReducer.js => foundReducer.ts} (59%) rename src/{getStoreRenderArgs.js => getStoreRenderArgs.ts} (71%) rename src/{hotRouteConfig.js => hotRouteConfig.ts} (75%) rename src/{makeRouteConfig.js => makeRouteConfig.ts} (65%) create mode 100644 src/typeUtils.ts delete mode 100644 src/useMatch.js create mode 100644 src/useMatch.ts delete mode 100644 src/useParams.js create mode 100644 src/useParams.ts delete mode 100644 src/useRouter.js create mode 100644 src/useRouter.ts diff --git a/package.json b/package.json index b58b5983..92a73731 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "scripts": { "tsc": "tsc", "build:ts": "yarn tsc --outDir lib/cjs --module commonjs && yarn tsc --outDir lib/esm --module es2020 && mv lib/esm/*.d.ts lib && mv lib/cjs/*.d.ts lib", - "build": "rimraf lib && 4c build --types false src && yarn build:pick && yarn build:types && yarn build:ts", + "build": "rimraf lib && 4c build --types false src && yarn build:pick && yarn build:types", "build:pick": "cherry-pick --cjs-dir cjs --esm-dir esm --cwd lib ../src", "build:types": "cpy types/*.d.ts lib", "build:docs": "yarn --cwd www build", diff --git a/src/Redirect.js b/src/Redirect.tsx similarity index 61% rename from src/Redirect.js rename to src/Redirect.tsx index 3f7d8c9e..c7b33507 100644 --- a/src/Redirect.js +++ b/src/Redirect.tsx @@ -1,14 +1,18 @@ import RedirectException from './RedirectException'; +import { Match, RedirectOptions } from './typeUtils'; -export default class Redirect { - constructor({ from, to, status }) { +class Redirect { + constructor({ from, to, status }: RedirectOptions) { + // @ts-ignore this.path = from; + // @ts-ignore this.to = to; + // @ts-ignore this.status = status; } - render({ match }) { - const { to, status } = this; + render({ match }: { match: Match }) { + const { to, status } = this as any; let toLocation; if (typeof to === 'function') { @@ -25,5 +29,7 @@ export default class Redirect { if (__DEV__) { // Workaround to make React Proxy give me the original class, to allow // makeRouteConfig to get the actual class, when using JSX for routes. - Redirect.prototype.isReactComponent = {}; + (Redirect.prototype as any).isReactComponent = {}; } + +export default Redirect; diff --git a/src/RouterContext.ts b/src/RouterContext.ts index 51b82c7d..d22769d1 100644 --- a/src/RouterContext.ts +++ b/src/RouterContext.ts @@ -1,3 +1,4 @@ import React from 'react'; -export default React.createContext(null); +// TODO: types +export default React.createContext(null); diff --git a/src/StaticContainer.js b/src/StaticContainer.ts similarity index 65% rename from src/StaticContainer.js rename to src/StaticContainer.ts index c3c8ff91..3763c659 100644 --- a/src/StaticContainer.js +++ b/src/StaticContainer.ts @@ -1,8 +1,12 @@ /* eslint-disable react/prop-types */ import React from 'react'; -class StaticContainer extends React.Component { - shouldComponentUpdate({ shouldUpdate }) { +interface Props { + shouldUpdate: boolean; +} + +class StaticContainer extends React.Component { + shouldComponentUpdate({ shouldUpdate }: Props) { return !!shouldUpdate; } diff --git a/src/additional.d.ts b/src/additional.d.ts index c32531b6..272ecc99 100644 --- a/src/additional.d.ts +++ b/src/additional.d.ts @@ -1,3 +1,13 @@ /* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable no-underscore-dangle */ -declare let __DEV__: boolean; + +declare global { + declare let __DEV__: boolean; + interface Window { + __FOUND_HOT_RELOAD__: boolean; + __FOUND_REPLACE_ROUTE_CONFIG__: (routeConfig: RouteConfig) => void; + } +} + +// Force this to be a module +export {}; diff --git a/src/createRender.js b/src/createRender.tsx similarity index 61% rename from src/createRender.js rename to src/createRender.tsx index 00009176..19cfe786 100644 --- a/src/createRender.js +++ b/src/createRender.tsx @@ -2,6 +2,7 @@ import React from 'react'; import ElementsRenderer from './ElementsRenderer'; import StaticContainer from './StaticContainer'; +import { CreateRenderOptions, RenderArgs } from './typeUtils'; /** * A convenience method for handling the 3 main states a route match might produce. @@ -10,17 +11,18 @@ export default function createRender({ renderPending, renderReady, renderError, -}) { - return function render(renderArgs) { - const { error, elements } = renderArgs; +}: CreateRenderOptions): (renderArgs: RenderArgs) => React.ReactElement { + return function render(renderArgs: RenderArgs): React.ReactElement { + // TODO: error and elements MIGHT exist, depending on type of renderArgs + const { error, elements } = renderArgs as any; let element; if (error) { - element = renderError ? renderError(renderArgs) : null; + element = renderError ? renderError(renderArgs as any) : null; } else if (!elements) { element = renderPending ? renderPending(renderArgs) : undefined; } else if (renderReady) { - element = renderReady(renderArgs); + element = renderReady(renderArgs as any); } else { element = ; } diff --git a/src/createStoreRouterObject.js b/src/createStoreRouterObject.ts similarity index 66% rename from src/createStoreRouterObject.js rename to src/createStoreRouterObject.ts index fa10a559..ed961f4c 100644 --- a/src/createStoreRouterObject.js +++ b/src/createStoreRouterObject.ts @@ -1,5 +1,7 @@ import FarceActions from 'farce/Actions'; -import { bindActionCreators } from 'redux'; +import { Store, bindActionCreators } from 'redux'; + +import { Router } from './typeUtils'; const NAVIGATION_ACTION_CREATORS = { push: FarceActions.push, @@ -7,8 +9,9 @@ const NAVIGATION_ACTION_CREATORS = { go: FarceActions.go, }; -export default function createStoreRouterObject(store) { - const { farce, found } = store; +export default function createStoreRouterObject(store: Store): Router { + // TODO: create an enhanced store type with found and farce maybe? + const { farce, found } = store as any; const { matcher } = found; return { diff --git a/src/foundReducer.js b/src/foundReducer.ts similarity index 59% rename from src/foundReducer.js rename to src/foundReducer.ts index d368f072..5bc1d72f 100644 --- a/src/foundReducer.js +++ b/src/foundReducer.ts @@ -1,6 +1,13 @@ +import { Reducer } from 'redux'; + import ActionTypes from './ActionTypes'; +import { FoundState } from './typeUtils'; -export default function foundReducer(state = null, action) { +// TODO: Re-check types here. +const foundReducer = ( + state: any = null, + action: { type: any; payload: any }, +) => { const { type, payload } = action; switch (type) { @@ -14,13 +21,15 @@ export default function foundReducer(state = null, action) { case ActionTypes.RESOLVE_MATCH: // It doesn't make sense to resolve a match if there wasn't already an // unresolved match. - return ( - state && { - match: state.match, - resolvedMatch: payload, - } - ); + return state + ? { + match: state.match, + resolvedMatch: payload, + } + : null; default: return state; } -} +}; + +export default foundReducer as Reducer; diff --git a/src/getStoreRenderArgs.js b/src/getStoreRenderArgs.ts similarity index 71% rename from src/getStoreRenderArgs.js rename to src/getStoreRenderArgs.ts index 41510f19..bf1d6a7d 100644 --- a/src/getStoreRenderArgs.js +++ b/src/getStoreRenderArgs.ts @@ -1,14 +1,16 @@ import createStoreRouterObject from './createStoreRouterObject'; import getRenderArgs from './getRenderArgs'; +import { GetStoreRenderArgsOptions, RenderArgs } from './typeUtils'; // This function returns a promise. It doesn't need to be an async function // because it doesn't use the promise's value. export default function getStoreRenderArgs({ store, - getFound = ({ found }) => found, + // TODO: check types of this + getFound = ({ found }: any) => found, matchContext, resolver, -}) { +}: GetStoreRenderArgsOptions): Promise { const router = createStoreRouterObject(store); const match = getFound(store.getState()).resolvedMatch; diff --git a/src/hotRouteConfig.js b/src/hotRouteConfig.ts similarity index 75% rename from src/hotRouteConfig.js rename to src/hotRouteConfig.ts index f4f14ecc..ea9a854a 100644 --- a/src/hotRouteConfig.js +++ b/src/hotRouteConfig.ts @@ -1,4 +1,6 @@ -export default function hotRouteConfig(routeConfig) { +import { RouteConfig } from './typeUtils'; + +export default function hotRouteConfig(routeConfig: RouteConfig): RouteConfig { if (__DEV__ && typeof window !== 'undefined') { /* eslint-env browser */ /* eslint-disable no-underscore-dangle */ diff --git a/src/makeRouteConfig.js b/src/makeRouteConfig.ts similarity index 65% rename from src/makeRouteConfig.js rename to src/makeRouteConfig.ts index 59d0ff35..a7b31590 100644 --- a/src/makeRouteConfig.js +++ b/src/makeRouteConfig.ts @@ -1,6 +1,12 @@ import React from 'react'; -function buildRouteConfig(node, routeConfig) { +import { RouteConfig } from './typeUtils'; + +// TODO: what is even happening in this file? Try to fix as any's(+ maybe simplify) +function buildRouteConfig( + node: React.ReactNode, + routeConfig: any[], +): RouteConfig { React.Children.forEach(node, (child) => { // Falsy children get coerced to null. We check for this instead of // implicit falsiness because we don't want to allow empty strings or 0. @@ -12,7 +18,7 @@ function buildRouteConfig(node, routeConfig) { throw new TypeError(`\`${child}\` is not a valid React element`); } - let Type = child.type; + let Type = child.type as React.JSXElementConstructor; const { children, ...props } = child.props; if (Type === React.Fragment) { @@ -21,13 +27,13 @@ function buildRouteConfig(node, routeConfig) { } if (__DEV__) { - if (Type.prototype.constructor !== Type) { + if ((Type as any).prototype.constructor !== Type) { // Unwrap proxies from react-proxy. This isn't strictly necessary. // eslint-disable-next-line no-param-reassign - Type = Type.prototype.constructor; + Type = (Type as any).prototype.constructor; } else if ( // eslint-disable-next-line no-underscore-dangle - Type.__reactstandin__getCurrent + (Type as any).__reactstandin__getCurrent ) { // Unwrap proxies from react-stand-in. // eslint-disable-next-line no-param-reassign @@ -35,17 +41,17 @@ function buildRouteConfig(node, routeConfig) { } } - const route = new Type(props); + const route = new (Type as any)(props); if (children) { if (React.isValidElement(children) || Array.isArray(children)) { // eslint-disable-next-line no-use-before-define - route.children = makeRouteConfig(children); + route.children = buildRouteConfig(children, []); } else { - const routeGroups = {}; - Object.entries(children).forEach(([groupName, groupRoutes]) => { + const routeGroups: Record = {}; + Object.entries(children).forEach(([groupName, groupRoutes]: any[]) => { // eslint-disable-next-line no-use-before-define - routeGroups[groupName] = makeRouteConfig(groupRoutes); + routeGroups[groupName] = buildRouteConfig(groupRoutes, []); }); route.children = routeGroups; @@ -61,6 +67,6 @@ function buildRouteConfig(node, routeConfig) { /** * Create a route configuration from JSX configuration elements. */ -export default function makeRouteConfig(node) { +export default function makeRouteConfig(node: React.ReactNode): RouteConfig { return buildRouteConfig(node, []); } diff --git a/src/typeUtils.ts b/src/typeUtils.ts new file mode 100644 index 00000000..4d7e5856 --- /dev/null +++ b/src/typeUtils.ts @@ -0,0 +1,550 @@ +/* eslint-disable react/no-unused-prop-types */ +/* eslint-disable max-classes-per-file */ +// TypeScript Version: 4.0 + +import { + FarceStoreExtension, + HistoryEnhancerOptions, + Location, + LocationDescriptor, + LocationDescriptorObject, + NavigationListener, + NavigationListenerOptions, + NavigationListenerResult, + Protocol, + Query, + QueryDescriptor, +} from 'farce'; +import * as React from 'react'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Middleware, Reducer, Store, StoreEnhancer } from 'redux'; + +import HttpError from './HttpError'; +import Matcher from './Matcher'; + +export { + type Query, + type QueryDescriptor, + type Location, + type LocationDescriptor, + type LocationDescriptorObject, + type NavigationListenerOptions, + type NavigationListenerResult, + type NavigationListener, +}; + +type Omit = Pick>; + +// export const ActionTypes: { +// UPDATE_MATCH: '@@found/UPDATE_MATCH'; +// RESOLVE_MATCH: '@@found/RESOLVE_MATCH'; +// }; + +export type Params = Record; + +export type ParamsDescriptor = Record< + string, + string | number | boolean | Record +>; + +// These need to be interfaces to avoid circular reference issues. +/* eslint-disable @typescript-eslint/no-empty-interface */ +interface GroupRouteIndices extends Record {} +export interface RouteIndices extends Array {} +/* eslint-enable @typescript-eslint/no-empty-interface */ + +export interface MatcherResult { + routeIndices: RouteIndices; + routeParams: Params[]; + /** + * The union of path parameters for *all* matched routes + */ + params: Params; +} + +export interface MatchBase extends MatcherResult { + /** + * The current location + */ + location: Location; +} + +/** + * The shape might be different with a custom matcher or history enhancer, + * but the default matcher assumes and provides this shape. As such, this + * validator is purely for user convenience and should not be used + * internally. + */ +export interface Match extends MatchBase { + /** + * An array of all matched route objects + */ + routes: RouteObject[]; + /** + * An object with static router properties. + */ + router: Router; + + /** + * matchContext from the router + */ + context: TContext; +} + +export interface Resolver { + resolveElements(match: Match): AsyncIterable; +} + +// export const resolver: Resolver; + +export interface FoundState { + match: MatchBase; + resolvedMatch: MatchBase; +} + +// export const foundReducer: Reducer; + +export interface IsActiveOptions { + exact?: boolean; +} + +/** + * An object implementing the matching algorithm. + * + * User code generally shouldn't need this, but it doesn't hurt to here, + * since we use it for routerShape below. + */ +// export class Matcher { +// constructor(routeConfig: RouteConfig); + +// match(location: Location): MatcherResult | null; + +// getRoutes: (match: MatchBase) => RouteObject[]; + +// /** +// * for match as above, returns whether match corresponds to location or a +// * subpath of location; if exact is set, returns whether match corresponds +// * exactly to location +// */ +// isActive: ( +// match: Match, +// location: LocationDescriptorObject, +// options?: IsActiveOptions, +// ) => boolean; + +// /** +// * Returns the path string for a pattern of the same format as a route path +// * and a object of the corresponding path parameters +// */ +// format: (pattern: string, params: ParamsDescriptor) => string; +// } + +export interface Router extends FarceStoreExtension, FoundStoreExtension { + /** + * Navigates to a new location + * @see farce + */ + push: (location: LocationDescriptor) => void; + /** + * Replace the current history entry + * @see farce + */ + replace: (location: LocationDescriptor) => void; + /** + * Moves delta steps in the history stack + * @see farce + */ + go: (delta: number) => void; + + isActive: Matcher['isActive']; +} + +/** + * The match for a specific route, including that route and its own params. + */ +export interface RouteMatch extends Omit { + /** + * The route object corresponding to this component + */ + route: RouteObject[]; + /** + * The path parameters for route + */ + routeParams: Params; +} + +export interface RenderProps extends RouteMatch { + /** + * The data for the route, as above; null if the data have not yet been + * loaded + */ + data?: any; +} + +/** + * @see https://github.com/4Catalyzer/found/blob/master/README.md#render + */ +export interface RouteRenderArgs { + match: Match; + /** + * The component for the route, if any; null if the component has not yet + * been loaded + */ + Component?: React.ComponentType; + /** + * The default props for the route component, specifically match with data + * as an additional property; null if data have not yet been loaded + */ + props?: RenderProps; + /** + * The data for the route, as above; null if the data have not yet been + * loaded + */ + data?: any; +} + +export type ResolvedElementValue = React.ReactElement | null; +export type ResolvedElement = + | ResolvedElementValue + | ((element: React.ReactElement) => ResolvedElementValue) + | ((groups: Record) => ResolvedElementValue); + +export interface RouteRenderMethod { + (args: RouteRenderArgs): ResolvedElement | undefined; +} + +/** + * Shared properties between JSX and object routes. + */ +export interface RouteObjectBase { + /** + * a string defining the pattern for the route + */ + path?: string; + /** + * the component for the route + */ + Component?: React.ComponentType; + /** + * a method that returns the component for the route + */ + getComponent?: ( + match: RouteMatch, + ) => React.ComponentType | Promise>; + /** + * additional data for the route + */ + data?: any; + /** + * a method that returns additional data for the route + */ + getData?: (match: RouteMatch) => any; + /** + * whether to defer getting data until ancestor data promises are resolved + */ + defer?: boolean; + /** + * @throws {HttpError} + * @throws {RedirectException} + */ + render?: RouteRenderMethod; + + // Provide indexer allowing for other properties. + [key: string]: any; +} + +/** + * Plain JavaScript route object, possibly from a resolved JSX route. + */ +export interface RouteObject extends RouteObjectBase { + children?: RouteConfig | Record; +} + +export type RouteConfig = RouteObject[]; + +export interface RouteProps extends RouteObjectBase { + children?: React.ReactNode | Record; +} + +/** + * JSX Route + */ +// export class Route extends React.Component { +// constructor(options: RouteObject | RouteProps); +// } + +// export function hotRouteConfig(routeConfig: RouteConfig): RouteConfig; + +// export class HttpError { +// status: number; + +// data: any; + +// constructor(status: number, data?: any); +// } + +export interface RedirectOptions { + from?: string; + to: string | ((match: Match) => LocationDescriptor); + status?: number; +} + +// It's more "natural" to call this "props" when used in the context of a +// React component. +export type RedirectProps = RedirectOptions; + +// export class Redirect extends React.Component { +// constructor(config: RedirectOptions); +// } + +export interface LinkPropsCommon { + to: LocationDescriptor; + // match: Match, provided by withRouter + // router: Router, provided by withRouter + exact?: boolean; + target?: string; + onClick?: (event: React.SyntheticEvent) => void; +} + +export interface LinkInjectedProps { + href: string; + onClick: (event: React.SyntheticEvent) => void; +} + +export interface LinkPropsNodeChild extends LinkPropsCommon { + activeClassName?: string; + activeStyle?: Record; + children?: React.ReactNode; +} + +type ReplaceLinkProps = Omit< + React.ComponentProps, + keyof TProps | keyof LinkInjectedProps +> & + TProps; + +export type LinkPropsSimple = ReplaceLinkProps<'a', LinkPropsNodeChild>; + +export type LinkPropsWithAs< + TInner extends React.ElementType, +> = ReplaceLinkProps< + TInner, + LinkPropsNodeChild & { + as: TInner; + activePropName?: null; + } +>; + +export type LinkPropsWithActivePropName< + TInner extends React.ComponentType< + LinkInjectedProps & { [activePropName in TActivePropName]: boolean } + >, + TActivePropName extends string, +> = ReplaceLinkProps< + TInner, + LinkPropsNodeChild & { + as: TInner; + activePropName: TActivePropName; + } & { + [activePropName in TActivePropName]?: null; + } +>; + +export interface LinkPropsWithFunctionChild extends LinkPropsCommon { + children: (linkRenderArgs: { + href: string; + active: boolean; + onClick: (event: React.SyntheticEvent) => void; + }) => React.ReactNode; +} + +export type LinkProps< + TInner extends React.ElementType = never, + TInnerWithActivePropName extends React.ComponentType< + LinkInjectedProps & { [activePropName in TActivePropName]: boolean } + > = never, + TActivePropName extends string = never, +> = + | LinkPropsSimple + | LinkPropsWithAs + | LinkPropsWithActivePropName + | LinkPropsWithFunctionChild; + +// export class Link< +// TInner extends React.ElementType = never, +// TInnerWithActivePropName extends React.ComponentType< +// LinkInjectedProps & { [activePropName in TActivePropName]: boolean } +// > = never, +// TActivePropName extends string = never, +// > extends React.Component< +// LinkProps +// > { +// props: LinkProps; +// } + +export interface RouterState { + match: Match; + router: Router; +} + +export type RouterProps = RouterState; + +export interface RouteComponentProps + extends RouterProps { + children?: React.ReactElement | null; +} + +export interface RouteComponentDataProps + extends RouteComponentProps { + data: T; +} + +/** + * Returns the Router and current route match from context + */ +// export function useRouter(): RouterState; + +/** Returns the current route Match */ +// export function useMatch(): Match; + +/** Returns the current route params */ +// export function useParams(): Params; + +/** Returns the current location object */ +// export function useLocation(): Location; + +// export function withRouter( +// Component: React.ComponentType, +// ): React.ComponentType>; + +// export class RedirectException { +// constructor(location: LocationDescriptor, status?: number); + +// location: LocationDescriptor; + +// status: number; +// } + +/** + * Create a route configuration from JSX configuration elements. + */ +// export function makeRouteConfig(node: React.ReactNode): RouteConfig; + +export interface FoundStoreExtension { + matcher: Matcher; + replaceRouteConfig: (routeConfig: RouteConfig) => void; +} + +// export function createMatchEnhancer( +// matcher: Matcher, +// ): StoreEnhancer<{ found: FoundStoreExtension }>; + +export type RenderPendingArgs = Match; + +// This is the folded resolver output from resolveRenderArgs. +export type RenderArgsElements = Array< + ResolvedElement | Record +>; + +export interface RenderReadyArgs extends Match { + elements: RenderArgsElements; +} + +export interface RenderErrorArgs extends Match { + error: HttpError; +} + +export type RenderArgs = RenderPendingArgs | RenderReadyArgs | RenderErrorArgs; + +export interface CreateRenderOptions { + renderPending?: (args: RenderPendingArgs) => React.ReactElement; + renderReady?: (args: RenderReadyArgs) => React.ReactElement; + renderError?: (args: RenderErrorArgs) => React.ReactNode; +} + +// export function createRender( +// options: CreateRenderOptions, +// ): (renderArgs: RenderArgs) => React.ReactElement; + +export interface ConnectedRouterOptions extends CreateRenderOptions { + render?: (args: RenderArgs) => React.ReactElement; + getFound?: (store: Store) => FoundState; +} + +export interface ConnectedRouterProps { + matchContext?: any; + resolver: Resolver; + initialRenderArgs?: RenderArgs; +} + +export type ConnectedRouter = React.ComponentType; + +// export function createConnectedRouter( +// options: ConnectedRouterOptions, +// ): ConnectedRouter; + +export interface FarceRouterOptions extends ConnectedRouterOptions { + store?: Store; + historyProtocol: Protocol; + historyMiddlewares?: Middleware[]; + historyOptions?: Omit; + routeConfig: RouteConfig; +} + +export type FarceRouterProps = ConnectedRouterProps; + +export type FarceRouter = React.ComponentType; + +// export function createFarceRouter(options: FarceRouterOptions): FarceRouter; + +export interface BrowserRouterOptions + extends Omit { + render?: (args: RenderArgs) => React.ReactElement; +} + +export interface BrowserRouterProps + extends Omit { + resolver?: Resolver; +} + +export type BrowserRouter = React.ComponentType; + +// export function createBrowserRouter( +// options: BrowserRouterOptions, +// ): BrowserRouter; + +export interface InitialFarceRouterOptions + extends Omit { + matchContext?: any; + resolver: Resolver; +} + +// export function createInitialFarceRouter( +// options: InitialFarceRouterOptions, +// ): Promise; + +export type InitialBrowserRouterOptions = Omit< + InitialFarceRouterOptions, + 'resolver' | 'historyProtocol' +>; + +// export function createInitialBrowserRouter( +// options: InitialBrowserRouterOptions, +// ): Promise; + +export interface ElementsRendererProps { + elements: RenderArgsElements; +} + +export type ElementsRenderer = React.ComponentType; + +export interface GetStoreRenderArgsOptions { + store: Store; + getFound?: (store: Store) => FoundState; + matchContext: any; + resolver: Resolver; +} + +// export function getStoreRenderArgs( +// options: GetStoreRenderArgsOptions, +// ): Promise; diff --git a/src/useMatch.js b/src/useMatch.js deleted file mode 100644 index c6d4a692..00000000 --- a/src/useMatch.js +++ /dev/null @@ -1,5 +0,0 @@ -import useRouter from './useRouter'; - -export default function useMatch() { - return useRouter().match; -} diff --git a/src/useMatch.ts b/src/useMatch.ts new file mode 100644 index 00000000..a6ff0fec --- /dev/null +++ b/src/useMatch.ts @@ -0,0 +1,7 @@ +import { Match } from './typeUtils'; +import useRouter from './useRouter'; + +/** Returns the current route Match */ +export default function useMatch(): Match { + return useRouter().match; +} diff --git a/src/useParams.js b/src/useParams.js deleted file mode 100644 index 61a78d77..00000000 --- a/src/useParams.js +++ /dev/null @@ -1,5 +0,0 @@ -import useMatch from './useMatch'; - -export default function useParams() { - return useMatch().params; -} diff --git a/src/useParams.ts b/src/useParams.ts new file mode 100644 index 00000000..93773cb5 --- /dev/null +++ b/src/useParams.ts @@ -0,0 +1,7 @@ +import { Params } from './typeUtils'; +import useMatch from './useMatch'; + +/** Returns the current route params */ +export default function useParams(): Params { + return useMatch().params; +} diff --git a/src/useRouter.js b/src/useRouter.js deleted file mode 100644 index fbdd1b0b..00000000 --- a/src/useRouter.js +++ /dev/null @@ -1,7 +0,0 @@ -import { useContext } from 'react'; - -import RouterContext from './RouterContext'; - -export default function useRouter() { - return useContext(RouterContext); -} diff --git a/src/useRouter.ts b/src/useRouter.ts new file mode 100644 index 00000000..2b77d28e --- /dev/null +++ b/src/useRouter.ts @@ -0,0 +1,11 @@ +import { useContext } from 'react'; + +import RouterContext from './RouterContext'; +import { RouterState } from './typeUtils'; + +/** + * Returns the Router and current route match from context + */ +export default function useRouter(): RouterState { + return useContext>(RouterContext); +} diff --git a/yarn.lock b/yarn.lock index ef7afa52..ab9def58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2402,20 +2402,6 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - "@jridgewell/resolve-uri@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.3.tgz#b80093f4edbb5490c49746231513669c8f518acb" @@ -3825,15 +3811,12 @@ browserslist@^4.17.5, browserslist@^4.18.1: node-releases "^2.0.1" picocolors "^1.0.0" -browserslist@^4.21.3, browserslist@^4.21.4: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" + fast-json-stable-stringify "2.x" bser@2.1.1: version "2.1.1" @@ -8219,7 +8202,7 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.3.1: +jest-util@^29.0.0, jest-util@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1" integrity sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ== @@ -8831,12 +8814,10 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" +make-error@1.x: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.x: version "1.0.11"