From 7a6aa2ad2689bf8221389924a608876866db7b0a Mon Sep 17 00:00:00 2001
From: Evan You
Date: Tue, 9 Feb 2021 09:56:13 -0500
Subject: [PATCH] feat(plugin-vue-jsx): register jsx module during ssr
---
.../ssr-vue/__tests__/ssr-vue.spec.ts | 9 ++
.../playground/ssr-vue/src/components/Foo.jsx | 1 +
.../playground/ssr-vue/src/components/foo.css | 3 +
.../playground/ssr-vue/src/pages/Home.vue | 6 +-
packages/plugin-vue-jsx/index.js | 87 ++++++++++++++-----
5 files changed, 83 insertions(+), 23 deletions(-)
create mode 100644 packages/playground/ssr-vue/src/components/foo.css
diff --git a/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts b/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts
index f4a490582f2cd1..24223b96c4967d 100644
--- a/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts
+++ b/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts
@@ -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"/
)
@@ -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')
}
})
diff --git a/packages/playground/ssr-vue/src/components/Foo.jsx b/packages/playground/ssr-vue/src/components/Foo.jsx
index d810b40886c596..c1670c6a13758f 100644
--- a/packages/playground/ssr-vue/src/components/Foo.jsx
+++ b/packages/playground/ssr-vue/src/components/Foo.jsx
@@ -1,4 +1,5 @@
import { defineComponent } from 'vue'
+import './foo.css'
// named exports w/ variable declaration: ok
export const Foo = defineComponent({
diff --git a/packages/playground/ssr-vue/src/components/foo.css b/packages/playground/ssr-vue/src/components/foo.css
new file mode 100644
index 00000000000000..bfaacd33a2f805
--- /dev/null
+++ b/packages/playground/ssr-vue/src/components/foo.css
@@ -0,0 +1,3 @@
+.jsx {
+ color: blue;
+}
\ No newline at end of file
diff --git a/packages/playground/ssr-vue/src/pages/Home.vue b/packages/playground/ssr-vue/src/pages/Home.vue
index bec719062d6805..29e93f3e3cde16 100644
--- a/packages/playground/ssr-vue/src/pages/Home.vue
+++ b/packages/playground/ssr-vue/src/pages/Home.vue
@@ -4,12 +4,12 @@
-
+
diff --git a/packages/plugin-vue-jsx/index.js b/packages/plugin-vue-jsx/index.js
index cdacfe1ae542d2..5a3479a91e5295 100644
--- a/packages/plugin-vue-jsx/index.js
+++ b/packages/plugin-vue-jsx/index.js
@@ -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}
@@ -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]]
@@ -53,7 +88,7 @@ function vueJsxPlugin(options = {}) {
sourceFileName: id
})
- if (ssr || !needHmr) {
+ if (!ssr && !needHmr) {
return {
code: result.code,
map: result.map
@@ -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 {