Skip to content

Commit

Permalink
fix(plugin/assets-retry): addQuery and switchDomain should work in as…
Browse files Browse the repository at this point in the history
…ync css chunk (#4315)
  • Loading branch information
SoonIter authored Jan 5, 2025
1 parent c2af94c commit 3fd33a4
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 45 deletions.
70 changes: 70 additions & 0 deletions e2e/cases/assets/assets-retry/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ async function createRsbuildWithMiddleware(
options: PluginAssetsRetryOptions,
entry?: string,
port?: number,
assetPrefix?: string
) {
const rsbuild = await dev({
cwd: __dirname,
Expand All @@ -93,6 +94,9 @@ async function createRsbuildWithMiddleware(
middlewares.unshift(...addMiddleWares);
},
],
...(assetPrefix ? {
assetPrefix
}: {})
},
...(port
? {
Expand Down Expand Up @@ -655,6 +659,42 @@ test('should work with addQuery boolean option', async ({ page }) => {
logger.level = 'log';
});

test('should work with addQuery boolean option when retrying async css chunk', async ({ page }) => {
logger.level = 'verbose';
const { logs, restore } = proxyConsole();

const asyncChunkBlockedMiddleware = createBlockMiddleware({
blockNum: 3,
urlPrefix: '/static/css/async/src_AsyncCompTest_tsx.css',
});
const rsbuild = await createRsbuildWithMiddleware(
asyncChunkBlockedMiddleware,
{
minify: true,
addQuery: true,
},
);

await gotoPage(page, rsbuild);
const asyncCompTestElement = page.locator('#async-comp-test');
await expect(asyncCompTestElement).toHaveText('Hello AsyncCompTest');
await expect(asyncCompTestElement).toHaveCSS('background-color', 'rgb(0, 0, 139)');

const blockedAsyncChunkResponseCount = count404ResponseByUrl(
logs,
'/static/css/async/src_AsyncCompTest_tsx.css',
);
expect(blockedAsyncChunkResponseCount).toMatchObject({
'/static/css/async/src_AsyncCompTest_tsx.css': 1,
'/static/css/async/src_AsyncCompTest_tsx.css?retry=1': 1,
'/static/css/async/src_AsyncCompTest_tsx.css?retry=2': 1,
});

await rsbuild.close();
restore();
logger.level = 'log';
});

test('should work with addQuery function type option', async ({ page }) => {
logger.level = 'verbose';
const { logs, restore } = proxyConsole();
Expand Down Expand Up @@ -860,3 +900,33 @@ test('onRetry and onFail options should work when multiple parallel retrying asy
});
await rsbuild.close();
});

test('should work when the first, second cdn are all failed and the third is success', async ({ page }) => {
// this is a real world case for assets-retry
const port = await getRandomPort();
const rsbuild = await createRsbuildWithMiddleware(
[],
{
minify: true,
domain: ['http://a.com/foo-path', 'http://b.com', `http://localhost:${port}`],
addQuery: true,
onRetry(context) {
console.info('onRetry', context);
},
onSuccess(context) {
console.info('onSuccess', context);
},
onFail(context) {
console.info('onFail', context);
},
},
undefined,
port,
'http://a.com/foo-path'
);

await gotoPage(page, rsbuild);
const compTestElement = page.locator('#async-comp-test');
await expect(compTestElement).toHaveText('Hello AsyncCompTest');
await expect(compTestElement).toHaveCSS('background-color', 'rgb(0, 0, 139)');
})
70 changes: 52 additions & 18 deletions packages/plugin-assets-retry/src/AsyncChunkRetryPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,45 @@ import type { PluginAssetsRetryOptions, RuntimeRetryOptions } from './types.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// https://github.com/web-infra-dev/rspack/pull/5370
function appendWebpackScript(module: any, appendSource: string) {
function modifyWebpackRuntimeModule(
module: any,
modifier: (originSource: string) => string,
) {
try {
const originSource = module.getGeneratedCode();
module.getGeneratedCode = () => `${originSource}\n${appendSource}`;
module.getGeneratedCode = () => modifier(originSource);
} catch (err) {
console.error('Failed to modify Webpack RuntimeModule');
throw err;
}
}

function appendRspackScript(
function modifyRspackRuntimeModule(
module: any, // JsRuntimeModule type is not exported by Rspack temporarily */
appendSource: string,
modifier: (originSource: string) => string,
) {
try {
const source = module.source.source.toString();
module.source.source = Buffer.from(`${source}\n${appendSource}`, 'utf-8');
const originSource = module.source.source.toString();
module.source.source = Buffer.from(modifier(originSource), 'utf-8');
} catch (err) {
console.error('Failed to modify Rspack RuntimeModule');
throw err;
}
}

// https://github.com/web-infra-dev/rspack/pull/5370
function modifyRuntimeModule(
module: any,
modifier: (originSource: string) => string,
isRspack: boolean,
) {
if (isRspack) {
modifyRspackRuntimeModule(module, modifier);
} else {
modifyWebpackRuntimeModule(module, modifier);
}
}

function pick<T, U extends keyof T>(obj: T, keys: ReadonlyArray<U>) {
return keys.reduce(
(ret, key) => {
Expand Down Expand Up @@ -89,6 +104,10 @@ class AsyncChunkRetryPlugin implements Rspack.RspackPluginInstance {
'__RUNTIME_GLOBALS_GET_MINI_CSS_EXTRACT_FILENAME__',
'__webpack_require__.miniCssF',
)
.replaceAll(
'__RUNTIME_GLOBALS_RSBUILD_LOAD_STYLESHEET__',
'__webpack_require__.rsbuildLoadStyleSheet',
)
.replaceAll('__RUNTIME_GLOBALS_PUBLIC_PATH__', RuntimeGlobals.publicPath)
.replaceAll('__RUNTIME_GLOBALS_LOAD_SCRIPT__', RuntimeGlobals.loadScript)
.replaceAll('__RETRY_OPTIONS__', serialize(this.runtimeOptions));
Expand All @@ -102,23 +121,38 @@ class AsyncChunkRetryPlugin implements Rspack.RspackPluginInstance {
? module.constructorName
: module.constructor?.name;

const isCssLoadingRuntimeModule =
constructorName === 'CssLoadingRuntimeModule';

// https://github.com/web-infra-dev/rspack/blob/734ba4cfbec00ab68ff55bac95e7740fe8228229/crates/rspack_plugin_extract_css/src/runtime/css_load.js#L54
if (isCssLoadingRuntimeModule) {
modifyRuntimeModule(
module,
(originSource) =>
originSource.replace(
'var fullhref = __webpack_require__.p + href;',
'var fullhref = __webpack_require__.rsbuildLoadStyleSheet ? __webpack_require__.rsbuildLoadStyleSheet(href, chunkId) : (__webpack_require__.p + href);',
),
isRspack,
);
return;
}

const isPublicPathModule =
module.name === 'publicPath' ||
constructorName === 'PublicPathRuntimeModule' ||
constructorName === 'AutoPublicPathRuntimeModule';

if (!isPublicPathModule) {
return;
}

const runtimeCode = this.getRawRuntimeRetryCode();
if (isPublicPathModule) {
const runtimeCode = this.getRawRuntimeRetryCode();

// Rspack currently does not have module.addRuntimeModule on the js side,
// so we insert our runtime code after PublicPathRuntimeModule or AutoPublicPathRuntimeModule.
if (isRspack) {
appendRspackScript(module, runtimeCode);
} else {
appendWebpackScript(module, runtimeCode);
// Rspack currently does not have module.addRuntimeModule on the js side,
// so we insert our runtime code after PublicPathRuntimeModule or AutoPublicPathRuntimeModule.
modifyRuntimeModule(
module,
(originSource) => `${originSource}\n${runtimeCode}`,
isRspack,
);
}
});
});
Expand Down
Loading

1 comment on commit 3fd33a4

@github-actions
Copy link
Contributor

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 ❌ failure
rslib ✅ success
examples ✅ success

Please sign in to comment.