Skip to content

Commit

Permalink
WIP6
Browse files Browse the repository at this point in the history
  • Loading branch information
smikhalevski committed Nov 21, 2024
1 parent ad99881 commit 74b5a92
Show file tree
Hide file tree
Showing 15 changed files with 165 additions and 140 deletions.
21 changes: 13 additions & 8 deletions src/main/____NEW/Outlet.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Component, createContext, createElement, ReactNode, Suspense } from 'react';
import { View } from './View';
import { useRouter } from './RouterProvider';
import { View } from './View';

export const ViewContext = createContext<View | undefined>(undefined);

Expand Down Expand Up @@ -48,21 +48,26 @@ function InternalOutlet(props: InternalOutletProps): ReactNode {

const router = useRouter();

router.rootView.routeMatch!.route.
let component;

const component = isSuspendable ? (controller.onSuspend(), controller.component) : controller.fallbackComponent;
if (isSuspendable) {
view.onSuspend();
component = view.component;
} else {
component = view.fallbackComponent;
}

if (component === undefined) {
return null;
}

return (
<OutletControllerContext.Provider value={controller}>
<ChildOutletControllerContext.Provider value={controller.childController}>
{isServer && isSuspendable && controller.renderHydrationScript()}
<ViewContext.Provider value={view}>
<OutletViewContext.Provider value={view.childView}>
{isSuspendable && router.renderHydrationScript()}
{createElement(component)}
</ChildOutletControllerContext.Provider>
</OutletControllerContext.Provider>
</OutletViewContext.Provider>
</ViewContext.Provider>
);
}

Expand Down
79 changes: 17 additions & 62 deletions src/main/____NEW/README.md
Original file line number Diff line number Diff line change
@@ -1,71 +1,26 @@
server
loader
notFound -> render
redirect -> render
render
notFound -> set fallbackComponent to notFoundComponent, set routeState to {status: 'notFound'}, throw
redirect -> throw

client
loader
notFound -> render
redirect -> render
render
notFound -> throw
redirect -> throw






```tsx
const history = createBrowserHistory();

const router = new Router({
ssr: { stateStringifier }
routes: []
});

// client
startSSRHydration(router, location, { stateParser });





router.navigate(location);



RouteMatch { route, params }
RouteState { status, data, error, url } // marshalled
RouteContent { component, state }
Slot { router, routeMatch, routeContent }





createRoute('/foo', props => props.router.redirect(loginRoute.getLocation({ url: props.router.history.url }))



















hydrateRouter(router, history.location);
```

```tsx
const history = createMemoryHistory(request.url);

const router = new SSRRouter({
routes: []
});

let html;
do {
html = renderToString(<App/>);
} while (await manager.hasChanges() || await router.hasChanges());

html += router.nextHydrationChunk();

respose.end(html);
```
2 changes: 0 additions & 2 deletions src/main/____NEW/Router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import isDeepEqual from 'fast-deep-equal';
/**
* A router that matches routes by a location.
*
* Use {@link createRouter} to create a {@link Router} instance.
*
* @template Context A context provided to {@link RouteOptions.loader route loaders}.
* @group Routing
*/
Expand Down
4 changes: 2 additions & 2 deletions src/main/____NEW/View.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RoutePayload } from './__loadRoute';
import { RouteState } from './__loadRoute';
import { RouteMatch } from './__matchRoutes';
import { Router } from './Router';

Expand All @@ -10,7 +10,7 @@ export class View {
readonly routeMatch?: RouteMatch
) {}

setPayload(routePayload: RoutePayload): void {}
setPayload(routePayload: RouteState): void {}

freeze(): void {}
}
22 changes: 5 additions & 17 deletions src/main/____NEW/__Route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import {
RouteOptions,
} from './__types';

type Squash<T> = { [K in keyof T]: T[K] } & unknown;
type Prettify<T> = { [K in keyof T]: T[K] } & {};

type PartialToVoid<T> = Partial<T> extends T ? T | void : T;
type PartialVoid<T> = Partial<T> extends T ? T | void : T;

type InferParams<R extends Route | null> = R extends Route<any, infer Params> ? Params : object | void;

/**
* A route that can be rendered by a router.
Expand All @@ -33,20 +35,6 @@ export class Route<
Data = any,
Context = any,
> {
/**
* The type of cumulative route params.
*
* @internal
*/
declare _params: PartialToVoid<ParentRoute extends Route ? Squash<ParentRoute['_params'] & Params> : Params>;

/**
* The type of route context.
*
* @internal
*/
declare _context: Context;

