2019-04-25 17:51:36 +02:00
import React from 'react'
import Loadable from './loadable'
const isServerSide = typeof window === 'undefined'
2019-05-30 03:19:32 +02:00
export type LoaderComponent < P = {} > = Promise <
React . ComponentType < P > | { default : React . ComponentType < P > }
>
2019-04-25 17:51:36 +02:00
export type Loader < P = {} > = ( ( ) = > LoaderComponent < P > ) | LoaderComponent < P >
export type LoaderMap = { [ mdule : string ] : ( ) = > Loader < any > }
export type LoadableGeneratedOptions = {
2019-05-30 03:19:32 +02:00
webpack ? ( ) : any
modules ? ( ) : LoaderMap
2019-04-25 17:51:36 +02:00
}
export type LoadableBaseOptions < P = {} > = LoadableGeneratedOptions & {
2019-05-30 03:19:32 +02:00
loading ? : ( {
error ,
isLoading ,
pastDelay ,
} : {
error? : Error | null
isLoading? : boolean
pastDelay? : boolean
2019-04-25 17:51:36 +02:00
timedOut? : boolean
2019-05-30 03:19:32 +02:00
} ) = > JSX . Element | null
2019-04-25 17:51:36 +02:00
loader? : Loader < P > | LoaderMap
loadableGenerated? : LoadableGeneratedOptions
ssr? : boolean
}
export type LoadableOptions < P = {} > = LoadableBaseOptions < P > & {
2019-05-30 03:19:32 +02:00
render ? ( loader : any , props : any ) : JSX . Element
2019-04-25 17:51:36 +02:00
}
export type DynamicOptions < P = {} > = LoadableBaseOptions < P > & {
2019-07-05 05:36:53 +02:00
/ * *
* @deprecated the modules option has been planned for removal
* /
2019-05-30 03:19:32 +02:00
render ? ( props : P , loaded : any ) : JSX . Element
2019-04-25 17:51:36 +02:00
}
2019-05-30 03:19:32 +02:00
export type LoadableFn < P = {} > = (
opts : LoadableOptions < P >
) = > React . ComponentType < P >
2019-04-25 17:51:36 +02:00
export type LoadableComponent < P = {} > = React . ComponentType < P >
2019-05-30 03:19:32 +02:00
export function noSSR < P = {} > (
LoadableInitializer : LoadableFn < P > ,
loadableOptions : LoadableOptions < P >
) {
2019-04-25 17:51:36 +02:00
// 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
2019-05-30 03:19:32 +02:00
return ( ) = > (
< Loading error = { null } isLoading pastDelay = { false } timedOut = { false } / >
)
2019-04-25 17:51:36 +02:00
}
// function dynamic<P = {}, O extends DynamicOptions>(options: O):
export default function dynamic < P = {} > (
dynamicOptions : DynamicOptions < P > | Loader < P > ,
2019-05-30 03:19:32 +02:00
options? : DynamicOptions < P >
2019-04-25 17:51:36 +02:00
) : React . ComponentType < P > {
let loadableFn : LoadableFn < P > = Loadable
let loadableOptions : LoadableOptions < P > = {
// 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 ) {
2019-06-28 03:07:07 +02:00
return null
2019-04-25 17:51:36 +02:00
}
if ( error ) {
2019-05-30 03:19:32 +02:00
return (
< p >
{ error . message }
< br / >
{ error . stack }
< / p >
)
2019-04-25 17:51:36 +02:00
}
}
2019-06-28 03:07:07 +02:00
return null
2019-04-25 17:51:36 +02:00
} ,
}
// 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
2019-05-30 03:19:32 +02:00
// Support for having import as a function, eg: dynamic(() => import('../hello-world'))
2019-04-25 17:51:36 +02:00
} else if ( typeof dynamicOptions === 'function' ) {
loadableOptions . loader = dynamicOptions
2019-05-30 03:19:32 +02:00
// Support for having first argument being options, eg: dynamic({loader: import('../hello-world')})
2019-04-25 17:51:36 +02:00
} else if ( typeof dynamicOptions === 'object' ) {
loadableOptions = { . . . loadableOptions , . . . dynamicOptions }
}
// Support for passing options, eg: dynamic(import('../hello-world'), {loading: () => <p>Loading something</p>})
loadableOptions = { . . . loadableOptions , . . . options }
2019-06-28 19:39:23 +02:00
if (
typeof dynamicOptions === 'object' &&
! ( dynamicOptions instanceof Promise )
) {
2019-07-15 16:35:36 +02:00
// show deprecation warning for `modules` key in development
2019-07-16 01:16:40 +02:00
if ( process . env . NODE_ENV !== 'production' ) {
if ( dynamicOptions . modules ) {
2019-07-15 16:35:36 +02:00
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'
)
}
2019-07-05 05:36:53 +02:00
}
2019-06-28 19:39:23 +02:00
// 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
}
}
2019-04-25 17:51:36 +02:00
// coming from build/babel/plugins/react-loadable-plugin.js
if ( loadableOptions . loadableGenerated ) {
2019-05-30 03:19:32 +02:00
loadableOptions = {
. . . loadableOptions ,
. . . loadableOptions . loadableGenerated ,
}
2019-04-25 17:51:36 +02:00
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 )
}