fontdue-js 2.20.0 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/components/CharacterViewer/index.js +5 -4
- package/dist/components/FontdueProvider/FontdueProviderClientComponent.js +4 -3
- package/dist/components/Root/index.js +20 -19
- package/dist/corsError.js +42 -7
- package/dist/hooks/useAutofit.js +5 -4
- package/dist/retryImport.d.ts +13 -0
- package/dist/retryImport.js +26 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 2.21.0
|
|
2
|
+
|
|
3
|
+
- Fixed `CharacterViewer` silently dropping characters from supplementary-plane Unicode blocks (e.g. Latin Extended-F, Enclosed Alphanumeric Supplement). Range expansion now uses `codePointAt` / `String.fromCodePoint` so characters above U+FFFF render correctly instead of getting filtered out as unpaired surrogates
|
|
4
|
+
- Fixed false-positive CORS error modal appearing on network failures unrelated to CORS (DNS, timeout, server unreachable). The modal now only shows for genuine CORS misconfigurations; other fetch failures log a warning instead
|
|
5
|
+
|
|
6
|
+
## 2.20.1
|
|
7
|
+
|
|
8
|
+
- Fixed `useAutofit` measuring multi-line text as a single line, causing the font size to be too small when type tester content contains line breaks
|
|
9
|
+
|
|
1
10
|
## 2.20.0
|
|
2
11
|
|
|
3
12
|
- **Font loading now uses the FontFace API instead of CSS `@font-face`**. Fonts loaded by fontdue-js components (TypeTester, StoreModal, CharacterViewer, etc.) are no longer loaded via CSS stylesheet injection. Instead, font files are fetched as binary data and loaded via the browser's `FontFace` API. **Note:** components like TypeTesters and CharacterViewer previously loaded the entire family's CSS, which made all styles available page-wide as a side effect. They now only load the specific style they're rendering. If you render font styles outside of fontdue-js components (e.g., in a specimen section), you'll need to load those fonts yourself — either with the new `useFont` hook and `webfontSources`, or with your own `@font-face` rules.
|
|
@@ -15,6 +15,7 @@ var _CharacterViewer_family2 = _interopRequireDefault(require("../../__generated
|
|
|
15
15
|
var _react = _interopRequireWildcard(require("react"));
|
|
16
16
|
var _reactRelay = require("react-relay");
|
|
17
17
|
var _resizeObserver = _interopRequireDefault(require("@react-hook/resize-observer"));
|
|
18
|
+
var _retryImport = _interopRequireDefault(require("../../retryImport"));
|
|
18
19
|
var _utils = require("../../utils");
|
|
19
20
|
var _useFontStyle = _interopRequireDefault(require("../useFontStyle"));
|
|
20
21
|
var _useSerializablePreloadedQuery = _interopRequireDefault(require("../../relay/useSerializablePreloadedQuery"));
|
|
@@ -83,9 +84,9 @@ function useUnicodeData() {
|
|
|
83
84
|
const [data, setData] = (0, _react.useState)();
|
|
84
85
|
(0, _react.useEffect)(() => {
|
|
85
86
|
function fetchData() {
|
|
86
|
-
Promise.resolve().then(() => _interopRequireWildcard(require('../../data/unicodeData'))).then(data => {
|
|
87
|
+
(0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../../data/unicodeData')))).then(data => {
|
|
87
88
|
if (!ignore) setData(data.default);
|
|
88
|
-
});
|
|
89
|
+
}).catch(() => {});
|
|
89
90
|
}
|
|
90
91
|
let ignore = false;
|
|
91
92
|
fetchData();
|
|
@@ -109,10 +110,10 @@ function compareGlyphs(a, b) {
|
|
|
109
110
|
return true;
|
|
110
111
|
}
|
|
111
112
|
function charCode(c) {
|
|
112
|
-
return c.
|
|
113
|
+
return c.codePointAt(0) ?? 0;
|
|
113
114
|
}
|
|
114
115
|
function fromCharCode(code) {
|
|
115
|
-
return String.
|
|
116
|
+
return String.fromCodePoint(code);
|
|
116
117
|
}
|
|
117
118
|
function flattenCharacterList(charSet, glyphNames) {
|
|
118
119
|
if (!glyphNames || !charSet || !charSet.characters) return null;
|
|
@@ -14,12 +14,13 @@ var _ConfigContext = _interopRequireWildcard(require("../ConfigContext"));
|
|
|
14
14
|
var _reducer = require("../../reducer");
|
|
15
15
|
var _ComponentsContext = _interopRequireDefault(require("../ComponentsContext"));
|
|
16
16
|
var _UrlContext = _interopRequireDefault(require("../UrlContext"));
|
|
17
|
+
var _retryImport = _interopRequireDefault(require("../../retryImport"));
|
|
17
18
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
19
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
19
20
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
20
|
-
const ConsentBanner = /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../ConsentBanner'))));
|
|
21
|
-
const Tracking = /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../Tracking'))));
|
|
22
|
-
const ServerConfigProvider = /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../ServerConfigProvider'))));
|
|
21
|
+
const ConsentBanner = /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../ConsentBanner')))));
|
|
22
|
+
const Tracking = /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../Tracking')))));
|
|
23
|
+
const ServerConfigProvider = /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../ServerConfigProvider')))));
|
|
23
24
|
function FontdueProviderClientComponent(_ref) {
|
|
24
25
|
let {
|
|
25
26
|
children,
|
|
@@ -9,6 +9,7 @@ var _reactErrorBoundary = require("react-error-boundary");
|
|
|
9
9
|
var _reactDom = _interopRequireDefault(require("react-dom"));
|
|
10
10
|
var _uuid = require("uuid");
|
|
11
11
|
var _reducer = require("../../reducer");
|
|
12
|
+
var _retryImport = _interopRequireDefault(require("../../retryImport"));
|
|
12
13
|
var _utils = require("../../utils");
|
|
13
14
|
var _FontdueProvider = _interopRequireDefault(require("../FontdueProvider"));
|
|
14
15
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -17,31 +18,31 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
|
|
|
17
18
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
18
19
|
const customElementMap = {
|
|
19
20
|
// @ts-ignore
|
|
20
|
-
'fontdue-add-to-cart-banner': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../AddToCartBanner')))),
|
|
21
|
+
'fontdue-add-to-cart-banner': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../AddToCartBanner'))))),
|
|
21
22
|
// @ts-ignore
|
|
22
|
-
'fontdue-buying-options': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../BuyingOptions')))),
|
|
23
|
-
'fontdue-buy-button': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../BuyButton')))),
|
|
24
|
-
'fontdue-cart': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../Cart')))),
|
|
25
|
-
'fontdue-cart-button': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CartButton')))),
|
|
26
|
-
'fontdue-character-viewer': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CharacterViewer')))),
|
|
23
|
+
'fontdue-buying-options': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../BuyingOptions'))))),
|
|
24
|
+
'fontdue-buy-button': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../BuyButton'))))),
|
|
25
|
+
'fontdue-cart': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../Cart'))))),
|
|
26
|
+
'fontdue-cart-button': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CartButton'))))),
|
|
27
|
+
'fontdue-character-viewer': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CharacterViewer'))))),
|
|
27
28
|
// @ts-ignore
|
|
28
|
-
'fontdue-collection-aa': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CollectionAa')))),
|
|
29
|
+
'fontdue-collection-aa': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CollectionAa'))))),
|
|
29
30
|
// @ts-ignore
|
|
30
|
-
'fontdue-cookie-notification': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CookieNotification')))),
|
|
31
|
-
'fontdue-font-families': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../FontFamilies')))),
|
|
32
|
-
'fontdue-customer-login-form': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CustomerLoginForm')))),
|
|
33
|
-
'fontdue-newsletter-signup': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../NewsletterSignup/NewsletterSignupElement')))),
|
|
34
|
-
'fontdue-node-password-form': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../NodePasswordForm')))),
|
|
31
|
+
'fontdue-cookie-notification': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CookieNotification'))))),
|
|
32
|
+
'fontdue-font-families': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../FontFamilies'))))),
|
|
33
|
+
'fontdue-customer-login-form': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../CustomerLoginForm'))))),
|
|
34
|
+
'fontdue-newsletter-signup': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../NewsletterSignup/NewsletterSignupElement'))))),
|
|
35
|
+
'fontdue-node-password-form': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../NodePasswordForm'))))),
|
|
35
36
|
// @ts-ignore
|
|
36
|
-
'fontdue-precart': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../Precart')))),
|
|
37
|
+
'fontdue-precart': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../Precart'))))),
|
|
37
38
|
// @ts-ignore
|
|
38
|
-
'fontdue-specimen-link': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../SpecimenLink')))),
|
|
39
|
+
'fontdue-specimen-link': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../SpecimenLink'))))),
|
|
39
40
|
// @ts-ignore
|
|
40
|
-
'fontdue-sticky-nav': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../StickyNav')))),
|
|
41
|
-
'fontdue-store-modal': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../StoreModal')))),
|
|
42
|
-
'fontdue-test-fonts-form': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../TestFontsForm/TestFontsFormElement')))),
|
|
43
|
-
'fontdue-type-tester': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../TypeTester/TypeTesterStandaloneElement')))),
|
|
44
|
-
'fontdue-type-testers': /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../TypeTesters/TypeTestersElement'))))
|
|
41
|
+
'fontdue-sticky-nav': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../StickyNav'))))),
|
|
42
|
+
'fontdue-store-modal': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../StoreModal'))))),
|
|
43
|
+
'fontdue-test-fonts-form': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../TestFontsForm/TestFontsFormElement'))))),
|
|
44
|
+
'fontdue-type-tester': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../TypeTester/TypeTesterStandaloneElement'))))),
|
|
45
|
+
'fontdue-type-testers': /*#__PURE__*/(0, _react.lazy)(() => (0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('../TypeTesters/TypeTestersElement')))))
|
|
45
46
|
};
|
|
46
47
|
Object.keys(customElementMap).forEach(elementName => {
|
|
47
48
|
// this might be more sophisticated in the future with a shadow DOM, etc,
|
package/dist/corsError.js
CHANGED
|
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.handlePossibleCorsError = handlePossibleCorsError;
|
|
7
|
+
var _retryImport = _interopRequireDefault(require("./retryImport"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
9
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
8
10
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
9
11
|
let detected = false;
|
|
@@ -35,6 +37,32 @@ function startPolling(fetchUrl) {
|
|
|
35
37
|
}
|
|
36
38
|
}, 3000);
|
|
37
39
|
}
|
|
40
|
+
async function isCorsBlocked(fetchUrl) {
|
|
41
|
+
// A no-cors fetch bypasses CORS entirely — if it also fails,
|
|
42
|
+
// the server is unreachable (network error), not CORS-blocked.
|
|
43
|
+
try {
|
|
44
|
+
await fetch(fetchUrl, {
|
|
45
|
+
method: 'HEAD',
|
|
46
|
+
mode: 'no-cors'
|
|
47
|
+
});
|
|
48
|
+
return true; // server reachable, so the original error was CORS
|
|
49
|
+
} catch {
|
|
50
|
+
return false; // server unreachable, network error
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function showCorsError(origin, fetchUrl) {
|
|
54
|
+
console.error(`[Fontdue] Cross-origin request to ${fetchUrl} was blocked.\n\n` + `Your website (${origin}) is not listed as an allowed origin ` + `in your Fontdue CORS settings.\n\n` + `To fix this:\n` + `1. Log in to your Fontdue dashboard\n` + `2. Go to Settings \u2192 Security\n` + `3. Add "${origin}" to the "Cross-origin API access" field\n` + `4. Save \u2014 this page will reload automatically`);
|
|
55
|
+
(0, _retryImport.default)(() => Promise.resolve().then(() => _interopRequireWildcard(require('./components/CorsErrorModal')))).then(_ref => {
|
|
56
|
+
let {
|
|
57
|
+
renderCorsErrorModal
|
|
58
|
+
} = _ref;
|
|
59
|
+
renderCorsErrorModal(origin, fetchUrl);
|
|
60
|
+
}).catch(() => {
|
|
61
|
+
// Chunk failed to load — the console.error above already
|
|
62
|
+
// told the developer what's wrong.
|
|
63
|
+
});
|
|
64
|
+
startPolling(fetchUrl);
|
|
65
|
+
}
|
|
38
66
|
function handlePossibleCorsError(error, fetchUrl) {
|
|
39
67
|
if (typeof window === 'undefined') return false;
|
|
40
68
|
if (!(error instanceof TypeError)) return false;
|
|
@@ -43,13 +71,20 @@ function handlePossibleCorsError(error, fetchUrl) {
|
|
|
43
71
|
if (detected) return true;
|
|
44
72
|
detected = true;
|
|
45
73
|
const origin = window.location.origin;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
74
|
+
|
|
75
|
+
// Verify this is actually a CORS error and not a network failure.
|
|
76
|
+
// We do this async — handlePossibleCorsError still returns true
|
|
77
|
+
// synchronously so the caller can return a stub response instead
|
|
78
|
+
// of throwing, but we only show the modal if CORS is confirmed.
|
|
79
|
+
isCorsBlocked(fetchUrl).then(blocked => {
|
|
80
|
+
if (blocked) {
|
|
81
|
+
showCorsError(origin, fetchUrl);
|
|
82
|
+
} else {
|
|
83
|
+
// Network error, not CORS — reset so a real CORS error
|
|
84
|
+
// can still be detected if connectivity recovers.
|
|
85
|
+
detected = false;
|
|
86
|
+
console.warn(`[Fontdue] Request to ${fetchUrl} failed — this looks like a network issue, not a CORS configuration problem.`);
|
|
87
|
+
}
|
|
52
88
|
});
|
|
53
|
-
startPolling(fetchUrl);
|
|
54
89
|
return true;
|
|
55
90
|
}
|
package/dist/hooks/useAutofit.js
CHANGED
|
@@ -87,10 +87,11 @@ const useAutofit = _ref => {
|
|
|
87
87
|
const availableWidth = containerWidth - padding;
|
|
88
88
|
if (availableWidth <= 0 || !text) return;
|
|
89
89
|
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
const
|
|
90
|
+
// Split by newlines and measure the widest line, since the rendered
|
|
91
|
+
// text preserves line breaks (Draft.js uses white-space: pre-wrap).
|
|
92
|
+
const lines = text.split('\n');
|
|
93
|
+
const measure = fontVariationSettings ? line => measureWithDOM(line, fontFamily, fontWeight, fontStyle, letterSpacing, fontVariationSettings) : line => measureWithCanvas(line, fontFamily, fontWeight, fontStyle, letterSpacing);
|
|
94
|
+
const refWidth = Math.max(...lines.map(measure));
|
|
94
95
|
if (refWidth <= 0) return;
|
|
95
96
|
|
|
96
97
|
// Text width scales linearly with font size.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a dynamic import in retry logic.
|
|
3
|
+
*
|
|
4
|
+
* Retries the factory up to `retries` times with a delay between attempts.
|
|
5
|
+
* Re-invoking the factory works on Safari (which doesn't cache module-map
|
|
6
|
+
* failures) and handles transient network blips on all browsers. Chrome and
|
|
7
|
+
* Firefox cache failed module fetches in the module map, so retries of the
|
|
8
|
+
* same specifier may return the cached rejection without a network request;
|
|
9
|
+
* the delay helps partially mitigate that, but is not a guaranteed fix.
|
|
10
|
+
*
|
|
11
|
+
* See: https://github.com/whatwg/html/issues/6768
|
|
12
|
+
*/
|
|
13
|
+
export default function retryImport<T>(load: () => Promise<T>, retries?: number, delay?: number): Promise<T>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = retryImport;
|
|
7
|
+
/**
|
|
8
|
+
* Wraps a dynamic import in retry logic.
|
|
9
|
+
*
|
|
10
|
+
* Retries the factory up to `retries` times with a delay between attempts.
|
|
11
|
+
* Re-invoking the factory works on Safari (which doesn't cache module-map
|
|
12
|
+
* failures) and handles transient network blips on all browsers. Chrome and
|
|
13
|
+
* Firefox cache failed module fetches in the module map, so retries of the
|
|
14
|
+
* same specifier may return the cached rejection without a network request;
|
|
15
|
+
* the delay helps partially mitigate that, but is not a guaranteed fix.
|
|
16
|
+
*
|
|
17
|
+
* See: https://github.com/whatwg/html/issues/6768
|
|
18
|
+
*/
|
|
19
|
+
function retryImport(load) {
|
|
20
|
+
let retries = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
|
|
21
|
+
let delay = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1000;
|
|
22
|
+
return load().catch(error => {
|
|
23
|
+
if (retries === 0) throw error;
|
|
24
|
+
return new Promise(resolve => setTimeout(resolve, delay)).then(() => retryImport(load, retries - 1, delay));
|
|
25
|
+
});
|
|
26
|
+
}
|