Skip to content

Commit 8d321ea

Browse files
authored
feat: support SSR (#60)
* feat: support SSR * updates * fix
1 parent 1e6fc21 commit 8d321ea

21 files changed

+4836
-1860
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module.exports = {
2626
},
2727
rules: {
2828
'object-curly-spacing': 'off',
29+
'@typescript-eslint/ban-ts-comment': 'off',
2930
'@typescript-eslint/no-non-null-assertion': 'off',
3031
'@typescript-eslint/explicit-function-return-type': 'off',
3132
'@typescript-eslint/member-delimiter-style': 'off',

README.md

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,102 @@
1212

1313
This library exports the following extensions:
1414

15+
1516
## :star: Features
16-
- module: `v-t` custom directive compiler module for the following:
17-
- `@vue/compiler-core` (`options` at `baseCompile` function)
18-
- `@vue/compiler-dom` (`options` at `compile` function)
19-
- `@vue/compiler-sfc` (`compilerOptions` at `compileTemplate` function)
20-
- `vue-loader` (`compilerOptions` at `options`)
17+
18+
- Server-side rendering for `v-t` custom directive
19+
- Pre-Translation
20+
2121

2222
## :cd: Installation
2323

2424
```sh
2525
$ npm i --save-dev @intlify/vue-i18n-extensions@alpha
2626
```
2727

28+
2829
## :rocket: Extensions
2930

30-
### module: `v-t` custom directive compiler module
31-
This module is `v-t` custom directive module for vue compilers. You can specify it.
31+
### Server-side rendering for `v-t` custom directive
32+
33+
You can use tnrasform offered with this package, to support Server-side rendering for `v-t` custom directives.
34+
35+
In order to use this feature, you need to specify to Vue compiler option.
36+
The following example that use `compile` of `@vue/compiler-ssr`:
37+
38+
```js
39+
import * as runtimeDom from '@vue/runtime-dom'
40+
import { compile } from '@vue/compiler-ssr'
41+
import { defineComponent, createSSRApp } from 'vue'
42+
import { renderToString } from '@vue/server-renderer'
43+
import { createI18n, useI18n } from 'vue-i18n'
44+
import { transformVTDirective } from '@intlify/vue-i18n-extensions'
45+
46+
// create i18n instance
47+
const i18n = createI18n({
48+
locale: 'ja',
49+
messages: {}
50+
})
51+
52+
// get transform from `transformVTDirective` function
53+
const transformVT = transformVTDirective()
54+
55+
// compile your source
56+
const source = `<div v-t="{ path: 'dessert', locale: 'en', plural: 2, args: { name: 'banana' } }"/>`
57+
const { code } = compile(source, {
58+
mode: 'function',
59+
directiveTransforms: { t: transformVT } // <- you need to specify to `directiveTransforms` option!
60+
})
61+
62+
// render functionalization
63+
const render = Function('require', 'Vue', code)(require, runtimeDom)
64+
65+
// e.g. set render function to App component
66+
const App = defineComponent({
67+
setup() {
68+
return useI18n({
69+
locale: 'en',
70+
inheritLocale: false,
71+
messages: {
72+
en: {
73+
apple: 'no apples | one apple | {count} apples',
74+
banana: 'no bananas | {n} banana | {n} bananas',
75+
dessert: 'I eat @:{name}!'
76+
}
77+
}
78+
})
79+
},
80+
ssrRender: render
81+
})
82+
83+
// create SSR app
84+
const app = createSSRApp(App)
85+
86+
// install vue-i18n
87+
app.use(i18n)
88+
89+
console.log(await renderToString(app))
90+
// output -> <div>I eat 2 bananas!</div>`
91+
```
92+
93+
94+
### Pre-Translation with using `v-t` custom directive
95+
96+
You can pre-translation i18n locale messsages of vue-i18n.
97+
98+
> :warning: NOTE: Only the scope of global i18n locale messages can be pre-translated !!
3299
33-
The following example that use `compile` function of `@vue/compiler-dom`:
100+
> :warning: NOTE: Currently only `v-t` custom directive is supported !!
101+
102+
In order to use this feature, you need to specify to Vue compiler option.
103+
The following example that use `compile` of `@vue/compiler-dom`:
34104

35105
```js
36106
import { compile } from '@vue/compiler-dom'
37107
import { createI18n } from 'vue-i18n'
38-
import { defineTransformVT } from '@intlify/vue-i18n-extensions'
108+
import { transformVTDirective } from '@intlify/vue-i18n-extensions'
39109

110+
// create i18n instance
40111
const i18n = createI18n({
41112
locale: 'ja',
42113
messages: {
@@ -48,23 +119,24 @@ const i18n = createI18n({
48119
}
49120
}
50121
})
51-
const transformVT = defineTransformVT(i18n)
122+
123+
// get transform from `transformVTDirective` function, with `i18n` option
124+
const transformVT = transformVTDirective({ i18n })
52125

53126
const { code } = compile(`<p v-t="'hello'"></p>`, {
54127
mode: 'function',
55128
hoistStatic: true,
56129
prefixIdentifiers: true,
57-
directiveTransforms: { t: transformVT }
130+
directiveTransforms: { t: transformVT } // <- you need to specify to `directiveTransforms` option!
58131
})
59-
console.log(codel)
60-
/* output -> 'hello'
61-
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
132+
console.log(code)
133+
/*
134+
output ->
135+
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
62136
63-
const _hoisted_1 = { textContent: "こんにちは" }
64-
65-
return function render(_ctx, _cache) {
66-
return (_openBlock(), _createBlock("p", _hoisted_1))
67-
}
137+
return function render(_ctx, _cache) {
138+
return (_openBlock(), _createBlock(\\"div\\", null, \\"こんにちは!\\"))
139+
}
68140
*/
69141
```
70142

@@ -73,7 +145,7 @@ The following configration example of `vue-loader`:
73145
```js
74146
const { VueLoaderPlugin } = require('vue-loader')
75147
const { createI18n } = require('vue-i18n')
76-
const { defineTransformVT } = require('@intlify/vue-i18n-extensions')
148+
const { transformVTDirective } = require('@intlify/vue-i18n-extensions')
77149

78150
const i18n = createI18n({
79151
locale: 'ja',
@@ -86,7 +158,7 @@ const i18n = createI18n({
86158
}
87159
}
88160
})
89-
const transformVT = defineTransformVT(i18n)
161+
const transformVT = transformVTDirective(i18n)
90162

91163
module.exports = {
92164
module: {
@@ -112,6 +184,16 @@ module.exports = {
112184
}
113185
```
114186

187+
You can specify the transform resulting from `transformVTDirective` in the compiler options for each package offered by vue-next, and tool chains:
188+
189+
- `@vue/compiler-core` (`options` at `baseCompile` function)
190+
- `@vue/compiler-dom` (`options` at `compile` function)
191+
- `@vue/compiler-ssr` (`options` at `compile` function)
192+
- `@vue/compiler-sfc` (`compilerOptions` at `compileTemplate` function)
193+
- `vue-loader` (`compilerOptions` at `options`)
194+
- `rollup-plugin-vue`
195+
196+
115197
## :copyright: License
116198

117199
[MIT](http://opensource.org/licenses/MIT)

e2e/example.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ describe(`pre-compilation`, () => {
55

66
test('rendering', async () => {
77
await expect(page).toMatch('こんにちは、世界!')
8+
await expect(page).toMatch('hello, world!')
89
})
910
})

example/App.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
<template>
22
<p v-t="'hello'"></p>
3+
<p v-t="{ path: hello, locale: 'en' }"></p>
34
</template>
45

56
<script>
7+
import { ref } from 'vue'
68
import { useI18n } from 'vue-i18n'
79
810
export default {
911
name: 'App',
1012
setup() {
11-
return useI18n()
13+
const hello = ref('hello')
14+
return { hello, ...useI18n() }
1215
}
1316
}
1417
</script>

example/webpack.config.js

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const { VueLoaderPlugin } = require('vue-loader')
33
const { createI18n } = require('vue-i18n')
44
const en = require('./locales/en.json')
55
const ja = require('./locales/ja.json')
6-
const { defineTransformVT } = require('../lib/index')
6+
const { transformVTDirective } = require('../lib/index')
77

88
const i18n = createI18n({
99
locale: 'ja',
@@ -12,7 +12,7 @@ const i18n = createI18n({
1212
ja
1313
}
1414
})
15-
const transformVT = defineTransformVT(i18n)
15+
const transformVT = transformVTDirective(i18n)
1616

1717
module.exports = {
1818
mode: 'development',
@@ -56,20 +56,6 @@ module.exports = {
5656
test: /\.js$/,
5757
loader: 'babel-loader'
5858
}
59-
/*,
60-
{
61-
resourceQuery: /blockType=i18n/,
62-
type: 'javascript/auto',
63-
use: [
64-
{
65-
loader: path.resolve(__dirname, '../lib/index.js'),
66-
options: {
67-
preCompile: true
68-
}
69-
}
70-
]
71-
}
72-
*/
7359
]
7460
},
7561
plugins: [new VueLoaderPlugin()]

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"@typescript-eslint/eslint-plugin": "^3.7.0",
3838
"@typescript-eslint/parser": "^3.7.0",
3939
"@vue/compiler-sfc": "^3.0.0-rc.5",
40+
"@vue/compiler-ssr": "^3.0.0-rc.5",
4041
"@vue/runtime-dom": "^3.0.0-rc.5",
4142
"@vue/server-renderer": "^3.0.0-rc.5",
4243
"babel-loader": "^8.1.0",

src/builder.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { isNumber } from './utils'
2+
3+
/**
4+
* Content Builder options
5+
*
6+
* @remarks
7+
* options that creating a {@link ContentBuilder}
8+
*
9+
* @public
10+
*/
11+
export interface ContentBuilderOptions {
12+
/**
13+
* Intdent level of Builder
14+
*/
15+
indentLevel?: number
16+
}
17+
18+
/**
19+
* Content Builder
20+
*
21+
* @public
22+
*/
23+
export interface ContentBuilder {
24+
/**
25+
* Indent level
26+
* @defaultValue 0
27+
*/
28+
readonly indentLevel: number
29+
/**
30+
* Content
31+
*/
32+
readonly content: string
33+
/**
34+
* Add content
35+
* @param content - additional content
36+
*/
37+
push(content: string): void
38+
/**
39+
* Add line break
40+
*/
41+
newline(): void
42+
/**
43+
* Add content with line break
44+
* @param content - additional content
45+
*/
46+
pushline(content: string): void
47+
/**
48+
* Indent content
49+
* @param withNewLine - whether indent to be added without new line
50+
*/
51+
indent(withoutNewLine?: boolean): void
52+
/**
53+
* DeIndent content
54+
* @param withoutNewLine - whether deindent to be added without new line
55+
*/
56+
deindent(withoutNewLine?: boolean): void
57+
}
58+
59+
/**
60+
* Create a Content Builder
61+
*
62+
* @param options - Content Builder options
63+
*
64+
* @returns A {@link ContentBuilder} instance
65+
*
66+
* @public
67+
*/
68+
export function createContentBuilder(
69+
options: ContentBuilderOptions = {}
70+
): ContentBuilder {
71+
let _indentLevel =
72+
options.indentLevel != null && isNumber(options.indentLevel)
73+
? options.indentLevel
74+
: 0
75+
let _content = ''
76+
77+
function push(content: string): void {
78+
_content += content
79+
}
80+
81+
function _newline(n: number) {
82+
push('\n' + ` `.repeat(n))
83+
}
84+
85+
function newline() {
86+
_newline(_indentLevel)
87+
}
88+
89+
function pushline(content: string) {
90+
push(content)
91+
newline()
92+
}
93+
94+
function indent(withoutNewLine?: boolean) {
95+
if (withoutNewLine) {
96+
++_indentLevel
97+
} else {
98+
_newline(++_indentLevel)
99+
}
100+
}
101+
102+
function deindent(withoutNewLine?: boolean) {
103+
if (withoutNewLine) {
104+
--_indentLevel
105+
} else {
106+
_newline(--_indentLevel)
107+
}
108+
}
109+
110+
return {
111+
get indentLevel(): number {
112+
return _indentLevel
113+
},
114+
get content(): string {
115+
return String(_content)
116+
},
117+
push,
118+
newline,
119+
pushline,
120+
indent,
121+
deindent
122+
}
123+
}

0 commit comments

Comments
 (0)