Skip to content

Commit 7a6aa2a

Browse files
committed
feat(plugin-vue-jsx): register jsx module during ssr
1 parent fa2d7d6 commit 7a6aa2a

File tree

5 files changed

+83
-23
lines changed

5 files changed

+83
-23
lines changed

packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ test('/', async () => {
5050
expect(html).toMatch(
5151
/link rel="stylesheet".*?href="\/assets\/Home\.\w{8}\.css"/
5252
)
53+
// JSX component preload registration
54+
expect(html).toMatch(
55+
/link rel="modulepreload".*?href="\/assets\/Foo\.\w{8}\.js"/
56+
)
57+
expect(html).toMatch(
58+
/link rel="stylesheet".*?href="\/assets\/Foo\.\w{8}\.css"/
59+
)
5360
expect(html).not.toMatch(
5461
/link rel="modulepreload".*?href="\/assets\/About\.\w{8}\.js"/
5562
)
@@ -62,10 +69,12 @@ test('/', async () => {
6269
test('css', async () => {
6370
if (isBuild) {
6471
expect(await getColor('h1')).toBe('green')
72+
expect(await getColor('.jsx')).toBe('blue')
6573
} else {
6674
// During dev, the CSS is loaded from async chunk and we may have to wait
6775
// when the test runs concurrently.
6876
await untilUpdated(() => getColor('h1'), 'green')
77+
await untilUpdated(() => getColor('.jsx'), 'blue')
6978
}
7079
})
7180

packages/playground/ssr-vue/src/components/Foo.jsx

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { defineComponent } from 'vue'
2+
import './foo.css'
23

34
// named exports w/ variable declaration: ok
45
export const Foo = defineComponent({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.jsx {
2+
color: blue;
3+
}

packages/playground/ssr-vue/src/pages/Home.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
<img src="../assets/logo.png" alt="logo" />
55
</p>
66
<button @click="state.count++">count is: {{ state.count }}</button>
7-
<Foo/>
7+
<Foo />
88
</template>
99

1010
<script setup>
11-
import { reactive } from 'vue'
12-
import { Foo } from '../components/Foo'
11+
import { reactive, defineAsyncComponent } from 'vue'
12+
const Foo = defineAsyncComponent(() => import('../components/Foo').then(mod => mod.Foo))
1313
1414
const state = reactive({ count: 0 })
1515
</script>

packages/plugin-vue-jsx/index.js

+67-20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,29 @@ const jsx = require('@vue/babel-plugin-jsx')
44
const importMeta = require('@babel/plugin-syntax-import-meta')
55
const hash = require('hash-sum')
66

7+
const ssrRegisterHelperId = '/__vue-jsx-ssr-register-helper'
8+
const ssrRegisterHelperCode =
9+
`import { useSSRContext } from "vue"\n` +
10+
`export ${ssrRegisterHelper.toString()}`
11+
12+
/**
13+
* This function is serialized with toString() and evaluated as a virtual
14+
* module during SSR
15+
* @param {import('vue').ComponentOptions} comp
16+
* @param {string} filename
17+
*/
18+
function ssrRegisterHelper(comp, filename) {
19+
const setup = comp.setup
20+
comp.setup = (props, ctx) => {
21+
// @ts-ignore
22+
const ssrContext = useSSRContext()
23+
;(ssrContext.modules || (ssrContext.modules = new Set())).add(filename)
24+
if (setup) {
25+
return setup(props, ctx)
26+
}
27+
}
28+
}
29+
730
/**
831
* @param {import('@vue/babel-plugin-jsx').VueJSXPluginOptions} options
932
* @returns {import('vite').Plugin}
@@ -35,6 +58,18 @@ function vueJsxPlugin(options = {}) {
3558
needSourceMap = config.command === 'serve' || !!config.build.sourcemap
3659
},
3760

61+
resolveId(id) {
62+
if (id === ssrRegisterHelperId) {
63+
return id
64+
}
65+
},
66+
67+
load(id) {
68+
if (id === ssrRegisterHelperId) {
69+
return ssrRegisterHelperCode
70+
}
71+
},
72+
3873
transform(code, id, ssr) {
3974
if (/\.[jt]sx$/.test(id)) {
4075
const plugins = [importMeta, [jsx, options]]
@@ -53,7 +88,7 @@ function vueJsxPlugin(options = {}) {
5388
sourceFileName: id
5489
})
5590

56-
if (ssr || !needHmr) {
91+
if (!ssr && !needHmr) {
5792
return {
5893
code: result.code,
5994
map: result.map
@@ -143,28 +178,40 @@ function vueJsxPlugin(options = {}) {
143178
}
144179

145180
if (hotComponents.length) {
146-
let code = result.code
147-
if (hasDefault) {
148-
code =
149-
code.replace(
150-
/export default defineComponent/g,
151-
`const __default__ = defineComponent`
152-
) + `\nexport default __default__`
153-
}
181+
if (needHmr && !ssr) {
182+
let code = result.code
183+
if (hasDefault) {
184+
code =
185+
code.replace(
186+
/export default defineComponent/g,
187+
`const __default__ = defineComponent`
188+
) + `\nexport default __default__`
189+
}
154190

155-
let callbackCode = ``
156-
for (const { local, exported, id } of hotComponents) {
157-
code +=
158-
`\n${local}.__hmrId = "${id}"` +
159-
`\n__VUE_HMR_RUNTIME__.createRecord("${id}", ${local})`
160-
callbackCode += `\n__VUE_HMR_RUNTIME__.reload("${id}", __${exported})`
161-
}
191+
let callbackCode = ``
192+
for (const { local, exported, id } of hotComponents) {
193+
code +=
194+
`\n${local}.__hmrId = "${id}"` +
195+
`\n__VUE_HMR_RUNTIME__.createRecord("${id}", ${local})`
196+
callbackCode += `\n__VUE_HMR_RUNTIME__.reload("${id}", __${exported})`
197+
}
198+
199+
code += `\nimport.meta.hot.accept(({${hotComponents
200+
.map((c) => `${c.exported}: __${c.exported}`)
201+
.join(',')}}) => {${callbackCode}\n})`
162202

163-
code += `\nimport.meta.hot.accept(({${hotComponents
164-
.map((c) => `${c.exported}: __${c.exported}`)
165-
.join(',')}}) => {${callbackCode}\n})`
203+
result.code = code
204+
}
166205

167-
result.code = code
206+
if (ssr) {
207+
let ssrInjectCode =
208+
`\nimport { ssrRegisterHelper } from "${ssrRegisterHelperId}"` +
209+
`\nconst __moduleId = ${JSON.stringify(id)}`
210+
for (const { local } of hotComponents) {
211+
ssrInjectCode += `\nssrRegisterHelper(${local}, __moduleId)`
212+
}
213+
result.code += ssrInjectCode
214+
}
168215
}
169216

170217
return {

0 commit comments

Comments
 (0)