| | import React from 'react' |
| | import Loadable from './loadable.shared-runtime' |
| |
|
| | const isServerSide = typeof window === 'undefined' |
| |
|
| | type ComponentModule<P = {}> = { default: React.ComponentType<P> } |
| |
|
| | export declare type LoaderComponent<P = {}> = Promise< |
| | React.ComponentType<P> | ComponentModule<P> |
| | > |
| |
|
| | export declare type Loader<P = {}> = |
| | | (() => LoaderComponent<P>) |
| | | LoaderComponent<P> |
| |
|
| | export type LoaderMap = { [module: string]: () => Loader<any> } |
| |
|
| | export type LoadableGeneratedOptions = { |
| | webpack?(): any |
| | modules?(): LoaderMap |
| | } |
| |
|
| | export type DynamicOptionsLoadingProps = { |
| | error?: Error | null |
| | isLoading?: boolean |
| | pastDelay?: boolean |
| | retry?: () => void |
| | timedOut?: boolean |
| | } |
| |
|
| | |
| | |
| | |
| | function convertModule<P>(mod: React.ComponentType<P> | ComponentModule<P>) { |
| | return { default: (mod as ComponentModule<P>)?.default || mod } |
| | } |
| |
|
| | export type DynamicOptions<P = {}> = LoadableGeneratedOptions & { |
| | loading?: (loadingProps: DynamicOptionsLoadingProps) => React.ReactNode |
| | loader?: Loader<P> | LoaderMap |
| | loadableGenerated?: LoadableGeneratedOptions |
| | ssr?: boolean |
| | } |
| |
|
| | export type LoadableOptions<P = {}> = DynamicOptions<P> |
| |
|
| | export type LoadableFn<P = {}> = ( |
| | opts: LoadableOptions<P> |
| | ) => React.ComponentType<P> |
| |
|
| | export type LoadableComponent<P = {}> = React.ComponentType<P> |
| |
|
| | export function noSSR<P = {}>( |
| | LoadableInitializer: LoadableFn<P>, |
| | loadableOptions: DynamicOptions<P> |
| | ): React.ComponentType<P> { |
| | |
| | delete loadableOptions.webpack |
| | delete loadableOptions.modules |
| |
|
| | |
| | if (!isServerSide) { |
| | return LoadableInitializer(loadableOptions) |
| | } |
| |
|
| | const Loading = loadableOptions.loading! |
| | |
| | return () => ( |
| | <Loading error={null} isLoading pastDelay={false} timedOut={false} /> |
| | ) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | export default function dynamic<P = {}>( |
| | dynamicOptions: DynamicOptions<P> | Loader<P>, |
| | options?: DynamicOptions<P> |
| | ): React.ComponentType<P> { |
| | let loadableFn = Loadable as LoadableFn<P> |
| |
|
| | let loadableOptions: LoadableOptions<P> = { |
| | |
| | loading: ({ error, isLoading, pastDelay }) => { |
| | if (!pastDelay) return null |
| | if (process.env.NODE_ENV !== 'production') { |
| | if (isLoading) { |
| | return null |
| | } |
| | if (error) { |
| | return ( |
| | <p> |
| | {error.message} |
| | <br /> |
| | {error.stack} |
| | </p> |
| | ) |
| | } |
| | } |
| | return null |
| | }, |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | if (dynamicOptions instanceof Promise) { |
| | loadableOptions.loader = () => dynamicOptions |
| | |
| | } else if (typeof dynamicOptions === 'function') { |
| | loadableOptions.loader = dynamicOptions |
| | |
| | } else if (typeof dynamicOptions === 'object') { |
| | loadableOptions = { ...loadableOptions, ...dynamicOptions } |
| | } |
| |
|
| | |
| | loadableOptions = { ...loadableOptions, ...options } |
| |
|
| | const loaderFn = loadableOptions.loader as () => LoaderComponent<P> |
| | const loader = () => |
| | loaderFn != null |
| | ? loaderFn().then(convertModule) |
| | : Promise.resolve(convertModule(() => null)) |
| |
|
| | |
| | if (loadableOptions.loadableGenerated) { |
| | loadableOptions = { |
| | ...loadableOptions, |
| | ...loadableOptions.loadableGenerated, |
| | } |
| | delete loadableOptions.loadableGenerated |
| | } |
| |
|
| | |
| | if (typeof loadableOptions.ssr === 'boolean' && !loadableOptions.ssr) { |
| | delete loadableOptions.webpack |
| | delete loadableOptions.modules |
| |
|
| | return noSSR(loadableFn, loadableOptions) |
| | } |
| |
|
| | return loadableFn({ ...loadableOptions, loader: loader as Loader<P> }) |
| | } |
| |
|