Skip to content

Commit

Permalink
feat: support API useDocumentData (#6408)
Browse files Browse the repository at this point in the history
* feat: support API useDocumentData

* refactor: make document data unstable

* refactor: document data loader

* chore: lint

* chore: lint

* chore: lint

---------

Co-authored-by: ZeroLing <[email protected]>
  • Loading branch information
ClarkXia and wssgcg1213 authored Aug 31, 2023
1 parent 477d14c commit 69e6863
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 11 deletions.
6 changes: 6 additions & 0 deletions .changeset/olive-bikes-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@ice/runtime': patch
'@ice/app': patch
---

feat: support API useDocumentData
24 changes: 23 additions & 1 deletion examples/basic-project/src/document.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { Meta, Title, Links, Main, Scripts, useAppData } from 'ice';
// eslint-disable-next-line
import { Meta, Title, Links, Main, Scripts, useAppData, defineDataLoader, unstable_useDocumentData } from 'ice';
import type { AppData } from '@/types';

export const dataLoader = defineDataLoader(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
title: 'documentData',
});
// ATTENTION: This async call will pause rendering document.
}, 1000);
});
});

function Document() {
const appData = useAppData<AppData>();
// Get document data when fallback to document only.
const documentData = unstable_useDocumentData();

console.log('document data', documentData);

return (
<html>
Expand All @@ -21,6 +37,12 @@ function Document() {
</head>
<body>
<Main />
<div>
<h1>Document Data</h1>
<code>
<pre>{JSON.stringify(documentData, null, 2)}</pre>
</code>
</div>
<Scripts />
</body>
</html>
Expand Down
1 change: 1 addition & 0 deletions packages/ice/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export const RUNTIME_EXPORTS = [
'defineServerDataLoader',
'defineStaticDataLoader',
'usePageLifecycle',
'unstable_useDocumentData',
],
alias: {
usePublicAppContext: 'useAppContext',
Expand Down
5 changes: 3 additions & 2 deletions packages/ice/templates/core/entry.server.ts.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as runtime from '@ice/runtime/server';
import { commons, statics } from './runtimeModules';
<% }-%>
import * as app from '@/app';
import Document from '@/document';
import * as Document from '@/document';
import type { RenderMode, DistType } from '@ice/runtime';
import type { RenderToPipeableStreamOptions } from 'react-dom/server';
// @ts-ignore
Expand Down Expand Up @@ -85,7 +85,8 @@ function mergeOptions(options) {
assetsManifest,
createRoutes,
runtimeModules,
Document,
documentDataLoader: Document.dataLoader,
Document: Document.default,
basename: basename || getRouterBasename(),
renderMode,
routesConfig,
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
],
"dependencies": {
"@ice/jsx-runtime": "^0.2.1",
"@ice/shared": "^1.0.1",
"@remix-run/router": "1.7.2",
"abortcontroller-polyfill": "1.7.5",
"ejs": "^3.1.6",
Expand Down
9 changes: 9 additions & 0 deletions packages/runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ function usePublicAppContext(): PublicAppContext {
};
}

function useDocumentData() {
const context = useInternalAppContext();
return context.documentData;
}

// @TODO: remove unstable prefix or refactor.
// eslint-disable-next-line
export const unstable_useDocumentData = useDocumentData;

export {
getAppConfig,
defineAppConfig,
Expand Down
42 changes: 34 additions & 8 deletions packages/runtime/src/runServerApp.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { ServerResponse, IncomingMessage } from 'http';
import * as React from 'react';
import type { RenderToPipeableStreamOptions } from 'react-dom/server';
import * as ReactDOMServer from 'react-dom/server';
import { parsePath } from 'history';
import type { Location } from 'history';
import type { RenderToPipeableStreamOptions } from 'react-dom/server';
import { parsePath } from 'history';
import { isFunction } from '@ice/shared';
import type {
AppContext, RouteItem, ServerContext,
AppExport, AssetsManifest,
Expand All @@ -13,7 +14,7 @@ import type {
DocumentComponent,
RuntimeModules,
AppData,
ServerAppRouterProps,
ServerAppRouterProps, DataLoaderConfig,
} from './types.js';
import Runtime from './runtime.js';
import { AppContextProvider } from './AppContext.js';
Expand All @@ -31,11 +32,13 @@ import ServerRouter from './ServerRouter.js';
import { renderHTMLToJS } from './renderHTMLToJS.js';
import addLeadingSlash from './utils/addLeadingSlash.js';


interface RenderOptions {
app: AppExport;
assetsManifest: AssetsManifest;
createRoutes: (options: Pick<RouteLoaderOptions, 'requestContext' | 'renderMode'>) => RouteItem[];
runtimeModules: RuntimeModules;
documentDataLoader?: DataLoaderConfig;
Document: DocumentComponent;
documentOnly?: boolean;
renderMode?: RenderMode;
Expand Down Expand Up @@ -262,7 +265,20 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio
await Promise.all(runtimeModules.statics.map(m => runtime.loadModule(m)).filter(Boolean));
}

// don't need to execute getAppData in CSR
// Execute document dataLoader.
let documentData: any;
if (renderOptions.documentDataLoader) {
const { loader } = renderOptions.documentDataLoader;
if (isFunction(loader)) {
documentData = await loader(requestContext);
// @TODO: document should have it's own context, not shared with app.
appContext.documentData = documentData;
} else {
console.warn('Document dataLoader only accepts function.');
}
}

// Not to execute [getAppData] when CSR.
if (!documentOnly) {
try {
appData = await getAppData(app, requestContext);
Expand All @@ -273,13 +289,13 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio

// HashRouter loads route modules by the CSR.
if (appConfig?.router?.type === 'hash') {
return renderDocument({ matches: [], routes, renderOptions });
return renderDocument({ matches: [], routes, renderOptions, documentData });
}

const matches = matchRoutes(routes, location, finalBasename);
const routePath = getCurrentRoutePath(matches);
if (documentOnly) {
return renderDocument({ matches, routePath, routes, renderOptions });
return renderDocument({ matches, routePath, routes, renderOptions, documentData });
} else if (!matches.length) {
return handleNotFoundResponse();
}
Expand Down Expand Up @@ -339,7 +355,7 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio
throw err;
}
console.error('Warning: render server entry error, downgrade to csr.', err);
return renderDocument({ matches, routePath, renderOptions, routes, downgrade: true });
return renderDocument({ matches, routePath, renderOptions, routes, downgrade: true, documentData });
}
}

Expand Down Expand Up @@ -399,7 +415,14 @@ async function renderServerEntry(
const pipe = renderToNodeStream(element);

const fallback = () => {
return renderDocument({ matches, routePath, renderOptions, routes, downgrade: true });
return renderDocument({
matches,
routePath,
renderOptions,
routes,
downgrade: true,
documentData: appContext.documentData,
});
};

return {
Expand All @@ -414,6 +437,7 @@ interface RenderDocumentOptions {
matches: RouteMatch[];
renderOptions: RenderOptions;
routes: RouteItem[];
documentData: any;
routePath?: string;
downgrade?: boolean;
}
Expand All @@ -428,6 +452,7 @@ function renderDocument(options: RenderDocumentOptions): Response {
routePath,
downgrade,
routes,
documentData,
}: RenderDocumentOptions = options;

const {
Expand Down Expand Up @@ -465,6 +490,7 @@ function renderDocument(options: RenderDocumentOptions): Response {
basename,
downgrade,
serverData,
documentData,
};

const documentContext = {
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export interface LoaderData {
export interface AppContext {
appConfig: AppConfig;
appData: any;
documentData?: any;
serverData?: any;
assetsManifest?: AssetsManifest;
loaderData?: LoadersData;
Expand Down
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 69e6863

Please sign in to comment.