Skip to content

Commit

Permalink
Fix HMR issue after patching the client module (#43819)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
  • Loading branch information
shuding and timneutkens committed Dec 7, 2022
1 parent 56ea306 commit 476bb09
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 9 deletions.
5 changes: 4 additions & 1 deletion packages/next/client/app-index.tsx
Expand Up @@ -29,7 +29,10 @@ __webpack_require__.u = (chunkId: any) => {
// Ignore the module ID transform in client.
// eslint-disable-next-line no-undef
// @ts-expect-error TODO: fix type
self.__next_require__ = __webpack_require__
self.__next_require__ = (id: string) => {
const modId = id.replace(/\?.+$/, '')
return __webpack_require__(modId)
}

// eslint-disable-next-line no-undef
;(self as any).__next_chunk_load__ = (chunk: string) => {
Expand Down
26 changes: 25 additions & 1 deletion packages/next/server/app-render.tsx
Expand Up @@ -1598,11 +1598,35 @@ export async function renderToHTMLOrFlight(
).slice(1),
]

const serverComponentManifestWithHMR = dev
? new Proxy(serverComponentManifest, {
get: (target, prop) => {
if (
typeof prop === 'string' &&
!prop.startsWith('_') &&
target[prop]
) {
// Attach TS (timestamp) query param to IDs to get rid of flight client's module cache on HMR.
const namedExports: any = {}
const ts = Date.now()
for (let key in target[prop]) {
namedExports[key] = {
...target[prop][key],
id: `${target[prop][key].id}?ts=${ts}`,
}
}
return namedExports
}
return target[prop]
},
})
: serverComponentManifest

// For app dir, use the bundled version of Fizz renderer (renderToReadableStream)
// which contains the subset React.
const readable = ComponentMod.renderToReadableStream(
flightData,
serverComponentManifest,
serverComponentManifestWithHMR,
{
context: serverContexts,
onError: flightDataRendererErrorHandler,
Expand Down
26 changes: 19 additions & 7 deletions test/e2e/app-dir/index.test.ts
Expand Up @@ -441,7 +441,7 @@ describe('app dir', () => {
}
})

it.skip('should HMR correctly when changing the component type', async () => {
it('should HMR correctly when changing the component type', async () => {
const filePath = 'app/dashboard/page/page.jsx'
const origContent = await next.readFile(filePath)

Expand All @@ -466,13 +466,12 @@ describe('app dir', () => {
)

// Change to client component
await new Promise((resolve) => setTimeout(resolve, 1000))
await next.patchFile(
filePath,
origContent
.replace("// 'use client'", "'use client'")
.replace(
'hello dashboard/page in server component!',
'hello dashboard/page!',
'hello dashboard/page in client component!'
)
)
Expand All @@ -482,18 +481,31 @@ describe('app dir', () => {
)

// Change back to server component
await next.patchFile(
filePath,
origContent.replace(
'hello dashboard/page!',
'hello dashboard/page in server component2!'
)
)
await check(
() => browser.elementByCss('p').text(),
/in server component2/
)

// Change to client component again
await next.patchFile(
filePath,
origContent
.replace("'use client'", "// 'use client'")
.replace("// 'use client'", "'use client'")
.replace(
'hello dashboard/page in client component!',
'hello dashboard/page in server component!'
'hello dashboard/page!',
'hello dashboard/page in client component2!'
)
)
await check(
() => browser.elementByCss('p').text(),
/in server component/
/in client component2/
)
} finally {
await next.patchFile(filePath, origContent)
Expand Down

0 comments on commit 476bb09

Please sign in to comment.