Skip to content

Commit a603c3a

Browse files
hi-ogawaclaudesheremet-va
authored
fix(docs): fix old /config/#option hash links causing hydration errors (#9610)
Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Vladimir <[email protected]>
1 parent 24603e3 commit a603c3a

File tree

6 files changed

+43
-34
lines changed

6 files changed

+43
-34
lines changed

docs/.vitepress/theme/index.ts

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,44 @@ import '@shikijs/vitepress-twoslash/style.css'
1414
import 'virtual:group-icons.css'
1515

1616
if (inBrowser) {
17+
// redirect old hash links (e.g. /config/#reporters -> /config/reporters)
18+
// before hydration to avoid SSG hydration mismatch
19+
const redirect = getRedirectPath(new URL(location.href))
20+
if (redirect) {
21+
location.replace(redirect)
22+
}
1723
import('./pwa')
1824
}
1925

26+
function getRedirectPath(url: URL) {
27+
if (url.pathname === '/api/' || url.pathname === '/api' || url.pathname === '/api/index.html') {
28+
return '/api/test'
29+
}
30+
if (!url.hash) {
31+
return
32+
}
33+
34+
// /config/#reporters -> /config/reporters
35+
// /config/#coverage-provider -> /config/coverage#coverage-provider
36+
// /config/#browser.enabled -> /config/browser/enabled
37+
if (url.pathname === '/config' || url.pathname === '/config/' || url.pathname === '/config.html') {
38+
if (url.hash.startsWith('#browser.')) {
39+
const [page, ...hash] = url.hash.slice('#browser.'.length).toLowerCase().split('-')
40+
return `/config/browser/${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''}`
41+
}
42+
const [page, ...hash] = url.hash.slice(1).toLowerCase().split('-')
43+
return `/config/${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''}`
44+
}
45+
// /guide/browser/config#browser.locators-testidattribute -> /config/browser/locators#browser-locators-testidattribute
46+
if (url.pathname === '/guide/browser/config' || url.pathname === '/guide/browser/config/' || url.pathname === '/guide/browser/config.html') {
47+
const [page, ...hash] = url.hash.slice('#browser.'.length).toLowerCase().split('-')
48+
return `/config/browser/${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''}`
49+
}
50+
}
51+
2052
export default {
2153
extends: VitestTheme as unknown as any,
22-
enhanceApp({ app, router }) {
23-
router.onBeforeRouteChange = (to) => {
24-
if (typeof location === 'undefined') {
25-
return true
26-
}
27-
const url = new URL(to, location.href)
28-
if (url.pathname === '/api/' || url.pathname === '/api' || url.pathname === '/api/index.html') {
29-
setTimeout(() => { router.go(`/api/test`) })
30-
return false
31-
}
32-
if (!url.hash) {
33-
return true
34-
}
35-
if (url.pathname === '/config' || url.pathname === '/config/' || url.pathname === '/config.html') {
36-
const [page, ...hash] = (url.hash.startsWith('#browser.') ? url.hash.slice(9) : url.hash.slice(1)).toLowerCase().split('-')
37-
setTimeout(() => { router.go(`/config/${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''}`) })
38-
return false
39-
}
40-
if (url.pathname === '/guide/browser/config' || url.pathname === '/guide/browser/config/' || url.pathname === '/guide/browser/config.html') {
41-
const [page, ...hash] = url.hash.slice('#browser.'.length).toLowerCase().split('-')
42-
setTimeout(() => { router.go(`/config/browser/${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''}`) })
43-
return false
44-
}
45-
}
54+
enhanceApp({ app }) {
4655
app.component('Version', Version)
4756
app.component('CRoot', CRoot)
4857
app.component('Experimental', Experimental)

packages/browser/context.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ export interface LocatorSelectors {
514514
*/
515515
getByTitle: (text: string | RegExp, options?: LocatorOptions) => Locator
516516
/**
517-
* Creates a locator capable of finding an element that matches the specified test id attribute. You can configure the attribute name with [`browser.locators.testIdAttribute`](/config/#browser-locators-testidattribute).
517+
* Creates a locator capable of finding an element that matches the specified test id attribute. You can configure the attribute name with [`browser.locators.testIdAttribute`](/config/browser/locators#browser-locators-testidattribute).
518518
* @see {@link https://vitest.dev/api/browser/locators#getbytestid}
519519
*/
520520
getByTestId: (text: string | RegExp) => Locator

packages/spy/src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ export interface MockInstance<T extends Procedure | Constructable = Procedure> e
224224
/**
225225
* Clears all information about every call. After calling it, all properties on `.mock` will return to their initial state. This method does not reset implementations. It is useful for cleaning up mocks between different assertions.
226226
*
227-
* To automatically call this method before each test, enable the [`clearMocks`](https://vitest.dev/config/#clearmocks) setting in the configuration.
227+
* To automatically call this method before each test, enable the [`clearMocks`](https://vitest.dev/config/clearmocks) setting in the configuration.
228228
* @see https://vitest.dev/api/mock#mockclear
229229
*/
230230
mockClear(): this
@@ -234,7 +234,7 @@ export interface MockInstance<T extends Procedure | Constructable = Procedure> e
234234
* Note that resetting a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`.
235235
* Resetting a mock from `vi.fn(impl)` will set implementation to `impl`. It is useful for completely resetting a mock to its default state.
236236
*
237-
* To automatically call this method before each test, enable the [`mockReset`](https://vitest.dev/config/#mockreset) setting in the configuration.
237+
* To automatically call this method before each test, enable the [`mockReset`](https://vitest.dev/config/mockreset) setting in the configuration.
238238
* @see https://vitest.dev/api/mock#mockreset
239239
*/
240240
mockReset(): this

packages/vitest/src/integrations/vi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ export interface VitestUtils {
4343
runOnlyPendingTimersAsync: () => Promise<VitestUtils>
4444
/**
4545
* This method will invoke every initiated timer until the timer queue is empty. It means that every timer called during `runAllTimers` will be fired.
46-
* If you have an infinite interval, it will throw after 10,000 tries (can be configured with [`fakeTimers.loopLimit`](https://vitest.dev/config/#faketimers-looplimit)).
46+
* If you have an infinite interval, it will throw after 10,000 tries (can be configured with [`fakeTimers.loopLimit`](https://vitest.dev/config/faketimers#faketimers-looplimit)).
4747
*/
4848
runAllTimers: () => VitestUtils
4949
/**
5050
* This method will asynchronously invoke every initiated timer until the timer queue is empty. It means that every timer called during `runAllTimersAsync` will be fired even asynchronous timers.
51-
* If you have an infinite interval, it will throw after 10 000 tries (can be configured with [`fakeTimers.loopLimit`](https://vitest.dev/config/#faketimers-looplimit)).
51+
* If you have an infinite interval, it will throw after 10 000 tries (can be configured with [`fakeTimers.loopLimit`](https://vitest.dev/config/faketimers#faketimers-looplimit)).
5252
*/
5353
runAllTimersAsync: () => Promise<VitestUtils>
5454
/**

packages/vitest/src/node/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,7 @@ export class Vitest {
14531453
}
14541454

14551455
if (!this.reporters.some(r => r instanceof HangingProcessReporter)) {
1456-
console.warn('You can try to identify the cause by enabling "hanging-process" reporter. See https://vitest.dev/config/#reporters')
1456+
console.warn('You can try to identify the cause by enabling "hanging-process" reporter. See https://vitest.dev/config/reporters')
14571457
}
14581458
}
14591459

packages/vitest/src/node/types/browser.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ export interface BrowserConfigOptions {
288288
/**
289289
* Enables tracking uncaught errors and exceptions so they can be reported by Vitest.
290290
*
291-
* If you need to hide certain errors, it is recommended to use [`onUnhandledError`](https://vitest.dev/config/#onunhandlederror) option instead.
291+
* If you need to hide certain errors, it is recommended to use [`onUnhandledError`](https://vitest.dev/config/onunhandlederror) option instead.
292292
*
293293
* Disabling this will completely remove all Vitest error handlers, which can help debugging with the "Pause on exceptions" checkbox turned on.
294294
* @default true
@@ -455,12 +455,12 @@ type ToMatchScreenshotResolvePath = (data: {
455455
screenshotDirectory: string
456456
/**
457457
* Absolute path to the project's
458-
* {@linkcode https://vitest.dev/config/#root|root}.
458+
* {@linkcode https://vitest.dev/config/root|root}.
459459
*/
460460
root: string
461461
/**
462462
* Path to the test file, relative to the project's
463-
* {@linkcode https://vitest.dev/config/#root|root}.
463+
* {@linkcode https://vitest.dev/config/root|root}.
464464
*/
465465
testFileDirectory: string
466466
/**
@@ -474,7 +474,7 @@ type ToMatchScreenshotResolvePath = (data: {
474474
testName: string
475475
/**
476476
* The value provided to
477-
* {@linkcode https://vitest.dev/config/#attachmentsdir|attachmentsDir},
477+
* {@linkcode https://vitest.dev/config/attachmentsdir|attachmentsDir},
478478
* if none is provided, its default value.
479479
*/
480480
attachmentsDir: string

0 commit comments

Comments
 (0)