react-clear-browser-cache
Version:
Library for clearing browser cache after react app updates
376 lines (312 loc) • 10.6 kB
JavaScript
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