}
export type LoadableGeneratedOptions = {
webpack?(): any
modules?(): LoaderMap
}
export type LoadableBaseOptions = LoadableGeneratedOptions & {
loading?: ({
error,
isLoading,
pastDelay,
}: {
error?: Error | null
isLoading?: boolean
pastDelay?: boolean
timedOut?: boolean
}) => JSX.Element | null
loader?: Loader
| LoaderMap
loadableGenerated?: LoadableGeneratedOptions
ssr?: boolean
}
export type LoadableOptions
= LoadableBaseOptions
& {
render?(loader: any, props: any): JSX.Element
}
export type DynamicOptions
= LoadableBaseOptions
& {
/**
* @deprecated the modules option has been planned for removal
*/
render?(props: P, loaded: any): JSX.Element
}
export type LoadableFn
= (
opts: LoadableOptions
) => React.ComponentType
export type LoadableComponent
= React.ComponentType
export function noSSR
(
LoadableInitializer: LoadableFn
,
loadableOptions: LoadableOptions
) {
// Removing webpack and modules means react-loadable won't try preloading
delete loadableOptions.webpack
delete loadableOptions.modules
// This check is neccesary to prevent react-loadable from initializing on the server
if (!isServerSide) {
return LoadableInitializer(loadableOptions)
}
const Loading = loadableOptions.loading!
// This will only be rendered on the server side
return () => (
)
}
// function dynamic
(options: O):
export default function dynamic
(
dynamicOptions: DynamicOptions
| Loader
,
options?: DynamicOptions
): React.ComponentType
{
let loadableFn: LoadableFn
= Loadable
let loadableOptions: LoadableOptions
= {
// A loading component is not required, so we default it
loading: ({ error, isLoading, pastDelay }) => {
if (!pastDelay) return null
if (process.env.NODE_ENV === 'development') {
if (isLoading) {
return null
}
if (error) {
return (
{error.message}
{error.stack}
)
}
}
return null
},
}
// Support for direct import(), eg: dynamic(import('../hello-world'))
// Note that this is only kept for the edge case where someone is passing in a promise as first argument
// The react-loadable babel plugin will turn dynamic(import('../hello-world')) into dynamic(() => import('../hello-world'))
// To make sure we don't execute the import without rendering first
if (dynamicOptions instanceof Promise) {
loadableOptions.loader = () => dynamicOptions
// Support for having import as a function, eg: dynamic(() => import('../hello-world'))
} else if (typeof dynamicOptions === 'function') {
loadableOptions.loader = dynamicOptions
// Support for having first argument being options, eg: dynamic({loader: import('../hello-world')})
} else if (typeof dynamicOptions === 'object') {
loadableOptions = { ...loadableOptions, ...dynamicOptions }
}
// Support for passing options, eg: dynamic(import('../hello-world'), {loading: () => Loading something
})
loadableOptions = { ...loadableOptions, ...options }
if (
typeof dynamicOptions === 'object' &&
!(dynamicOptions instanceof Promise)
) {
// show deprecation warning for `modules` key in development
if (process.env.NODE_ENV !== 'production') {
if (dynamicOptions.modules) {
console.warn(
'The modules option for next/dynamic has been deprecated. See here for more info https://err.sh/zeit/next.js/next-dynamic-modules'
)
}
}
// Support for `render` when using a mapping, eg: `dynamic({ modules: () => {return {HelloWorld: import('../hello-world')}, render(props, loaded) {} } })
if (dynamicOptions.render) {
loadableOptions.render = (loaded, props) =>
dynamicOptions.render!(props, loaded)
}
// Support for `modules` when using a mapping, eg: `dynamic({ modules: () => {return {HelloWorld: import('../hello-world')}, render(props, loaded) {} } })
if (dynamicOptions.modules) {
loadableFn = Loadable.Map
const loadModules: LoaderMap = {}
const modules = dynamicOptions.modules()
Object.keys(modules).forEach(key => {
const value: any = modules[key]
if (typeof value.then === 'function') {
loadModules[key] = () => value.then((mod: any) => mod.default || mod)
return
}
loadModules[key] = value
})
loadableOptions.loader = loadModules
}
}
// coming from build/babel/plugins/react-loadable-plugin.js
if (loadableOptions.loadableGenerated) {
loadableOptions = {
...loadableOptions,
...loadableOptions.loadableGenerated,
}
delete loadableOptions.loadableGenerated
}
// support for disabling server side rendering, eg: dynamic(import('../hello-world'), {ssr: false})
if (typeof loadableOptions.ssr === 'boolean') {
if (!loadableOptions.ssr) {
delete loadableOptions.ssr
return noSSR(loadableFn, loadableOptions)
}
delete loadableOptions.ssr
}
return loadableFn(loadableOptions)
}