Skip to content

Commit e971e39

Browse files
committed
feat(plugin-theme-data): extract theme data injection to a plugin
1 parent 627799e commit e971e39

File tree

18 files changed

+423
-0
lines changed

18 files changed

+423
-0
lines changed

docs/.vuepress/configs/sidebar/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const en: SidebarConfig = {
8484
'/reference/plugin/palette-stylus.md',
8585
'/reference/plugin/pwa.md',
8686
'/reference/plugin/pwa-popup.md',
87+
'/reference/plugin/theme-data.md',
8788
],
8889
},
8990
],

docs/.vuepress/configs/sidebar/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const zh: SidebarConfig = {
8787
'/zh/reference/plugin/palette-stylus.md',
8888
'/zh/reference/plugin/pwa.md',
8989
'/zh/reference/plugin/pwa-popup.md',
90+
'/zh/reference/plugin/theme-data.md',
9091
],
9192
},
9293
],

docs/reference/plugin/theme-data.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# theme-data
2+
3+
> @vuepress/plugin-theme-data
4+
5+
Provide client data for your theme, with VuePress [i18n](../../guide/i18n.md) support.
6+
7+
This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.
8+
9+
For theme authors, this plugin will help you to use the same i18n mechanism as VuePress and the default theme. But if you don't want to provide i18n support, or you want to implement in your own way, you don't need this plugin.
10+
11+
## Options
12+
13+
### themeData
14+
15+
- Type: `ThemeData`
16+
17+
- Default: `{}`
18+
19+
- Details:
20+
21+
The theme data object that you want to use in client side.
22+
23+
You can provide theme data in Node side via this option, and use it in client side via [useThemeData](#useThemeData) and [useThemeLocaleData](#useThemeLocaleData).
24+
25+
- Example:
26+
27+
```js
28+
module.exports = {
29+
plugins: [
30+
[
31+
'@vuepress/plugin-theme-data',
32+
{
33+
themeData: {
34+
foo: 'foo',
35+
locales: {
36+
'/zh/': {
37+
foo: 'zh-foo',
38+
},
39+
},
40+
},
41+
},
42+
],
43+
],
44+
}
45+
```
46+
47+
::: warning
48+
The theme data object will be processed by `JSON.stringify()` before forwarding to client side, so you should ensure that you are providing a JSON-friendly object.
49+
:::
50+
51+
## Composition API
52+
53+
### useThemeData
54+
55+
- Details:
56+
57+
Returns the theme data ref object.
58+
59+
The value is provided by [themeData](#themeData) option.
60+
61+
- Example:
62+
63+
```ts
64+
import { useThemeData } from '@vuepress/plugin-theme-data/lib/composables'
65+
import type { ThemeData } from '@vuepress/plugin-theme-data'
66+
67+
type MyThemeData = ThemeData<{
68+
foo: string
69+
}>
70+
71+
export default {
72+
setup() {
73+
const themeData = useThemeData<MyThemeData>()
74+
console.log(themeData.value)
75+
},
76+
}
77+
```
78+
79+
### useThemeLocaleData
80+
81+
- Details:
82+
83+
Returns the theme data ref object in current locale.
84+
85+
The values of current locale has been merged into the root-level.
86+
87+
- Example:
88+
89+
```ts
90+
import { useThemeLocaleData } from '@vuepress/plugin-theme-data/lib/composables'
91+
import type { ThemeData } from '@vuepress/plugin-theme-data'
92+
93+
type MyThemeData = ThemeData<{
94+
foo: string
95+
}>
96+
97+
export default {
98+
setup() {
99+
const themeLocaleData = useThemeLocaleData<MyThemeData>()
100+
console.log(themeLocaleData.value)
101+
},
102+
}
103+
```
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# theme-data
2+
3+
> @vuepress/plugin-theme-data
4+
5+
为你主题提供客户端数据,包含 VuePress 的 [多语言支持](../../guide/i18n.md)
6+
7+
该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。
8+
9+
对于主题作者,该插件可以提供与 VuePress 及默认主题相同的多语言支持机制。但是如果你的主题不需要提供多语言支持,或者你想用你自己的方式来实现多语言支持,那么你不需要使用该插件。
10+
11+
## 配置项
12+
13+
### themeData
14+
15+
- 类型: `ThemeData`
16+
17+
- 默认值: `{}`
18+
19+
- 详情:
20+
21+
你希望在 Client 端中使用的主题数据对象。
22+
23+
你可以通过该配置项,在 Node 端提供主题数据,然后在客户端通过 [useThemeData](#useThemeData)[useThemeLocaleData](#useThemeLocaleData) 来使用主题数据。
24+
25+
- 示例:
26+
27+
```js
28+
module.exports = {
29+
plugins: [
30+
[
31+
'@vuepress/plugin-theme-data',
32+
{
33+
themeData: {
34+
foo: 'foo',
35+
locales: {
36+
'/zh/': {
37+
foo: 'zh-foo',
38+
},
39+
},
40+
},
41+
},
42+
],
43+
],
44+
}
45+
```
46+
47+
::: warning
48+
主题数据对象在传递到客户端之前,会使用 `JSON.stringify()` 进行处理,因此你需要保证你提供的是一个可以被 JSON 序列化的对象。
49+
:::
50+
51+
## Composition API
52+
53+
### useThemeData
54+
55+
- 详情:
56+
57+
返回主题数据的 Ref 对象。
58+
59+
数据是通过 [themeData](#themeData) 配置项提供的。
60+
61+
- 示例:
62+
63+
```ts
64+
import { useThemeData } from '@vuepress/plugin-theme-data/lib/composables'
65+
import type { ThemeData } from '@vuepress/plugin-theme-data'
66+
67+
type MyThemeData = ThemeData<{
68+
foo: string
69+
}>
70+
71+
export default {
72+
setup() {
73+
const themeData = useThemeData<MyThemeData>()
74+
console.log(themeData.value)
75+
},
76+
}
77+
```
78+
79+
### useThemeLocaleData
80+
81+
- 详情:
82+
83+
返回当前 locale 下主题数据的 Ref 对象。
84+
85+
当前 locale 中的字段已被合并到顶层字段中。
86+
87+
- 示例:
88+
89+
```ts
90+
import { useThemeLocaleData } from '@vuepress/plugin-theme-data/lib/composables'
91+
import type { ThemeData } from '@vuepress/plugin-theme-data'
92+
93+
type MyThemeData = ThemeData<{
94+
foo: string
95+
}>
96+
97+
export default {
98+
setup() {
99+
const themeLocaleData = useThemeLocaleData<MyThemeData>()
100+
console.log(themeLocaleData.value)
101+
},
102+
}
103+
```
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@vuepress/plugin-theme-data",
3+
"version": "2.0.0-alpha.24",
4+
"description": "VuePress plugin - theme data",
5+
"keywords": [
6+
"vuepress-plugin",
7+
"vuepress",
8+
"plugin",
9+
"theme"
10+
],
11+
"homepage": "https://github.com/vuepress",
12+
"bugs": {
13+
"url": "https://github.com/vuepress/vuepress-next/issues"
14+
},
15+
"repository": {
16+
"type": "git",
17+
"url": "git+https://github.com/vuepress/vuepress-next.git"
18+
},
19+
"license": "MIT",
20+
"author": "meteorlxy",
21+
"main": "lib/index.js",
22+
"types": "lib/index.d.ts",
23+
"files": [
24+
"lib"
25+
],
26+
"scripts": {
27+
"build": "tsc -b tsconfig.build.json",
28+
"clean": "rimraf lib *.tsbuildinfo"
29+
},
30+
"dependencies": {
31+
"@vuepress/client": "2.0.0-alpha.24",
32+
"@vuepress/core": "2.0.0-alpha.24",
33+
"@vuepress/shared": "2.0.0-alpha.24",
34+
"@vuepress/utils": "2.0.0-alpha.24"
35+
},
36+
"publishConfig": {
37+
"access": "public"
38+
}
39+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { computed } from 'vue'
2+
import { routeLocaleSymbol } from '@vuepress/client'
3+
import type { ClientAppEnhance } from '@vuepress/client'
4+
import {
5+
useThemeData,
6+
resolveThemeLocaleData,
7+
themeLocaleDataSymbol,
8+
} from './composables'
9+
10+
const clientAppEnhance: ClientAppEnhance = ({ app }) => {
11+
// provide theme data & theme locale data
12+
const themeData = useThemeData()
13+
const routeLocale =
14+
app._context.provides[(routeLocaleSymbol as unknown) as string]
15+
const themeLocaleData = computed(() =>
16+
resolveThemeLocaleData(themeData.value, routeLocale.value)
17+
)
18+
app.provide(themeLocaleDataSymbol, themeLocaleData)
19+
20+
Object.defineProperties(app.config.globalProperties, {
21+
$theme: {
22+
get() {
23+
return themeData.value
24+
},
25+
},
26+
$themeLocale: {
27+
get() {
28+
return themeLocaleData.value
29+
},
30+
},
31+
})
32+
}
33+
34+
export default clientAppEnhance
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './useThemeData'
2+
export * from './useThemeLocaleData'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { readonly, ref } from 'vue'
2+
import type { Ref } from 'vue'
3+
import { themeData as themeDataRaw } from '@internal/themeData'
4+
import type { ThemeData } from '../types'
5+
6+
export type ThemeDataRef<T extends ThemeData = ThemeData> = Ref<T>
7+
8+
export const themeData: ThemeDataRef = ref(readonly(themeDataRaw) as ThemeData)
9+
10+
export const useThemeData = <
11+
T extends ThemeData = ThemeData
12+
>(): ThemeDataRef<T> => themeData as ThemeDataRef<T>
13+
14+
if (import.meta.webpackHot) {
15+
import.meta.webpackHot!.accept('@internal/themeData', () => {
16+
themeData.value = readonly(themeDataRaw) as ThemeData
17+
console.log('[vuepress] themeData is updated')
18+
})
19+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { inject } from 'vue'
2+
import type { ComputedRef, InjectionKey } from 'vue'
3+
import type { RouteLocale } from '@vuepress/client'
4+
import type { ThemeData } from '../types'
5+
6+
declare const __DEV__: boolean
7+
8+
export type ThemeLocaleDataRef<T extends ThemeData = ThemeData> = ComputedRef<T>
9+
10+
export const themeLocaleDataSymbol: InjectionKey<ThemeLocaleDataRef> = Symbol(
11+
__DEV__ ? 'themeLocaleData' : ''
12+
)
13+
14+
export const useThemeLocaleData = <
15+
T extends ThemeData = ThemeData
16+
>(): ThemeLocaleDataRef<T> => {
17+
const themeLocaleData = inject(themeLocaleDataSymbol)
18+
if (!themeLocaleData) {
19+
throw new Error('useThemeLocaleData() is called without provider.')
20+
}
21+
return themeLocaleData as ThemeLocaleDataRef<T>
22+
}
23+
24+
/**
25+
* Merge the locales fields to the root fields
26+
* according to the route path
27+
*/
28+
export const resolveThemeLocaleData = (
29+
theme: ThemeData,
30+
routeLocale: RouteLocale
31+
): ThemeData => ({
32+
...theme,
33+
...theme.locales?.[routeLocale],
34+
})
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Plugin } from '@vuepress/core'
2+
import { path } from '@vuepress/utils'
3+
import type { ThemeData } from './types'
4+
5+
export type { ThemeData }
6+
7+
/**
8+
* Options of @vuepress/plugin-theme-data
9+
*/
10+
export interface ThemeDataPluginOptions {
11+
/**
12+
* Theme data to be used in client side
13+
*/
14+
themeData?: ThemeData
15+
}
16+
17+
export const themeDataPlugin: Plugin<ThemeDataPluginOptions> = ({
18+
themeData = {},
19+
}) => ({
20+
name: '@vuepress/plugin-theme-data',
21+
22+
clientAppEnhanceFiles: path.resolve(__dirname, './clientAppEnhance.js'),
23+
24+
onPrepared: async (app) => {
25+
const content = `\
26+
export const themeData = ${JSON.stringify(themeData, null, 2)}
27+
`
28+
29+
await app.writeTemp('internal/themeData.js', content)
30+
},
31+
})
32+
33+
export default themeDataPlugin

0 commit comments

Comments
 (0)