Skip to content

Commit

Permalink
feat(plugin-vue-jsx): register jsx module during ssr
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Feb 9, 2021
1 parent fa2d7d6 commit 7a6aa2a
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 23 deletions.
9 changes: 9 additions & 0 deletions packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ test('/', async () => {
expect(html).toMatch(
/link rel="stylesheet".*?href="\/assets\/Home\.\w{8}\.css"/
)
// JSX component preload registration
expect(html).toMatch(
/link rel="modulepreload".*?href="\/assets\/Foo\.\w{8}\.js"/
)
expect(html).toMatch(
/link rel="stylesheet".*?href="\/assets\/Foo\.\w{8}\.css"/
)
expect(html).not.toMatch(
/link rel="modulepreload".*?href="\/assets\/About\.\w{8}\.js"/
)
Expand All @@ -62,10 +69,12 @@ test('/', async () => {
test('css', async () => {
if (isBuild) {
expect(await getColor('h1')).toBe('green')
expect(await getColor('.jsx')).toBe('blue')
} else {
// During dev, the CSS is loaded from async chunk and we may have to wait
// when the test runs concurrently.
await untilUpdated(() => getColor('h1'), 'green')
await untilUpdated(() => getColor('.jsx'), 'blue')
}
})

Expand Down
1 change: 1 addition & 0 deletions packages/playground/ssr-vue/src/components/Foo.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineComponent } from 'vue'
import './foo.css'

// named exports w/ variable declaration: ok
export const Foo = defineComponent({
Expand Down
3 changes: 3 additions & 0 deletions packages/playground/ssr-vue/src/components/foo.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.jsx {
color: blue;
}
6 changes: 3 additions & 3 deletions packages/playground/ssr-vue/src/pages/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
<img src="../assets/logo.png" alt="logo" />
</p>
<button @click="state.count++">count is: {{ state.count }}</button>
<Foo/>
<Foo />
</template>

<script setup>
import { reactive } from 'vue'
import { Foo } from '../components/Foo'
import { reactive, defineAsyncComponent } from 'vue'
const Foo = defineAsyncComponent(() => import('../components/Foo').then(mod => mod.Foo))
const state = reactive({ count: 0 })
</script>
Expand Down
87 changes: 67 additions & 20 deletions packages/plugin-vue-jsx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@ const jsx = require('@vue/babel-plugin-jsx')
const importMeta = require('@babel/plugin-syntax-import-meta')
const hash = require('hash-sum')

const ssrRegisterHelperId = '/__vue-jsx-ssr-register-helper'
const ssrRegisterHelperCode =
`import { useSSRContext } from "vue"\n` +
`export ${ssrRegisterHelper.toString()}`

/**
* This function is serialized with toString() and evaluated as a virtual
* module during SSR
* @param {import('vue').ComponentOptions} comp
* @param {string} filename
*/
function ssrRegisterHelper(comp, filename) {
const setup = comp.setup
comp.setup = (props, ctx) => {
// @ts-ignore
const ssrContext = useSSRContext()
;(ssrContext.modules || (ssrContext.modules = new Set())).add(filename)
if (setup) {
return setup(props, ctx)
}
}
}

/**
* @param {import('@vue/babel-plugin-jsx').VueJSXPluginOptions} options
* @returns {import('vite').Plugin}
Expand Down Expand Up @@ -35,6 +58,18 @@ function vueJsxPlugin(options = {}) {
needSourceMap = config.command === 'serve' || !!config.build.sourcemap
},

resolveId(id) {
if (id === ssrRegisterHelperId) {
return id
}
},

load(id) {
if (id === ssrRegisterHelperId) {
return ssrRegisterHelperCode
}
},

transform(code, id, ssr) {
if (/\.[jt]sx$/.test(id)) {
const plugins = [importMeta, [jsx, options]]
Expand All @@ -53,7 +88,7 @@ function vueJsxPlugin(options = {}) {
sourceFileName: id
})

if (ssr || !needHmr) {
if (!ssr && !needHmr) {
return {
code: result.code,
map: result.map
Expand Down Expand Up @@ -143,28 +178,40 @@ function vueJsxPlugin(options = {}) {
}

if (hotComponents.length) {
let code = result.code
if (hasDefault) {
code =
code.replace(
/export default defineComponent/g,
`const __default__ = defineComponent`
) + `\nexport default __default__`
}
if (needHmr && !ssr) {
let code = result.code
if (hasDefault) {
code =
code.replace(
/export default defineComponent/g,
`const __default__ = defineComponent`
) + `\nexport default __default__`
}

let callbackCode = ``
for (const { local, exported, id } of hotComponents) {
code +=
`\n${local}.__hmrId = "${id}"` +
`\n__VUE_HMR_RUNTIME__.createRecord("${id}", ${local})`
callbackCode += `\n__VUE_HMR_RUNTIME__.reload("${id}", __${exported})`
}
let callbackCode = ``
for (const { local, exported, id } of hotComponents) {
code +=
`\n${local}.__hmrId = "${id}"` +
`\n__VUE_HMR_RUNTIME__.createRecord("${id}", ${local})`
callbackCode += `\n__VUE_HMR_RUNTIME__.reload("${id}", __${exported})`
}

code += `\nimport.meta.hot.accept(({${hotComponents
.map((c) => `${c.exported}: __${c.exported}`)
.join(',')}}) => {${callbackCode}\n})`

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

result.code = code
if (ssr) {
let ssrInjectCode =
`\nimport { ssrRegisterHelper } from "${ssrRegisterHelperId}"` +
`\nconst __moduleId = ${JSON.stringify(id)}`
for (const { local } of hotComponents) {
ssrInjectCode += `\nssrRegisterHelper(${local}, __moduleId)`
}
result.code += ssrInjectCode
}
}

return {
Expand Down

0 comments on commit 7a6aa2a

Please sign in to comment.