Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: hmr doesn't work when modifying the code of jsx in sfc #4563

Merged
merged 2 commits into from Sep 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/playground/vue-jsx/Script.vue
@@ -0,0 +1,14 @@
<script lang="jsx">
import { defineComponent, ref } from 'vue'
export default defineComponent(() => {
const count = ref(4)
const inc = () => count.value++
return () => (
<button class="script" onClick={inc}>
script {count.value}
</button>
)
})
</script>
12 changes: 12 additions & 0 deletions packages/playground/vue-jsx/SrcImport.jsx
@@ -0,0 +1,12 @@
import { defineComponent, ref } from 'vue'

export default defineComponent(() => {
const count = ref(5)
const inc = () => count.value++

return () => (
<button class="src-import" onClick={inc}>
src import {count.value}
</button>
)
})
1 change: 1 addition & 0 deletions packages/playground/vue-jsx/SrcImport.vue
@@ -0,0 +1 @@
<script src="./SrcImport.jsx"></script>
28 changes: 28 additions & 0 deletions packages/playground/vue-jsx/__tests__/vue-jsx.spec.ts
Expand Up @@ -5,6 +5,8 @@ test('should render', async () => {
expect(await page.textContent('.named-specifier')).toMatch('1')
expect(await page.textContent('.default')).toMatch('2')
expect(await page.textContent('.default-tsx')).toMatch('3')
expect(await page.textContent('.script')).toMatch('4')
expect(await page.textContent('.src-import')).toMatch('5')
expect(await page.textContent('.other-ext')).toMatch('Other Ext')
})

Expand All @@ -17,6 +19,10 @@ test('should update', async () => {
expect(await page.textContent('.default')).toMatch('3')
await page.click('.default-tsx')
expect(await page.textContent('.default-tsx')).toMatch('4')
await page.click('.script')
expect(await page.textContent('.script')).toMatch('5')
await page.click('.src-import')
expect(await page.textContent('.src-import')).toMatch('6')
})

if (!isBuild) {
Expand Down Expand Up @@ -74,4 +80,26 @@ if (!isBuild) {
// should not affect other components on the page
expect(await page.textContent('.named')).toMatch('1')
})

test('hmr: script in .vue', async () => {
editFile('Script.vue', (code) =>
code.replace('script {count', 'script updated {count')
)
await untilUpdated(() => page.textContent('.script'), 'script updated 4')

expect(await page.textContent('.src-import')).toMatch('6')
})

test('hmr: src import in .vue', async () => {
await page.click('.script')
editFile('SrcImport.jsx', (code) =>
code.replace('src import {count', 'src import updated {count')
)
await untilUpdated(
() => page.textContent('.src-import'),
'src import updated 5'
)

expect(await page.textContent('.script')).toMatch('5')
})
}
4 changes: 4 additions & 0 deletions packages/playground/vue-jsx/main.jsx
Expand Up @@ -2,6 +2,8 @@ import { createApp } from 'vue'
import { Named, NamedSpec, default as Default } from './Comps'
import { default as TsxDefault } from './Comp'
import OtherExt from './OtherExt.tesx'
import JsxScript from './Script.vue'
import JsxSrcImport from './SrcImport.vue'

function App() {
return (
Expand All @@ -11,6 +13,8 @@ function App() {
<Default />
<TsxDefault />
<OtherExt />
<JsxScript />
<JsxSrcImport />
</>
)
}
Expand Down
3 changes: 2 additions & 1 deletion packages/playground/vue-jsx/package.json
Expand Up @@ -9,6 +9,7 @@
"serve": "vite preview"
},
"devDependencies": {
"@vitejs/plugin-vue-jsx": "^1.0.0"
"@vitejs/plugin-vue-jsx": "^1.0.0",
"@vitejs/plugin-vue": "^1.3.0"
}
}
4 changes: 3 additions & 1 deletion packages/playground/vue-jsx/vite.config.js
@@ -1,4 +1,5 @@
const vueJsxPlugin = require('@vitejs/plugin-vue-jsx')
const vuePlugin = require('@vitejs/plugin-vue')

/**
* @type {import('vite').UserConfig}
Expand All @@ -7,7 +8,8 @@ module.exports = {
plugins: [
vueJsxPlugin({
include: [/\.tesx$/, /\.[jt]sx$/]
})
}),
vuePlugin()
],
build: {
// to make tests faster
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-vue-jsx/index.js
Expand Up @@ -207,7 +207,7 @@ function vueJsxPlugin(options = {}) {
) + `\nexport default __default__`
}

if (needHmr && !ssr) {
if (needHmr && !ssr && !/\?vue&type=script/.test(id)) {
let code = result.code
let callbackCode = ``
for (const { local, exported, id } of hotComponents) {
Expand Down
9 changes: 8 additions & 1 deletion packages/plugin-vue/src/handleHotUpdate.ts
Expand Up @@ -46,7 +46,14 @@ export async function handleHotUpdate({
!isEqualBlock(descriptor.script, prevDescriptor.script) ||
!isEqualBlock(descriptor.scriptSetup, prevDescriptor.scriptSetup)
) {
affectedModules.add(mainModule)
let scriptModule: ModuleNode | undefined
if (descriptor.script?.lang && !descriptor.script.src) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is not necessary to fix the case once vue-plugin-jsx stops generating HMR code when detecting it's inside a SFC script block?

Is it correct that this is added only to handle the case where user or other plugins use HMR API inside <script>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's to handle the case where <script lang='jsx'> is used, sfc request the file .vue?vue&type=script&lang.jsx.
When the jsx code is changed, the sfc file will be requested again.
Because the module of .vue?vue&type=script&lang.jsx is excluded by the handleHotUpdate of plugin-vue,
.vue?vue&type=script&lang.jsx will not be injected with t= lastHMRTimestamp, so it will not be reloaded.

const scriptModuleRE = new RegExp(
`type=script.*&lang\.${descriptor.script.lang}$`
)
scriptModule = modules.find((m) => scriptModuleRE.test(m.url))
}
affectedModules.add(scriptModule || mainModule)
}

if (!isEqualBlock(descriptor.template, prevDescriptor.template)) {
Expand Down