Skip to content

Commit 170fdeb

Browse files
committed
Add context-aware version switching + fallback to root
Add eslint + fix linting issues
1 parent e9919cc commit 170fdeb

File tree

9 files changed

+1411
-39
lines changed

9 files changed

+1411
-39
lines changed

.vitepress/cake.js

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,45 +16,36 @@ import {
1616
sidebarConfig
1717
} from './cake/config.js'
1818

19-
// Re-export configuration for backward compatibility
20-
export { supportedLocales, versions, localizedVersions, sidebarConfig }
21-
2219
// Helper functions for version management
2320
export function getCurrentVersion(locale = 'en') {
2421
const versionList = getVersionsByLocale(locale)
2522
return versionList.find(v => v.isCurrentVersion)
2623
}
2724

2825
export function getVersionsByLocale(locale = 'en') {
29-
// Return English versions by default
3026
if (locale === 'en') {
3127
return versions
3228
}
3329

34-
// Check if we have localized versions for this locale
3530
if (localizedVersions[locale]) {
3631
return localizedVersions[locale]
3732
}
3833

39-
// Fallback to English versions if locale not found
4034
return versions
4135
}
4236

4337
export function getVersionByPath(path) {
4438
// Detect locale from path
4539
const locale = detectLocaleFromPath(path)
4640

47-
// Get version list for detected locale
4841
const versionList = getVersionsByLocale(locale)
4942

50-
// Check for version-specific paths in the detected locale
5143
for (const version of versionList) {
5244
if (path.startsWith(version.publicPath)) {
5345
return version
5446
}
5547
}
5648

57-
// Default to current version for the detected locale
5849
return getCurrentVersion(locale)
5950
}
6051

@@ -68,17 +59,56 @@ export function getAllVersionPaths(locale = 'en') {
6859
return versionList.map(v => v.publicPath)
6960
}
7061

71-
// Navigation configuration for version dropdown
72-
export function getVersionNavItems(locale = 'en') {
73-
const versionList = getVersionsByLocale(locale)
74-
return versionList.map(version => ({
75-
text: version.displayName,
76-
link: version.publicPath,
77-
path: version.publicPath,
78-
version: version.version
79-
}))
62+
export function convertPathToVersion(currentPath, targetVersion, locale = 'en') {
63+
const currentLocale = detectLocaleFromPath(currentPath)
64+
const currentVersionObj = getVersionByPath(currentPath)
65+
66+
if (currentVersionObj.version === targetVersion) {
67+
return currentPath
68+
}
69+
70+
const versionList = getVersionsByLocale(currentLocale)
71+
const targetVersionObj = versionList.find(v => v.version === targetVersion)
72+
73+
if (!targetVersionObj) {
74+
return locale === 'en' ? `/${targetVersion}.x/` : `/${locale}/${targetVersion}.x/`
75+
}
76+
77+
let relativePath = currentPath
78+
if (relativePath.startsWith(currentVersionObj.publicPath)) {
79+
relativePath = relativePath.substring(currentVersionObj.publicPath.length)
80+
}
81+
82+
if (!relativePath || relativePath === '' || relativePath === '/') {
83+
return targetVersionObj.publicPath
84+
}
85+
86+
let targetPath = targetVersionObj.publicPath
87+
if (!targetPath.endsWith('/')) {
88+
targetPath += '/'
89+
}
90+
if (relativePath.startsWith('/')) {
91+
relativePath = relativePath.substring(1)
92+
}
93+
94+
return targetPath + relativePath
8095
}
8196

97+
export function getVersionNavItems(locale = 'en', currentPath = null) {
98+
const versionList = getVersionsByLocale(locale)
99+
return versionList.map(version => {
100+
const link = currentPath
101+
? convertPathToVersion(currentPath, version.version, locale)
102+
: version.publicPath
103+
104+
return {
105+
text: version.displayName,
106+
link: link,
107+
path: version.publicPath,
108+
version: version.version
109+
}
110+
})
111+
}
82112

83113
// Helper function to get supported locales
84114
export function getSupportedLocales() {
@@ -98,7 +128,7 @@ export function detectLocaleFromPath(path) {
98128
return locale
99129
}
100130
}
101-
// Default to 'en' if no locale prefix found
131+
102132
return 'en'
103133
}
104134

