Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,32 @@ If you want Assets Retry plugin to work on resources in custom templates, you ca
</html>
```

#### Identifying retry scripts in HTML templates

The Assets Retry plugin adds a unique `data-rsbuild-assets-retry` attribute to retry scripts, allowing you to easily identify them in custom HTML templates.

You can import the attribute constant:

```js
import { ASSETS_RETRY_DATA_ATTRIBUTE } from '@rsbuild/plugin-assets-retry';
```

The attribute values are:
- `"inline"` for inline scripts (when `inlineScript: true`)
- `"external"` for external scripts (when `inlineScript: false`)

Example usage in HTML templates:

```html
<!-- Filter retry scripts -->
<%= htmlWebpackPlugin.tags.headTags.filter(tag => tag.attributes['data-rsbuild-assets-retry'] === 'inline') %>

<!-- Filter non-retry scripts -->
<%= htmlWebpackPlugin.tags.headTags.filter(tag => !tag.attributes['data-rsbuild-assets-retry']) %>
```

This allows you to place retry scripts at the top of your HTML head for optimal loading order.

## License

[MIT](./LICENSE).
26 changes: 26 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,32 @@ Assets Retry 插件通过监听页面 error 事件来获悉当前资源是否加
</html>
```

#### 在 HTML 模板中识别重试脚本

Assets Retry 插件为重试脚本添加了唯一的 `data-rsbuild-assets-retry` 属性,使您可以在自定义 HTML 模板中轻松识别它们。

您可以导入属性常量:

```js
import { ASSETS_RETRY_DATA_ATTRIBUTE } from '@rsbuild/plugin-assets-retry';
```

属性值包括:
- `"inline"` 用于内联脚本(当 `inlineScript: true` 时)
- `"external"` 用于外部脚本(当 `inlineScript: false` 时)

在 HTML 模板中的使用示例:

```html
<!-- 筛选重试脚本 -->
<%= htmlWebpackPlugin.tags.headTags.filter(tag => tag.attributes['data-rsbuild-assets-retry'] === 'inline') %>

<!-- 筛选非重试脚本 -->
<%= htmlWebpackPlugin.tags.headTags.filter(tag => !tag.attributes['data-rsbuild-assets-retry']) %>
```

这允许您将重试脚本放置在 HTML 头部的顶部以获得最佳的加载顺序。

## License

[MIT](./LICENSE).
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export type { PluginAssetsRetryOptions };

export const PLUGIN_ASSETS_RETRY_NAME = 'rsbuild:assets-retry';

// Unique identifier for filtering retry scripts in HTML templates
export const ASSETS_RETRY_DATA_ATTRIBUTE = 'data-rsbuild-assets-retry';

function getRuntimeOptions(
userOptions: PluginAssetsRetryOptions,
defaultCrossOrigin: boolean | 'anonymous' | 'use-credentials',
Expand Down Expand Up @@ -117,7 +120,9 @@ export const pluginAssetsRetry = (

headTags.unshift({
tag: 'script',
attrs: {},
attrs: {
[ASSETS_RETRY_DATA_ATTRIBUTE]: 'inline',
},
children: code,
});

Expand All @@ -133,6 +138,7 @@ export const pluginAssetsRetry = (
tag: 'script',
attrs: {
src: url,
[ASSETS_RETRY_DATA_ATTRIBUTE]: 'external',
},
});

Expand Down
96 changes: 96 additions & 0 deletions test/basic/dataAttribute.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { expect, test } from '@playwright/test';
import { createRsbuild } from '@rsbuild/core';
import { ASSETS_RETRY_DATA_ATTRIBUTE, pluginAssetsRetry } from '../../dist';

test('should add data attribute to inline retry script', async ({ page }) => {
const rsbuild = await createRsbuild({
cwd: import.meta.dirname,
rsbuildConfig: {
plugins: [
pluginAssetsRetry({
inlineScript: true,
}),
],
},
});

const { server, urls } = await rsbuild.startDevServer();

await page.goto(urls[0]);

// Check if inline script has correct data attribute
const inlineScript = await page.locator(
`script[${ASSETS_RETRY_DATA_ATTRIBUTE}="inline"]`,
);
expect(await inlineScript.count()).toBe(1);

// Verify script content contains retry logic
const scriptContent = await inlineScript.innerHTML();
expect(scriptContent).toContain('document.addEventListener');

await server.close();
});

test('should add data attribute to external retry script', async ({ page }) => {
const rsbuild = await createRsbuild({
cwd: import.meta.dirname,
rsbuildConfig: {
plugins: [
pluginAssetsRetry({
inlineScript: false,
}),
],
},
});

const { server, urls } = await rsbuild.startDevServer();

await page.goto(urls[0]);

// Check if external script has correct data attribute
const externalScript = await page.locator(
`script[${ASSETS_RETRY_DATA_ATTRIBUTE}="external"]`,
);
expect(await externalScript.count()).toBe(1);

// Verify script has src attribute
const src = await externalScript.getAttribute('src');
expect(src).toContain('assets-retry');

await server.close();
});

test('should be able to filter retry script in HTML template', async ({
page,
}) => {
const rsbuild = await createRsbuild({
cwd: import.meta.dirname,
rsbuildConfig: {
plugins: [
pluginAssetsRetry({
inlineScript: true,
}),
],
},
});

const { server, urls } = await rsbuild.startDevServer();

await page.goto(urls[0]);

// Simulate using htmlWebpackPlugin.tags.headTags.filter in HTML template
// Verify that retry scripts can be filtered by data attribute
const allScripts = await page.locator('script');
const retryScripts = await page.locator(
`script[${ASSETS_RETRY_DATA_ATTRIBUTE}]`,
);

const allScriptsCount = await allScripts.count();
const retryScriptsCount = await retryScripts.count();

// Should have at least one script, and retry script should be one of them
expect(allScriptsCount).toBeGreaterThan(0);
expect(retryScriptsCount).toBe(1);

await server.close();
});