loadable.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _extends = require("@swc/helpers/lib/_extends.js").default;
  7. var _interop_require_default = require("@swc/helpers/lib/_interop_require_default.js").default;
  8. var _react = _interop_require_default(require("react"));
  9. var _loadableContext = require("./loadable-context");
  10. const { useSyncExternalStore } = process.env.__NEXT_REACT_ROOT ? require('react') : require('use-sync-external-store/shim');
  11. const ALL_INITIALIZERS = [];
  12. const READY_INITIALIZERS = [];
  13. let initialized = false;
  14. function load(loader) {
  15. let promise = loader();
  16. let state = {
  17. loading: true,
  18. loaded: null,
  19. error: null
  20. };
  21. state.promise = promise.then((loaded)=>{
  22. state.loading = false;
  23. state.loaded = loaded;
  24. return loaded;
  25. }).catch((err)=>{
  26. state.loading = false;
  27. state.error = err;
  28. throw err;
  29. });
  30. return state;
  31. }
  32. function resolve(obj) {
  33. return obj && obj.__esModule ? obj.default : obj;
  34. }
  35. function createLoadableComponent(loadFn, options) {
  36. let opts = Object.assign({
  37. loader: null,
  38. loading: null,
  39. delay: 200,
  40. timeout: null,
  41. webpack: null,
  42. modules: null,
  43. suspense: false
  44. }, options);
  45. if (opts.suspense) {
  46. opts.lazy = _react.default.lazy(opts.loader);
  47. }
  48. /** @type LoadableSubscription */ let subscription = null;
  49. function init() {
  50. if (!subscription) {
  51. const sub = new LoadableSubscription(loadFn, opts);
  52. subscription = {
  53. getCurrentValue: sub.getCurrentValue.bind(sub),
  54. subscribe: sub.subscribe.bind(sub),
  55. retry: sub.retry.bind(sub),
  56. promise: sub.promise.bind(sub)
  57. };
  58. }
  59. return subscription.promise();
  60. }
  61. // Server only
  62. if (typeof window === 'undefined') {
  63. ALL_INITIALIZERS.push(init);
  64. }
  65. // Client only
  66. if (!initialized && typeof window !== 'undefined') {
  67. // require.resolveWeak check is needed for environments that don't have it available like Jest
  68. const moduleIds = opts.webpack && typeof require.resolveWeak === 'function' ? opts.webpack() : opts.modules;
  69. if (moduleIds) {
  70. READY_INITIALIZERS.push((ids)=>{
  71. for (const moduleId of moduleIds){
  72. if (ids.indexOf(moduleId) !== -1) {
  73. return init();
  74. }
  75. }
  76. });
  77. }
  78. }
  79. function useLoadableModule() {
  80. init();
  81. const context = _react.default.useContext(_loadableContext.LoadableContext);
  82. if (context && Array.isArray(opts.modules)) {
  83. opts.modules.forEach((moduleName)=>{
  84. context(moduleName);
  85. });
  86. }
  87. }
  88. function LoadableImpl(props, ref) {
  89. useLoadableModule();
  90. const state = useSyncExternalStore(subscription.subscribe, subscription.getCurrentValue, subscription.getCurrentValue);
  91. _react.default.useImperativeHandle(ref, ()=>({
  92. retry: subscription.retry
  93. }), []);
  94. return _react.default.useMemo(()=>{
  95. if (state.loading || state.error) {
  96. return _react.default.createElement(opts.loading, {
  97. isLoading: state.loading,
  98. pastDelay: state.pastDelay,
  99. timedOut: state.timedOut,
  100. error: state.error,
  101. retry: subscription.retry
  102. });
  103. } else if (state.loaded) {
  104. return _react.default.createElement(resolve(state.loaded), props);
  105. } else {
  106. return null;
  107. }
  108. }, [
  109. props,
  110. state
  111. ]);
  112. }
  113. function LazyImpl(props, ref) {
  114. useLoadableModule();
  115. return _react.default.createElement(opts.lazy, _extends({}, props, {
  116. ref
  117. }));
  118. }
  119. const LoadableComponent = opts.suspense ? LazyImpl : LoadableImpl;
  120. LoadableComponent.preload = ()=>init();
  121. LoadableComponent.displayName = 'LoadableComponent';
  122. return _react.default.forwardRef(LoadableComponent);
  123. }
  124. class LoadableSubscription {
  125. promise() {
  126. return this._res.promise;
  127. }
  128. retry() {
  129. this._clearTimeouts();
  130. this._res = this._loadFn(this._opts.loader);
  131. this._state = {
  132. pastDelay: false,
  133. timedOut: false
  134. };
  135. const { _res: res , _opts: opts } = this;
  136. if (res.loading) {
  137. if (typeof opts.delay === 'number') {
  138. if (opts.delay === 0) {
  139. this._state.pastDelay = true;
  140. } else {
  141. this._delay = setTimeout(()=>{
  142. this._update({
  143. pastDelay: true
  144. });
  145. }, opts.delay);
  146. }
  147. }
  148. if (typeof opts.timeout === 'number') {
  149. this._timeout = setTimeout(()=>{
  150. this._update({
  151. timedOut: true
  152. });
  153. }, opts.timeout);
  154. }
  155. }
  156. this._res.promise.then(()=>{
  157. this._update({});
  158. this._clearTimeouts();
  159. }).catch((_err)=>{
  160. this._update({});
  161. this._clearTimeouts();
  162. });
  163. this._update({});
  164. }
  165. _update(partial) {
  166. this._state = _extends({}, this._state, {
  167. error: this._res.error,
  168. loaded: this._res.loaded,
  169. loading: this._res.loading
  170. }, partial);
  171. this._callbacks.forEach((callback)=>callback());
  172. }
  173. _clearTimeouts() {
  174. clearTimeout(this._delay);
  175. clearTimeout(this._timeout);
  176. }
  177. getCurrentValue() {
  178. return this._state;
  179. }
  180. subscribe(callback) {
  181. this._callbacks.add(callback);
  182. return ()=>{
  183. this._callbacks.delete(callback);
  184. };
  185. }
  186. constructor(loadFn, opts){
  187. this._loadFn = loadFn;
  188. this._opts = opts;
  189. this._callbacks = new Set();
  190. this._delay = null;
  191. this._timeout = null;
  192. this.retry();
  193. }
  194. }
  195. function Loadable(opts) {
  196. return createLoadableComponent(load, opts);
  197. }
  198. function flushInitializers(initializers, ids) {
  199. let promises = [];
  200. while(initializers.length){
  201. let init = initializers.pop();
  202. promises.push(init(ids));
  203. }
  204. return Promise.all(promises).then(()=>{
  205. if (initializers.length) {
  206. return flushInitializers(initializers, ids);
  207. }
  208. });
  209. }
  210. Loadable.preloadAll = ()=>{
  211. return new Promise((resolveInitializers, reject)=>{
  212. flushInitializers(ALL_INITIALIZERS).then(resolveInitializers, reject);
  213. });
  214. };
  215. Loadable.preloadReady = (ids = [])=>{
  216. return new Promise((resolvePreload)=>{
  217. const res = ()=>{
  218. initialized = true;
  219. return resolvePreload();
  220. };
  221. // We always will resolve, errors should be handled within loading UIs.
  222. flushInitializers(READY_INITIALIZERS, ids).then(res, res);
  223. });
  224. };
  225. if (typeof window !== 'undefined') {
  226. window.__NEXT_PRELOADREADY = Loadable.preloadReady;
  227. }
  228. var _default = Loadable;
  229. exports.default = _default;
  230. //# sourceMappingURL=loadable.js.map