;
+}
+
+// App Configuration Types
+export type App = Partial<{
+ rootId: string;
+ strict: boolean;
+ errorBoundary: boolean;
+ onRecoverableError: (error: unknown, errorInfo: ErrorStack) => void;
+ onBeforeHydrate: () => void;
+}>;
+
+export interface AppConfig {
+ app?: App;
+ router?: {
+ type?: 'hash' | 'browser' | 'memory';
+ basename?: string;
+ initialEntries?: InitialEntry[];
+ };
+}
+
+export interface AppExport {
+ default?: AppConfig;
+ dataLoader?: DataLoaderConfig;
+ [key: string]: any;
+}
+
+// Route & Component Types
+export type ComponentWithChildren = ComponentType>;
+export type AppProvider = ComponentWithChildren;
+export type RouteWrapper = ComponentType;
+
+export type InitialEntry = string | Partial;
+export type Params = {
+ readonly [key in Key]: string | undefined;
+};
+
+// Data Loading Types
+export interface DataLoaderOptions {
+ defer?: boolean;
+}
+
+export interface StaticDataLoader {
+ key?: string;
+ prefetch_type?: string;
+ api: string;
+ v: string;
+ data: any;
+ ext_headers: Object;
+}
+
+export type DataLoaderResult = (Promise | RouteData) | RouteData;
+export type DataLoader = (ctx: RequestContext) => DataLoaderResult;
+export type Loader = DataLoader | StaticDataLoader | Array;
+
+export interface DataLoaderConfig {
+ loader: Loader;
+ options?: DataLoaderOptions;
+}
+
+// Route Configuration Types
+export type RouteConfig = T & {
+ title?: string;
+ meta?: React.MetaHTMLAttributes[];
+ links?: React.LinkHTMLAttributes[];
+ scripts?: React.ScriptHTMLAttributes[];
+};
+
+export type PageConfig = (args: { data?: RouteData }) => RouteConfig;
+
+export interface LoaderData {
+ data?: RouteData;
+ pageConfig?: RouteConfig;
+}
+
+export interface LoadersData {
+ [routeId: string]: LoaderData;
+}
+
+// Component & Module Types
+export type ComponentModule = {
+ default?: ComponentType;
+ Component?: ComponentType;
+ staticDataLoader?: DataLoaderConfig;
+ serverDataLoader?: DataLoaderConfig;
+ dataLoader?: DataLoaderConfig;
+ pageConfig?: PageConfig;
+ [key: string]: any;
+};
+
+export interface RouteModules {
+ [routeId: string]: ComponentModule;
+}
+
+export interface RouteWrapperConfig {
+ Wrapper: RouteWrapper;
+ layout?: boolean;
+}
+
+// Runtime Types
+export interface AppContext {
+ appConfig: AppConfig;
+ appData: any;
+ documentData?: any;
+ serverData?: any;
+ assetsManifest?: AssetsManifest;
+ loaderData?: LoadersData;
+ routeModules?: RouteModules;
+ RouteWrappers?: RouteWrapperConfig[];
+ routePath?: string;
+ matches?: {
+ params: Params;
+ pathname: string;
+ pathnameBase: string;
+ route: T;
+ }[];
+ routes?: T[];
+ documentOnly?: boolean;
+ matchedIds?: string[];
+ appExport?: AppExport;
+ basename?: string;
+ downgrade?: boolean;
+ renderMode?: RenderMode;
+ requestContext?: RequestContext;
+ revalidate?: boolean;
+}
+
+// Runtime API Types
+export type Renderer = (
+ container: Element | Document,
+ initialChildren: React.ReactNode,
+ options?: HydrationOptions,
+) => Root;
+
+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[];
+
+type UseConfig = () => RouteConfig>;
+type UseData = () => RouteData;
+type UseAppContext = () => AppContext;
+
+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: T;
+}
+
+// Plugin Types
+export interface RuntimePlugin, H = History> {
+ (apis: RuntimeAPI, runtimeOptions?: T): Promise | void;
+}
+
+export interface StaticRuntimeAPI {
+ appContext: {
+ appExport: AppExport;
+ };
+}
+
+export interface StaticRuntimePlugin> {
+ (apis: StaticRuntimeAPI, runtimeOptions?: T): Promise | void;
+}
+
+export interface CommonJsRuntime {
+ default: RuntimePlugin | StaticRuntimePlugin;
+}
+
+// Assets & Runtime Modules
+export interface AssetsManifest {
+ dataLoader?: string;
+ publicPath: string;
+ entries: {
+ [assetPath: string]: string[];
+ };
+ pages: {
+ [assetPath: string]: string[];
+ };
+ assets?: {
+ [assetPath: string]: string;
+ };
+}
+
+export interface RuntimeModules {
+ statics?: (StaticRuntimePlugin | CommonJsRuntime)[];
+ commons?: (RuntimePlugin | CommonJsRuntime)[];
+}
+
+// Loader & Routes Types
+export interface RouteLoaderOptions {
+ routeId: string;
+ requestContext?: RequestContext;
+ module: ComponentModule;
+ renderMode: RenderMode;
+}
+
+export type CreateRoutes = (options: Pick) => T[];
+
+export interface RunClientAppOptions {
+ app: AppExport;
+ runtimeModules: RuntimeModules;
+ createRoutes?: CreateRoutes;
+ hydrate?: boolean;
+ basename?: string;
+ memoryRouter?: boolean;
+ runtimeOptions?: Record;
+ dataLoaderFetcher?: (config: StaticDataLoader) => any;
+ dataLoaderDecorator?: (loader: Loader, index?: number) => (requestContext: RequestContext) => DataLoaderResult;
+}
+
+declare global {
+ interface ImportMeta {
+ target: string;
+ renderer: 'client' | 'server';
+ env: Record;
+ }
+}
diff --git a/packages/runtime-kit/tsconfig.json b/packages/runtime-kit/tsconfig.json
new file mode 100644
index 0000000000..79cf8a2f2d
--- /dev/null
+++ b/packages/runtime-kit/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "baseUrl": "./",
+ "rootDir": "src",
+ "outDir": "esm",
+ "module": "ES2020",
+ "moduleResolution": "NodeNext",
+ },
+ "include": ["src"]
+}
diff --git a/packages/runtime/package.json b/packages/runtime/package.json
index 8a18717a50..998eb47608 100644
--- a/packages/runtime/package.json
+++ b/packages/runtime/package.json
@@ -60,7 +60,8 @@
"abortcontroller-polyfill": "1.7.5",
"history": "^5.3.0",
"react-router-dom": "6.21.3",
- "semver": "^7.4.0"
+ "semver": "^7.4.0",
+ "@ice/runtime-kit": "^0.1.0"
},
"peerDependencies": {
"react": "^18.1.0",
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
index c37b3b178e..2d7ca42621 100644
--- a/packages/runtime/src/dataLoader.ts
+++ b/packages/runtime/src/dataLoader.ts
@@ -1,281 +1 @@
-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;
+export { defineDataLoader, defineServerDataLoader, defineStaticDataLoader } from '@ice/runtime-kit';
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/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 f268cb0d2e..18ee466837 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -1,43 +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/packages/runtime/tests/appConfig.test.ts b/packages/runtime/tests/appConfig.test.ts
index bc3c9daee3..3c664467f6 100644
--- a/packages/runtime/tests/appConfig.test.ts
+++ b/packages/runtime/tests/appConfig.test.ts
@@ -1,5 +1,5 @@
import { expect, it, describe } from 'vitest';
-import getAppConfig, { defineAppConfig } from '../src/appConfig.js';
+import { getAppConfig, defineAppConfig } from '@ice/runtime-kit';
describe('AppConfig', () => {
it('getAppConfig', () => {
diff --git a/packages/runtime/tests/routes.test.tsx b/packages/runtime/tests/routes.test.tsx
index 066360c16a..9c9c02f17e 100644
--- a/packages/runtime/tests/routes.test.tsx
+++ b/packages/runtime/tests/routes.test.tsx
@@ -155,6 +155,7 @@ describe('routes', () => {
});
it('load async route', async () => {
+ process.env.ICE_CORE_ROUTER = 'true';
const { data: deferredResult } = await createRouteLoader({
routeId: 'home',
module: InfoItem,
diff --git a/packages/runtime/tests/templateParse.test.ts b/packages/runtime/tests/templateParse.test.ts
index 84a4847301..b1f4d9300c 100644
--- a/packages/runtime/tests/templateParse.test.ts
+++ b/packages/runtime/tests/templateParse.test.ts
@@ -3,7 +3,7 @@
*/
import { expect, it, describe, beforeEach, afterEach, vi } from 'vitest';
-import { parseTemplate } from '../src/dataLoader';
+import { parseTemplate } from '@ice/runtime-kit';
describe('parseTemplate', () => {
let locationSpy;
@@ -128,4 +128,4 @@ describe('parseTemplate', () => {
ext_headers: {},
});
});
-});
\ No newline at end of file
+});
diff --git a/packages/shared-config/package.json b/packages/shared-config/package.json
index 8556e56cce..eeaa677261 100644
--- a/packages/shared-config/package.json
+++ b/packages/shared-config/package.json
@@ -28,7 +28,8 @@
"esbuild": "^0.17.16",
"postcss": "^8.4.31",
"webpack": "^5.86.0",
- "webpack-dev-server": "4.15.0"
+ "webpack-dev-server": "4.15.0",
+ "@ice/route-manifest": "workspace:*"
},
"scripts": {
"watch": "tsc -w --sourceMap",
diff --git a/packages/shared-config/src/types.ts b/packages/shared-config/src/types.ts
index 26d469c334..feba32c3ce 100644
--- a/packages/shared-config/src/types.ts
+++ b/packages/shared-config/src/types.ts
@@ -14,6 +14,7 @@ import type Server from 'webpack-dev-server';
import type { SwcCompilationConfig } from '@ice/bundles';
import type { BuildOptions } from 'esbuild';
import type { ProcessOptions } from 'postcss';
+import type { NestedRouteManifest } from '@ice/route-manifest';
export type ECMA = 5 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020;
@@ -90,6 +91,17 @@ export type { webpack };
type PluginFunction = (this: Compiler, compiler: Compiler) => void;
+export interface RouteDefinitionOptions {
+ manifest: NestedRouteManifest[];
+ lazy?: boolean;
+ depth?: number;
+ matchRoute?: (route: NestedRouteManifest) => boolean;
+}
+export interface RouteDefinition {
+ routeImports: string[];
+ routeDefinition: string;
+}
+
export interface Config {
// The name of the task, used for the output log.
name?: string;
@@ -233,4 +245,19 @@ export interface Config {
useDataLoader?: boolean;
optimizePackageImports?: string[];
+
+ runtime?: {
+ source?: string;
+ server?: string;
+ exports?: {
+ specifier: string[];
+ source: string;
+ alias?: Record;
+ }[];
+ router?: {
+ routesDefinition?: (options: RouteDefinitionOptions) => RouteDefinition;
+ source?: string;
+ template?: string;
+ };
+ };
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index de3814851c..4693ffc435 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -252,6 +252,31 @@ importers:
specifier: ^5.88.0
version: 5.88.2
+ examples/custom-runtime:
+ dependencies:
+ '@ice/app':
+ specifier: workspace:*
+ version: link:../../packages/ice
+ '@ice/runtime':
+ specifier: workspace:*
+ version: link:../../packages/runtime
+ '@ice/runtime-kit':
+ specifier: workspace:*
+ version: link:../../packages/runtime-kit
+ react:
+ specifier: ^18.2.0
+ version: 18.2.0
+ react-dom:
+ specifier: ^18.2.0
+ version: 18.2.0(react@18.2.0)
+ devDependencies:
+ '@types/react':
+ specifier: ^18.0.0
+ version: 18.0.34
+ '@types/react-dom':
+ specifier: ^18.0.2
+ version: 18.0.11
+
examples/disable-data-loader:
dependencies:
'@ice/app':
@@ -1665,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
@@ -2403,6 +2431,9 @@ importers:
'@ice/jsx-runtime':
specifier: ^0.3.1
version: link:../jsx-runtime
+ '@ice/runtime-kit':
+ specifier: ^0.1.0
+ version: link:../runtime-kit
'@ice/shared':
specifier: ^1.1.0
version: link:../shared
@@ -2441,6 +2472,21 @@ importers:
specifier: ^0.13.9
version: 0.13.11
+ packages/runtime-kit:
+ devDependencies:
+ '@types/react':
+ specifier: ^18.0.8
+ version: 18.0.34
+ '@types/react-dom':
+ specifier: ^18.0.3
+ version: 18.0.11
+ react:
+ specifier: ^18.0.0
+ version: 18.2.0
+ react-dom:
+ specifier: ^18.0.0
+ version: 18.2.0(react@18.2.0)
+
packages/shared:
devDependencies:
typescript:
@@ -2468,6 +2514,9 @@ importers:
specifier: ^0.11.10
version: 0.11.10
devDependencies:
+ '@ice/route-manifest':
+ specifier: workspace:*
+ version: link:../route-manifest
esbuild:
specifier: ^0.17.16
version: 0.17.16
@@ -3096,7 +3145,7 @@ packages:
'@babel/traverse': 7.23.9
'@babel/types': 7.23.9
convert-source-map: 2.0.0
- debug: 4.3.4
+ debug: 4.4.0
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -4520,7 +4569,7 @@ packages:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.23.9
'@babel/types': 7.23.9
- debug: 4.3.4
+ debug: 4.4.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -12077,6 +12126,18 @@ packages:
dependencies:
ms: 2.1.2
+ /debug@4.4.0:
+ resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ dependencies:
+ ms: 2.1.3
+ dev: true
+
/decamelize-keys@1.1.1:
resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
engines: {node: '>=0.10.0'}
diff --git a/tests/utils/browser.ts b/tests/utils/browser.ts
index 5591e9921d..be37c830ce 100644
--- a/tests/utils/browser.ts
+++ b/tests/utils/browser.ts
@@ -84,7 +84,11 @@ export default class Browser {
}
async start() {
- this.browser = await puppeteer.launch();
+ this.browser = await puppeteer.launch(
+ {
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
+ },
+ );
}
async close() {