|
| 1 | +/* eslint-disable no-new-func */ |
| 2 | +import * as React from 'react'; |
| 3 | +import type { RSCPromise, RSCConfig, RSCProps, RSCSource } from '../@types'; |
| 4 | +import RSC from './ServerComponent'; |
| 5 | +import axios, { type AxiosRequestConfig } from 'axios'; |
| 6 | + |
| 7 | +const defaultGlobal = Object.freeze({ |
| 8 | + require: (moduleId: string) => { |
| 9 | + if (moduleId === 'react') { |
| 10 | + // @ts-ignore |
| 11 | + return require('react'); |
| 12 | + } else if (moduleId === 'react-native') { |
| 13 | + // @ts-ignore |
| 14 | + return require('react-native'); |
| 15 | + } |
| 16 | + return null; |
| 17 | + }, |
| 18 | +}); |
| 19 | + |
| 20 | +const createComponent = |
| 21 | + (global: any) => |
| 22 | + async (src: string): Promise<React.Component> => { |
| 23 | + const globalName = '__SERVER_COMPONENT__'; |
| 24 | + const Component = await new Function( |
| 25 | + globalName, |
| 26 | + `${Object.keys(global) |
| 27 | + .map((key) => `var ${key} = ${globalName}.${key};`) |
| 28 | + .join('\n')}; const exports = {}; ${src}; return exports.default` |
| 29 | + )(global); |
| 30 | + |
| 31 | + return Component; |
| 32 | + }; |
| 33 | + |
| 34 | +const axiosRequest = (config: AxiosRequestConfig) => axios(config); |
| 35 | + |
| 36 | +const buildRSC = |
| 37 | + ({ |
| 38 | + openURI, |
| 39 | + }: { |
| 40 | + readonly openURI: ( |
| 41 | + uri: string, |
| 42 | + callback: RSCPromise<React.Component> |
| 43 | + ) => void; |
| 44 | + }) => |
| 45 | + async (source: RSCSource): Promise<React.Component> => { |
| 46 | + // TODO handle string source |
| 47 | + if (source && typeof source === 'object') { |
| 48 | + const { uri } = source; |
| 49 | + // TODO handle uri validation |
| 50 | + if (typeof uri === 'string') { |
| 51 | + return new Promise<React.Component>((resolve, reject) => |
| 52 | + openURI(uri, { resolve, reject }) |
| 53 | + ); |
| 54 | + } |
| 55 | + } |
| 56 | + }; |
| 57 | + |
| 58 | +const buildRequest = |
| 59 | + ({ |
| 60 | + component, |
| 61 | + }: { |
| 62 | + readonly component: (src: string) => Promise<React.Component>; |
| 63 | + }) => |
| 64 | + async (uri: string) => { |
| 65 | + //const handler = completionHandler(); |
| 66 | + |
| 67 | + try { |
| 68 | + const result = await axiosRequest({ url: uri, method: 'get' }); |
| 69 | + const { data } = result; |
| 70 | + if (typeof data !== 'string') { |
| 71 | + throw new Error( |
| 72 | + `[ServerComponent]: Expected string data, encountered ${typeof data}` |
| 73 | + ); |
| 74 | + } |
| 75 | + |
| 76 | + component(data); |
| 77 | + } catch (e) { |
| 78 | + console.log(`[ServerComponent]: Build Request caught error ${e}`); |
| 79 | + } |
| 80 | + }; |
| 81 | + |
| 82 | +const buildURIForRSC = |
| 83 | + ({ uriRequest }: { readonly uriRequest: (uri: string) => void }) => |
| 84 | + (uri: string, callback: RSCPromise<React.Component>): void => { |
| 85 | + const { resolve, reject } = callback; |
| 86 | + // TODO: handle caching and queueing here |
| 87 | + return uriRequest(uri); |
| 88 | + }; |
| 89 | + |
| 90 | +export default function createServerComponent({ |
| 91 | + global = defaultGlobal, |
| 92 | +}: RSCConfig) { |
| 93 | + //const handler = completionHandler(); |
| 94 | + |
| 95 | + const component = createComponent(global); |
| 96 | + |
| 97 | + const uriRequest = buildRequest({ component }); |
| 98 | + |
| 99 | + const openURI = buildURIForRSC({ uriRequest }); |
| 100 | + |
| 101 | + const openRSC = buildRSC({ openURI }); |
| 102 | + |
| 103 | + const ServerComponent = (props: RSCProps) => ( |
| 104 | + <RSC {...props} openRSC={openRSC} /> |
| 105 | + ); |
| 106 | + |
| 107 | + return Object.freeze({ |
| 108 | + ServerComponent, |
| 109 | + }); |
| 110 | +} |
0 commit comments