Skip to content

Commit

Permalink
fix(plugin-assets-retry): support output.assetPrefix with absolute …
Browse files Browse the repository at this point in the history
…url (#4129)
  • Loading branch information
SoonIter authored Dec 7, 2024
1 parent 8e9d9be commit 762c3b4
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 50 deletions.
20 changes: 10 additions & 10 deletions e2e/cases/assets/assets-retry/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ test('react ErrorBoundary should catch error when all retries failed', async ({
await gotoPage(page, rsbuild);
const compTestElement = page.locator('#async-comp-test-error');
await expect(compTestElement).toHaveText(
/ChunkLoadError: Loading chunk src_AsyncCompTest_tsx from \/static\/js\/async\/src_AsyncCompTest_tsx\.js failed after 3 retries: "Loading chunk src_AsyncCompTest_tsx failed.*/,
/ChunkLoadError: Loading chunk src_AsyncCompTest_tsx from "static\/js\/async\/src_AsyncCompTest_tsx\.js" failed after 3 retries: "Loading chunk src_AsyncCompTest_tsx failed.*/,
);
const blockedResponseCount = count404Response(
logs,
Expand Down Expand Up @@ -448,7 +448,7 @@ test('onRetry and onSuccess options should work when retrying async chunk succes
await rsbuild.close();
});

test('onRetry and onFail options should work when retrying initial chunk failed', async ({
test('domain, onRetry and onFail options should work when retrying initial chunk failed', async ({
page,
}) => {
const blockedMiddleware = createBlockMiddleware({
Expand All @@ -461,7 +461,7 @@ test('onRetry and onFail options should work when retrying initial chunk failed'
blockedMiddleware,
{
minify: true,
domain: [`http://localhost:${port}`, 'http://a.com', 'http://b.com'],
domain: [`http://localhost:${port}`, 'http://a.com/foo-path', 'http://b.com'],
onRetry(context) {
console.info('onRetry', context);
},
Expand Down Expand Up @@ -497,8 +497,8 @@ test('onRetry and onFail options should work when retrying initial chunk failed'
},
{
times: 1,
domain: 'http://a.com',
url: 'http://a.com/static/js/index.js',
domain: 'http://a.com/foo-path',
url: 'http://a.com/foo-path/static/js/index.js',
tagName: 'script',
isAsyncChunk: false,
},
Expand All @@ -524,7 +524,7 @@ test('onRetry and onFail options should work when retrying initial chunk failed'
await rsbuild.close();
});

test('onRetry and onFail options should work when retrying async chunk failed', async ({
test('domain, onRetry and onFail options should work when retrying async chunk failed', async ({
page,
}) => {
const blockedMiddleware = createBlockMiddleware({
Expand All @@ -537,7 +537,7 @@ test('onRetry and onFail options should work when retrying async chunk failed',
blockedMiddleware,
{
minify: true,
domain: [`http://localhost:${port}`, 'http://a.com', 'http://b.com'],
domain: [`http://localhost:${port}`, 'http://a.com/foo-path', 'http://b.com'],
onRetry(context) {
console.info('onRetry', context);
},
Expand All @@ -558,7 +558,7 @@ test('onRetry and onFail options should work when retrying async chunk failed',
await gotoPage(page, rsbuild);
const compTestElement = page.locator('#async-comp-test-error');
await expect(compTestElement).toHaveText(
/ChunkLoadError: Loading chunk src_AsyncCompTest_tsx from \/static\/js\/async\/src_AsyncCompTest_tsx\.js failed after 3 retries: "Loading chunk src_AsyncCompTest_tsx failed.*/,
/ChunkLoadError: Loading chunk src_AsyncCompTest_tsx from "static\/js\/async\/src_AsyncCompTest_tsx\.js" failed after 3 retries: "Loading chunk src_AsyncCompTest_tsx failed.*/,
);
await delay();

Expand All @@ -577,8 +577,8 @@ test('onRetry and onFail options should work when retrying async chunk failed',
},
{
times: 1,
domain: 'http://a.com',
url: 'http://a.com/static/js/async/src_AsyncCompTest_tsx.js',
domain: 'http://a.com/foo-path',
url: 'http://a.com/foo-path/static/js/async/src_AsyncCompTest_tsx.js',
tagName: 'script',
isAsyncChunk: true,
},
Expand Down
67 changes: 30 additions & 37 deletions packages/plugin-assets-retry/src/runtime/asyncChunkRetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Retry = {
nextRetryUrl: ChunkSrcUrl;
originalScriptFilename: ChunkFilename;
originalSrcUrl: ChunkSrcUrl;
originalQuery: string;
};

type RetryCollector = Record<ChunkId, Record<number, Retry>>;
Expand Down Expand Up @@ -99,37 +100,16 @@ function getUrlRetryQuery(
return '';
}

function removeDomainFromUrl(url: string): string {
const protocolStartIndex = url.indexOf('//');

// case /app/main/static/js/index.js
if (protocolStartIndex === -1 && url.startsWith('/')) {
return url;
}

// case "//cdn.com/app/main/static/js/index.js"
// case "http://cdn.com/app/main/static/js/index.js"
const protocolEndIndex = protocolStartIndex + 2;
const pathStartIndex = url.indexOf('/', protocolEndIndex);

return url.slice(pathStartIndex);
}

// "http://cdn.com/app/main/static/js/index.js?query=1#hash" -> "/app/main/static/js/index.js"
function getAbsolutePathFromUrl(url: string): string {
return cleanUrl(removeDomainFromUrl(url));
}

function getNextRetryUrl(
existRetryTimes: number,
currRetryUrl: string,
domain: string,
nextDomain: string,
originalSrcUrl: string,
existRetryTimes: number,
originalQuery: string,
) {
const absolutePath = getAbsolutePathFromUrl(originalSrcUrl);
return (
nextDomain +
absolutePath +
getUrlRetryQuery(existRetryTimes, getQueryFromUrl(originalSrcUrl))
cleanUrl(currRetryUrl.replace(domain, nextDomain)) +
getUrlRetryQuery(existRetryTimes + 1, originalQuery)
);
}

Expand All @@ -145,18 +125,28 @@ function getCurrentRetry(
function initRetry(chunkId: string): Retry {
const originalScriptFilename = originalGetChunkScriptFilename(chunkId);

const originalSrcUrl =
__RUNTIME_GLOBALS_PUBLIC_PATH__ + originalScriptFilename;
const originalPublicPath = __RUNTIME_GLOBALS_PUBLIC_PATH__;
const originalSrcUrl = originalPublicPath.startsWith('/')
? window.origin + originalPublicPath + originalScriptFilename
: originalPublicPath + originalScriptFilename;
const originalQuery = getQueryFromUrl(originalSrcUrl);

const existRetryTimes = 1;
const existRetryTimes = 0;
const nextDomain = config.domain?.[0] ?? window.origin;

return {
nextDomain,
nextRetryUrl: getNextRetryUrl(existRetryTimes, nextDomain, originalSrcUrl),
nextRetryUrl: getNextRetryUrl(
originalSrcUrl,
nextDomain,
nextDomain,
existRetryTimes,
originalQuery,
),

originalScriptFilename,
originalSrcUrl,
originalQuery,
};
}

Expand All @@ -170,19 +160,22 @@ function nextRetry(chunkId: string, existRetryTimes: number): Retry {
nextRetry = initRetry(chunkId);
retryCollector[chunkId] = [];
} else {
const { originalScriptFilename, originalSrcUrl } = currRetry;
const { originalScriptFilename, originalSrcUrl, originalQuery } = currRetry;
const nextDomain = findNextDomain(currRetry.nextDomain);

nextRetry = {
nextDomain,
nextRetryUrl: getNextRetryUrl(
nextExistRetryTimes,
currRetry.nextRetryUrl,
currRetry.nextDomain,
nextDomain,
originalSrcUrl,
existRetryTimes,
originalQuery,
),

originalScriptFilename,
originalSrcUrl,
originalQuery,
};
}

Expand Down Expand Up @@ -258,13 +251,13 @@ function ensureChunk(
// the first calling is not retry
// if the failed request is 4 in network panel, callingCounter.count === 4, the first one is the normal request, and existRetryTimes is 3, retried 3 times
const existRetryTimes = callingCounter.count - 1;
let originalSrcUrl: string;
let originalScriptFilename: string;
let nextRetryUrl: string;
let nextDomain: string;

try {
const retryResult = nextRetry(chunkId, existRetryTimes);
originalSrcUrl = retryResult.originalSrcUrl;
originalScriptFilename = retryResult.originalScriptFilename;
nextRetryUrl = retryResult.nextRetryUrl;
nextDomain = retryResult.nextDomain;
} catch (e) {
Expand Down Expand Up @@ -292,7 +285,7 @@ function ensureChunk(
if (existRetryTimes >= maxRetries) {
error.message = error.message?.includes('retries:')
? error.message
: `Loading chunk ${chunkId} from ${originalSrcUrl} failed after ${maxRetries} retries: "${error.message}"`;
: `Loading chunk ${chunkId} from "${originalScriptFilename}" failed after ${maxRetries} retries: "${error.message}"`;
if (typeof config.onFail === 'function') {
config.onFail(context);
}
Expand Down
21 changes: 18 additions & 3 deletions packages/plugin-assets-retry/src/runtime/initialChunkRetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ declare global {
var __RB_ASYNC_CHUNKS__: Record<string, boolean>;
}

// this function is the same as async chunk retry
function findCurrentDomain(url: string, domainList: string[]) {
let domain = '';
for (let i = 0; i < domainList.length; i++) {
Expand All @@ -33,6 +34,7 @@ function findCurrentDomain(url: string, domainList: string[]) {
return domain || window.origin;
}

// this function is the same as async chunk retry
function findNextDomain(url: string, domainList: string[]) {
const currentDomain = findCurrentDomain(url, domainList);
const index = domainList.indexOf(currentDomain);
Expand Down Expand Up @@ -246,6 +248,8 @@ function retry(config: RuntimeRetryOptions, e: Event) {
// 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"
const originalQuery =
target.dataset.rsbuildOriginalQuery ?? getQueryFromUrl(url);

// this function is the same as async chunk retry
function getUrlRetryQuery(existRetryTimes: number): string {
if (config.addQuery === true) {
return originalQuery !== ''
Expand All @@ -258,15 +262,26 @@ function retry(config: RuntimeRetryOptions, e: Event) {
return '';
}

// this function is the same as async chunk retry
function getNextRetryUrl(
currRetryUrl: string,
domain: string,
nextDomain: string,
existRetryTimes: number,
) {
return (
cleanUrl(currRetryUrl.replace(domain, nextDomain)) +
getUrlRetryQuery(existRetryTimes + 1)
);
}

const isAsync =
Boolean(target.dataset.rsbuildAsync) ||
(target as HTMLScriptElement).async ||
(target as HTMLScriptElement).defer;

const attributes: ScriptElementAttributes = {
url:
cleanUrl(url.replace(domain, nextDomain)) +
getUrlRetryQuery(existRetryTimes + 1),
url: getNextRetryUrl(url, domain, nextDomain, existRetryTimes),
times: existRetryTimes + 1,
crossOrigin: config.crossOrigin,
isAsync,
Expand Down

1 comment on commit 762c3b4

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ran ecosystem CI: Open

suite result
modernjs ❌ failure
plugins ✅ success
rspress ✅ success
rslib ✅ success
examples ✅ success

Please sign in to comment.