@rsbuild/plugin-assets-retry 1.2.2 → 1.3.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/README.md +18 -14
- package/README.zh-CN.md +18 -14
- package/dist/index.cjs +27 -36
- package/dist/index.d.ts +96 -5
- package/dist/index.js +27 -36
- package/dist/runtime/asyncChunkRetry.js +165 -219
- package/dist/runtime/asyncChunkRetry.min.js +1 -1
- package/dist/runtime/initialChunkRetry.js +181 -264
- package/dist/runtime/initialChunkRetry.min.js +1 -1
- package/package.json +3 -2
- package/dist/AsyncChunkRetryPlugin.d.ts +0 -15
- package/dist/runtime/asyncChunkRetry.d.ts +0 -19
- package/dist/runtime/initialChunkRetry.d.ts +0 -5
- package/dist/types.d.ts +0 -72
|
@@ -1,291 +1,208 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
var TAG_TYPE = {
|
|
10
|
-
link: HTMLLinkElement,
|
|
11
|
-
script: HTMLScriptElement,
|
|
12
|
-
img: HTMLImageElement
|
|
13
|
-
};
|
|
14
|
-
// this function is the same as async chunk retry
|
|
15
|
-
function findCurrentDomain(url, domains) {
|
|
16
|
-
var domain = '';
|
|
17
|
-
for(var i = 0; i < domains.length; i++){
|
|
18
|
-
if (url.indexOf(domains[i]) !== -1) {
|
|
1
|
+
(function() {
|
|
2
|
+
"use strict";
|
|
3
|
+
var ERROR_PREFIX = '[@rsbuild/plugin-assets-retry] ';
|
|
4
|
+
function findCurrentDomain(url, config) {
|
|
5
|
+
var domains = config.domain;
|
|
6
|
+
var domain = '';
|
|
7
|
+
for(var i = 0; i < domains.length; i++)if (-1 !== url.indexOf(domains[i])) {
|
|
19
8
|
domain = domains[i];
|
|
20
9
|
break;
|
|
21
10
|
}
|
|
11
|
+
return domain || window.origin;
|
|
22
12
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
var index = domains.indexOf(currentDomain);
|
|
29
|
-
return domains[(index + 1) % domains.length] || url;
|
|
30
|
-
}
|
|
31
|
-
function getRequestUrl(element) {
|
|
32
|
-
if (_instanceof(element, HTMLScriptElement) || _instanceof(element, HTMLImageElement)) {
|
|
33
|
-
// For <script src="" /> or <img src="" />
|
|
34
|
-
// element.getAttribute('src') === '' but element.src === baseURI
|
|
35
|
-
if (!element.getAttribute('src')) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
return element.src;
|
|
13
|
+
function findNextDomain(url, config) {
|
|
14
|
+
var domains = config.domain;
|
|
15
|
+
var currentDomain = findCurrentDomain(url, config);
|
|
16
|
+
var index = domains.indexOf(currentDomain);
|
|
17
|
+
return domains[(index + 1) % domains.length] || url;
|
|
39
18
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
return element.href;
|
|
19
|
+
var postfixRE = /[?#].*$/;
|
|
20
|
+
function cleanUrl(url) {
|
|
21
|
+
return url.replace(postfixRE, '');
|
|
46
22
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
var target = e.target;
|
|
51
|
-
var tagName = target.tagName.toLocaleLowerCase();
|
|
52
|
-
var allowTags = config.type;
|
|
53
|
-
var url = getRequestUrl(target);
|
|
54
|
-
if (!tagName || allowTags.indexOf(tagName) === -1 || !TAG_TYPE[tagName] || !_instanceof(target, TAG_TYPE[tagName]) || !url) {
|
|
55
|
-
return false;
|
|
23
|
+
function getQueryFromUrl(url) {
|
|
24
|
+
var parts = url.split('?')[1];
|
|
25
|
+
return parts ? "?".concat(parts.split('#')[0]) : '';
|
|
56
26
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function cleanUrl(url) {
|
|
65
|
-
return url.replace(postfixRE, '');
|
|
66
|
-
}
|
|
67
|
-
function getQueryFromUrl(url) {
|
|
68
|
-
var parts = url.split('?')[1];
|
|
69
|
-
return parts ? "?".concat(parts.split('#')[0]) : '';
|
|
70
|
-
}
|
|
71
|
-
function createElement(origin, attributes) {
|
|
72
|
-
var crossOrigin = attributes.crossOrigin === true ? 'anonymous' : attributes.crossOrigin;
|
|
73
|
-
var crossOriginAttr = crossOrigin ? 'crossorigin="'.concat(crossOrigin, '"') : '';
|
|
74
|
-
var retryTimesAttr = attributes.times ? 'data-rb-retry-times="'.concat(attributes.times, '"') : '';
|
|
75
|
-
var originalQueryAttr = attributes.originalQuery ? 'data-rb-original-query="'.concat(attributes.originalQuery, '"') : '';
|
|
76
|
-
var isAsyncAttr = attributes.isAsync ? 'data-rb-async' : '';
|
|
77
|
-
if (_instanceof(origin, HTMLScriptElement)) {
|
|
78
|
-
var script = document.createElement('script');
|
|
79
|
-
script.src = attributes.url;
|
|
80
|
-
if (crossOrigin) {
|
|
81
|
-
script.crossOrigin = crossOrigin;
|
|
82
|
-
}
|
|
83
|
-
if (attributes.times) {
|
|
84
|
-
script.dataset.rbRetryTimes = String(attributes.times);
|
|
85
|
-
}
|
|
86
|
-
if (attributes.isAsync) {
|
|
87
|
-
script.dataset.rbAsync = '';
|
|
88
|
-
}
|
|
89
|
-
if (attributes.originalQuery !== undefined) {
|
|
90
|
-
script.dataset.rbOriginalQuery = attributes.originalQuery;
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
element: script,
|
|
94
|
-
str: // biome-ignore lint/style/useTemplate: use "</" + "script>" instead of script tag to avoid syntax error when inlining in html
|
|
95
|
-
'<script src="'.concat(attributes.url, '" ').concat(crossOriginAttr, " ").concat(retryTimesAttr, " ").concat(isAsyncAttr, " ").concat(originalQueryAttr, ">") + '</' + 'script>'
|
|
96
|
-
};
|
|
27
|
+
function getUrlRetryQuery(existRetryTimes, originalQuery, config) {
|
|
28
|
+
if (true === config.addQuery) return '' !== originalQuery ? "".concat(originalQuery, "&retry=").concat(existRetryTimes) : "?retry=".concat(existRetryTimes);
|
|
29
|
+
if ('function' == typeof config.addQuery) return config.addQuery({
|
|
30
|
+
times: existRetryTimes,
|
|
31
|
+
originalQuery: originalQuery
|
|
32
|
+
});
|
|
33
|
+
return '';
|
|
97
34
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
35
|
+
function getNextRetryUrl(currRetryUrl, domain, nextDomain, existRetryTimes, originalQuery, config) {
|
|
36
|
+
return cleanUrl(currRetryUrl.replace(domain, nextDomain)) + getUrlRetryQuery(existRetryTimes + 1, originalQuery, config);
|
|
37
|
+
}
|
|
38
|
+
function _instanceof(left, right) {
|
|
39
|
+
if (null != right && "undefined" != typeof Symbol && right[Symbol.hasInstance]) return !!right[Symbol.hasInstance](left);
|
|
40
|
+
return left instanceof right;
|
|
41
|
+
}
|
|
42
|
+
var TAG_TYPE = {
|
|
43
|
+
link: HTMLLinkElement,
|
|
44
|
+
script: HTMLScriptElement,
|
|
45
|
+
img: HTMLImageElement
|
|
46
|
+
};
|
|
47
|
+
function getRequestUrl(element) {
|
|
48
|
+
if (_instanceof(element, HTMLScriptElement) || _instanceof(element, HTMLImageElement)) return element.src;
|
|
49
|
+
if (_instanceof(element, HTMLLinkElement)) return element.href;
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
function validateTargetInfo(config, e) {
|
|
53
|
+
var target = e.target;
|
|
54
|
+
var tagName = target.tagName.toLocaleLowerCase();
|
|
55
|
+
var allowTags = config.type;
|
|
56
|
+
var url = getRequestUrl(target);
|
|
57
|
+
if (!tagName || -1 === allowTags.indexOf(tagName) || !TAG_TYPE[tagName] || !_instanceof(target, TAG_TYPE[tagName]) || !url) return false;
|
|
114
58
|
return {
|
|
115
|
-
|
|
116
|
-
|
|
59
|
+
target: target,
|
|
60
|
+
tagName: tagName,
|
|
61
|
+
url: url
|
|
117
62
|
};
|
|
118
63
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
64
|
+
function createElement(origin, attributes) {
|
|
65
|
+
var crossOrigin = true === attributes.crossOrigin ? 'anonymous' : attributes.crossOrigin;
|
|
66
|
+
var crossOriginAttr = crossOrigin ? 'crossorigin="'.concat(crossOrigin, '"') : '';
|
|
67
|
+
var retryTimesAttr = attributes.times ? 'data-rb-retry-times="'.concat(attributes.times, '"') : '';
|
|
68
|
+
var originalQueryAttr = attributes.originalQuery ? 'data-rb-original-query="'.concat(attributes.originalQuery, '"') : '';
|
|
69
|
+
var isAsyncAttr = attributes.isAsync ? 'data-rb-async' : '';
|
|
70
|
+
if (_instanceof(origin, HTMLScriptElement)) {
|
|
71
|
+
var script = document.createElement("script");
|
|
72
|
+
script.src = attributes.url;
|
|
73
|
+
if (crossOrigin) script.crossOrigin = crossOrigin;
|
|
74
|
+
if (attributes.times) script.dataset.rbRetryTimes = String(attributes.times);
|
|
75
|
+
if (attributes.isAsync) script.dataset.rbAsync = '';
|
|
76
|
+
if (void 0 !== attributes.originalQuery) script.dataset.rbOriginalQuery = attributes.originalQuery;
|
|
77
|
+
return {
|
|
78
|
+
element: script,
|
|
79
|
+
str: '<script src="'.concat(attributes.url, '" ').concat(crossOriginAttr, " ").concat(retryTimesAttr, " ").concat(isAsyncAttr, " ").concat(originalQueryAttr, ">") + "<\/script>"
|
|
80
|
+
};
|
|
126
81
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
var targetInfo = validateTargetInfo(config, e);
|
|
139
|
-
if (targetInfo === false) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
var target = targetInfo.target, tagName = targetInfo.tagName, url = targetInfo.url;
|
|
143
|
-
// If the requested failed chunk is async chunk,skip it, because async chunk will be retried by asyncChunkRetry runtime
|
|
144
|
-
if (typeof window !== 'undefined' && Object.keys(window.__RB_ASYNC_CHUNKS__ || {}).some(function(chunkName) {
|
|
145
|
-
return url.indexOf(chunkName) !== -1;
|
|
146
|
-
})) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
// Filter by config.test and config.domain
|
|
150
|
-
var tester = config.test;
|
|
151
|
-
if (tester) {
|
|
152
|
-
if (typeof tester === 'string') {
|
|
153
|
-
var regexp = new RegExp(tester);
|
|
154
|
-
tester = function(str) {
|
|
155
|
-
return regexp.test(str);
|
|
82
|
+
if (_instanceof(origin, HTMLLinkElement)) {
|
|
83
|
+
var link = document.createElement('link');
|
|
84
|
+
link.rel = origin.rel || 'stylesheet';
|
|
85
|
+
if (origin.as) link.as = origin.as;
|
|
86
|
+
link.href = attributes.url;
|
|
87
|
+
if (crossOrigin) link.crossOrigin = crossOrigin;
|
|
88
|
+
if (attributes.times) link.dataset.rbRetryTimes = String(attributes.times);
|
|
89
|
+
if (void 0 !== attributes.originalQuery) link.dataset.rbOriginalQuery = attributes.originalQuery;
|
|
90
|
+
return {
|
|
91
|
+
element: link,
|
|
92
|
+
str: '<link rel="'.concat(link.rel, '" href="').concat(attributes.url, '" ').concat(crossOriginAttr, " ").concat(retryTimesAttr, " ").concat(link.as ? 'as="'.concat(link.as, '"') : '', " ").concat(originalQueryAttr, "></link>")
|
|
156
93
|
};
|
|
157
94
|
}
|
|
158
|
-
|
|
95
|
+
}
|
|
96
|
+
function reloadElementResource(origin, fresh, attributes) {
|
|
97
|
+
if (_instanceof(origin, HTMLScriptElement)) if (attributes.isAsync) document.body.appendChild(fresh.element);
|
|
98
|
+
else console.warn(ERROR_PREFIX, "load sync script failed, for security only async/defer script can be retried", origin);
|
|
99
|
+
if (_instanceof(origin, HTMLLinkElement)) document.getElementsByTagName('head')[0].appendChild(fresh.element);
|
|
100
|
+
if (_instanceof(origin, HTMLImageElement)) {
|
|
101
|
+
origin.src = attributes.url;
|
|
102
|
+
origin.dataset.rbRetryTimes = String(attributes.times);
|
|
103
|
+
origin.dataset.rbOriginalQuery = String(attributes.originalQuery);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function retry(config, e) {
|
|
107
|
+
var targetInfo = validateTargetInfo(config, e);
|
|
108
|
+
if (false === targetInfo) return;
|
|
109
|
+
var target = targetInfo.target, tagName = targetInfo.tagName, url = targetInfo.url;
|
|
110
|
+
if ('undefined' != typeof window && Object.keys(window.__RB_ASYNC_CHUNKS__ || {}).some(function(chunkName) {
|
|
111
|
+
return -1 !== url.indexOf(chunkName);
|
|
112
|
+
})) return;
|
|
113
|
+
var tester = config.test;
|
|
114
|
+
if (tester) {
|
|
115
|
+
if ('string' == typeof tester) {
|
|
116
|
+
var regexp = new RegExp(tester);
|
|
117
|
+
tester = function(str) {
|
|
118
|
+
return regexp.test(str);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if ('function' != typeof tester || !tester(url)) return;
|
|
122
|
+
}
|
|
123
|
+
var domain = findCurrentDomain(url, config);
|
|
124
|
+
if (config.domain && config.domain.length > 0 && -1 === config.domain.indexOf(domain)) return;
|
|
125
|
+
var existRetryTimes = Number(target.dataset.rbRetryTimes) || 0;
|
|
126
|
+
if (existRetryTimes === config.max) {
|
|
127
|
+
if ('function' == typeof config.onFail) {
|
|
128
|
+
var context = {
|
|
129
|
+
times: existRetryTimes,
|
|
130
|
+
domain: domain,
|
|
131
|
+
url: url,
|
|
132
|
+
tagName: tagName,
|
|
133
|
+
isAsyncChunk: false
|
|
134
|
+
};
|
|
135
|
+
config.onFail(context);
|
|
136
|
+
}
|
|
159
137
|
return;
|
|
160
138
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
139
|
+
var nextDomain = findNextDomain(domain, config);
|
|
140
|
+
var _target_dataset_rbOriginalQuery;
|
|
141
|
+
var originalQuery = null != (_target_dataset_rbOriginalQuery = target.dataset.rbOriginalQuery) ? _target_dataset_rbOriginalQuery : getQueryFromUrl(url);
|
|
142
|
+
var isAsync = Boolean(target.dataset.rbAsync) || target.async || target.defer;
|
|
143
|
+
var attributes = {
|
|
144
|
+
url: getNextRetryUrl(url, domain, nextDomain, existRetryTimes, originalQuery, config),
|
|
145
|
+
times: existRetryTimes + 1,
|
|
146
|
+
crossOrigin: config.crossOrigin,
|
|
147
|
+
isAsync: isAsync,
|
|
148
|
+
originalQuery: originalQuery
|
|
149
|
+
};
|
|
150
|
+
var element = createElement(target, attributes);
|
|
151
|
+
var context1 = {
|
|
152
|
+
times: existRetryTimes,
|
|
153
|
+
domain: domain,
|
|
154
|
+
url: url,
|
|
155
|
+
tagName: tagName,
|
|
156
|
+
isAsyncChunk: false
|
|
157
|
+
};
|
|
158
|
+
if ('function' == typeof config.onRetry) config.onRetry(context1);
|
|
159
|
+
var delayValue = 'function' == typeof config.delay ? config.delay(context1) : config.delay;
|
|
160
|
+
if (delayValue > 0) setTimeout(function() {
|
|
161
|
+
reloadElementResource(target, element, attributes);
|
|
162
|
+
}, delayValue);
|
|
163
|
+
else reloadElementResource(target, element, attributes);
|
|
164
|
+
}
|
|
165
|
+
function load(config, e) {
|
|
166
|
+
var targetInfo = validateTargetInfo(config, e);
|
|
167
|
+
if (false === targetInfo) return;
|
|
168
|
+
var target = targetInfo.target, tagName = targetInfo.tagName, url = targetInfo.url;
|
|
169
|
+
var domain = findCurrentDomain(url, config);
|
|
170
|
+
var retryTimes = Number(target.dataset.rbRetryTimes) || 0;
|
|
171
|
+
if (0 === retryTimes) return;
|
|
172
|
+
if ('function' == typeof config.onSuccess) {
|
|
170
173
|
var context = {
|
|
171
|
-
times:
|
|
174
|
+
times: retryTimes,
|
|
172
175
|
domain: domain,
|
|
173
176
|
url: url,
|
|
174
177
|
tagName: tagName,
|
|
175
178
|
isAsyncChunk: false
|
|
176
179
|
};
|
|
177
|
-
config.
|
|
178
|
-
}
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
// Then, we will start to retry
|
|
182
|
-
var nextDomain = findNextDomain(domain, config.domain);
|
|
183
|
-
var _target_dataset_rbOriginalQuery;
|
|
184
|
-
// if the initial request is "/static/js/async/src_Hello_tsx.js?q=1", retry url would be "/static/js/async/src_Hello_tsx.js?q=1&retry=1"
|
|
185
|
-
var originalQuery = (_target_dataset_rbOriginalQuery = target.dataset.rbOriginalQuery) !== null && _target_dataset_rbOriginalQuery !== void 0 ? _target_dataset_rbOriginalQuery : getQueryFromUrl(url);
|
|
186
|
-
// this function is the same as async chunk retry
|
|
187
|
-
function getUrlRetryQuery(existRetryTimes) {
|
|
188
|
-
if (config.addQuery === true) {
|
|
189
|
-
return originalQuery !== '' ? "".concat(originalQuery, "&retry=").concat(existRetryTimes) : "?retry=".concat(existRetryTimes);
|
|
190
|
-
}
|
|
191
|
-
if (typeof config.addQuery === 'function') {
|
|
192
|
-
return config.addQuery({
|
|
193
|
-
times: existRetryTimes,
|
|
194
|
-
originalQuery: originalQuery
|
|
195
|
-
});
|
|
180
|
+
config.onSuccess(context);
|
|
196
181
|
}
|
|
197
|
-
return '';
|
|
198
|
-
}
|
|
199
|
-
// this function is the same as async chunk retry
|
|
200
|
-
function getNextRetryUrl(currRetryUrl, domain, nextDomain, existRetryTimes) {
|
|
201
|
-
return cleanUrl(currRetryUrl.replace(domain, nextDomain)) + getUrlRetryQuery(existRetryTimes + 1);
|
|
202
|
-
}
|
|
203
|
-
var isAsync = Boolean(target.dataset.rbAsync) || target.async || target.defer;
|
|
204
|
-
var attributes = {
|
|
205
|
-
url: getNextRetryUrl(url, domain, nextDomain, existRetryTimes),
|
|
206
|
-
times: existRetryTimes + 1,
|
|
207
|
-
crossOrigin: config.crossOrigin,
|
|
208
|
-
isAsync: isAsync,
|
|
209
|
-
originalQuery: originalQuery
|
|
210
|
-
};
|
|
211
|
-
var element = createElement(target, attributes);
|
|
212
|
-
var context1 = {
|
|
213
|
-
times: existRetryTimes,
|
|
214
|
-
domain: domain,
|
|
215
|
-
url: url,
|
|
216
|
-
tagName: tagName,
|
|
217
|
-
isAsyncChunk: false
|
|
218
|
-
};
|
|
219
|
-
if (typeof config.onRetry === 'function') {
|
|
220
|
-
config.onRetry(context1);
|
|
221
|
-
}
|
|
222
|
-
var _config_delay;
|
|
223
|
-
// Delay retry
|
|
224
|
-
var delayValue = typeof config.delay === 'function' ? config.delay(context1) : (_config_delay = config.delay) !== null && _config_delay !== void 0 ? _config_delay : 0;
|
|
225
|
-
if (delayValue > 0) {
|
|
226
|
-
setTimeout(function() {
|
|
227
|
-
reloadElementResource(target, element, attributes);
|
|
228
|
-
}, delayValue);
|
|
229
|
-
} else {
|
|
230
|
-
reloadElementResource(target, element, attributes);
|
|
231
182
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
var targetInfo = validateTargetInfo(config, e);
|
|
235
|
-
if (targetInfo === false) {
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
var target = targetInfo.target, tagName = targetInfo.tagName, url = targetInfo.url;
|
|
239
|
-
var domain = findCurrentDomain(url, config.domain);
|
|
240
|
-
var retryTimes = Number(target.dataset.rbRetryTimes) || 0;
|
|
241
|
-
if (retryTimes === 0) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
if (typeof config.onSuccess === 'function') {
|
|
245
|
-
var context = {
|
|
246
|
-
times: retryTimes,
|
|
247
|
-
domain: domain,
|
|
248
|
-
url: url,
|
|
249
|
-
tagName: tagName,
|
|
250
|
-
isAsyncChunk: false
|
|
251
|
-
};
|
|
252
|
-
config.onSuccess(context);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function resourceMonitor(error, success) {
|
|
256
|
-
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
|
|
257
|
-
document.addEventListener('error', function(e) {
|
|
258
|
-
if (e && _instanceof(e.target, Element)) {
|
|
259
|
-
error(e);
|
|
260
|
-
}
|
|
261
|
-
}, true);
|
|
262
|
-
document.addEventListener('load', function(e) {
|
|
263
|
-
if (e && _instanceof(e.target, Element)) {
|
|
264
|
-
success(e);
|
|
265
|
-
}
|
|
266
|
-
}, true);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
// init global variables shared with async chunk
|
|
270
|
-
if (typeof window !== 'undefined' && !window.__RB_ASYNC_CHUNKS__) {
|
|
271
|
-
window.__RB_ASYNC_CHUNKS__ = {};
|
|
272
|
-
}
|
|
273
|
-
// Bind event in window
|
|
274
|
-
try {
|
|
275
|
-
var config = __RUNTIME_GLOBALS_OPTIONS__;
|
|
276
|
-
resourceMonitor(function(e) {
|
|
277
|
-
try {
|
|
278
|
-
retry(config, e);
|
|
279
|
-
} catch (err) {
|
|
280
|
-
console.error('retry error captured', err);
|
|
281
|
-
}
|
|
282
|
-
}, function(e) {
|
|
183
|
+
function registerInitialChunkRetry() {
|
|
184
|
+
if ('undefined' != typeof window && !window.__RB_ASYNC_CHUNKS__) window.__RB_ASYNC_CHUNKS__ = {};
|
|
283
185
|
try {
|
|
284
|
-
|
|
186
|
+
var config = __RETRY_OPTIONS__;
|
|
187
|
+
if ('undefined' != typeof window && void 0 !== window.document) {
|
|
188
|
+
document.addEventListener('error', function(e) {
|
|
189
|
+
if (e && _instanceof(e.target, Element)) try {
|
|
190
|
+
retry(config, e);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.error('retry error captured', err);
|
|
193
|
+
}
|
|
194
|
+
}, true);
|
|
195
|
+
document.addEventListener('load', function(e) {
|
|
196
|
+
if (e && _instanceof(e.target, Element)) try {
|
|
197
|
+
load(config, e);
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.error('load error captured', err);
|
|
200
|
+
}
|
|
201
|
+
}, true);
|
|
202
|
+
}
|
|
285
203
|
} catch (err) {
|
|
286
|
-
console.error('
|
|
204
|
+
console.error('monitor error captured', err);
|
|
287
205
|
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
206
|
+
}
|
|
207
|
+
registerInitialChunkRetry();
|
|
208
|
+
})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e,t){return null!=t&&"undefined"!=typeof Symbol&&t[Symbol.hasInstance]?!!t[Symbol.hasInstance](e):e instanceof t}var
|
|
1
|
+
!function(){"use strict";function e(e,t){for(var n=t.domain,r="",i=0;i<n.length;i++)if(-1!==e.indexOf(n[i])){r=n[i];break}return r||window.origin}var t=/[?#].*$/;function n(e,t){return null!=t&&"undefined"!=typeof Symbol&&t[Symbol.hasInstance]?!!t[Symbol.hasInstance](e):e instanceof t}var r={link:HTMLLinkElement,script:HTMLScriptElement,img:HTMLImageElement};function i(e,t){var i=t.target,a=i.tagName.toLocaleLowerCase(),o=e.type,c=n(i,HTMLScriptElement)||n(i,HTMLImageElement)?i.src:n(i,HTMLLinkElement)?i.href:null;return!!a&&-1!==o.indexOf(a)&&!!r[a]&&!!n(i,r[a])&&!!c&&{target:i,tagName:a,url:c}}function a(e,t,r){n(e,HTMLScriptElement)&&(r.isAsync?document.body.appendChild(t.element):console.warn("[@rsbuild/plugin-assets-retry] ","load sync script failed, for security only async/defer script can be retried",e)),n(e,HTMLLinkElement)&&document.getElementsByTagName("head")[0].appendChild(t.element),n(e,HTMLImageElement)&&(e.src=r.url,e.dataset.rbRetryTimes=String(r.times),e.dataset.rbOriginalQuery=String(r.originalQuery))}"undefined"==typeof window||window.__RB_ASYNC_CHUNKS__||(window.__RB_ASYNC_CHUNKS__={});try{var o=__RETRY_OPTIONS__;"undefined"!=typeof window&&void 0!==window.document&&(document.addEventListener("error",function(r){if(r&&n(r.target,Element))try{!function(r,o){var c,s=i(r,o);if(!1!==s){var l=s.target,u=s.tagName,d=s.url;if(!("undefined"!=typeof window&&Object.keys(window.__RB_ASYNC_CHUNKS__||{}).some(function(e){return -1!==d.indexOf(e)}))){var m=r.test;if(m){if("string"==typeof m){var y=new RegExp(m);m=function(e){return y.test(e)}}if("function"!=typeof m||!m(d))return}var f=e(d,r);if(!r.domain||!(r.domain.length>0)||-1!==r.domain.indexOf(f)){var g,p,b,v,_=Number(l.dataset.rbRetryTimes)||0;if(_===r.max){"function"==typeof r.onFail&&r.onFail({times:_,domain:f,url:d,tagName:u,isAsyncChunk:!1});return}var E=(g=r.domain,p=e(f,r),b=g.indexOf(p),g[(b+1)%g.length]||f),S=null!=(c=l.dataset.rbOriginalQuery)?c:(v=d.split("?")[1])?"?".concat(v.split("#")[0]):"",T=!!l.dataset.rbAsync||l.async||l.defer,w={url:function(e,n,r,i,a,o){var c;return e.replace(n,r).replace(t,"")+(c=i+1,!0===o.addQuery?""!==a?"".concat(a,"&retry=").concat(c):"?retry=".concat(c):"function"==typeof o.addQuery?o.addQuery({times:c,originalQuery:a}):"")}(d,f,E,_,S,r),times:_+1,crossOrigin:r.crossOrigin,isAsync:T,originalQuery:S},L=function(e,t){var r=!0===t.crossOrigin?"anonymous":t.crossOrigin,i=r?'crossorigin="'.concat(r,'"'):"",a=t.times?'data-rb-retry-times="'.concat(t.times,'"'):"",o=t.originalQuery?'data-rb-original-query="'.concat(t.originalQuery,'"'):"",c=t.isAsync?"data-rb-async":"";if(n(e,HTMLScriptElement)){var s=document.createElement("script");return s.src=t.url,r&&(s.crossOrigin=r),t.times&&(s.dataset.rbRetryTimes=String(t.times)),t.isAsync&&(s.dataset.rbAsync=""),void 0!==t.originalQuery&&(s.dataset.rbOriginalQuery=t.originalQuery),{element:s,str:'<script src="'.concat(t.url,'" ').concat(i," ").concat(a," ").concat(c," ").concat(o,">")+"<\/script>"}}if(n(e,HTMLLinkElement)){var l=document.createElement("link");return l.rel=e.rel||"stylesheet",e.as&&(l.as=e.as),l.href=t.url,r&&(l.crossOrigin=r),t.times&&(l.dataset.rbRetryTimes=String(t.times)),void 0!==t.originalQuery&&(l.dataset.rbOriginalQuery=t.originalQuery),{element:l,str:'<link rel="'.concat(l.rel,'" href="').concat(t.url,'" ').concat(i," ").concat(a," ").concat(l.as?'as="'.concat(l.as,'"'):""," ").concat(o,"></link>")}}}(l,w),h={times:_,domain:f,url:d,tagName:u,isAsyncChunk:!1};"function"==typeof r.onRetry&&r.onRetry(h);var O="function"==typeof r.delay?r.delay(h):r.delay;O>0?setTimeout(function(){a(l,L,w)},O):a(l,L,w)}}}}(o,r)}catch(e){console.error("retry error captured",e)}},!0),document.addEventListener("load",function(t){if(t&&n(t.target,Element))try{!function(t,n){var r=i(t,n);if(!1!==r){var a=r.target,o=r.tagName,c=r.url,s=e(c,t),l=Number(a.dataset.rbRetryTimes)||0;0!==l&&"function"==typeof t.onSuccess&&t.onSuccess({times:l,domain:s,url:c,tagName:o,isAsyncChunk:!1})}}(o,t)}catch(e){console.error("load error captured",e)}},!0))}catch(e){console.error("monitor error captured",e)}}();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsbuild/plugin-assets-retry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"repository": "https://github.com/rspack-contrib/rsbuild-plugin-assets-retry",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"pre-commit": "npm run lint:write"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
+
"@microsoft/api-extractor": "^7.52.8",
|
|
31
32
|
"@biomejs/biome": "^1.9.4",
|
|
32
33
|
"@playwright/test": "^1.53.0",
|
|
33
34
|
"@rsbuild/core": "^1.3.22",
|
|
@@ -53,7 +54,7 @@
|
|
|
53
54
|
"optional": true
|
|
54
55
|
}
|
|
55
56
|
},
|
|
56
|
-
"packageManager": "pnpm@
|
|
57
|
+
"packageManager": "pnpm@10.12.2",
|
|
57
58
|
"publishConfig": {
|
|
58
59
|
"access": "public",
|
|
59
60
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { type Rspack } from '@rsbuild/core';
|
|
2
|
-
import type { PluginAssetsRetryOptions, RuntimeRetryOptions } from './types.js';
|
|
3
|
-
declare class AsyncChunkRetryPlugin implements Rspack.RspackPluginInstance {
|
|
4
|
-
readonly name = "ASYNC_CHUNK_RETRY_PLUGIN";
|
|
5
|
-
readonly options: PluginAssetsRetryOptions & {
|
|
6
|
-
isRspack: boolean;
|
|
7
|
-
};
|
|
8
|
-
readonly runtimeOptions: RuntimeRetryOptions;
|
|
9
|
-
constructor(options: PluginAssetsRetryOptions & {
|
|
10
|
-
isRspack: boolean;
|
|
11
|
-
});
|
|
12
|
-
getRawRuntimeRetryCode(): string;
|
|
13
|
-
apply(compiler: Rspack.Compiler): void;
|
|
14
|
-
}
|
|
15
|
-
export { AsyncChunkRetryPlugin };
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
type ChunkId = string;
|
|
2
|
-
type ChunkFilename = string;
|
|
3
|
-
type ChunkSrcUrl = string;
|
|
4
|
-
type EnsureChunk = (chunkId: ChunkId, ...args: unknown[]) => Promise<unknown>;
|
|
5
|
-
type LoadScript = (url: ChunkSrcUrl, done: unknown, key: string, chunkId: ChunkId, ...args: unknown[]) => void;
|
|
6
|
-
type LoadStyleSheet = (href: string, chunkId: ChunkId) => string;
|
|
7
|
-
declare global {
|
|
8
|
-
var __RUNTIME_GLOBALS_REQUIRE__: unknown;
|
|
9
|
-
var __RUNTIME_GLOBALS_ENSURE_CHUNK__: EnsureChunk;
|
|
10
|
-
var __RUNTIME_GLOBALS_GET_CHUNK_SCRIPT_FILENAME__: (chunkId: ChunkId, ...args: unknown[]) => string;
|
|
11
|
-
var __RUNTIME_GLOBALS_GET_CSS_FILENAME__: ((chunkId: ChunkId, ...args: unknown[]) => string) | undefined;
|
|
12
|
-
var __RUNTIME_GLOBALS_GET_MINI_CSS_EXTRACT_FILENAME__: ((chunkId: ChunkId, ...args: unknown[]) => string) | undefined;
|
|
13
|
-
var __RUNTIME_GLOBALS_LOAD_SCRIPT__: LoadScript;
|
|
14
|
-
var __RUNTIME_GLOBALS_RSBUILD_LOAD_STYLESHEET__: LoadStyleSheet;
|
|
15
|
-
var __RUNTIME_GLOBALS_PUBLIC_PATH__: string;
|
|
16
|
-
var __RETRY_OPTIONS__: RuntimeRetryOptions;
|
|
17
|
-
var __RB_ASYNC_CHUNKS__: Record<ChunkFilename, boolean>;
|
|
18
|
-
}
|
|
19
|
-
export {};
|
package/dist/types.d.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { CrossOrigin } from '@rsbuild/core';
|
|
2
|
-
export type PluginAssetsRetryOptions = {
|
|
3
|
-
/**
|
|
4
|
-
* The maximum number of retries for a single asset.
|
|
5
|
-
* @default 3
|
|
6
|
-
*/
|
|
7
|
-
max?: number;
|
|
8
|
-
/**
|
|
9
|
-
* Used to specify the HTML tag types that need to be retried.
|
|
10
|
-
* @default ['script', 'link', 'img']
|
|
11
|
-
*/
|
|
12
|
-
type?: string[];
|
|
13
|
-
/**
|
|
14
|
-
* The test function of the asset to be retried.
|
|
15
|
-
*/
|
|
16
|
-
test?: string | ((url: string) => boolean);
|
|
17
|
-
/**
|
|
18
|
-
* Specifies the retry domain when assets fail to load.
|
|
19
|
-
*/
|
|
20
|
-
domain?: string[];
|
|
21
|
-
/**
|
|
22
|
-
* Set the `crossorigin` attribute for tags.
|
|
23
|
-
* @default config.html.crossorigin
|
|
24
|
-
*/
|
|
25
|
-
crossOrigin?: boolean | CrossOrigin;
|
|
26
|
-
/**
|
|
27
|
-
* The callback function when the asset is failed to be retried.
|
|
28
|
-
*/
|
|
29
|
-
onFail?: (context: AssetsRetryHookContext) => void;
|
|
30
|
-
/**
|
|
31
|
-
* The callback function when the asset is being retried.
|
|
32
|
-
*/
|
|
33
|
-
onRetry?: (context: AssetsRetryHookContext) => void;
|
|
34
|
-
/**
|
|
35
|
-
* The callback function when the asset is successfully retried.
|
|
36
|
-
*/
|
|
37
|
-
onSuccess?: (context: AssetsRetryHookContext) => void;
|
|
38
|
-
/**
|
|
39
|
-
* The function to add query parameters to the URL of the asset being retried.
|
|
40
|
-
* @param times e.g: 1 -> 2 -> 3
|
|
41
|
-
* @param originalQuery initial request url's query e.g: <script src="https://cdn.com/a.js?version=1"></script> -> "?version=1"
|
|
42
|
-
* @default false
|
|
43
|
-
* @description true -> hasQuery(originalQuery) ? `${getQuery(originalQuery)}&retry=${existRetryTimes}` : `?retry=${existRetryTimes}`
|
|
44
|
-
*/
|
|
45
|
-
addQuery?: boolean | ((context: {
|
|
46
|
-
times: number;
|
|
47
|
-
originalQuery: string;
|
|
48
|
-
}) => string);
|
|
49
|
-
/**
|
|
50
|
-
* Whether to inline the runtime JavaScript code of Assets Retry plugin into the HTML file.
|
|
51
|
-
* @default true
|
|
52
|
-
*/
|
|
53
|
-
inlineScript?: boolean;
|
|
54
|
-
/**
|
|
55
|
-
* Whether to minify the runtime JavaScript code of Assets Retry plugin.
|
|
56
|
-
* @default rsbuildConfig.mode === 'production'
|
|
57
|
-
*/
|
|
58
|
-
minify?: boolean;
|
|
59
|
-
/**
|
|
60
|
-
* The delay time between retries. Unit: ms
|
|
61
|
-
* @default 0
|
|
62
|
-
*/
|
|
63
|
-
delay?: number | ((context: AssetsRetryHookContext) => number);
|
|
64
|
-
};
|
|
65
|
-
export type RuntimeRetryOptions = Omit<PluginAssetsRetryOptions, 'inlineScript' | 'minify'>;
|
|
66
|
-
export type AssetsRetryHookContext = {
|
|
67
|
-
url: string;
|
|
68
|
-
times: number;
|
|
69
|
-
domain: string;
|
|
70
|
-
tagName: string;
|
|
71
|
-
isAsyncChunk: boolean;
|
|
72
|
-
};
|