UNPKG

react-clear-browser-cache

Version:

Library for clearing browser cache after react app updates

376 lines (312 loc) 10.6 kB
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = _interopDefault(require('react')); 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 _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } // A type of promise-like that resolves synchronously and supports only one observer const _iteratorSymbol = /*#__PURE__*/ typeof Symbol !== "undefined" ? (Symbol.iterator || (Symbol.iterator = Symbol("Symbol.iterator"))) : "@@iterator"; const _asyncIteratorSymbol = /*#__PURE__*/ typeof Symbol !== "undefined" ? (Symbol.asyncIterator || (Symbol.asyncIterator = Symbol("Symbol.asyncIterator"))) : "@@asyncIterator"; // Asynchronously call a function and send errors to recovery continuation function _catch(body, recover) { try { var result = body(); } catch(e) { return recover(e); } if (result && result.then) { return result.then(void 0, recover); } return result; } function createErrorChecker(name, regexpForMesssage) { return function (error) { return error.name === name && Boolean(regexpForMesssage.exec(error.message)); }; } var chunkFailedMessageRegex = /Loading chunk [\d]+ failed/; var syntaxErrorMessageRegex = /['"\s]<['"\s]/; var defaultErrorCheckers = [createErrorChecker('ChunkLoadError', chunkFailedMessageRegex), createErrorChecker('SyntaxError', syntaxErrorMessageRegex)]; var storageKey = 'APP_VERSION'; var filename = 'meta.json'; var latestVersion = 'latest'; var disabledVersion = 'disabled'; var defaultProps = { fallback: null, errorCheckers: [], auto: false, storageKey: storageKey, filename: filename, storage: { get: function get() { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return window.localStorage.getItem.apply(window.localStorage, args); }, set: function set() { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } window.localStorage.setItem.apply(window.localStorage, args); } } }; var ClearBrowserCacheCtx = React.createContext({}); function useClearBrowserCache() { return React.useContext(ClearBrowserCacheCtx); } function ClearBrowserCache(_ref) { var children = _ref.children; var ctx = useClearBrowserCache(); return children(ctx); } function createAppVersionStorage(storageKey, storage) { return { set: function set(version) { try { storage.set(storageKey, version); } catch (error) {} }, get: function get() { try { return storage.get(storageKey) || latestVersion; } catch (error) { return disabledVersion; } } }; } var ClearBrowserCacheBoundary = /*#__PURE__*/function (_React$Component) { _inheritsLoose(ClearBrowserCacheBoundary, _React$Component); function ClearBrowserCacheBoundary(props) { var _this; _this = _React$Component.call(this, props) || this; _this.checkInterval = null; _this.debug = function (error, errorInfo) { var debug = _this.props.debug; if (debug !== null && debug !== void 0 && debug.call) { debug({ state: _this.state, error: error, errorInfo: errorInfo }); } }; _this.startVersionCheck = function () { var _this$props = _this.props, duration = _this$props.duration, auto = _this$props.auto; var disabled = _this.state.disabled; if (!disabled && duration) { _this.checkInterval = setInterval(function () { return _this.checkVersion(auto, true); }, duration); } }; _this.stopVersionCheck = function () { if (_this.checkInterval) { clearInterval(_this.checkInterval); } }; _this.fetchMeta = function () { try { var _filename = _this.props.filename; var baseUrl = "/" + _filename + "?time=" + Date.now(); return Promise.resolve(fetch(baseUrl).then(function (r) { return r.json(); })); } catch (e) { return Promise.reject(e); } }; _this.clearCacheAndReload = function (newVersion) { try { var _temp3 = function _temp3() { _this.appVersionStorage.set(newVersion || _this.state.latestVersion); window.location.reload(true); }; var _temp4 = function () { if ('caches' in window) { return Promise.resolve(window.caches.keys()).then(function (cacheKeys) { return Promise.resolve(Promise.all(cacheKeys.map(function (key) { return window.caches["delete"](key); }))).then(function () {}); }); } }(); return Promise.resolve(_temp4 && _temp4.then ? _temp4.then(_temp3) : _temp3(_temp4)); } catch (e) { return Promise.reject(e); } }; _this.checkVersion = function (auto, silent) { if (auto === void 0) { auto = false; } if (silent === void 0) { silent = false; } try { var _temp9 = _catch(function () { var appVersion = _this.state.latestVersion; return Promise.resolve(_this.fetchMeta()).then(function (meta) { var newVersion = meta.version; var isUpdated = newVersion === appVersion; var _temp7 = function () { if (!isUpdated) { var _temp10 = function _temp10() { _this.appVersionStorage.set(newVersion); }; var _temp11 = function () { if (auto) { return Promise.resolve(_this.clearCacheAndReload()).then(function () {}); } else { _this.setState({ latestVersion: newVersion, loading: false, isLatestVersion: false }); } }(); return _temp11 && _temp11.then ? _temp11.then(_temp10) : _temp10(_temp11); } else if (!silent) { _this.setState({ loading: false, isLatestVersion: true }); } }(); if (_temp7 && _temp7.then) return _temp7.then(function () {}); }); }, function (error) { _this.debug(error); _this.setState({ loading: false }); }); return Promise.resolve(_temp9 && _temp9.then ? _temp9.then(function () {}) : void 0); } catch (e) { return Promise.reject(e); } }; _this.errorCheckers = defaultErrorCheckers.concat(props.errorCheckers); _this.appVersionStorage = createAppVersionStorage(props.storageKey, props.storage); var latestVersion = _this.appVersionStorage.get(); var disabled = latestVersion === disabledVersion; _this.state = { loading: !disabled, latestVersion: latestVersion, disabled: disabled, isLatestVersion: true }; return _this; } var _proto = ClearBrowserCacheBoundary.prototype; _proto.componentDidMount = function componentDidMount() { var disabled = this.state.disabled; if (!disabled) { this.checkVersion(true); } window.addEventListener('focus', this.startVersionCheck); window.addEventListener('blur', this.stopVersionCheck); this.debug(); }; _proto.componentDidUpdate = function componentDidUpdate() { var loading = this.state.loading; this.stopVersionCheck(); if (!loading) { this.startVersionCheck(); } this.debug(); }; _proto.componentWillUnmount = function componentWillUnmount() { this.stopVersionCheck(); window.removeEventListener('focus', this.startVersionCheck); window.removeEventListener('blur', this.stopVersionCheck); }; _proto.componentDidCatch = function componentDidCatch(error, errorInfo) { var _this2 = this; var _this$state = this.state, loading = _this$state.loading, disabled = _this$state.disabled; function throwError() { throw error; } if (disabled) throwError(); if (loading) return; var needCheckVersion = this.errorCheckers.some(function (checkError) { return checkError(error); }); if (needCheckVersion) { this.setState({ loading: true }); this.checkVersion(true, false).then(function () { _this2.debug(error, errorInfo); _this2.setState(throwError); }); } else { throwError(); } }; _proto.render = function render() { var _this3 = this; var _this$props2 = this.props, children = _this$props2.children, fallback = _this$props2.fallback; var loading = this.state.loading; if (loading) { return fallback; } return React.createElement(ClearBrowserCacheCtx.Provider, { value: _extends({}, this.state, { clearCacheAndReload: function clearCacheAndReload() { return _this3.clearCacheAndReload(); } }) }, children); }; return ClearBrowserCacheBoundary; }(React.Component); ClearBrowserCacheBoundary.defaultProps = defaultProps; function lazyRetry(fn, retriesLeft, interval) { if (retriesLeft === void 0) { retriesLeft = 2; } if (interval === void 0) { interval = 1000; } return new Promise(function (resolve, reject) { fn().then(resolve)["catch"](function (error) { setTimeout(function () { if (retriesLeft === 1) { reject(error); return; } lazyRetry(fn, retriesLeft - 1, interval).then(resolve, reject); }, interval); }); }); } exports.ClearBrowserCache = ClearBrowserCache; exports.ClearBrowserCacheBoundary = ClearBrowserCacheBoundary; exports.createErrorChecker = createErrorChecker; exports.lazyRetry = lazyRetry; exports.useClearBrowserCache = useClearBrowserCache; //# sourceMappingURL=index.js.map