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
18 changes: 18 additions & 0 deletions e2e/cases/server/history-api-fallback-rewrites/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expect, rspackTest } from '@e2e/helper';

rspackTest(
'should apply `historyApiFallback.rewrites` correctly',
async ({ page, devOnly }) => {
const rsbuild = await devOnly();

await page.goto(`http://localhost:${rsbuild.port}`);
expect(await page.locator('#root').innerHTML()).toEqual('index');

// `/baz` should be rewritten to `/foo`
await page.goto(`http://localhost:${rsbuild.port}/baz`);
expect(await page.locator('#root').innerHTML()).toEqual('foo');

await page.goto(`http://localhost:${rsbuild.port}/bar`);
expect(await page.locator('#root').innerHTML()).toEqual('bar');
},
);
19 changes: 19 additions & 0 deletions e2e/cases/server/history-api-fallback-rewrites/rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
plugins: [pluginReact()],
source: {
entry: {
index: './src/index.js',
foo: './src/foo.js',
bar: './src/bar.js',
},
},
server: {
historyApiFallback: {
index: '/index.html',
rewrites: [{ from: /^\/baz/, to: '/foo.html' }],
},
},
});
1 change: 1 addition & 0 deletions e2e/cases/server/history-api-fallback-rewrites/src/bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
document.querySelector('#root').innerHTML = 'bar';
1 change: 1 addition & 0 deletions e2e/cases/server/history-api-fallback-rewrites/src/foo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
document.querySelector('#root').innerHTML = 'foo';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
document.querySelector('#root').innerHTML = 'index';
21 changes: 11 additions & 10 deletions packages/core/src/server/devMiddlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,16 +217,7 @@ const applyDefaultMiddlewares = ({
callback();
}

if (buildManager) {
middlewares.push(
getHtmlFallbackMiddleware({
buildManager,
distPath: context.distPath,
htmlFallback: server.htmlFallback,
}),
);
}

// historyApiFallback takes precedence over the default htmlFallback.
if (server.historyApiFallback) {
middlewares.push(
historyApiFallbackMiddleware(
Expand All @@ -240,6 +231,16 @@ const applyDefaultMiddlewares = ({
}
}

if (buildManager) {
middlewares.push(
getHtmlFallbackMiddleware({
buildManager,
distPath: context.distPath,
htmlFallback: server.htmlFallback,
}),
);
}

middlewares.push(faviconFallbackMiddleware);

return {
Expand Down
4 changes: 4 additions & 0 deletions website/docs/en/config/server/history-api-fallback.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

When Rsbuild's default [page routing](/guide/basic/server#page-routing) behavior cannot meet your needs, for example, if you want to be able to access `main.html` when accessing `/`, you can achieve this through the `server.historyApiFallback` configuration.

:::tip
The `server.historyApiFallback` option has a higher priority than [server.htmlFallback](/config/server/html-fallback).
:::

## Example

When `server.historyApiFallback` is set to `true`, all HTML GET requests that do not match an actual resource will return `index.html`. This ensures that routing in single-page applications works correctly.
Expand Down
14 changes: 3 additions & 11 deletions website/docs/en/guide/basic/server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,10 @@ export default {

### Fallback behavior

By default, requests that meet the following conditions fall back to `index.html` when the corresponding resource is not found:
If a request meets the following conditions but no corresponding static asset is found, [server.htmlFallback](/config/server/html-fallback) will be triggered and will fall back to `index.html` by default:

- The request is a `GET` or `HEAD` request
- The request accepts `text/html` (the request header accept type is `text/html` or `*/*`)

```ts title="rsbuild.config.ts"
export default {
server: {
htmlFallback: 'index',
},
};
```
- The request method is `GET` or `HEAD`
- The `Accept` header contains `text/html` (for example, `text/html` or `*/*`)

### Custom fallback behavior

Expand Down
4 changes: 4 additions & 0 deletions website/docs/zh/config/server/history-api-fallback.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

当 Rsbuild 默认的 [页面路由](/guide/basic/server#page-routing) 行为无法满足你的需求时,例如,希望在访问 `/` 时可以访问 `main.html` ,你可以通过 `server.historyApiFallback` 配置项来实现这个功能。

:::tip
`server.historyApiFallback` 的优先级高于 [server.htmlFallback](/config/server/html-fallback)。
:::

## 示例

将 `server.historyApiFallback` 设置为 `true` 时,所有未匹配到实际资源的 HTML GET 请求都会返回 `index.html`,从而保证单页应用的路由能够正常工作。
Expand Down
14 changes: 3 additions & 11 deletions website/docs/zh/guide/basic/server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,10 @@ export default {

### Fallback 行为

当请求满足以下条件且未找到对应资源时,会被 `server.htmlFallback` 处理,默认会回退到 index.html
当请求满足以下条件且未找到对应的静态资源时,[server.htmlFallback](/config/server/html-fallback) 将生效,默认会回退到 `index.html`:

- 当前请求是 GET 或 HEAD 请求
- 当前请求头接受 `text/html` (请求头 accept 类型为 `text/html` 或 `*/*`)

```ts title="rsbuild.config.ts"
export default {
server: {
htmlFallback: 'index',
},
};
```
- 请求方法为 `GET` 或 `HEAD`
- 请求头中的 `Accept` 包含 `text/html`(即类型为 `text/html` 或 `*/*`)

### 自定义 Fallback 行为

Expand Down
Loading