@rsbuild/plugin-assets-retry 0.6.6 → 0.6.8

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/dist/index.d.ts CHANGED
@@ -24,10 +24,6 @@ type PluginAssetsRetryOptions = {
24
24
  * Set the `crossorigin` attribute for tags.
25
25
  */
26
26
  crossOrigin?: boolean | CrossOrigin;
27
- /**
28
- * Whether to inline the runtime JavaScript code of Assets Retry plugin into the HTML file.
29
- */
30
- inlineScript?: boolean;
31
27
  /**
32
28
  * The callback function when the asset is failed to be retried.
33
29
  */
@@ -40,6 +36,10 @@ type PluginAssetsRetryOptions = {
40
36
  * The callback function when the asset is successfully retried.
41
37
  */
42
38
  onSuccess?: (options: AssetsRetryHookContext) => void;
39
+ /**
40
+ * Whether to inline the runtime JavaScript code of Assets Retry plugin into the HTML file.
41
+ */
42
+ inlineScript?: boolean;
43
43
  /**
44
44
  * Whether to minify the runtime JavaScript code of Assets Retry plugin.
45
45
  */
package/dist/index.js CHANGED
@@ -58,12 +58,12 @@ var AssetsRetryPlugin_exports = {};
58
58
  __export(AssetsRetryPlugin_exports, {
59
59
  AssetsRetryPlugin: () => AssetsRetryPlugin
60
60
  });
61
- var import_node_path, import_shared, _retryOptions, AssetsRetryPlugin;
61
+ var import_node_path2, import_shared2, _retryOptions, AssetsRetryPlugin;
62
62
  var init_AssetsRetryPlugin = __esm({
63
63
  "src/AssetsRetryPlugin.ts"() {
64
64
  "use strict";
65
- import_node_path = __toESM(require("path"));
66
- import_shared = require("@rsbuild/shared");
65
+ import_node_path2 = __toESM(require("path"));
66
+ import_shared2 = require("@rsbuild/shared");
67
67
  AssetsRetryPlugin = class {
68
68
  constructor(options) {
69
69
  __publicField(this, "name");
@@ -77,7 +77,7 @@ var init_AssetsRetryPlugin = __esm({
77
77
  distDir,
78
78
  HtmlPlugin,
79
79
  inlineScript = true,
80
- minify = process.env.NODE_ENV === "production",
80
+ minify,
81
81
  ...retryOptions
82
82
  } = options;
83
83
  this.name = "AssetsRetryPlugin";
@@ -89,21 +89,23 @@ var init_AssetsRetryPlugin = __esm({
89
89
  this.minify = minify;
90
90
  }
91
91
  async getRetryCode() {
92
- const { default: serialize } = await import("serialize-javascript");
93
- const runtimeFilePath = import_node_path.default.join(
92
+ const { default: serialize2 } = await import("serialize-javascript");
93
+ const filename = "initialChunkRetry";
94
+ const runtimeFilePath = import_node_path2.default.join(
94
95
  __dirname,
95
- this.minify ? "runtime.min.js" : "runtime.js"
96
+ "runtime",
97
+ this.minify ? `${filename}.min.js` : `${filename}.js`
96
98
  );
97
- const runtimeCode = await import_shared.fse.readFile(runtimeFilePath, "utf-8");
98
- return `(function(){${runtimeCode};init(${serialize(
99
+ const runtimeCode = await import_shared2.fse.readFile(runtimeFilePath, "utf-8");
100
+ return `(function(){${runtimeCode};init(${serialize2(
99
101
  __privateGet(this, _retryOptions)
100
102
  )});})()`;
101
103
  }
102
104
  async getScriptPath() {
103
105
  if (!this.scriptPath) {
104
- this.scriptPath = import_node_path.default.posix.join(
106
+ this.scriptPath = import_node_path2.default.posix.join(
105
107
  this.distDir,
106
- `assets-retry.${"0.6.6"}.js`
108
+ `assets-retry.${"0.6.8"}.js`
107
109
  );
108
110
  }
109
111
  return this.scriptPath;
@@ -134,15 +136,15 @@ var init_AssetsRetryPlugin = __esm({
134
136
  this.HtmlPlugin.getHooks(compilation).alterAssetTagGroups.tapPromise(
135
137
  this.name,
136
138
  async (data) => {
137
- const scriptTag = (0, import_shared.generateScriptTag)();
139
+ const scriptTag = (0, import_shared2.generateScriptTag)();
138
140
  if (this.inlineScript) {
139
141
  data.headTags.unshift({
140
142
  ...scriptTag,
141
143
  innerHTML: await this.getRetryCode()
142
144
  });
143
145
  } else {
144
- const publicPath = (0, import_shared.getPublicPathFromCompiler)(compiler);
145
- const url = (0, import_shared.withPublicPath)(await this.getScriptPath(), publicPath);
146
+ const publicPath = (0, import_shared2.getPublicPathFromCompiler)(compiler);
147
+ const url = (0, import_shared2.withPublicPath)(await this.getScriptPath(), publicPath);
146
148
  data.headTags.unshift({
147
149
  ...scriptTag,
148
150
  attributes: {
@@ -167,32 +169,115 @@ __export(src_exports, {
167
169
  pluginAssetsRetry: () => pluginAssetsRetry
168
170
  });
169
171
  module.exports = __toCommonJS(src_exports);
170
- var import_shared2 = require("@rsbuild/shared");
172
+ var import_shared3 = require("@rsbuild/shared");
173
+
174
+ // src/AsyncChunkRetryPlugin.ts
175
+ var import_node_path = __toESM(require("path"));
176
+ var import_core = require("@rsbuild/core");
177
+ var import_shared = require("@rsbuild/shared");
178
+ var import_serialize_javascript = __toESM(require("serialize-javascript"));
179
+ function appendWebpackScript(module2, appendSource) {
180
+ try {
181
+ const originSource = module2.getGeneratedCode();
182
+ module2.getGeneratedCode = () => `${originSource}
183
+ ${appendSource}`;
184
+ } catch (err) {
185
+ console.error("Failed to modify Webpack RuntimeModule");
186
+ throw err;
187
+ }
188
+ }
189
+ function appendRspackScript(module2, appendSource) {
190
+ try {
191
+ const source = module2.source.source.toString();
192
+ module2.source.source = Buffer.from(`${source}
193
+ ${appendSource}`, "utf-8");
194
+ } catch (err) {
195
+ console.error("Failed to modify Rspack RuntimeModule");
196
+ throw err;
197
+ }
198
+ }
199
+ var AsyncChunkRetryPlugin = class {
200
+ constructor(options) {
201
+ __publicField(this, "name", "ASYNC_CHUNK_RETRY_PLUGIN");
202
+ __publicField(this, "options");
203
+ __publicField(this, "runtimeOptions");
204
+ this.options = options;
205
+ this.runtimeOptions = (0, import_shared.pick)(options, [
206
+ "domain",
207
+ "max",
208
+ "onRetry",
209
+ "onSuccess",
210
+ "onFail",
211
+ "test"
212
+ ]);
213
+ }
214
+ getRawRuntimeRetryCode() {
215
+ const { RuntimeGlobals } = import_core.rspack;
216
+ const filename = "asyncChunkRetry";
217
+ const runtimeFilePath = import_node_path.default.join(
218
+ __dirname,
219
+ "runtime",
220
+ this.options.minify ? `${filename}.min.js` : `${filename}.js`
221
+ );
222
+ const rawText = import_shared.fse.readFileSync(runtimeFilePath, "utf-8");
223
+ return rawText.replaceAll("__RUNTIME_GLOBALS_REQUIRE__", RuntimeGlobals.require).replaceAll(
224
+ "__RUNTIME_GLOBALS_ENSURE_CHUNK__",
225
+ RuntimeGlobals.ensureChunk
226
+ ).replaceAll(
227
+ "__RUNTIME_GLOBALS_GET_CHUNK_SCRIPT_FILENAME__",
228
+ RuntimeGlobals.getChunkScriptFilename
229
+ ).replaceAll("__RUNTIME_GLOBALS_PUBLIC_PATH__", RuntimeGlobals.publicPath).replaceAll("__RUNTIME_GLOBALS_LOAD_SCRIPT__", RuntimeGlobals.loadScript).replaceAll("__RETRY_OPTIONS__", (0, import_serialize_javascript.default)(this.runtimeOptions));
230
+ }
231
+ apply(compiler) {
232
+ compiler.hooks.thisCompilation.tap(this.name, (compilation) => {
233
+ compilation.hooks.runtimeModule.tap(this.name, (module2) => {
234
+ const { isRspack } = this.options;
235
+ const constructorName = isRspack ? module2.constructorName : module2.constructor?.name;
236
+ const isPublicPathModule = module2.name === "publicPath" || constructorName === "PublicPathRuntimeModule";
237
+ if (!isPublicPathModule) {
238
+ return;
239
+ }
240
+ const runtimeCode = this.getRawRuntimeRetryCode();
241
+ if (isRspack) {
242
+ appendRspackScript(module2, runtimeCode);
243
+ } else {
244
+ appendWebpackScript(module2, runtimeCode);
245
+ }
246
+ });
247
+ });
248
+ }
249
+ };
250
+
251
+ // src/index.ts
171
252
  var pluginAssetsRetry = (options = {}) => ({
172
253
  name: "rsbuild:assets-retry",
173
254
  setup(api) {
174
- api.modifyBundlerChain(async (chain, { CHAIN_ID, target, HtmlPlugin }) => {
175
- const config = api.getNormalizedConfig();
176
- if (!options || (0, import_shared2.isHtmlDisabled)(config, target)) {
177
- return;
178
- }
179
- const { AssetsRetryPlugin: AssetsRetryPlugin2 } = await Promise.resolve().then(() => (init_AssetsRetryPlugin(), AssetsRetryPlugin_exports));
180
- const distDir = (0, import_shared2.getDistPath)(config, "js");
181
- if (options.crossOrigin === void 0) {
182
- options.crossOrigin = config.html.crossorigin;
183
- }
184
- if (options.minify === void 0) {
185
- const minify = typeof config.output.minify === "boolean" ? config.output.minify : config.output.minify?.js ?? true;
186
- options.minify = minify;
187
- }
188
- chain.plugin(CHAIN_ID.PLUGIN.ASSETS_RETRY).use(AssetsRetryPlugin2, [
189
- {
190
- ...options,
191
- distDir,
192
- HtmlPlugin
255
+ api.modifyBundlerChain(
256
+ async (chain, { CHAIN_ID, target, HtmlPlugin, isProd }) => {
257
+ const config = api.getNormalizedConfig();
258
+ if (!options || (0, import_shared3.isHtmlDisabled)(config, target)) {
259
+ return;
193
260
  }
194
- ]);
195
- });
261
+ const { AssetsRetryPlugin: AssetsRetryPlugin2 } = await Promise.resolve().then(() => (init_AssetsRetryPlugin(), AssetsRetryPlugin_exports));
262
+ const distDir = (0, import_shared3.getDistPath)(config, "js");
263
+ if (options.crossOrigin === void 0) {
264
+ options.crossOrigin = config.html.crossorigin;
265
+ }
266
+ if (options.minify === void 0) {
267
+ const minify = typeof config.output.minify === "boolean" ? config.output.minify : config.output.minify?.js;
268
+ options.minify = minify && isProd;
269
+ }
270
+ chain.plugin(CHAIN_ID.PLUGIN.ASSETS_RETRY).use(AssetsRetryPlugin2, [
271
+ {
272
+ ...options,
273
+ distDir,
274
+ HtmlPlugin
275
+ }
276
+ ]);
277
+ const isRspack = api.context.bundlerType === "rspack";
278
+ chain.plugin(CHAIN_ID.PLUGIN.ASYNC_CHUNK_RETRY).use(AsyncChunkRetryPlugin, [{ ...options, isRspack }]);
279
+ }
280
+ );
196
281
  }
197
282
  });
198
283
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.mjs CHANGED
@@ -34,12 +34,12 @@ var __privateSet = (obj, member, value, setter) => {
34
34
  return value;
35
35
  };
36
36
 
37
- // ../../node_modules/.pnpm/@modern-js+module-tools@2.49.0_eslint@8.57.0_typescript@5.4.5/node_modules/@modern-js/module-tools/shims/esm.js
37
+ // ../../node_modules/.pnpm/@modern-js+module-tools@2.49.2_eslint@8.57.0_typescript@5.4.5/node_modules/@modern-js/module-tools/shims/esm.js
38
38
  import { fileURLToPath } from "url";
39
39
  import path from "path";
40
40
  var getFilename, getDirname, __dirname;
41
41
  var init_esm = __esm({
42
- "../../node_modules/.pnpm/@modern-js+module-tools@2.49.0_eslint@8.57.0_typescript@5.4.5/node_modules/@modern-js/module-tools/shims/esm.js"() {
42
+ "../../node_modules/.pnpm/@modern-js+module-tools@2.49.2_eslint@8.57.0_typescript@5.4.5/node_modules/@modern-js/module-tools/shims/esm.js"() {
43
43
  "use strict";
44
44
  getFilename = () => fileURLToPath(import.meta.url);
45
45
  getDirname = () => path.dirname(getFilename());
@@ -52,9 +52,9 @@ var AssetsRetryPlugin_exports = {};
52
52
  __export(AssetsRetryPlugin_exports, {
53
53
  AssetsRetryPlugin: () => AssetsRetryPlugin
54
54
  });
55
- import path2 from "path";
55
+ import path3 from "path";
56
56
  import {
57
- fse,
57
+ fse as fse2,
58
58
  generateScriptTag,
59
59
  getPublicPathFromCompiler,
60
60
  withPublicPath
@@ -77,7 +77,7 @@ var init_AssetsRetryPlugin = __esm({
77
77
  distDir,
78
78
  HtmlPlugin,
79
79
  inlineScript = true,
80
- minify = process.env.NODE_ENV === "production",
80
+ minify,
81
81
  ...retryOptions
82
82
  } = options;
83
83
  this.name = "AssetsRetryPlugin";
@@ -89,21 +89,23 @@ var init_AssetsRetryPlugin = __esm({
89
89
  this.minify = minify;
90
90
  }
91
91
  async getRetryCode() {
92
- const { default: serialize } = await import("serialize-javascript");
93
- const runtimeFilePath = path2.join(
92
+ const { default: serialize2 } = await import("serialize-javascript");
93
+ const filename = "initialChunkRetry";
94
+ const runtimeFilePath = path3.join(
94
95
  __dirname,
95
- this.minify ? "runtime.min.js" : "runtime.js"
96
+ "runtime",
97
+ this.minify ? `${filename}.min.js` : `${filename}.js`
96
98
  );
97
- const runtimeCode = await fse.readFile(runtimeFilePath, "utf-8");
98
- return `(function(){${runtimeCode};init(${serialize(
99
+ const runtimeCode = await fse2.readFile(runtimeFilePath, "utf-8");
100
+ return `(function(){${runtimeCode};init(${serialize2(
99
101
  __privateGet(this, _retryOptions)
100
102
  )});})()`;
101
103
  }
102
104
  async getScriptPath() {
103
105
  if (!this.scriptPath) {
104
- this.scriptPath = path2.posix.join(
106
+ this.scriptPath = path3.posix.join(
105
107
  this.distDir,
106
- `assets-retry.${"0.6.6"}.js`
108
+ `assets-retry.${"0.6.8"}.js`
107
109
  );
108
110
  }
109
111
  return this.scriptPath;
@@ -164,31 +166,115 @@ var init_AssetsRetryPlugin = __esm({
164
166
  // src/index.ts
165
167
  init_esm();
166
168
  import { getDistPath, isHtmlDisabled } from "@rsbuild/shared";
169
+
170
+ // src/AsyncChunkRetryPlugin.ts
171
+ init_esm();
172
+ import path2 from "path";
173
+ import { rspack } from "@rsbuild/core";
174
+ import { fse, pick } from "@rsbuild/shared";
175
+ import serialize from "serialize-javascript";
176
+ function appendWebpackScript(module, appendSource) {
177
+ try {
178
+ const originSource = module.getGeneratedCode();
179
+ module.getGeneratedCode = () => `${originSource}
180
+ ${appendSource}`;
181
+ } catch (err) {
182
+ console.error("Failed to modify Webpack RuntimeModule");
183
+ throw err;
184
+ }
185
+ }
186
+ function appendRspackScript(module, appendSource) {
187
+ try {
188
+ const source = module.source.source.toString();
189
+ module.source.source = Buffer.from(`${source}
190
+ ${appendSource}`, "utf-8");
191
+ } catch (err) {
192
+ console.error("Failed to modify Rspack RuntimeModule");
193
+ throw err;
194
+ }
195
+ }
196
+ var AsyncChunkRetryPlugin = class {
197
+ constructor(options) {
198
+ __publicField(this, "name", "ASYNC_CHUNK_RETRY_PLUGIN");
199
+ __publicField(this, "options");
200
+ __publicField(this, "runtimeOptions");
201
+ this.options = options;
202
+ this.runtimeOptions = pick(options, [
203
+ "domain",
204
+ "max",
205
+ "onRetry",
206
+ "onSuccess",
207
+ "onFail",
208
+ "test"
209
+ ]);
210
+ }
211
+ getRawRuntimeRetryCode() {
212
+ const { RuntimeGlobals } = rspack;
213
+ const filename = "asyncChunkRetry";
214
+ const runtimeFilePath = path2.join(
215
+ __dirname,
216
+ "runtime",
217
+ this.options.minify ? `${filename}.min.js` : `${filename}.js`
218
+ );
219
+ const rawText = fse.readFileSync(runtimeFilePath, "utf-8");
220
+ return rawText.replaceAll("__RUNTIME_GLOBALS_REQUIRE__", RuntimeGlobals.require).replaceAll(
221
+ "__RUNTIME_GLOBALS_ENSURE_CHUNK__",
222
+ RuntimeGlobals.ensureChunk
223
+ ).replaceAll(
224
+ "__RUNTIME_GLOBALS_GET_CHUNK_SCRIPT_FILENAME__",
225
+ RuntimeGlobals.getChunkScriptFilename
226
+ ).replaceAll("__RUNTIME_GLOBALS_PUBLIC_PATH__", RuntimeGlobals.publicPath).replaceAll("__RUNTIME_GLOBALS_LOAD_SCRIPT__", RuntimeGlobals.loadScript).replaceAll("__RETRY_OPTIONS__", serialize(this.runtimeOptions));
227
+ }
228
+ apply(compiler) {
229
+ compiler.hooks.thisCompilation.tap(this.name, (compilation) => {
230
+ compilation.hooks.runtimeModule.tap(this.name, (module) => {
231
+ const { isRspack } = this.options;
232
+ const constructorName = isRspack ? module.constructorName : module.constructor?.name;
233
+ const isPublicPathModule = module.name === "publicPath" || constructorName === "PublicPathRuntimeModule";
234
+ if (!isPublicPathModule) {
235
+ return;
236
+ }
237
+ const runtimeCode = this.getRawRuntimeRetryCode();
238
+ if (isRspack) {
239
+ appendRspackScript(module, runtimeCode);
240
+ } else {
241
+ appendWebpackScript(module, runtimeCode);
242
+ }
243
+ });
244
+ });
245
+ }
246
+ };
247
+
248
+ // src/index.ts
167
249
  var pluginAssetsRetry = (options = {}) => ({
168
250
  name: "rsbuild:assets-retry",
169
251
  setup(api) {
170
- api.modifyBundlerChain(async (chain, { CHAIN_ID, target, HtmlPlugin }) => {
171
- const config = api.getNormalizedConfig();
172
- if (!options || isHtmlDisabled(config, target)) {
173
- return;
174
- }
175
- const { AssetsRetryPlugin: AssetsRetryPlugin2 } = await Promise.resolve().then(() => (init_AssetsRetryPlugin(), AssetsRetryPlugin_exports));
176
- const distDir = getDistPath(config, "js");
177
- if (options.crossOrigin === void 0) {
178
- options.crossOrigin = config.html.crossorigin;
179
- }
180
- if (options.minify === void 0) {
181
- const minify = typeof config.output.minify === "boolean" ? config.output.minify : config.output.minify?.js ?? true;
182
- options.minify = minify;
183
- }
184
- chain.plugin(CHAIN_ID.PLUGIN.ASSETS_RETRY).use(AssetsRetryPlugin2, [
185
- {
186
- ...options,
187
- distDir,
188
- HtmlPlugin
252
+ api.modifyBundlerChain(
253
+ async (chain, { CHAIN_ID, target, HtmlPlugin, isProd }) => {
254
+ const config = api.getNormalizedConfig();
255
+ if (!options || isHtmlDisabled(config, target)) {
256
+ return;
189
257
  }
190
- ]);
191
- });
258
+ const { AssetsRetryPlugin: AssetsRetryPlugin2 } = await Promise.resolve().then(() => (init_AssetsRetryPlugin(), AssetsRetryPlugin_exports));
259
+ const distDir = getDistPath(config, "js");
260
+ if (options.crossOrigin === void 0) {
261
+ options.crossOrigin = config.html.crossorigin;
262
+ }
263
+ if (options.minify === void 0) {
264
+ const minify = typeof config.output.minify === "boolean" ? config.output.minify : config.output.minify?.js;
265
+ options.minify = minify && isProd;
266
+ }
267
+ chain.plugin(CHAIN_ID.PLUGIN.ASSETS_RETRY).use(AssetsRetryPlugin2, [
268
+ {
269
+ ...options,
270
+ distDir,
271
+ HtmlPlugin
272
+ }
273
+ ]);
274
+ const isRspack = api.context.bundlerType === "rspack";
275
+ chain.plugin(CHAIN_ID.PLUGIN.ASYNC_CHUNK_RETRY).use(AsyncChunkRetryPlugin, [{ ...options, isRspack }]);
276
+ }
277
+ );
192
278
  }
193
279
  });
194
280
  export {
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+
3
+ // rsbuild/runtime/async-chunk-retry
4
+
5
+ // e.g: src_AsyncCompTest_tsx
6
+ // e.g: static/js/async/src_AsyncCompTest_tsx.js
7
+ // publicPath + ChunkFilename e.g: http://localhost:3000/static/js/async/src_AsyncCompTest_tsx.js
8
+
9
+ // init retryCollector and nextRetry function
10
+ var config = __RETRY_OPTIONS__;
11
+ var maxRetries = config.max || 3;
12
+ var retryCollector = {};
13
+ function findCurrentDomain(url) {
14
+ var _config$domain;
15
+ var domainList = (_config$domain = config.domain) !== null && _config$domain !== void 0 ? _config$domain : [];
16
+ var domain = '';
17
+ for (var i = 0; i < domainList.length; i++) {
18
+ if (url.indexOf(domainList[i]) !== -1) {
19
+ domain = domainList[i];
20
+ break;
21
+ }
22
+ }
23
+ return domain || url;
24
+ }
25
+ function findNextDomain(url) {
26
+ var _config$domain2;
27
+ var domainList = (_config$domain2 = config.domain) !== null && _config$domain2 !== void 0 ? _config$domain2 : [];
28
+ var currentDomain = findCurrentDomain(url);
29
+ var index = domainList.indexOf(currentDomain);
30
+ return domainList[(index + 1) % domainList.length] || url;
31
+ }
32
+
33
+ // TODO: add option query to onRetry with initial chunk together
34
+ // function getUrlRetryQuery(existRetryTimes: number): string {
35
+ // return `?retry-attempt=${existRetryTimes}`;
36
+ // }
37
+
38
+ function getCurrentRetry(chunkId) {
39
+ return retryCollector[chunkId];
40
+ }
41
+ function initRetry(chunkId) {
42
+ var _config$domain$, _config$domain3;
43
+ var originalScriptFilename = originalGetChunkScriptFilename(chunkId);
44
+ var originalSrcUrl = __RUNTIME_GLOBALS_PUBLIC_PATH__ + originalScriptFilename;
45
+ var nextDomain = (_config$domain$ = (_config$domain3 = config.domain) === null || _config$domain3 === void 0 ? void 0 : _config$domain3[0]) !== null && _config$domain$ !== void 0 ? _config$domain$ : __RUNTIME_GLOBALS_PUBLIC_PATH__;
46
+ var existRetryTimes = 1;
47
+ return {
48
+ existRetryTimes: existRetryTimes,
49
+ nextDomain: nextDomain,
50
+ nextRetryUrl: nextDomain + (nextDomain[nextDomain.length - 1] === '/' ? '' : '/') + originalScriptFilename,
51
+ // + getUrlRetryQuery(existRetryTimes),
52
+
53
+ originalScriptFilename: originalScriptFilename,
54
+ originalSrcUrl: originalSrcUrl
55
+ };
56
+ }
57
+ function nextRetry(chunkId) {
58
+ var currRetry = getCurrentRetry(chunkId);
59
+ var nextRetry;
60
+ if (!currRetry) {
61
+ nextRetry = initRetry(chunkId);
62
+ } else {
63
+ var existRetryTimes = currRetry.existRetryTimes + 1;
64
+ var nextDomain = findNextDomain(currRetry.nextDomain);
65
+ nextRetry = {
66
+ existRetryTimes: existRetryTimes,
67
+ nextDomain: nextDomain,
68
+ nextRetryUrl: nextDomain + (nextDomain[nextDomain.length - 1] === '/' ? '' : '/') + currRetry.originalScriptFilename,
69
+ // + getUrlRetryQuery(existRetryTimes),
70
+
71
+ originalScriptFilename: currRetry.originalScriptFilename,
72
+ originalSrcUrl: currRetry.originalSrcUrl
73
+ };
74
+ }
75
+ retryCollector[chunkId] = nextRetry;
76
+ return nextRetry;
77
+ }
78
+
79
+ // rewrite webpack runtime with nextRetry()
80
+ var originalEnsureChunk = __RUNTIME_GLOBALS_ENSURE_CHUNK__;
81
+ var originalGetChunkScriptFilename = __RUNTIME_GLOBALS_GET_CHUNK_SCRIPT_FILENAME__;
82
+ var originalLoadScript = __RUNTIME_GLOBALS_LOAD_SCRIPT__;
83
+
84
+ // if users want to support es5, add Promise polyfill first https://github.com/webpack/webpack/issues/12877
85
+ function ensureChunk(chunkId) {
86
+ var result = originalEnsureChunk(chunkId);
87
+ var originalScriptFilename = originalGetChunkScriptFilename(chunkId);
88
+
89
+ // mark the async chunk name in the global variables and share it with initial chunk retry
90
+ if (typeof window !== 'undefined' && !window.__RB_ASYNC_CHUNKS__[originalScriptFilename]) {
91
+ window.__RB_ASYNC_CHUNKS__[originalScriptFilename] = true;
92
+ }
93
+ return result.catch(function (error) {
94
+ var _nextRetry = nextRetry(chunkId),
95
+ existRetryTimes = _nextRetry.existRetryTimes,
96
+ originalSrcUrl = _nextRetry.originalSrcUrl,
97
+ nextRetryUrl = _nextRetry.nextRetryUrl,
98
+ nextDomain = _nextRetry.nextDomain;
99
+ var createContext = function createContext(times) {
100
+ return {
101
+ times: times,
102
+ domain: nextDomain,
103
+ url: nextRetryUrl,
104
+ tagName: 'script'
105
+ };
106
+ };
107
+ var context = createContext(existRetryTimes - 1);
108
+ if (existRetryTimes > maxRetries) {
109
+ error.message = "Loading chunk ".concat(chunkId, " from ").concat(originalSrcUrl, " failed after ").concat(maxRetries, " retries: \"").concat(error.message, "\"");
110
+ if (typeof config.onFail === 'function') {
111
+ config.onFail(context);
112
+ }
113
+ throw error;
114
+ }
115
+
116
+ // Filter by config.test and config.domain
117
+ var tester = config.test;
118
+ if (tester) {
119
+ if (typeof tester === 'string') {
120
+ var regexp = new RegExp(tester);
121
+ tester = function tester(str) {
122
+ return regexp.test(str);
123
+ };
124
+ }
125
+ if (typeof tester !== 'function' || !tester(nextRetryUrl)) {
126
+ throw error;
127
+ }
128
+ }
129
+ if (config.domain && config.domain.length > 0 && config.domain.indexOf(nextDomain) === -1) {
130
+ throw error;
131
+ }
132
+
133
+ // Start retry
134
+ if (typeof config.onRetry === 'function') {
135
+ config.onRetry(context);
136
+ }
137
+
138
+ // biome-ignore lint/complexity/useArrowFunction: use function instead of () => {}
139
+ return ensureChunk(chunkId).then(function (result) {
140
+ if (typeof config.onSuccess === 'function') {
141
+ var _getCurrentRetry;
142
+ var _context = createContext(existRetryTimes);
143
+ var _ref = (_getCurrentRetry = getCurrentRetry(chunkId)) !== null && _getCurrentRetry !== void 0 ? _getCurrentRetry : {},
144
+ currRetryTimes = _ref.existRetryTimes;
145
+ if (currRetryTimes === existRetryTimes) {
146
+ config.onSuccess(_context);
147
+ }
148
+ }
149
+ return result;
150
+ });
151
+ });
152
+ }
153
+ function loadScript(originalUrl, done, key, chunkId) {
154
+ var retry = getCurrentRetry(chunkId);
155
+ return originalLoadScript(retry ? retry.nextRetryUrl : originalUrl, done, key, chunkId);
156
+ }
157
+ function registerAsyncChunkRetry() {
158
+ // init global variables shared with async chunk
159
+ if (typeof window !== 'undefined' && !window.__RB_ASYNC_CHUNKS__) {
160
+ window.__RB_ASYNC_CHUNKS__ = {};
161
+ }
162
+ if (typeof __RUNTIME_GLOBALS_REQUIRE__ !== 'undefined') {
163
+ try {
164
+ __RUNTIME_GLOBALS_ENSURE_CHUNK__ = ensureChunk;
165
+ __RUNTIME_GLOBALS_LOAD_SCRIPT__ = loadScript;
166
+ } catch (e) {
167
+ console.error('[@rsbuild/plugin-assets-retry] Register async chunk retry runtime failed', e);
168
+ }
169
+ }
170
+ }
171
+ registerAsyncChunkRetry();
@@ -0,0 +1 @@
1
+ "use strict";var config=__RETRY_OPTIONS__,maxRetries=config.max||3,retryCollector={};function findCurrentDomain(n){for(var i,e=null!==(i=config.domain)&&void 0!==i?i:[],r="",t=0;t<e.length;t++)if(-1!==n.indexOf(e[t])){r=e[t];break}return r||n}function findNextDomain(n){var i,e=null!==(i=config.domain)&&void 0!==i?i:[],r=findCurrentDomain(n),t=e.indexOf(r);return e[(t+1)%e.length]||n}function getCurrentRetry(n){return retryCollector[n]}function initRetry(n){var i,e,r=originalGetChunkScriptFilename(n),t=__RUNTIME_GLOBALS_PUBLIC_PATH__+r,o=null!==(i=null===(e=config.domain)||void 0===e?void 0:e[0])&&void 0!==i?i:__RUNTIME_GLOBALS_PUBLIC_PATH__;return{existRetryTimes:1,nextDomain:o,nextRetryUrl:o+("/"===o[o.length-1]?"":"/")+r,originalScriptFilename:r,originalSrcUrl:t}}function nextRetry(n){var i,e=getCurrentRetry(n);if(e){var r=e.existRetryTimes+1,t=findNextDomain(e.nextDomain);i={existRetryTimes:r,nextDomain:t,nextRetryUrl:t+("/"===t[t.length-1]?"":"/")+e.originalScriptFilename,originalScriptFilename:e.originalScriptFilename,originalSrcUrl:e.originalSrcUrl}}else i=initRetry(n);return retryCollector[n]=i,i}var originalEnsureChunk=__RUNTIME_GLOBALS_ENSURE_CHUNK__,originalGetChunkScriptFilename=__RUNTIME_GLOBALS_GET_CHUNK_SCRIPT_FILENAME__,originalLoadScript=__RUNTIME_GLOBALS_LOAD_SCRIPT__;function ensureChunk(n){var i=originalEnsureChunk(n),e=originalGetChunkScriptFilename(n);return"undefined"==typeof window||window.__RB_ASYNC_CHUNKS__[e]||(window.__RB_ASYNC_CHUNKS__[e]=!0),i.catch((function(i){var e=nextRetry(n),r=e.existRetryTimes,t=e.originalSrcUrl,o=e.nextRetryUrl,_=e.nextDomain,a=function(n){return{times:n,domain:_,url:o,tagName:"script"}},c=a(r-1);if(r>maxRetries)throw i.message="Loading chunk ".concat(n," from ").concat(t," failed after ").concat(maxRetries,' retries: "').concat(i.message,'"'),"function"==typeof config.onFail&&config.onFail(c),i;var u=config.test;if(u){if("string"==typeof u){var f=new RegExp(u);u=function(n){return f.test(n)}}if("function"!=typeof u||!u(o))throw i}if(config.domain&&config.domain.length>0&&-1===config.domain.indexOf(_))throw i;return"function"==typeof config.onRetry&&config.onRetry(c),ensureChunk(n).then((function(i){if("function"==typeof config.onSuccess){var e,t=a(r);(null!==(e=getCurrentRetry(n))&&void 0!==e?e:{}).existRetryTimes===r&&config.onSuccess(t)}return i}))}))}function loadScript(n,i,e,r){var t=getCurrentRetry(r);return originalLoadScript(t?t.nextRetryUrl:n,i,e,r)}function registerAsyncChunkRetry(){if("undefined"==typeof window||window.__RB_ASYNC_CHUNKS__||(window.__RB_ASYNC_CHUNKS__={}),"undefined"!=typeof __RUNTIME_GLOBALS_REQUIRE__)try{__RUNTIME_GLOBALS_ENSURE_CHUNK__=ensureChunk,__RUNTIME_GLOBALS_LOAD_SCRIPT__=loadScript}catch(n){console.error("[@rsbuild/plugin-assets-retry] Register async chunk retry runtime failed",n)}}registerAsyncChunkRetry();
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- // rsbuild/initial-chunk/retry
3
+ // rsbuild/runtime/initial-chunk-retry
4
4
 
5
5
  var TAG_TYPE = {
6
6
  link: HTMLLinkElement,
@@ -120,6 +120,14 @@ function retry(config, e) {
120
120
  var target = targetInfo.target,
121
121
  tagName = targetInfo.tagName,
122
122
  url = targetInfo.url;
123
+
124
+ // If the requested failed chunk is async chunk,skip it, because async chunk will be retried by asyncChunkRetry runtime
125
+ if (typeof window !== 'undefined' && Object.keys(window.__RB_ASYNC_CHUNKS__ || {}).some(function (chunkName) {
126
+ return url.indexOf(chunkName) !== -1;
127
+ })) {
128
+ return;
129
+ }
130
+
123
131
  // Filter by config.test and config.domain
124
132
  var tester = config.test;
125
133
  if (tester) {
@@ -235,6 +243,10 @@ function init(options) {
235
243
  config.domain = config.domain.filter(Boolean);
236
244
  }
237
245
 
246
+ // init global variables shared with async chunk
247
+ if (typeof window !== 'undefined' && !window.__RB_ASYNC_CHUNKS__) {
248
+ window.__RB_ASYNC_CHUNKS__ = {};
249
+ }
238
250
  // Bind event in window
239
251
  try {
240
252
  resourceMonitor(function (e) {
@@ -0,0 +1 @@
1
+ "use strict";var TAG_TYPE={link:HTMLLinkElement,script:HTMLScriptElement,img:HTMLImageElement},TYPES=Object.keys(TAG_TYPE);function findCurrentDomain(e,n){for(var t="",r=0;r<n.length;r++)if(-1!==e.indexOf(n[r])){t=n[r];break}return t||e}function findNextDomain(e,n){var t=findCurrentDomain(e,n),r=n.indexOf(t);return n[(r+1)%n.length]||e}function getRequestUrl(e){return e instanceof HTMLScriptElement||e instanceof HTMLImageElement?e.src:e instanceof HTMLLinkElement?e.href:null}var defaultConfig={max:3,type:TYPES,domain:[],crossOrigin:!1};function validateTargetInfo(e,n){var t,r=n.target,i=null===(t=r.tagName)||void 0===t?void 0:t.toLocaleLowerCase(),a=e.type,o=getRequestUrl(r);return!!(i&&-1!==a.indexOf(i)&&TAG_TYPE[i]&&r instanceof TAG_TYPE[i]&&o)&&{target:r,tagName:i,url:o}}function createElement(e,n){var t=!0===n.crossOrigin?"anonymous":n.crossOrigin,r=t?'crossorigin="'.concat(t,'"'):"",i=n.times?'data-rsbuild-retry-times="'.concat(n.times,'"'):"",a=n.isAsync?"data-rsbuild-async":"";if(e instanceof HTMLScriptElement){var o=document.createElement("script");return o.src=n.url,t&&(o.crossOrigin=t),n.times&&(o.dataset.rsbuildRetryTimes=String(n.times)),n.isAsync&&(o.dataset.rsbuildAsync=""),{element:o,str:'<script src="'.concat(n.url,'" ').concat(r," ").concat(i," ").concat(a,">")+"<\/script>"}}if(e instanceof HTMLLinkElement){var s=document.createElement("link");return s.rel=e.rel||"stylesheet",e.as&&(s.as=e.as),s.href=n.url,t&&(s.crossOrigin=t),n.times&&(s.dataset.rsbuildRetryTimes=String(n.times)),{element:s,str:'<link rel="'.concat(s.rel,'" href="').concat(n.url,'" ').concat(r," ").concat(i," ").concat(s.as?'as="'.concat(s.as,'"'):"","></link>")}}}function reloadElementResource(e,n,t){e instanceof HTMLScriptElement&&(t.isAsync?document.body.appendChild(n.element):document.write(n.str)),e instanceof HTMLLinkElement&&document.getElementsByTagName("head")[0].appendChild(n.element),e instanceof HTMLImageElement&&(e.src=t.url,e.dataset.rsbuildRetryTimes=String(t.times))}function retry(e,n){var t=validateTargetInfo(e,n);if(!1!==t){var r=t.target,i=t.tagName,a=t.url;if("undefined"==typeof window||!Object.keys(window.__RB_ASYNC_CHUNKS__||{}).some((function(e){return-1!==a.indexOf(e)}))){var o=e.test;if(o){if("string"==typeof o){var s=new RegExp(o);o=function(e){return s.test(e)}}if("function"!=typeof o||!o(a))return}var c=findCurrentDomain(a,e.domain);if(!(e.domain&&e.domain.length>0&&-1===e.domain.indexOf(c))){var d=Number(r.dataset.rsbuildRetryTimes)||0;if(d!==e.max){var l=findNextDomain(c,e.domain),m=Boolean(r.dataset.rsbuildAsync)||r.async||r.defer,f={url:a.replace(c,l),times:d+1,crossOrigin:e.crossOrigin,isAsync:m},u=createElement(r,f);if(e.onRetry&&"function"==typeof e.onRetry){var y={times:d,domain:c,url:a,tagName:i};e.onRetry(y)}reloadElementResource(r,u,f)}else if("function"==typeof e.onFail){var g={times:d,domain:c,url:a,tagName:i};e.onFail(g)}}}}}function load(e,n){var t=validateTargetInfo(e,n);if(!1!==t){var r=t.target,i=t.tagName,a=t.url,o=findCurrentDomain(a,e.domain),s=Number(r.dataset.rsbuildRetryTimes)||0;if(0!==s&&"function"==typeof e.onSuccess){var c={times:s,domain:o,url:a,tagName:i};e.onSuccess(c)}}}function resourceMonitor(e,n){"undefined"!=typeof window&&void 0!==window.document&&(document.addEventListener("error",(function(n){n&&n.target instanceof Element&&e(n)}),!0),document.addEventListener("load",(function(e){e&&e.target instanceof Element&&n(e)}),!0))}function init(e){var n={};for(var t in defaultConfig)n[t]=defaultConfig[t];for(var r in e)n[r]=e[r];Array.isArray(n.type)&&0!==n.type.length||(n.type=defaultConfig.type),Array.isArray(n.domain)&&0!==n.domain.length||(n.domain=defaultConfig.domain),Array.isArray(n.domain)&&(n.domain=n.domain.filter(Boolean)),"undefined"==typeof window||window.__RB_ASYNC_CHUNKS__||(window.__RB_ASYNC_CHUNKS__={});try{resourceMonitor((function(e){try{retry(n,e)}catch(e){console.error("retry error captured",e)}}),(function(e){try{load(n,e)}catch(e){console.error("load error captured",e)}}))}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": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "description": "Assets retry plugin for Rsbuild",
5
5
  "homepage": "https://rsbuild.dev",
6
6
  "repository": {
@@ -24,21 +24,21 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "serialize-javascript": "^6.0.2",
27
- "@rsbuild/shared": "0.6.6"
27
+ "@rsbuild/shared": "0.6.8"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@babel/core": "^7.24.4",
31
31
  "@babel/preset-env": "^7.24.4",
32
32
  "@babel/preset-typescript": "^7.24.1",
33
33
  "@types/serialize-javascript": "^5.0.4",
34
- "html-webpack-plugin": "npm:html-rspack-plugin@5.6.2",
35
- "terser": "5.30.3",
34
+ "html-webpack-plugin": "npm:html-rspack-plugin@5.7.0",
35
+ "terser": "5.30.4",
36
36
  "typescript": "^5.4.2",
37
- "@rsbuild/core": "0.6.6",
38
- "@scripts/test-helper": "0.6.6"
37
+ "@rsbuild/core": "0.6.8",
38
+ "@scripts/test-helper": "0.6.8"
39
39
  },
40
40
  "peerDependencies": {
41
- "@rsbuild/core": "^0.6.6"
41
+ "@rsbuild/core": "^0.6.8"
42
42
  },
43
43
  "publishConfig": {
44
44
  "access": "public",
@@ -1 +0,0 @@
1
- "use strict";var TAG_TYPE={link:HTMLLinkElement,script:HTMLScriptElement,img:HTMLImageElement},TYPES=Object.keys(TAG_TYPE);function findCurrentDomain(e,t){for(var n="",r=0;r<t.length;r++)if(-1!==e.indexOf(t[r])){n=t[r];break}return n||e}function findNextDomain(e,t){var n=findCurrentDomain(e,t),r=t.indexOf(n);return t[(r+1)%t.length]||e}function getRequestUrl(e){return e instanceof HTMLScriptElement||e instanceof HTMLImageElement?e.src:e instanceof HTMLLinkElement?e.href:null}var defaultConfig={max:3,type:TYPES,domain:[],crossOrigin:!1};function validateTargetInfo(e,t){var n,r=t.target,i=null===(n=r.tagName)||void 0===n?void 0:n.toLocaleLowerCase(),a=e.type,o=getRequestUrl(r);return!!(i&&-1!==a.indexOf(i)&&TAG_TYPE[i]&&r instanceof TAG_TYPE[i]&&o)&&{target:r,tagName:i,url:o}}function createElement(e,t){var n=!0===t.crossOrigin?"anonymous":t.crossOrigin,r=n?'crossorigin="'.concat(n,'"'):"",i=t.times?'data-rsbuild-retry-times="'.concat(t.times,'"'):"",a=t.isAsync?"data-rsbuild-async":"";if(e instanceof HTMLScriptElement){var o=document.createElement("script");return o.src=t.url,n&&(o.crossOrigin=n),t.times&&(o.dataset.rsbuildRetryTimes=String(t.times)),t.isAsync&&(o.dataset.rsbuildAsync=""),{element:o,str:'<script src="'.concat(t.url,'" ').concat(r," ").concat(i," ").concat(a,">")+"<\/script>"}}if(e instanceof HTMLLinkElement){var s=document.createElement("link");return s.rel=e.rel||"stylesheet",e.as&&(s.as=e.as),s.href=t.url,n&&(s.crossOrigin=n),t.times&&(s.dataset.rsbuildRetryTimes=String(t.times)),{element:s,str:'<link rel="'.concat(s.rel,'" href="').concat(t.url,'" ').concat(r," ").concat(i," ").concat(s.as?'as="'.concat(s.as,'"'):"","></link>")}}}function reloadElementResource(e,t,n){e instanceof HTMLScriptElement&&(n.isAsync?document.body.appendChild(t.element):document.write(t.str)),e instanceof HTMLLinkElement&&document.getElementsByTagName("head")[0].appendChild(t.element),e instanceof HTMLImageElement&&(e.src=n.url,e.dataset.rsbuildRetryTimes=String(n.times))}function retry(e,t){var n=validateTargetInfo(e,t);if(!1!==n){var r=n.target,i=n.tagName,a=n.url,o=e.test;if(o){if("string"==typeof o){var s=new RegExp(o);o=function(e){return s.test(e)}}if("function"!=typeof o||!o(a))return}var c=findCurrentDomain(a,e.domain);if(!(e.domain&&e.domain.length>0&&-1===e.domain.indexOf(c))){var l=Number(r.dataset.rsbuildRetryTimes)||0;if(l!==e.max){var m=findNextDomain(c,e.domain),d=Boolean(r.dataset.rsbuildAsync)||r.async||r.defer,u={url:a.replace(c,m),times:l+1,crossOrigin:e.crossOrigin,isAsync:d},f=createElement(r,u);if(e.onRetry&&"function"==typeof e.onRetry){var g={times:l,domain:c,url:a,tagName:i};e.onRetry(g)}reloadElementResource(r,f,u)}else if("function"==typeof e.onFail){var y={times:l,domain:c,url:a,tagName:i};e.onFail(y)}}}}function load(e,t){var n=validateTargetInfo(e,t);if(!1!==n){var r=n.target,i=n.tagName,a=n.url,o=findCurrentDomain(a,e.domain),s=Number(r.dataset.rsbuildRetryTimes)||0;if(0!==s&&"function"==typeof e.onSuccess){var c={times:s,domain:o,url:a,tagName:i};e.onSuccess(c)}}}function resourceMonitor(e,t){"undefined"!=typeof window&&void 0!==window.document&&(document.addEventListener("error",(function(t){t&&t.target instanceof Element&&e(t)}),!0),document.addEventListener("load",(function(e){e&&e.target instanceof Element&&t(e)}),!0))}function init(e){var t={};for(var n in defaultConfig)t[n]=defaultConfig[n];for(var r in e)t[r]=e[r];Array.isArray(t.type)&&0!==t.type.length||(t.type=defaultConfig.type),Array.isArray(t.domain)&&0!==t.domain.length||(t.domain=defaultConfig.domain),Array.isArray(t.domain)&&(t.domain=t.domain.filter(Boolean));try{resourceMonitor((function(e){try{retry(t,e)}catch(e){console.error("retry error captured",e)}}),(function(e){try{load(t,e)}catch(e){console.error("load error captured",e)}}))}catch(e){console.error("monitor error captured",e)}}