/**
* A parent route or `null` if there is no parent.
*/
Expand Down Expand Up @@ -174,7 +162,7 @@ export class Route<
* @param params Route params.
* @param options Location options.
*/
getLocation(params: this['_params'], options?: LocationOptions): Location {
getLocation(params: PartialVoid<Prettify<InferParams<ParentRoute> & Params>>, options?: LocationOptions): Location {
let pathname = '';
let searchParams: Dict = {};
let hasLooseParams = false;
Expand Down
6 changes: 4 additions & 2 deletions src/main/____NEW/__createRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { ComponentType } from 'react';
import { Route } from './__Route';
import { RouteOptions } from './__types';

type InferContext<R extends Route> = R extends Route<any, any, any, infer Context> ? Context : any;

/**
* Creates a route that is rendered in an {@link Outlet} of a {@link Router}.
*
Expand All @@ -28,8 +30,8 @@ export function createRoute<Params extends object | void = object | void, Data =
*/
export function createRoute<ParentRoute extends Route, Params extends object | void = object | void, Data = void>(
parentRoute: ParentRoute,
options?: RouteOptions<Params, Data, ParentRoute['_context']>
): Route<ParentRoute, Params, Data, ParentRoute['_context']>;
options?: RouteOptions<Params, Data, InferContext<ParentRoute>>
): Route<ParentRoute, Params, Data, InferContext<ParentRoute>>;

/**
* Creates a route that is rendered in an {@link Outlet} of a {@link Router}.
Expand Down
21 changes: 0 additions & 21 deletions src/main/____NEW/__createRouter.ts

This file was deleted.

20 changes: 9 additions & 11 deletions src/main/____NEW/__loadRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { LoaderOptions } from './__types';
import { isPromiseLike } from './utils';

/**
* A loaded route payload.
* A loaded route state.
*/
export interface RoutePayload {
export interface RouteState {
/**
* A status of a route loading.
*/
Expand All @@ -22,32 +22,30 @@ export interface RoutePayload {
/**
* Loads a route component and data.
*/
export function loadRoute(route: Route, loaderOptions: LoaderOptions<any, any>): Promise<RoutePayload> | RoutePayload {
const { loader } = route;

export function loadRoute(route: Route, loaderOptions: LoaderOptions): Promise<RouteState> | RouteState {
let component;
let data;

try {
component = route.loadComponent();

data = loader?.(loaderOptions);
data = route.loader === undefined ? undefined : route.loader(loaderOptions);
} catch (error) {
return createErrorPayload(error);
return createErrorState(error);
}

if (isPromiseLike(component) || isPromiseLike(data)) {
return Promise.all([component, data]).then(result => createOkPayload(result[1]), createErrorPayload);
return Promise.all([component, data]).then(result => createOkState(result[1]), createErrorState);
}

return createOkPayload(data);
return createOkState(data);
}

export function createOkPayload(data: unknown): RoutePayload {
export function createOkState(data: unknown): RouteState {
return { status: 'ok', data };
}

export function createErrorPayload(error: unknown): RoutePayload {
export function createErrorState(error: unknown): RouteState {
if (error instanceof NotFoundError) {
return { status: 'not_found', data: undefined };
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/____NEW/__types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export type RenderingDisposition = 'server' | 'client';
* @template Context A router context.
* @group Routing
*/
export interface LoaderOptions<Params, Context> {
export interface LoaderOptions<Params = any, Context = any> {
/**
* Route params extracted from a location.
*/
Expand Down
34 changes: 34 additions & 0 deletions src/main/____NEW/ssr/PipeableSSRRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Writable } from 'stream';
import { SSRRouter, SSRRouterOptions } from './SSRRouter';

export class PipeableSSRRouter<Context> extends SSRRouter<Context> {
readonly stream: NodeJS.WritableStream;

constructor(stream: NodeJS.WritableStream, options: SSRRouterOptions<Context>) {
super(options);

this.stream = new Writable({
write: (chunk, encoding, callback) => {
stream.write(chunk, encoding, error => {
if (error) {
callback(error);
return;
}

const hydrationChunk = this.nextHydrationChunk();

if (hydrationChunk !== '') {
stream.write(hydrationChunk, callback);
return;
}

callback();
});
},

final: callback => {
stream.end(callback);
},
});
}
}
25 changes: 25 additions & 0 deletions src/main/____NEW/ssr/ReadableSSRRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { SSRRouter, SSRRouterOptions } from './SSRRouter';

export class ReadableSSRRouter<Context = any> extends SSRRouter<Context> implements ReadableWritablePair {
readonly readable: ReadableStream;
readonly writable: WritableStream;

constructor(options: SSRRouterOptions<Context>) {
super(options);

const transformer = new TransformStream({
transform: (chunk, controller) => {
controller.enqueue(chunk);

const hydrationChunk = this.nextHydrationChunk();

if (hydrationChunk !== '') {
controller.enqueue(hydrationChunk);
}
},
});

this.readable = transformer.readable;
this.writable = transformer.writable;
}
}
18 changes: 18 additions & 0 deletions src/main/____NEW/ssr/SSRRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { RouteState } from '../__loadRoute';
import { RouterOptions } from '../__types';
import { Router } from '../Router';

export interface SSRRouterOptions<Context> extends RouterOptions<Context> {
stateStringifier?: (state: RouteState) => string;
nonce?: string;
}

export class SSRRouter<Context = any> extends Router<Context> {
hasChanges(): Promise<boolean> {
return Promise.resolve(false);
}

nextHydrationChunk(): string {
return '';
}
}
7 changes: 7 additions & 0 deletions src/main/____NEW/ssr/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare global {
interface Window {
__REACT_CORSAIR_SSR_STATE__?: { set(routeIndex: number, stateStr: string): void };
}
}

export {};
Loading

0 comments on commit 74b5a92

Please sign in to comment.