UNPKG

@loadable/component

Version:
620 lines (508 loc) 19.1 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('hoist-non-react-statics')) : typeof define === 'function' && define.amd ? define(['exports', 'react', 'hoist-non-react-statics'], factory) : (global = global || self, factory(global.loadable = {}, global.React, global.hoistNonReactStatics)); }(this, (function (exports, React, hoistNonReactStatics) { 'use strict'; React = React && React.hasOwnProperty('default') ? React['default'] : React; hoistNonReactStatics = hoistNonReactStatics && hoistNonReactStatics.hasOwnProperty('default') ? hoistNonReactStatics['default'] : hoistNonReactStatics; /* eslint-disable import/prefer-default-export */ function invariant(condition, message) { if (condition) return; var error = new Error("loadable: " + message); error.framesToPop = 1; error.name = 'Invariant Violation'; throw error; } function warn(message) { // eslint-disable-next-line no-console console.warn("loadable: " + message); } var Context = /*#__PURE__*/ React.createContext(); var LOADABLE_REQUIRED_CHUNKS_KEY = '__LOADABLE_REQUIRED_CHUNKS__'; function getRequiredChunkKey(namespace) { return "" + namespace + LOADABLE_REQUIRED_CHUNKS_KEY; } var sharedInternals = /*#__PURE__*/Object.freeze({ __proto__: null, getRequiredChunkKey: getRequiredChunkKey, invariant: invariant, Context: Context }); function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var LOADABLE_SHARED = { initialChunks: {} }; var STATUS_PENDING = 'PENDING'; var STATUS_RESOLVED = 'RESOLVED'; var STATUS_REJECTED = 'REJECTED'; function resolveConstructor(ctor) { if (typeof ctor === 'function') { return { requireAsync: ctor, resolve: function resolve() { return undefined; }, chunkName: function chunkName() { return undefined; } }; } return ctor; } var withChunkExtractor = function withChunkExtractor(Component) { var LoadableWithChunkExtractor = function LoadableWithChunkExtractor(props) { return React.createElement(Context.Consumer, null, function (extractor) { return React.createElement(Component, Object.assign({ __chunkExtractor: extractor }, props)); }); }; if (Component.displayName) { LoadableWithChunkExtractor.displayName = Component.displayName + "WithChunkExtractor"; } return LoadableWithChunkExtractor; }; var identity = function identity(v) { return v; }; function createLoadable(_ref) { var _ref$defaultResolveCo = _ref.defaultResolveComponent, defaultResolveComponent = _ref$defaultResolveCo === void 0 ? identity : _ref$defaultResolveCo, _render = _ref.render, onLoad = _ref.onLoad; function loadable(loadableConstructor, options) { if (options === void 0) { options = {}; } var ctor = resolveConstructor(loadableConstructor); var cache = {}; /** * Cachekey represents the component to be loaded * if key changes - component has to be reloaded * @param props * @returns {null|Component} */ function _getCacheKey(props) { if (options.cacheKey) { return options.cacheKey(props); } if (ctor.resolve) { return ctor.resolve(props); } return 'static'; } /** * Resolves loaded `module` to a specific `Component * @param module * @param props * @param Loadable * @returns Component */ function resolve(module, props, Loadable) { var Component = options.resolveComponent ? options.resolveComponent(module, props) : defaultResolveComponent(module); // FIXME: suppressed due to https://github.com/gregberge/loadable-components/issues/990 // if (options.resolveComponent && !ReactIs.isValidElementType(Component)) { // throw new Error( // `resolveComponent returned something that is not a React component!`, // ) // } hoistNonReactStatics(Loadable, Component, { preload: true }); return Component; } var cachedLoad = function cachedLoad(props) { var cacheKey = _getCacheKey(props); var promise = cache[cacheKey]; if (!promise || promise.status === STATUS_REJECTED) { promise = ctor.requireAsync(props); promise.status = STATUS_PENDING; cache[cacheKey] = promise; promise.then(function () { promise.status = STATUS_RESOLVED; }, function (error) { console.error('loadable-components: failed to asynchronously load component', { fileName: ctor.resolve(props), chunkName: ctor.chunkName(props), error: error ? error.message : error }); promise.status = STATUS_REJECTED; }); } return promise; }; var InnerLoadable = /*#__PURE__*/ function (_React$Component) { _inheritsLoose(InnerLoadable, _React$Component); InnerLoadable.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) { var cacheKey = _getCacheKey(props); return _extends({}, state, { cacheKey: cacheKey, // change of a key triggers loading state automatically loading: state.loading || state.cacheKey !== cacheKey }); }; function InnerLoadable(props) { var _this; _this = _React$Component.call(this, props) || this; _this.state = { result: null, error: null, loading: true, cacheKey: _getCacheKey(props) }; invariant(!props.__chunkExtractor || ctor.requireSync, 'SSR requires `@loadable/babel-plugin`, please install it'); // Server-side if (props.__chunkExtractor) { // This module has been marked with no SSR if (options.ssr === false) { return _assertThisInitialized(_this); } // We run load function, we assume that it won't fail and that it // triggers a synchronous loading of the module ctor.requireAsync(props)["catch"](function () { return null; }); // So we can require now the module synchronously _this.loadSync(); props.__chunkExtractor.addChunk(ctor.chunkName(props)); return _assertThisInitialized(_this); } // Client-side with `isReady` method present (SSR probably) // If module is already loaded, we use a synchronous loading // Only perform this synchronous loading if the component has not // been marked with no SSR, else we risk hydration mismatches if (options.ssr !== false && ( // is ready - was loaded in this session ctor.isReady && ctor.isReady(props) || // is ready - was loaded during SSR process ctor.chunkName && LOADABLE_SHARED.initialChunks[ctor.chunkName(props)])) { _this.loadSync(); } return _this; } var _proto = InnerLoadable.prototype; _proto.componentDidMount = function componentDidMount() { this.mounted = true; // retrieve loading promise from a global cache var cachedPromise = this.getCache(); // if promise exists, but rejected - clear cache if (cachedPromise && cachedPromise.status === STATUS_REJECTED) { this.setCache(); } // component might be resolved synchronously in the constructor if (this.state.loading) { this.loadAsync(); } }; _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) { // Component has to be reloaded on cacheKey change if (prevState.cacheKey !== this.state.cacheKey) { this.loadAsync(); } }; _proto.componentWillUnmount = function componentWillUnmount() { this.mounted = false; }; _proto.safeSetState = function safeSetState(nextState, callback) { if (this.mounted) { this.setState(nextState, callback); } } /** * returns a cache key for the current props * @returns {Component|string} */ ; _proto.getCacheKey = function getCacheKey() { return _getCacheKey(this.props); } /** * access the persistent cache */ ; _proto.getCache = function getCache() { return cache[this.getCacheKey()]; } /** * sets the cache value. If called without value sets it as undefined */ ; _proto.setCache = function setCache(value) { if (value === void 0) { value = undefined; } cache[this.getCacheKey()] = value; }; _proto.triggerOnLoad = function triggerOnLoad() { var _this2 = this; if (onLoad) { setTimeout(function () { onLoad(_this2.state.result, _this2.props); }); } } /** * Synchronously loads component * target module is expected to already exists in the module cache * or be capable to resolve synchronously (webpack target=node) */ ; _proto.loadSync = function loadSync() { // load sync is expecting component to be in the "loading" state already // sounds weird, but loading=true is the initial state of InnerLoadable if (!this.state.loading) return; try { var loadedModule = ctor.requireSync(this.props); var result = resolve(loadedModule, this.props, Loadable); this.state.result = result; this.state.loading = false; } catch (error) { console.error('loadable-components: failed to synchronously load component, which expected to be available', { fileName: ctor.resolve(this.props), chunkName: ctor.chunkName(this.props), error: error ? error.message : error }); this.state.error = error; } } /** * Asynchronously loads a component. */ ; _proto.loadAsync = function loadAsync() { var _this3 = this; var promise = this.resolveAsync(); promise.then(function (loadedModule) { var result = resolve(loadedModule, _this3.props, Loadable); _this3.safeSetState({ result: result, loading: false }, function () { return _this3.triggerOnLoad(); }); })["catch"](function (error) { return _this3.safeSetState({ error: error, loading: false }); }); return promise; } /** * Asynchronously resolves(not loads) a component. * Note - this function does not change the state */ ; _proto.resolveAsync = function resolveAsync() { var _this$props = this.props, __chunkExtractor = _this$props.__chunkExtractor, forwardedRef = _this$props.forwardedRef, props = _objectWithoutPropertiesLoose(_this$props, ["__chunkExtractor", "forwardedRef"]); return cachedLoad(props); }; _proto.render = function render() { var _this$props2 = this.props, forwardedRef = _this$props2.forwardedRef, propFallback = _this$props2.fallback, __chunkExtractor = _this$props2.__chunkExtractor, props = _objectWithoutPropertiesLoose(_this$props2, ["forwardedRef", "fallback", "__chunkExtractor"]); var _this$state = this.state, error = _this$state.error, loading = _this$state.loading, result = _this$state.result; if (options.suspense) { var cachedPromise = this.getCache() || this.loadAsync(); if (cachedPromise.status === STATUS_PENDING) { throw this.loadAsync(); } } if (error) { throw error; } var fallback = propFallback || options.fallback || null; if (loading) { return fallback; } return _render({ fallback: fallback, result: result, options: options, props: _extends({}, props, { ref: forwardedRef }) }); }; return InnerLoadable; }(React.Component); var EnhancedInnerLoadable = withChunkExtractor(InnerLoadable); var Loadable = React.forwardRef(function (props, ref) { return React.createElement(EnhancedInnerLoadable, Object.assign({ forwardedRef: ref }, props)); }); Loadable.displayName = 'Loadable'; // In future, preload could use `<link rel="preload">` Loadable.preload = function (props) { Loadable.load(props); }; Loadable.load = function (props) { return cachedLoad(props); }; return Loadable; } function lazy(ctor, options) { return loadable(ctor, _extends({}, options, { suspense: true })); } return { loadable: loadable, lazy: lazy }; } function defaultResolveComponent(loadedModule) { // eslint-disable-next-line no-underscore-dangle return loadedModule.__esModule ? loadedModule["default"] : loadedModule["default"] || loadedModule; } /* eslint-disable no-use-before-define, react/no-multi-comp */ var _createLoadable = /*#__PURE__*/ createLoadable({ defaultResolveComponent: defaultResolveComponent, render: function render(_ref) { var Component = _ref.result, props = _ref.props; return React.createElement(Component, props); } }), loadable = _createLoadable.loadable, lazy = _createLoadable.lazy; /* eslint-disable no-use-before-define, react/no-multi-comp */ var _createLoadable$1 = /*#__PURE__*/ createLoadable({ onLoad: function onLoad(result, props) { if (result && props.forwardedRef) { if (typeof props.forwardedRef === 'function') { props.forwardedRef(result); } else { props.forwardedRef.current = result; } } }, render: function render(_ref) { var result = _ref.result, props = _ref.props; if (props.children) { return props.children(result); } return null; } }), loadable$1 = _createLoadable$1.loadable, lazy$1 = _createLoadable$1.lazy; /* eslint-disable no-underscore-dangle, camelcase */ var BROWSER = typeof window !== 'undefined'; function loadableReady(done, _temp) { if (done === void 0) { done = function done() {}; } var _ref = _temp === void 0 ? {} : _temp, _ref$namespace = _ref.namespace, namespace = _ref$namespace === void 0 ? '' : _ref$namespace, _ref$chunkLoadingGlob = _ref.chunkLoadingGlobal, chunkLoadingGlobal = _ref$chunkLoadingGlob === void 0 ? '__LOADABLE_LOADED_CHUNKS__' : _ref$chunkLoadingGlob; if (!BROWSER) { warn('`loadableReady()` must be called in browser only'); done(); return Promise.resolve(); } var requiredChunks = null; if (BROWSER) { var id = getRequiredChunkKey(namespace); var dataElement = document.getElementById(id); if (dataElement) { requiredChunks = JSON.parse(dataElement.textContent); var extElement = document.getElementById(id + "_ext"); if (extElement) { var _JSON$parse = JSON.parse(extElement.textContent), namedChunks = _JSON$parse.namedChunks; namedChunks.forEach(function (chunkName) { LOADABLE_SHARED.initialChunks[chunkName] = true; }); } else { // version mismatch throw new Error('loadable-component: @loadable/server does not match @loadable/component'); } } } if (!requiredChunks) { warn('`loadableReady()` requires state, please use `getScriptTags` or `getScriptElements` server-side'); done(); return Promise.resolve(); } var resolved = false; return new Promise(function (resolve) { window[chunkLoadingGlobal] = window[chunkLoadingGlobal] || []; var loadedChunks = window[chunkLoadingGlobal]; var originalPush = loadedChunks.push.bind(loadedChunks); function checkReadyState() { if (requiredChunks.every(function (chunk) { return loadedChunks.some(function (_ref2) { var chunks = _ref2[0]; return chunks.indexOf(chunk) > -1; }); })) { if (!resolved) { resolved = true; resolve(); } } } loadedChunks.push = function () { originalPush.apply(void 0, arguments); checkReadyState(); }; checkReadyState(); }).then(done); } /* eslint-disable no-underscore-dangle */ var loadable$2 = loadable; loadable$2.lib = loadable$1; var lazy$2 = lazy; lazy$2.lib = lazy$1; var __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = sharedInternals; exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; exports.default = loadable$2; exports.lazy = lazy$2; exports.loadableReady = loadableReady; Object.defineProperty(exports, '__esModule', { value: true }); })));