.vitepress/plugins/version-replacer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { getVersionByPath } from '../cake.js'
1212
* @param {Object} options - plugin options
1313
* @returns {void}
1414
*/
15-
export function versionReplacer(md, options = {}) {
15+
export function versionReplacer(md, _options = {}) {
1616
// Store original render method
1717
const originalRender = md.render.bind(md)
1818

.vitepress/sidebar.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import { readFileSync } from 'fs'
88
import { fileURLToPath } from 'url'
99
import { dirname, join } from 'path'
10-
import { versions, getCurrentVersion, sidebarConfig, localizedVersions, getVersionsByLocale } from './cake.js'
10+
import { versions, sidebarConfig, localizedVersions } from './cake/config.js'
11+
import { getVersionsByLocale } from './cake.js'
1112

1213
// Get current directory for JSON imports
1314
const __filename = fileURLToPath(import.meta.url)

.vitepress/theme/components/PageLoader.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
before = () => { visible.value = true }
1313
after = () => {
1414
// Small delay to ensure rendering is done
15-
setTimeout(() => { visible.value = false }, 100)
15+
setTimeout(() => { visible.value = false }, 50)
1616
}
1717
router.onBeforeRouteChange = before
1818
router.onAfterRouteChange = after

.vitepress/theme/components/VersionDropdown.vue

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
<script setup>
2-
import { ref, computed, onMounted, onUnmounted } from 'vue'
3-
import { useRoute, useData, withBase } from 'vitepress'
2+
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
3+
import { useRoute, useRouter, useData, withBase } from 'vitepress'
44
import { getVersionNavItems, getVersionByPath, getVersionLabel, isLocaleSupported } from '../../cake.js'
55
66
const route = useRoute()
7-
const { localeIndex, site } = useData()
7+
const router = useRouter()
8+
const { localeIndex } = useData()
89
const isOpen = ref(false)
10+
const pendingVersionNavigation = ref(null)
911
1012
// Get current locale from VitePress's locale system
1113
const currentLocale = computed(() => {
12-
// localeIndex gives us the current locale (e.g., 'ja', 'root' for English)
13-
// Convert 'root' to 'en' for our system
1414
const locale = localeIndex.value === 'root' ? 'en' : localeIndex.value
15-
16-
// Fallback to 'en' if locale is not supported by our version system
1715
return isLocaleSupported(locale) ? locale : 'en'
1816
})
1917
2018
// Get version navigation items for current locale
2119
const versionNavItems = computed(() => {
22-
return getVersionNavItems(currentLocale.value)
20+
return getVersionNavItems(currentLocale.value, route.path)
2321
})
2422
2523
const currentPath = computed(() => {
@@ -39,15 +37,57 @@ const closeDropdown = () => {
3937
isOpen.value = false
4038
}
4139
42-
// Close dropdown when clicking outside
40+
const handleVersionClick = (version, event) => {
41+
closeDropdown()
42+
43+
pendingVersionNavigation.value = {
44+
fallbackPath: version.path,
45+
targetPath: version.link
46+
}
47+
}
48+
4349
const handleClickOutside = (event) => {
4450
if (!event.target.closest('.version-dropdown')) {
4551
closeDropdown()
4652
}
4753
}
4854
55+
// Check for 404 after route changes
56+
const check404AndFallback = () => {
57+
if (!pendingVersionNavigation.value) return
58+
59+
setTimeout(() => {
60+
const is404 = document.title.includes('404') ||
61+
document.querySelector('.not-found') ||
62+
document.querySelector('[class*="404"]') ||
63+
route.path.includes('404')
64+
65+
if (is404) {
66+
router.go(pendingVersionNavigation.value.fallbackPath)
67+
}
68+
69+
pendingVersionNavigation.value = null
70+
}, 10)
71+
}
72+
4973
onMounted(() => {
5074
document.addEventListener('click', handleClickOutside)
75+
76+
// Store the existing onAfterRouteChange handler
77+
const originalOnAfterRouteChange = router.onAfterRouteChange
78+
79+
// Add our logic to the existing hook
80+
router.onAfterRouteChange = () => {
81+
// Call the original handler first
82+
if (originalOnAfterRouteChange) {
83+
originalOnAfterRouteChange()
84+
}
85+
86+
// Then check for 404s
87+
if (pendingVersionNavigation.value) {
88+
check404AndFallback()
89+
}
90+
}
5191
})
5292
5393
onUnmounted(() => {
@@ -66,7 +106,7 @@ onUnmounted(() => {
66106
<a
67107
:href="withBase(version.link)"
68108
:class="{ active: withBase(version.path) === currentPath }"
69-
@click="closeDropdown"
109+
@click="handleVersionClick(version, $event)"
70110
>
71111
{{ version.text }}
72112
</a>

.vitepress/theme/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { h } from "vue";
66

77
export default {
88
extends: DefaultTheme,
9-
enhanceApp({ app, router, siteData }) {
9+
enhanceApp({ app, _router, _siteData }) {
1010
app.component('VersionDropdown', VersionDropdown)
1111
},
1212
Layout() {

eslint.config.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import js from '@eslint/js'
2+
import vue from 'eslint-plugin-vue'
3+
import globals from 'globals'
4+
5+
export default [
6+
js.configs.recommended,
7+
...vue.configs['flat/recommended'],
8+
{
9+
languageOptions: {
10+
globals: {
11+
...globals.browser,
12+
...globals.node
13+
},
14+
ecmaVersion: 'latest',
15+
sourceType: 'module'
16+
},
17+
rules: {
18+
// General JavaScript rules
19+
'no-console': 'warn',
20+
'no-debugger': 'error',
21+
'no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }],
22+
'prefer-const': 'error',
23+
'no-var': 'error',
24+
25+
// Vue-specific rules
26+
'vue/multi-word-component-names': 'off',
27+
'vue/no-unused-vars': 'error',
28+
'vue/component-definition-name-casing': ['error', 'PascalCase'],
29+
'vue/html-self-closing': ['error', {
30+
'html': {
31+
'void': 'always',
32+
'normal': 'always',
33+
'component': 'always'
34+
},
35+
'svg': 'always',
36+
'math': 'always'
37+
}]
38+
}
39+
},
40+
{
41+
files: ['.vitepress/**/*.js', 'validate-sidebar.js'],
42+
rules: {
43+
// More relaxed rules for config files
44+
'no-console': 'off'
45+
}
46+
},
47+
{
48+
ignores: [
49+
'.vitepress/cache/**',
50+
'.vitepress/dist/**',
51+
'node_modules/**'
52+
]
53+
}
54+
]

0 commit comments

Comments
 (0)