-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: provider useDemoData
which is given by getDemoById
#1876
Changes from 13 commits
2e4994b
da0100d
1b657d4
e0da9bd
6bc6d21
1d1bea2
ff2c333
5f84d24
283e131
434aa49
a31b7ca
e38d447
9a8a9ab
9d570d2
5041d7b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,2 @@ | ||
import * as demo from '../../docs/index.md?type=demo'; | ||
import * as demoIndex from '../../docs/index.md?type=demo-index'; | ||
import * as frontmatter from '../../docs/index.md?type=frontmatter'; | ||
import * as text from '../../docs/index.md?type=text'; | ||
|
||
console.log('frontmatter', frontmatter); | ||
console.log('demo', demo); | ||
console.log('demoIndex', demoIndex); | ||
console.log('text', text); | ||
|
||
// Customize Page for dumi test | ||
export default () => 'Customize Dumi Test Page'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default () => 'External Demo Block'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,7 @@ | |
```jsx | ||
export default () => 'Hello Foo!'; | ||
``` | ||
|
||
你好,Foo! | ||
|
||
<code src="./demo/work.tsx">示例框</code> |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import Container from 'dumi/theme/builtins/Container'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 将 |
||
import React, { type FC, type ReactNode } from 'react'; | ||
import { ErrorBoundary } from 'react-error-boundary'; | ||
|
||
const DemoErrorBoundary: FC<{ children: ReactNode }> = (props) => ( | ||
<ErrorBoundary | ||
fallbackRender={({ error }: any) => ( | ||
<Container type="error"> | ||
<p> | ||
<strong>{error.message || 'This demo has been crashed.'}</strong> | ||
</p> | ||
{error.stack && ( | ||
<p> | ||
<details open> | ||
<summary>Error stack</summary> | ||
<pre>{error.stack}</pre> | ||
</details> | ||
</p> | ||
)} | ||
</Container> | ||
)} | ||
> | ||
{props.children} | ||
</ErrorBoundary> | ||
); | ||
|
||
export default DemoErrorBoundary; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { SP_ROUTE_PREFIX } from '@/constants'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 将 |
||
import { useAppData, useDemoData, useSiteData } from 'dumi'; | ||
import React, { createElement, type FC } from 'react'; | ||
import type { IPreviewerProps } from '../types'; | ||
|
||
import Previewer from 'dumi/theme/builtins/Previewer'; | ||
import DemoErrorBoundary from './DemoErrorBoundary'; | ||
|
||
export interface IDumiDemoProps { | ||
demo: { | ||
id: string; | ||
inline?: boolean; | ||
}; | ||
previewerProps: Omit<IPreviewerProps, 'asset' | 'children'>; | ||
} | ||
|
||
const InternalDumiDemo = (props: IDumiDemoProps) => { | ||
const { historyType } = useSiteData(); | ||
const { basename } = useAppData(); | ||
const demoInfo = useDemoData(props.demo.id); | ||
zombieJ marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// hide debug demo in production | ||
if (process.env.NODE_ENV === 'production' && props.previewerProps.debug) | ||
return null; | ||
|
||
if (!demoInfo) { | ||
return `Demo '${props.demo.id}' not found!`; | ||
} | ||
|
||
const { component, asset } = demoInfo; | ||
|
||
const demoNode = ( | ||
<DemoErrorBoundary>{createElement(component)}</DemoErrorBoundary> | ||
); | ||
|
||
if (props.demo.inline) { | ||
return demoNode; | ||
} | ||
|
||
const isHashRoute = historyType === 'hash'; | ||
|
||
return ( | ||
<Previewer | ||
asset={asset} | ||
demoUrl={ | ||
// allow user override demoUrl by frontmatter | ||
props.previewerProps.demoUrl || | ||
// when use hash route, browser can automatically handle relative paths starting with # | ||
`${isHashRoute ? `#` : ''}${basename}${SP_ROUTE_PREFIX}demos/${ | ||
props.demo.id | ||
}` | ||
} | ||
{...props.previewerProps} | ||
> | ||
{props.previewerProps.iframe ? null : demoNode} | ||
</Previewer> | ||
); | ||
}; | ||
|
||
export const DumiDemo: FC<IDumiDemoProps> = React.memo( | ||
InternalDumiDemo, | ||
(prev, next) => { | ||
// compare length for performance | ||
return JSON.stringify(prev).length === JSON.stringify(next).length; | ||
}, | ||
); | ||
|
||
if (process.env.NODE_ENV !== 'production') { | ||
InternalDumiDemo.displayName = 'DumiDemo'; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,20 @@ | ||
import type { PICKED_PKG_FIELDS } from '@/constants'; | ||
import type { AtomComponentAsset } from 'dumi-assets-types'; | ||
import { createContext, useContext, type ComponentType } from 'react'; | ||
import type { ILocalesConfig, IPreviewerProps, IThemeConfig } from './types'; | ||
import type { ILocalesConfig, IPreviewerProps, IThemeConfig } from '../types'; | ||
import use from './use'; | ||
|
||
export type DemoInfo = { | ||
component: ComponentType; | ||
asset: IPreviewerProps['asset']; | ||
routeId: string; | ||
}; | ||
|
||
interface ISiteContext { | ||
pkg: Partial<Record<keyof typeof PICKED_PKG_FIELDS, any>>; | ||
historyType: 'browser' | 'hash' | 'memory'; | ||
entryExports: Record<string, any>; | ||
demos: Record< | ||
string, | ||
{ | ||
component: ComponentType; | ||
asset: IPreviewerProps['asset']; | ||
routeId: string; | ||
} | ||
>; | ||
demos: Record<string, DemoInfo>; | ||
components: Record<string, AtomComponentAsset>; | ||
locales: ILocalesConfig; | ||
themeConfig: IThemeConfig; | ||
|
@@ -25,6 +25,11 @@ interface ISiteContext { | |
* private field, do not use it in your code | ||
*/ | ||
_2_level_nav_available: boolean; | ||
|
||
// =================== Demo Map =================== | ||
// Demo map is root providing the id => route mapping info. | ||
// Which is generated by `meta-demos` template | ||
getDemoById: (id: string) => Promise<DemoInfo | null>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 提供新的 |
||
} | ||
|
||
export const SiteContext = createContext<ISiteContext>({ | ||
|
@@ -38,8 +43,22 @@ export const SiteContext = createContext<ISiteContext>({ | |
loading: false, | ||
setLoading: () => {}, | ||
_2_level_nav_available: true, | ||
getDemoById: async () => null, | ||
}); | ||
|
||
export const useSiteData = () => { | ||
return useContext(SiteContext); | ||
}; | ||
|
||
const cache = new Map<string, Promise<DemoInfo | null>>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cache 是不是做在 getDemoById 里更合适 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个是 React 的 Suspense data 一套的,放 |
||
|
||
// Async load demo data | ||
export function useDemoData(demoId: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 有 hook 的话要么 getDemoById 就作为内部方法不通过 dumi 和 context 暴露了? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. get 了,那我建议这个晚点搞。 |
||
const { getDemoById } = useSiteData(); | ||
|
||
if (!cache.has(demoId)) { | ||
cache.set(demoId, getDemoById(demoId)); | ||
} | ||
|
||
return use(cache.get(demoId)!); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个 use 的作用是什么呢 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 见上,React Suspense 的实现。官方现在没有提供封装,所以直接抄的文档代码 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copy from React official demo. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 从 |
||
// This will be replace if React release new version of use hooks | ||
type ReactPromise<T> = Promise<T> & { | ||
status?: 'pending' | 'fulfilled' | 'rejected'; | ||
value?: T; | ||
reason?: any; | ||
}; | ||
|
||
export default function use<T>(promise: ReactPromise<T>): T { | ||
if (promise.status === 'fulfilled') { | ||
return promise.value!; | ||
} else if (promise.status === 'rejected') { | ||
throw promise.reason; | ||
} else if (promise.status === 'pending') { | ||
throw promise; | ||
} else { | ||
promise.status = 'pending'; | ||
promise.then( | ||
(result) => { | ||
promise.status = 'fulfilled'; | ||
promise.value = result; | ||
}, | ||
(reason) => { | ||
promise.status = 'rejected'; | ||
promise.reason = reason; | ||
}, | ||
); | ||
throw promise; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,6 +65,18 @@ export default (api: IApi) => { | |
}, | ||
}); | ||
|
||
// generate meta lazy entry | ||
api.writeTmpFile({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 新的 |
||
noPluginDir: true, | ||
path: 'dumi/meta/demos.ts', | ||
tplPath: winPath(join(TEMPLATES_DIR, 'meta-demos.ts.tpl')), | ||
context: { | ||
metaFiles: parsedMetaFiles.filter((metaFile) => | ||
metaFile.file.endsWith('.md'), | ||
), | ||
}, | ||
}); | ||
|
||
// generate runtime plugin, to append page meta to route object | ||
api.writeTmpFile({ | ||
noPluginDir: true, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
替换成了单独加载的
useDemoData
hook