From def21302f394741f4384c570fc1bba3536f49357 Mon Sep 17 00:00:00 2001 From: ClarkXia Date: Tue, 21 Jan 2025 17:32:14 +0800 Subject: [PATCH] feat: runtime kit --- examples/custom-runtime/ice.config.mts | 3 + examples/custom-runtime/runtime.tsx | 13 +- examples/custom-runtime/src/routes.tsx | 13 + examples/with-antd-mobile/src/store.tsx | 3 +- packages/ice/package.json | 1 + packages/ice/src/bundler/config/getUrls.ts | 2 +- packages/ice/src/bundler/types.ts | 2 +- packages/ice/src/createService.ts | 2 +- packages/ice/src/esbuild/assets.ts | 2 +- packages/ice/src/types/plugin.ts | 2 +- packages/ice/src/utils/injectInitialEntry.ts | 2 +- packages/ice/src/utils/runtimeEnv.ts | 2 +- packages/plugin-auth/src/runtime/index.tsx | 2 +- packages/plugin-auth/src/types.ts | 2 +- packages/plugin-i18n/src/runtime/index.tsx | 4 +- .../plugin-icestark/src/runtime/child.tsx | 2 +- .../plugin-icestark/src/runtime/framework.tsx | 2 +- packages/plugin-intl/src/runtime-simple.ts | 2 +- packages/plugin-intl/src/runtime.tsx | 2 +- packages/plugin-miniapp/src/runtime/index.ts | 2 +- packages/plugin-request/src/runtime.ts | 2 +- packages/plugin-store/src/runtime.tsx | 2 +- packages/runtime-kit/package.json | 1 + packages/runtime-kit/src/dataLoader.ts | 21 +- packages/runtime-kit/src/requestContext.ts | 2 +- packages/runtime-kit/src/types.ts | 11 +- packages/runtime-kit/tsconfig.json | 6 +- packages/runtime/src/AppContext.tsx | 2 +- packages/runtime/src/Document.tsx | 3 +- packages/runtime/src/RouteContext.ts | 2 +- packages/runtime/src/RouteWrapper.tsx | 2 +- packages/runtime/src/Suspense.tsx | 2 +- packages/runtime/src/appConfig.ts | 37 --- packages/runtime/src/appData.ts | 4 +- packages/runtime/src/dataLoader.ts | 281 ------------------ packages/runtime/src/index.ts | 24 +- packages/runtime/src/renderDocument.tsx | 7 +- .../runtime/src/reportRecoverableError.ts | 2 +- packages/runtime/src/requestContext.ts | 54 ---- packages/runtime/src/routes.tsx | 16 +- packages/runtime/src/routesConfig.ts | 3 +- packages/runtime/src/runClientApp.tsx | 46 ++- packages/runtime/src/runServerApp.tsx | 7 +- packages/runtime/src/runtime.tsx | 12 +- .../runtime/src/server/getDocumentData.ts | 3 +- packages/runtime/src/singleRouter.tsx | 3 +- packages/runtime/src/types.ts | 246 +-------------- pnpm-lock.yaml | 3 + 48 files changed, 153 insertions(+), 716 deletions(-) create mode 100644 examples/custom-runtime/src/routes.tsx delete mode 100644 packages/runtime/src/appConfig.ts delete mode 100644 packages/runtime/src/dataLoader.ts delete mode 100644 packages/runtime/src/requestContext.ts diff --git a/examples/custom-runtime/ice.config.mts b/examples/custom-runtime/ice.config.mts index 26d2dd805b..6a20c845ed 100644 --- a/examples/custom-runtime/ice.config.mts +++ b/examples/custom-runtime/ice.config.mts @@ -22,6 +22,9 @@ export default defineConfig(() => ({ ], source: '../runtime', server: '@ice/runtime/server', + router: { + source: '@/routes', + }, }; }) }, diff --git a/examples/custom-runtime/runtime.tsx b/examples/custom-runtime/runtime.tsx index 49f495de95..828cab673a 100644 --- a/examples/custom-runtime/runtime.tsx +++ b/examples/custom-runtime/runtime.tsx @@ -5,8 +5,17 @@ import { getAppConfig } from '@ice/runtime-kit'; import ReactDOM from 'react-dom'; const runClientApp = (options: RunClientAppOptions) => { - console.log('runClientApp', options); - ReactDOM.render(
Hello World
, document.getElementById('ice-container')); + const { basename = '', createRoutes } = options; + // Normalize pathname with leading slash + const pathname = `/${window.location.pathname.replace(basename, '').replace(/^\/+/, '')}`; + + const routes = createRoutes?.({ renderMode: 'CSR' }); + const Component = routes?.find(route => route.path === pathname)?.component; + + ReactDOM.render( + Component ? :
404
, + document.getElementById('ice-container'), + ); }; export { diff --git a/examples/custom-runtime/src/routes.tsx b/examples/custom-runtime/src/routes.tsx new file mode 100644 index 0000000000..76c5344c0f --- /dev/null +++ b/examples/custom-runtime/src/routes.tsx @@ -0,0 +1,13 @@ +import Index from './pages/index'; +import Home from './pages/home'; + +export default () => [ + { + path: '/', + component: Index, + }, + { + path: '/home', + component: Home, + }, +]; diff --git a/examples/with-antd-mobile/src/store.tsx b/examples/with-antd-mobile/src/store.tsx index c2a334f6b7..983366b766 100644 --- a/examples/with-antd-mobile/src/store.tsx +++ b/examples/with-antd-mobile/src/store.tsx @@ -1,4 +1,3 @@ -import type { ComponentWithChildren } from '@ice/runtime/types'; import { useState } from 'react'; import constate from 'constate'; @@ -12,7 +11,7 @@ function useCounter() { const [CounterProvider, useCounterContext] = constate(useCounter); -export const StoreProvider: ComponentWithChildren = ({ children }) => { +export const StoreProvider = ({ children }) => { return { children } ; }; diff --git a/packages/ice/package.json b/packages/ice/package.json index baf4d45dfb..f4211d3ca9 100644 --- a/packages/ice/package.json +++ b/packages/ice/package.json @@ -50,6 +50,7 @@ "@ice/bundles": "workspace:*", "@ice/route-manifest": "workspace:*", "@ice/runtime": "workspace:^", + "@ice/runtime-kit": "workspace:^", "@ice/shared-config": "workspace:*", "@ice/webpack-config": "workspace:*", "@ice/rspack-config": "workspace:*", diff --git a/packages/ice/src/bundler/config/getUrls.ts b/packages/ice/src/bundler/config/getUrls.ts index 5faa89342c..1cf97a7df7 100644 --- a/packages/ice/src/bundler/config/getUrls.ts +++ b/packages/ice/src/bundler/config/getUrls.ts @@ -1,6 +1,6 @@ import type { TaskConfig } from 'build-scripts'; import type { Config } from '@ice/shared-config/types'; -import type { AppConfig } from '@ice/runtime/types'; +import type { AppConfig } from '@ice/runtime-kit'; import type { Configuration as DevServerConfiguration } from 'webpack-dev-server'; import type { Configuration as RSPackDevServerConfiguration } from '@rspack/dev-server'; diff --git a/packages/ice/src/bundler/types.ts b/packages/ice/src/bundler/types.ts index 0cf4f110ed..02fe5ce786 100644 --- a/packages/ice/src/bundler/types.ts +++ b/packages/ice/src/bundler/types.ts @@ -1,7 +1,7 @@ import type { Config } from '@ice/shared-config/types'; import type ora from '@ice/bundles/compiled/ora/index.js'; import type { Stats as WebpackStats } from '@ice/bundles/compiled/webpack/index.js'; -import type { AppConfig } from '@ice/runtime/types'; +import type { AppConfig } from '@ice/runtime'; import type { Configuration, MultiCompiler, MultiStats } from '@rspack/core'; import type { Context as DefaultContext, TaskConfig } from 'build-scripts'; import type { ServerCompiler, GetAppConfig, GetRoutesConfig, GetDataloaderConfig, ExtendsPluginAPI } from '../types/plugin.js'; diff --git a/packages/ice/src/createService.ts b/packages/ice/src/createService.ts index d42ad23127..e7d2bdc110 100644 --- a/packages/ice/src/createService.ts +++ b/packages/ice/src/createService.ts @@ -7,7 +7,7 @@ import webpack from '@ice/bundles/compiled/webpack/index.js'; import { Context } from 'build-scripts'; import type { CommandArgs, CommandName, TaskConfig } from 'build-scripts'; import type { Config } from '@ice/shared-config/types'; -import type { AppConfig } from '@ice/runtime/types'; +import type { AppConfig } from '@ice/runtime'; import * as config from './config.js'; import test from './commands/test.js'; import webpackBundler from './bundler/webpack/index.js'; diff --git a/packages/ice/src/esbuild/assets.ts b/packages/ice/src/esbuild/assets.ts index ea594ffd4e..aca0ac8059 100644 --- a/packages/ice/src/esbuild/assets.ts +++ b/packages/ice/src/esbuild/assets.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import * as mrmime from 'mrmime'; import fs from 'fs-extra'; import type { PluginBuild } from 'esbuild'; -import type { AssetsManifest } from '@ice/runtime/types'; +import type { AssetsManifest } from '@ice/runtime-kit'; export const ASSET_TYPES = [ // images diff --git a/packages/ice/src/types/plugin.ts b/packages/ice/src/types/plugin.ts index 174e1cbc86..0fd4688152 100644 --- a/packages/ice/src/types/plugin.ts +++ b/packages/ice/src/types/plugin.ts @@ -4,7 +4,7 @@ import type { Configuration, Stats, WebpackOptionsNormalized } from '@ice/bundle import type { esbuild } from '@ice/bundles'; import type { DefineExtraRoutes, NestedRouteManifest } from '@ice/route-manifest'; import type { Config } from '@ice/shared-config/types'; -import type { AppConfig, AssetsManifest } from '@ice/runtime/types'; +import type { AppConfig, AssetsManifest } from '@ice/runtime-kit'; import type ServerCompileTask from '../utils/ServerCompileTask.js'; import type { CreateLogger } from '../utils/logger.js'; import type { DeclarationData, AddRenderFile, AddTemplateFiles, ModifyRenderData, AddDataLoaderImport, Render } from './generator.js'; diff --git a/packages/ice/src/utils/injectInitialEntry.ts b/packages/ice/src/utils/injectInitialEntry.ts index a8375983b0..175da9682d 100644 --- a/packages/ice/src/utils/injectInitialEntry.ts +++ b/packages/ice/src/utils/injectInitialEntry.ts @@ -1,6 +1,6 @@ import path from 'path'; import fse from 'fs-extra'; -import type { RouteItem } from '@ice/runtime/types'; +import type { RouteItem } from '@ice/runtime'; import matchRoutes from '@ice/runtime/matchRoutes'; import { logger } from './logger.js'; import type RouteManifest from './routeManifest.js'; diff --git a/packages/ice/src/utils/runtimeEnv.ts b/packages/ice/src/utils/runtimeEnv.ts index 08337167b6..78f9cdbbcb 100644 --- a/packages/ice/src/utils/runtimeEnv.ts +++ b/packages/ice/src/utils/runtimeEnv.ts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import * as dotenv from 'dotenv'; import { expand as dotenvExpand } from 'dotenv-expand'; import type { CommandArgs } from 'build-scripts'; -import type { AppConfig } from '@ice/runtime/types'; +import type { AppConfig } from '@ice/runtime-kit'; export interface Envs { [key: string]: string; diff --git a/packages/plugin-auth/src/runtime/index.tsx b/packages/plugin-auth/src/runtime/index.tsx index cf0fda848d..5e44e6d6f7 100644 --- a/packages/plugin-auth/src/runtime/index.tsx +++ b/packages/plugin-auth/src/runtime/index.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import type { RuntimePlugin, AppProvider, RouteWrapper } from '@ice/runtime/types'; +import type { RuntimePlugin, AppProvider, RouteWrapper } from '@ice/runtime'; import type { AuthConfig, AuthType, Auth } from '../types.js'; import { AuthProvider, useAuth, withAuth } from './Auth.js'; import type { InjectProps } from './Auth.js'; diff --git a/packages/plugin-auth/src/types.ts b/packages/plugin-auth/src/types.ts index 318a0a670d..7fc292a80c 100644 --- a/packages/plugin-auth/src/types.ts +++ b/packages/plugin-auth/src/types.ts @@ -1,5 +1,5 @@ import type * as React from 'react'; -import type { RouteConfig } from '@ice/runtime/types'; +import type { RouteConfig } from '@ice/runtime'; export interface AuthConfig { initialAuth: { diff --git a/packages/plugin-i18n/src/runtime/index.tsx b/packages/plugin-i18n/src/runtime/index.tsx index dee5e6a12c..420759cf2f 100644 --- a/packages/plugin-i18n/src/runtime/index.tsx +++ b/packages/plugin-i18n/src/runtime/index.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import type { RuntimePlugin } from '@ice/runtime/types'; +import type { RuntimePlugin } from '@ice/runtime'; import detectLocale from '../utils/detectLocale.js'; import type { I18nAppConfig, I18nConfig } from '../types.js'; import getLocaleRedirectPath from '../utils/getLocaleRedirectPath.js'; @@ -86,4 +86,4 @@ const runtime: RuntimePlugin<{ i18nConfig: I18nConfig }> = async ( export default runtime; -export { useLocale, withLocale }; \ No newline at end of file +export { useLocale, withLocale }; diff --git a/packages/plugin-icestark/src/runtime/child.tsx b/packages/plugin-icestark/src/runtime/child.tsx index 11dc169362..ed62c64569 100644 --- a/packages/plugin-icestark/src/runtime/child.tsx +++ b/packages/plugin-icestark/src/runtime/child.tsx @@ -1,5 +1,5 @@ import * as ReactDOM from 'react-dom/client'; -import type { RuntimePlugin } from '@ice/runtime/types'; +import type { RuntimePlugin } from '@ice/runtime'; import type { LifecycleOptions } from '../types'; const runtime: RuntimePlugin = ({ setRender }, runtimeOptions) => { diff --git a/packages/plugin-icestark/src/runtime/framework.tsx b/packages/plugin-icestark/src/runtime/framework.tsx index e8743b9905..2be519208a 100644 --- a/packages/plugin-icestark/src/runtime/framework.tsx +++ b/packages/plugin-icestark/src/runtime/framework.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { AppRouter, AppRoute } from '@ice/stark'; -import type { RuntimePlugin, ClientAppRouterProps } from '@ice/runtime/types'; +import type { RuntimePlugin, ClientAppRouterProps } from '@ice/runtime'; import type { RouteInfo, AppConfig } from '../types'; const { useState, useEffect } = React; diff --git a/packages/plugin-intl/src/runtime-simple.ts b/packages/plugin-intl/src/runtime-simple.ts index 504e18cf09..0084423eac 100644 --- a/packages/plugin-intl/src/runtime-simple.ts +++ b/packages/plugin-intl/src/runtime-simple.ts @@ -1,4 +1,4 @@ -import type { RuntimePlugin } from '@ice/runtime/types'; +import type { RuntimePlugin } from '@ice/runtime'; import { getDefaultLocale, getLocaleMessages, EXPORT_NAME } from './intl-until.js'; let currentLocale = getDefaultLocale(); diff --git a/packages/plugin-intl/src/runtime.tsx b/packages/plugin-intl/src/runtime.tsx index d8c47f5f39..82a48a980f 100644 --- a/packages/plugin-intl/src/runtime.tsx +++ b/packages/plugin-intl/src/runtime.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { createIntl, createIntlCache, RawIntlProvider, useIntl } from 'react-intl'; import type { IntlShape } from 'react-intl'; -import type { RuntimePlugin } from '@ice/runtime/types'; +import type { RuntimePlugin } from '@ice/runtime'; import { getDefaultLocale, getLocaleMessages, EXPORT_NAME } from './intl-until.js'; import type { LocaleConfig } from './types.js'; diff --git a/packages/plugin-miniapp/src/runtime/index.ts b/packages/plugin-miniapp/src/runtime/index.ts index 90ec50aab2..8848958e31 100644 --- a/packages/plugin-miniapp/src/runtime/index.ts +++ b/packages/plugin-miniapp/src/runtime/index.ts @@ -1,4 +1,4 @@ -import type { RuntimePlugin } from '@ice/runtime/types'; +import type { RuntimePlugin } from '@ice/runtime'; import type { MiniappLifecycles } from '@ice/miniapp-runtime/esm/types'; export function defineMiniappConfig( diff --git a/packages/plugin-request/src/runtime.ts b/packages/plugin-request/src/runtime.ts index 5c4c8800f3..d47e6b8f03 100644 --- a/packages/plugin-request/src/runtime.ts +++ b/packages/plugin-request/src/runtime.ts @@ -1,4 +1,4 @@ -import type { StaticRuntimePlugin } from '@ice/runtime/types'; +import type { StaticRuntimePlugin } from '@ice/runtime'; import { createAxiosInstance, setAxiosInstance } from './request.js'; import type { RequestConfig } from './types'; diff --git a/packages/plugin-store/src/runtime.tsx b/packages/plugin-store/src/runtime.tsx index cfb6fc9b80..d75bfd4741 100644 --- a/packages/plugin-store/src/runtime.tsx +++ b/packages/plugin-store/src/runtime.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import type { RuntimePlugin, AppProvider, RouteWrapper } from '@ice/runtime/types'; +import type { RuntimePlugin, AppProvider, RouteWrapper } from '@ice/runtime'; import { PAGE_STORE_INITIAL_STATES, PAGE_STORE_PROVIDER } from './constants.js'; import type { StoreConfig } from './types.js'; diff --git a/packages/runtime-kit/package.json b/packages/runtime-kit/package.json index bf26a7156c..fa7387e86e 100644 --- a/packages/runtime-kit/package.json +++ b/packages/runtime-kit/package.json @@ -5,6 +5,7 @@ "main": "./esm/index.js", "module": "./esm/index.js", "types": "./esm/index.d.ts", + "type": "module", "files": [ "esm" ], diff --git a/packages/runtime-kit/src/dataLoader.ts b/packages/runtime-kit/src/dataLoader.ts index 2e5da6cb35..bd9c7014c4 100644 --- a/packages/runtime-kit/src/dataLoader.ts +++ b/packages/runtime-kit/src/dataLoader.ts @@ -1,8 +1,9 @@ -import getRequestContext from './requestContext.js'; +import { getRequestContext } from './requestContext.js'; import type { RequestContext, RenderMode, AppExport, RuntimeModules, StaticRuntimePlugin, CommonJsRuntime, Loader, DataLoaderResult, StaticDataLoader, DataLoaderConfig, DataLoaderOptions, + RunClientAppOptions, } from './types.js'; interface Loaders { @@ -14,8 +15,8 @@ interface CachedResult { } interface Options { - fetcher: (config: StaticDataLoader) => Promise; - decorator: (dataLoader: Function, id?: number) => Function; + fetcher: RunClientAppOptions['dataLoaderFetcher']; + decorator: RunClientAppOptions['dataLoaderDecorator']; runtimeModules: RuntimeModules['statics']; appExport: AppExport; } @@ -49,18 +50,16 @@ export function defineStaticDataLoader(dataLoader: Loader): DataLoaderConfig { * Custom fetcher for load static data loader config. * Set globally to avoid passing this fetcher too deep. */ -let dataLoaderFetcher: (config: StaticDataLoader) => Promise; -export function setFetcher(customFetcher: (config: StaticDataLoader) => Promise): void { +let dataLoaderFetcher: RunClientAppOptions['dataLoaderFetcher']; +export function setFetcher(customFetcher: RunClientAppOptions['dataLoaderFetcher']): void { dataLoaderFetcher = customFetcher; } /** * Custom decorator for deal with data loader. */ -let dataLoaderDecorator = (dataLoader: Function, id?: number): Function => { - return dataLoader; -}; -export function setDecorator(customDecorator: (dataLoader: Function, id?: number) => Function): void { +let dataLoaderDecorator: RunClientAppOptions['dataLoaderDecorator']; +export function setDecorator(customDecorator: RunClientAppOptions['dataLoaderDecorator']): void { dataLoaderDecorator = customDecorator; } @@ -125,7 +124,7 @@ export function loadDataByCustomFetcher(config: StaticDataLoader): Promise } catch (error) { console.error('parse template error: ', error); } - return dataLoaderFetcher(parsedConfig); + return dataLoaderFetcher?.(parsedConfig); } /** @@ -142,7 +141,7 @@ export function callDataLoader(dataLoader: Loader, requestContext: RequestContex return loadDataByCustomFetcher(dataLoader); } - return dataLoaderDecorator(dataLoader)(requestContext); + return dataLoaderDecorator?.(dataLoader)?.(requestContext); } const cache = new Map(); diff --git a/packages/runtime-kit/src/requestContext.ts b/packages/runtime-kit/src/requestContext.ts index 04eef93116..72482f5455 100644 --- a/packages/runtime-kit/src/requestContext.ts +++ b/packages/runtime-kit/src/requestContext.ts @@ -8,7 +8,7 @@ interface Location { /** * context for getData both in server and client side. */ -export default function getRequestContext(location: Location, serverContext: ServerContext = {}): RequestContext { +export function getRequestContext(location: Location, serverContext: ServerContext = {}): RequestContext { const { pathname, search } = location; // Use query form server context first to avoid unnecessary parsing. // @ts-ignore diff --git a/packages/runtime-kit/src/types.ts b/packages/runtime-kit/src/types.ts index eb08e7cc46..4149fe76a2 100644 --- a/packages/runtime-kit/src/types.ts +++ b/packages/runtime-kit/src/types.ts @@ -3,6 +3,7 @@ import type { ComponentType, PropsWithChildren } from 'react'; import type { HydrationOptions, Root } from 'react-dom/client'; // Basic Types +export type AppData = any; export type RouteData = any; export type RenderMode = 'SSR' | 'SSG' | 'CSR'; @@ -181,7 +182,7 @@ type UseConfig = () => RouteConfig>; type UseData = () => RouteData; type UseAppContext = () => AppContext; -export interface RuntimeAPI { +export interface RuntimeAPI { setAppRouter?: SetAppRouter; getAppRouter: GetAppRouter; addProvider: AddProvider; @@ -193,12 +194,12 @@ export interface RuntimeAPI { useData: UseData; useConfig: UseConfig; useAppContext: UseAppContext; - history: History; + history: T; } // Plugin Types -export interface RuntimePlugin> { - (apis: RuntimeAPI, runtimeOptions?: T): Promise | void; +export interface RuntimePlugin, H = History> { + (apis: RuntimeAPI, runtimeOptions?: T): Promise | void; } export interface StaticRuntimeAPI { @@ -253,7 +254,7 @@ export interface RunClientAppOptions { basename?: string; memoryRouter?: boolean; runtimeOptions?: Record; - dataLoaderFetcher?: (config: StaticDataLoader) => void; + dataLoaderFetcher?: (config: StaticDataLoader) => any; dataLoaderDecorator?: (loader: Loader, index?: number) => (requestContext: RequestContext) => DataLoaderResult; } diff --git a/packages/runtime-kit/tsconfig.json b/packages/runtime-kit/tsconfig.json index d4290f3d1f..79cf8a2f2d 100644 --- a/packages/runtime-kit/tsconfig.json +++ b/packages/runtime-kit/tsconfig.json @@ -4,8 +4,8 @@ "baseUrl": "./", "rootDir": "src", "outDir": "esm", - "module": "ES2020" + "module": "ES2020", + "moduleResolution": "NodeNext", }, - "include": ["src"], - "exclude": ["node_modules", "dist"] + "include": ["src"] } diff --git a/packages/runtime/src/AppContext.tsx b/packages/runtime/src/AppContext.tsx index 4aff665a9c..79c16f2c0e 100644 --- a/packages/runtime/src/AppContext.tsx +++ b/packages/runtime/src/AppContext.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import type { AppContext } from './types.js'; +import type { AppContext } from '@ice/runtime-kit'; const Context = React.createContext(undefined); diff --git a/packages/runtime/src/Document.tsx b/packages/runtime/src/Document.tsx index 53827764d6..0dd06af6b5 100644 --- a/packages/runtime/src/Document.tsx +++ b/packages/runtime/src/Document.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import type { WindowContext, RouteMatch, AssetsManifest } from './types.js'; +import type { AssetsManifest } from '@ice/runtime-kit'; +import type { WindowContext, RouteMatch } from './types.js'; import { useAppContext, useAppData } from './AppContext.js'; import { getMeta, getTitle, getLinks, getScripts } from './routesConfig.js'; import getCurrentRoutePath from './utils/getCurrentRoutePath.js'; diff --git a/packages/runtime/src/RouteContext.ts b/packages/runtime/src/RouteContext.ts index 0165af32e5..1b424c5b0e 100644 --- a/packages/runtime/src/RouteContext.ts +++ b/packages/runtime/src/RouteContext.ts @@ -1,5 +1,5 @@ import { useLoaderData } from 'react-router-dom'; -import type { RouteConfig } from './types.js'; +import type { RouteConfig } from '@ice/runtime-kit'; function useData(): T { return (useLoaderData() as any)?.data; diff --git a/packages/runtime/src/RouteWrapper.tsx b/packages/runtime/src/RouteWrapper.tsx index adacd13289..1e42ee3585 100644 --- a/packages/runtime/src/RouteWrapper.tsx +++ b/packages/runtime/src/RouteWrapper.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import type { RouteWrapperConfig, ComponentModule } from './types.js'; +import type { RouteWrapperConfig, ComponentModule } from '@ice/runtime-kit'; interface Props { id: string; diff --git a/packages/runtime/src/Suspense.tsx b/packages/runtime/src/Suspense.tsx index 5323436308..5865d93193 100644 --- a/packages/runtime/src/Suspense.tsx +++ b/packages/runtime/src/Suspense.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import type { ReactNode } from 'react'; +import type { RequestContext } from '@ice/runtime-kit'; import { useAppContext } from './AppContext.js'; import proxyData from './proxyData.js'; -import type { RequestContext } from './types.js'; const LOADER = '__ICE_SUSPENSE_LOADER__'; const isClient = typeof window !== 'undefined' && 'onload' in window; diff --git a/packages/runtime/src/appConfig.ts b/packages/runtime/src/appConfig.ts deleted file mode 100644 index be66c3212c..0000000000 --- a/packages/runtime/src/appConfig.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { AppConfig, AppExport } from './types.js'; - -const defaultAppConfig: AppConfig = { - app: { - strict: false, - rootId: 'ice-container', - }, - router: { - type: 'browser', - }, -}; - -export default function getAppConfig(appExport: AppExport): AppConfig { - const appConfig = appExport?.default || {}; - - const { app, router, ...others } = appConfig; - - return { - app: { - ...defaultAppConfig.app, - ...(app || {}), - }, - router: { - ...defaultAppConfig.router, - ...(router || {}), - }, - ...others, - }; -} - -export function defineAppConfig(appConfigOrDefineAppConfig: AppConfig | (() => AppConfig)): AppConfig { - if (typeof appConfigOrDefineAppConfig === 'function') { - return appConfigOrDefineAppConfig(); - } else { - return appConfigOrDefineAppConfig; - } -} diff --git a/packages/runtime/src/appData.ts b/packages/runtime/src/appData.ts index de9a3cea37..25714742a7 100644 --- a/packages/runtime/src/appData.ts +++ b/packages/runtime/src/appData.ts @@ -1,5 +1,5 @@ -import type { AppExport, AppData, RequestContext, Loader } from './types.js'; -import { callDataLoader } from './dataLoader.js'; +import type { AppExport, AppData, RequestContext, Loader } from '@ice/runtime-kit'; +import { callDataLoader } from '@ice/runtime-kit'; /** * Call the getData of app config. diff --git a/packages/runtime/src/dataLoader.ts b/packages/runtime/src/dataLoader.ts deleted file mode 100644 index c37b3b178e..0000000000 --- a/packages/runtime/src/dataLoader.ts +++ /dev/null @@ -1,281 +0,0 @@ -import getRequestContext from './requestContext.js'; -import type { - RequestContext, RenderMode, AppExport, - RuntimeModules, StaticRuntimePlugin, CommonJsRuntime, - Loader, DataLoaderResult, StaticDataLoader, DataLoaderConfig, DataLoaderOptions, -} from './types.js'; -interface Loaders { - [routeId: string]: DataLoaderConfig; -} - -interface CachedResult { - value: any; -} - -interface Options { - fetcher: Function; - decorator: Function; - runtimeModules: RuntimeModules['statics']; - appExport: AppExport; -} - -export interface LoadRoutesDataOptions { - renderMode: RenderMode; - requestContext?: RequestContext; -} - -export function defineDataLoader(dataLoader: Loader, options?: DataLoaderOptions): DataLoaderConfig { - return { - loader: dataLoader, - options, - }; -} - -export function defineServerDataLoader(dataLoader: Loader, options?: DataLoaderOptions): DataLoaderConfig { - return { - loader: dataLoader, - options, - }; -} - -export function defineStaticDataLoader(dataLoader: Loader): DataLoaderConfig { - return { - loader: dataLoader, - }; -} - -/** - * Custom fetcher for load static data loader config. - * Set globally to avoid passing this fetcher too deep. - */ -let dataLoaderFetcher; -export function setFetcher(customFetcher) { - dataLoaderFetcher = customFetcher; -} - -/** - * Custom decorator for deal with data loader. - */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -let dataLoaderDecorator = (dataLoader: Function, id?: number) => { - return dataLoader; -}; -export function setDecorator(customDecorator) { - dataLoaderDecorator = customDecorator; -} - -/** - * Parse template for static dataLoader. - */ -export function parseTemplate(config: StaticDataLoader) { - const queryParams = {}; - const getQueryParams = () => { - if (Object.keys(queryParams).length === 0) { - if (location.search.includes('?')) { - location.search.substring(1).split('&').forEach(query => { - const res = query.split('='); - // ?test=1&hello=world - if (res[0] !== undefined && res[1] !== undefined) { - queryParams[res[0]] = res[1]; - } - }); - } - } - - return queryParams; - }; - - const cookie = {}; - const getCookie = () => { - if (Object.keys(cookie).length === 0) { - document.cookie.split(';').forEach(c => { - const [key, value] = c.split('='); - if (key !== undefined && value !== undefined) { - cookie[key.trim()] = value.trim(); - } - }); - } - - return cookie; - }; - - // Match all template of query cookie and storage. - let strConfig = JSON.stringify(config) || ''; - const regexp = /\$\{(queryParams|cookie|storage)(\.(\w|-)+)?}/g; - let cap = []; - let matched = []; - while ((cap = regexp.exec(strConfig)) !== null) { - matched.push(cap); - } - - matched.forEach(item => { - const [origin, key, value] = item; - if (item && origin && key && value && value.startsWith('.')) { - if (key === 'queryParams') { - // Replace query params. - strConfig = strConfig.replace(origin, getQueryParams()[value.substring(1)] || ''); - } else if (key === 'cookie') { - // Replace cookie. - strConfig = strConfig.replace(origin, getCookie()[value.substring(1)] || ''); - } else if (key === 'storage') { - // Replace storage. - strConfig = strConfig.replace(origin, localStorage.getItem(value.substring(1)) || ''); - } - } - }); - - // Replace url. - strConfig = strConfig.replace('${url}', location.href); - - return JSON.parse(strConfig); -} - -export function loadDataByCustomFetcher(config: StaticDataLoader) { - let parsedConfig = config; - try { - // Not parse template in SSG/SSR. - if (import.meta.renderer === 'client') { - parsedConfig = parseTemplate(config); - } - } catch (error) { - console.error('parse template error: ', error); - } - return dataLoaderFetcher(parsedConfig); -} - -/** - * Handle for different dataLoader. - */ -export function callDataLoader(dataLoader: Loader, requestContext: RequestContext): DataLoaderResult { - if (Array.isArray(dataLoader)) { - const loaders = dataLoader.map((loader, index) => { - return typeof loader === 'object' ? loadDataByCustomFetcher(loader) : dataLoaderDecorator(loader, index)(requestContext); - }); - - return loaders; - } - - if (typeof dataLoader === 'object') { - return loadDataByCustomFetcher(dataLoader); - } - - return dataLoaderDecorator(dataLoader)(requestContext); -} - -const cache = new Map(); - -/** - * Start getData once data-loader.js is ready in client, and set to cache. - */ -function loadInitialDataInClient(loaders: Loaders) { - const context = (window as any).__ICE_APP_CONTEXT__ || {}; - const matchedIds = context.matchedIds || []; - const loaderData = context.loaderData || {}; - const { renderMode } = context; - - const ids = ['__app'].concat(matchedIds); - ids.forEach(id => { - const dataFromSSR = loaderData[id]?.data; - if (dataFromSSR) { - cache.set(renderMode === 'SSG' ? `${id}_ssg` : id, { - value: dataFromSSR, - }); - - if (renderMode === 'SSR') { - return; - } - } - - const dataLoaderConfig = loaders[id]; - - if (dataLoaderConfig) { - const requestContext = getRequestContext(window.location); - const { loader } = dataLoaderConfig; - const promise = callDataLoader(loader, requestContext); - - cache.set(id, { - value: promise, - }); - } - }); -} - -/** - * Init data loader in client side. - * Load initial data and register global loader. - * In order to load data, JavaScript modules, CSS and other assets in parallel. - */ -async function init(loaders: Loaders, options: Options) { - const { - fetcher, - decorator, - runtimeModules, - appExport, - } = options; - - const runtimeApi = { - appContext: { - appExport, - }, - }; - - if (runtimeModules) { - await Promise.all(runtimeModules.map(module => { - const runtimeModule = ((module as CommonJsRuntime).default || module) as StaticRuntimePlugin; - return runtimeModule(runtimeApi); - }).filter(Boolean)); - } - - if (fetcher) { - setFetcher(fetcher); - } - - if (decorator) { - setDecorator(decorator); - } - - try { - loadInitialDataInClient(loaders); - } catch (error) { - console.error('Load initial data error: ', error); - } - - (window as any).__ICE_DATA_LOADER__ = { - getLoader: (id) => { - return loaders[id]; - }, - getData: (id, options: LoadRoutesDataOptions) => { - let result; - - // First render for ssg use data from build time, second render for ssg will use data from data loader. - const cacheKey = `${id}${options?.renderMode === 'SSG' ? '_ssg' : ''}`; - - // In CSR, all dataLoader is called by global data loader to avoid bundle dataLoader in page bundle duplicate. - result = cache.get(cacheKey); - // Always fetch new data after cache is been used. - cache.delete(cacheKey); - - // Already send data request. - if (result) { - return result.value; - } - - const dataLoaderConfig = loaders[id]; - - // No data loader. - if (!dataLoaderConfig) { - return null; - } - - // Call dataLoader. - const { loader } = dataLoaderConfig; - return callDataLoader(loader, options?.requestContext || getRequestContext(window.location)); - }, - }; -} - -export const dataLoader = { - init, -}; - -export default dataLoader; diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 7371d5cefa..f59bf85084 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1,20 +1,27 @@ import type { + RunClientAppOptions, + CreateRoutes, RuntimePlugin, AppContext, - PublicAppContext, AppConfig, RouteConfig, - RouteItem, - ServerContext, - AppProvider, + RouteWrapperConfig, RouteWrapper, RenderMode, Loader, - RouteWrapperConfig, + ServerContext, + AppProvider, + StaticRuntimePlugin, +} from '@ice/runtime-kit'; +import { dataLoader, defineDataLoader, defineServerDataLoader, defineStaticDataLoader, callDataLoader, getRequestContext } from '@ice/runtime-kit'; +import { getAppConfig, defineAppConfig } from '@ice/runtime-kit'; +import type { + PublicAppContext, + RouteItem, + ClientAppRouterProps, } from './types.js'; import Runtime from './runtime.js'; import runClientApp from './runClientApp.js'; -import type { RunClientAppOptions, CreateRoutes } from './runClientApp.js'; import { useAppContext as useInternalAppContext, useAppData, AppContextProvider } from './AppContext.js'; import { getAppData } from './appData.js'; import { useData, useConfig } from './RouteContext.js'; @@ -37,10 +44,7 @@ import type { DataType, MainType, } from './Document.js'; -import dataLoader, { defineDataLoader, defineServerDataLoader, defineStaticDataLoader, callDataLoader } from './dataLoader.js'; -import getRequestContext from './requestContext.js'; import AppErrorBoundary from './AppErrorBoundary.js'; -import getAppConfig, { defineAppConfig } from './appConfig.js'; import { routerHistory as history } from './history.js'; import KeepAliveOutlet from './KeepAliveOutlet.js'; import { useActive } from './Activity.js'; @@ -150,6 +154,7 @@ export { } from 'react-router-dom'; export type { + StaticRuntimePlugin, RuntimePlugin, AppContext, AppConfig, @@ -170,4 +175,5 @@ export type { DataType, MainType, CreateRoutes, + ClientAppRouterProps, }; diff --git a/packages/runtime/src/renderDocument.tsx b/packages/runtime/src/renderDocument.tsx index 87eb3f8bb8..fcb5ebe237 100644 --- a/packages/runtime/src/renderDocument.tsx +++ b/packages/runtime/src/renderDocument.tsx @@ -1,24 +1,23 @@ import * as React from 'react'; import * as ReactDOMServer from 'react-dom/server'; -import getAppConfig from './appConfig.js'; +import { getRequestContext, getAppConfig } from '@ice/runtime-kit'; +import type { ServerContext, AppContext } from '@ice/runtime-kit'; import { AppContextProvider } from './AppContext.js'; import { DocumentContextProvider } from './Document.js'; import addLeadingSlash from './utils/addLeadingSlash.js'; -import getRequestContext from './requestContext.js'; import matchRoutes from './matchRoutes.js'; import getDocumentData from './server/getDocumentData.js'; import getCurrentRoutePath from './utils/getCurrentRoutePath.js'; import { sendResponse, getLocation } from './server/response.js'; import type { - AppContext, RouteItem, RouteMatch, RenderOptions, Response, - ServerContext, } from './types.js'; + interface RenderDocumentOptions { matches: RouteMatch[]; renderOptions: RenderOptions; diff --git a/packages/runtime/src/reportRecoverableError.ts b/packages/runtime/src/reportRecoverableError.ts index 5bb9968fa0..9b18648db8 100644 --- a/packages/runtime/src/reportRecoverableError.ts +++ b/packages/runtime/src/reportRecoverableError.ts @@ -1,4 +1,4 @@ -import type { ErrorStack } from './types.js'; +import type { ErrorStack } from '@ice/runtime-kit'; interface ErrorOptions { ignoreRuntimeWarning?: boolean; diff --git a/packages/runtime/src/requestContext.ts b/packages/runtime/src/requestContext.ts deleted file mode 100644 index aff80f144d..0000000000 --- a/packages/runtime/src/requestContext.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { ServerContext, RequestContext } from './types.js'; - -export interface Location { - pathname: string; - search: string; -} - -/** - * context for getData both in server and client side. - */ -export default function getRequestContext(location: Location, serverContext: ServerContext = {}): RequestContext { - const { pathname, search } = location; - // Use query form server context first to avoid unnecessary parsing. - // @ts-ignore - const query = serverContext?.req?.query || parseSearch(search); - - const requestContext: RequestContext = { - ...(serverContext || {}), - pathname, - query, - }; - - return requestContext; -} - -/** - * Search string to object - * URLSearchParams is not compatible with iOS9 and IE. - */ -export function parseSearch(search: string) { - // remove first '?' - if (search.indexOf('?') === 0) { - search = search.slice(1); - } - - const result = {}; - - let pairs = search.split('&'); - - for (let j = 0; j < pairs.length; j++) { - const value = pairs[j]; - const index = value.indexOf('='); - - if (index > -1) { - const k = value.slice(0, index); - const v = value.slice(index + 1); - result[k] = v; - } else if (value) { - result[value] = ''; - } - } - - return result; -} diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx index fe95deb476..f2855ce865 100644 --- a/packages/runtime/src/routes.tsx +++ b/packages/runtime/src/routes.tsx @@ -3,21 +3,13 @@ import { useRouteError, defer, Await as ReactRouterAwait } from 'react-router-do import type { AwaitProps } from 'react-router-dom'; // eslint-disable-next-line camelcase import type { UNSAFE_DeferredData, LoaderFunctionArgs } from '@remix-run/router'; -import type { - RouteItem, - RouteModules, - RenderMode, - RequestContext, - ComponentModule, - DataLoaderConfig, - DataLoaderOptions, - Loader, -} from './types.js'; +import type { RouteModules, RenderMode, RequestContext, ComponentModule, DataLoaderConfig, DataLoaderOptions, Loader } from '@ice/runtime-kit'; +import { callDataLoader } from '@ice/runtime-kit'; +import { parseSearch } from '@ice/runtime-kit'; +import type { RouteItem } from './types.js'; import RouteWrapper from './RouteWrapper.js'; import { useAppContext } from './AppContext.js'; -import { callDataLoader } from './dataLoader.js'; import { updateRoutesConfig } from './routesConfig.js'; -import { parseSearch } from './requestContext.js'; type RouteModule = Pick; diff --git a/packages/runtime/src/routesConfig.ts b/packages/runtime/src/routesConfig.ts index ceba4f3aba..a2c8335d40 100644 --- a/packages/runtime/src/routesConfig.ts +++ b/packages/runtime/src/routesConfig.ts @@ -1,4 +1,5 @@ -import type { RouteMatch, LoadersData, LoaderData, RouteConfig } from './types.js'; +import type { RouteConfig, LoadersData, LoaderData } from '@ice/runtime-kit'; +import type { RouteMatch } from './types.js'; export function getMeta( matches: RouteMatch[], diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx index d03ed992ea..18ee466837 100644 --- a/packages/runtime/src/runClientApp.tsx +++ b/packages/runtime/src/runClientApp.tsx @@ -1,42 +1,40 @@ import React from 'react'; import * as ReactDOM from 'react-dom/client'; -import { createHashHistory, createBrowserHistory, createMemoryHistory } from '@remix-run/router'; +import { + createHashHistory, + createBrowserHistory, + createMemoryHistory, +} from '@remix-run/router'; import type { History } from '@remix-run/router'; import type { - AppContext, WindowContext, AppExport, RouteItem, RuntimeModules, AppConfig, AssetsManifest, ClientAppRouterProps, + AppContext, + AppConfig, + AssetsManifest, + RunClientAppOptions, ErrorStack, +} from '@ice/runtime-kit'; +import { setFetcher, setDecorator, getRequestContext, getAppConfig } from '@ice/runtime-kit'; +import type { + WindowContext, + RouteItem, + ClientAppRouterProps, } from './types.js'; -import { createHistory as createHistorySingle, getSingleRoute } from './singleRouter.js'; -import { setHistory } from './history.js'; import Runtime from './runtime.js'; +import { + createHistory as createHistorySingle, + getSingleRoute, +} from './singleRouter.js'; +import { setHistory } from './history.js'; import { getAppData } from './appData.js'; import { getRoutesPath, loadRouteModule } from './routes.js'; -import type { RouteLoaderOptions } from './routes.js'; -import getRequestContext from './requestContext.js'; -import getAppConfig from './appConfig.js'; import matchRoutes from './matchRoutes.js'; -import { setFetcher, setDecorator } from './dataLoader.js'; import ClientRouter from './ClientRouter.js'; -import addLeadingSlash from './utils/addLeadingSlash.js'; import { AppContextProvider } from './AppContext.js'; +import addLeadingSlash from './utils/addLeadingSlash.js'; import { deprecatedHistory } from './utils/deprecatedHistory.js'; import reportRecoverableError from './reportRecoverableError.js'; -export type CreateRoutes = (options: Pick) => RouteItem[]; - -export interface RunClientAppOptions { - app: AppExport; - runtimeModules: RuntimeModules; - createRoutes?: CreateRoutes; - hydrate?: boolean; - basename?: string; - memoryRouter?: boolean; - runtimeOptions?: Record; - dataLoaderFetcher?: Function; - dataLoaderDecorator?: Function; -} - -export default async function runClientApp(options: RunClientAppOptions) { +export default async function runClientApp(options: RunClientAppOptions) { const { app, createRoutes, diff --git a/packages/runtime/src/runServerApp.tsx b/packages/runtime/src/runServerApp.tsx index 8205e20487..3515d4b876 100644 --- a/packages/runtime/src/runServerApp.tsx +++ b/packages/runtime/src/runServerApp.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; import type { Location } from 'history'; +import type { AppContext, ServerContext, AppData } from '@ice/runtime-kit'; +import { getAppConfig, getRequestContext } from '@ice/runtime-kit'; import type { OnAllReadyParams, OnShellReadyParams } from './server/streamRender.js'; import type { - AppContext, - ServerContext, RouteMatch, - AppData, ServerAppRouterProps, RenderOptions, Response, @@ -13,11 +12,9 @@ import type { import Runtime from './runtime.js'; import { AppContextProvider } from './AppContext.js'; import { getAppData } from './appData.js'; -import getAppConfig from './appConfig.js'; import { DocumentContextProvider } from './Document.js'; import { loadRouteModules } from './routes.js'; import { pipeToString, renderToNodeStream } from './server/streamRender.js'; -import getRequestContext from './requestContext.js'; import matchRoutes from './matchRoutes.js'; import getCurrentRoutePath from './utils/getCurrentRoutePath.js'; import ServerRouter from './ServerRouter.js'; diff --git a/packages/runtime/src/runtime.tsx b/packages/runtime/src/runtime.tsx index 0c102aa610..c4a506bb92 100644 --- a/packages/runtime/src/runtime.tsx +++ b/packages/runtime/src/runtime.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import type { ComponentType } from 'react'; -import { routerHistory as history } from './history.js'; import type { Renderer, AppContext, @@ -14,14 +13,15 @@ import type { AddWrapper, RouteWrapperConfig, SetRender, - AppRouterProps, ComponentWithChildren, ResponseHandler, -} from './types.js'; +} from '@ice/runtime-kit'; +import type { History } from '@remix-run/router'; +import { routerHistory as history } from './history.js'; +import type { AppRouterProps } from './types.js'; import { useData, useConfig } from './RouteContext.js'; import { useData as useSingleRouterData, useConfig as useSingleRouterConfig } from './singleRouter.js'; import { useAppContext } from './AppContext.js'; - class Runtime { private appContext: AppContext; @@ -73,7 +73,7 @@ class Runtime { public getWrappers = () => this.RouteWrappers; public loadModule(module: RuntimePlugin | StaticRuntimePlugin | CommonJsRuntime) { - let runtimeAPI: RuntimeAPI = { + let runtimeAPI: RuntimeAPI = { addProvider: this.addProvider, addResponseHandler: this.addResponseHandler, getResponseHandlers: this.getResponseHandlers, @@ -88,7 +88,7 @@ class Runtime { history, }; - const runtimeModule = ((module as CommonJsRuntime).default || module) as RuntimePlugin; + const runtimeModule = ((module as CommonJsRuntime).default || module) as RuntimePlugin; if (module) { return runtimeModule(runtimeAPI, this.runtimeOptions); } diff --git a/packages/runtime/src/server/getDocumentData.ts b/packages/runtime/src/server/getDocumentData.ts index 875eceaf39..dfc691a45f 100644 --- a/packages/runtime/src/server/getDocumentData.ts +++ b/packages/runtime/src/server/getDocumentData.ts @@ -1,4 +1,5 @@ -import type { DocumentDataLoaderConfig, RequestContext } from '../types.js'; +import type { RequestContext } from '@ice/runtime-kit'; +import type { DocumentDataLoaderConfig } from '../types.js'; interface Options { loaderConfig: DocumentDataLoaderConfig; diff --git a/packages/runtime/src/singleRouter.tsx b/packages/runtime/src/singleRouter.tsx index 06d4b30783..1f6242f373 100644 --- a/packages/runtime/src/singleRouter.tsx +++ b/packages/runtime/src/singleRouter.tsx @@ -5,7 +5,8 @@ import * as React from 'react'; import type { History } from '@remix-run/router'; import type { RouteObject } from 'react-router-dom'; -import type { LoaderData, RouteItem } from './types.js'; +import type { LoaderData } from '@ice/runtime-kit'; +import type { RouteItem } from './types.js'; import { loadRouteModules } from './routes.js'; const Context = React.createContext(undefined); diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts index 80f2adeeee..38ebfe009f 100644 --- a/packages/runtime/src/types.ts +++ b/packages/runtime/src/types.ts @@ -1,93 +1,20 @@ -import type { IncomingMessage, ServerResponse } from 'http'; -import type { InitialEntry, AgnosticRouteObject, Location, History, RouterInit, StaticHandlerContext } from '@remix-run/router'; -import type { ComponentType, PropsWithChildren } from 'react'; -import type { HydrationOptions, Root } from 'react-dom/client'; +import type { ComponentType } from 'react'; +import type { AgnosticRouteObject, Location, RouterInit, StaticHandlerContext } from '@remix-run/router'; import type { Params, RouteObject } from 'react-router-dom'; +import type { + AppContext, + AppExport, + ComponentWithChildren, + DataLoaderResult, + LoaderData, + PageConfig, + RenderMode, + RequestContext, + RuntimeModules, + AssetsManifest, +} from '@ice/runtime-kit'; import type { RouteLoaderOptions } from './routes.js'; -import type { RenderToPipeableStreamOptions, NodeWritablePiper } from './server/streamRender.js'; - -type UseConfig = () => RouteConfig>; -type UseData = () => RouteData; -type UseAppContext = () => AppContext; - -type VoidFunction = () => void; -type AppLifecycle = 'onShow' | 'onHide' | 'onPageNotFound' | 'onShareAppMessage' | 'onUnhandledRejection' | 'onLaunch' | 'onError' | 'onTabItemClick'; -type App = Partial<{ - rootId: string; - strict: boolean; - errorBoundary: boolean; - onRecoverableError: (error: unknown, errorInfo: ErrorStack) => void; - onBeforeHydrate: () => void; -} & Record>; - -export interface ErrorStack { - componentStack?: string; - digest?: string; -} - -export type AppData = any; -export type RouteData = any; - -// route.pageConfig return value -export type RouteConfig = T & { - // Support for extends config. - title?: string; - meta?: React.MetaHTMLAttributes[]; - links?: React.LinkHTMLAttributes[]; - scripts?: React.ScriptHTMLAttributes[]; -}; - -export interface AppExport { - default?: AppConfig; - [key: string]: any; - dataLoader?: DataLoaderConfig; -} - -export type DataLoaderResult = (Promise | RouteData) | RouteData; -export type DataLoader = (ctx: RequestContext) => DataLoaderResult; - -export interface StaticDataLoader { - key?: string; - prefetch_type?: string; - api: string; - v: string; - data: any; - ext_headers: Object; -} - -// route.defineDataLoader -// route.defineServerDataLoader -// route.defineStaticDataLoader -export type Loader = DataLoader | StaticDataLoader | Array; - -// route.pageConfig -export type PageConfig = (args: { data?: RouteData }) => RouteConfig; - -export interface AppConfig { - app?: App; - router?: { - type?: 'hash' | 'browser' | 'memory'; - basename?: string; - initialEntries?: InitialEntry[]; - }; -} - -export interface RoutesConfig { - [routeId: string]: RouteConfig; -} - -export interface RoutesData { - [routeId: string]: RouteData; -} - -export interface DataLoaderOptions { - defer?: boolean; -} - -export interface DataLoaderConfig { - loader: Loader; - options?: DataLoaderOptions; -} +import type { NodeWritablePiper, RenderToPipeableStreamOptions } from './server/streamRender.js'; interface DocumentLoaderOptions { documentOnly?: boolean; @@ -98,38 +25,6 @@ export interface DocumentDataLoaderConfig { loader: DocumentDataLoader; } -export interface LoadersData { - [routeId: string]: LoaderData; -} - -export interface LoaderData { - data?: RouteData; - pageConfig?: RouteConfig; -} - -// useAppContext -export interface AppContext { - appConfig: AppConfig; - appData: any; - documentData?: any; - serverData?: any; - assetsManifest?: AssetsManifest; - loaderData?: LoadersData; - routeModules?: RouteModules; - RouteWrappers?: RouteWrapperConfig[]; - routePath?: string; - matches?: RouteMatch[]; - routes?: RouteItem[]; - documentOnly?: boolean; - matchedIds?: string[]; - appExport?: AppExport; - basename?: string; - downgrade?: boolean; - renderMode?: RenderMode; - requestContext?: RequestContext; - revalidate?: boolean; -} - export type PublicAppContext = Pick< AppContext, 'appConfig' | 'routePath' | 'downgrade' | 'documentOnly' | 'renderMode' @@ -140,31 +35,6 @@ AppContext, 'appData' | 'loaderData' | 'routePath' | 'downgrade' | 'matchedIds' | 'documentOnly' | 'renderMode' | 'serverData' | 'revalidate' >; -export type Renderer = ( - container: Element | Document, - initialChildren: React.ReactNode, - options?: HydrationOptions, -) => Root; - -export interface ServerContext { - req?: IncomingMessage; - res?: ServerResponse; -} - -export interface RequestContext extends ServerContext { - pathname: string; - query: Record; -} - -export type ComponentModule = { - default?: ComponentType; - Component?: ComponentType; - staticDataLoader?: DataLoaderConfig; - serverDataLoader?: DataLoaderConfig; - dataLoader?: DataLoaderConfig; - pageConfig?: PageConfig; - [key: string]: any; -}; export type RouteItem = AgnosticRouteObject & { componentName: string; @@ -174,94 +44,10 @@ export type RouteItem = AgnosticRouteObject & { children?: RouteItem[]; }; -export type ComponentWithChildren

= ComponentType>; - export type DocumentComponent = ComponentWithChildren<{ pagePath: string; }>; -export interface RouteWrapperConfig { - Wrapper: RouteWrapper; - layout?: boolean; -} - -export type AppProvider = ComponentWithChildren; -export type RouteWrapper = ComponentType; -export type ResponseHandler = ( - req: IncomingMessage, - res: ServerResponse, -) => any | Promise; - -export type SetAppRouter = (AppRouter: ComponentType) => void; -export type GetAppRouter = () => AppProvider; -export type AddProvider = (Provider: AppProvider) => void; -export type SetRender = (render: Renderer) => void; -export type AddWrapper = (wrapper: RouteWrapper, forLayout?: boolean) => void; -export type AddResponseHandler = (handler: ResponseHandler) => void; -export type GetResponseHandlers = () => ResponseHandler[]; - -export interface RouteModules { - [routeId: string]: ComponentModule; -} - -export interface AssetsManifest { - dataLoader?: string; - publicPath: string; - entries: { - [assetPath: string]: string[]; - }; - pages: { - [assetPath: string]: string[]; - }; - assets?: { - [assetPath: string]: string; - }; -} - -export interface RuntimeAPI { - setAppRouter?: SetAppRouter; - getAppRouter: GetAppRouter; - addProvider: AddProvider; - addResponseHandler: AddResponseHandler; - getResponseHandlers: GetResponseHandlers; - setRender: SetRender; - addWrapper: AddWrapper; - appContext: AppContext; - useData: UseData; - useConfig: UseConfig; - useAppContext: UseAppContext; - history: History; -} - -export interface StaticRuntimeAPI { - appContext: { - appExport: AppExport; - }; -} - -export interface RuntimePlugin> { - ( - apis: RuntimeAPI, - runtimeOptions?: T, - ): Promise | void; -} - -export interface StaticRuntimePlugin> { - ( - apis: StaticRuntimeAPI, - runtimeOptions?: T, - ): Promise | void; -} - -export interface CommonJsRuntime { - default: RuntimePlugin | StaticRuntimePlugin; -} - -export interface RuntimeModules { - statics?: (StaticRuntimePlugin | CommonJsRuntime)[]; - commons?: (RuntimePlugin | CommonJsRuntime)[]; -} - export interface AppRouterProps { routes?: RouteObject[]; location?: Location; @@ -301,8 +87,6 @@ export interface RouteMatch { route: RouteItem; } -export type RenderMode = 'SSR' | 'SSG' | 'CSR'; - interface Piper { pipe: NodeWritablePiper; fallback: Function; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 115b1131e4..4693ffc435 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1690,6 +1690,9 @@ importers: '@ice/runtime': specifier: workspace:^ version: link:../runtime + '@ice/runtime-kit': + specifier: workspace:^ + version: link:../runtime-kit '@ice/shared-config': specifier: workspace:* version: link:../shared-config