Skip to content

Commit

Permalink
Update server-only changes HMR handling (#34298)
Browse files Browse the repository at this point in the history
* Update server-only changes HMR handling

* Add failing tests for GS(S)P server only changes

* update test

* normalize backslashes

* Update to xor the chunk hashes

* remove test change

* remove other test change
  • Loading branch information
ijjk committed Feb 16, 2022
1 parent d288d43 commit 59714db
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 212 deletions.
65 changes: 50 additions & 15 deletions packages/next/server/dev/hot-reloader.ts
Expand Up @@ -3,7 +3,7 @@ import { IncomingMessage, ServerResponse } from 'http'
import { WebpackHotMiddleware } from './hot-middleware'
import { join, relative, isAbsolute } from 'path'
import { UrlObject } from 'url'
import { webpack } from 'next/dist/compiled/webpack/webpack'
import { webpack, StringXor } from 'next/dist/compiled/webpack/webpack'
import type { webpack5 } from 'next/dist/compiled/webpack/webpack'
import {
createEntrypoints,
Expand Down Expand Up @@ -594,21 +594,56 @@ export default class HotReloader {
const trackPageChanges =
(pageHashMap: Map<string, string>, changedItems: Set<string>) =>
(stats: webpack5.Compilation) => {
stats.entrypoints.forEach((entry, key) => {
if (key.startsWith('pages/')) {
// TODO this doesn't handle on demand loaded chunks
entry.chunks.forEach((chunk: any) => {
if (chunk.id === key) {
const prevHash = pageHashMap.get(key)

if (prevHash && prevHash !== chunk.hash) {
changedItems.add(key)
try {
stats.entrypoints.forEach((entry, key) => {
if (key.startsWith('pages/')) {
// TODO this doesn't handle on demand loaded chunks
entry.chunks.forEach((chunk) => {
if (chunk.id === key) {
const modsIterable: any =
stats.chunkGraph.getChunkModulesIterable(chunk)

let chunksHash = new StringXor()

modsIterable.forEach((mod: any) => {
if (
mod.resource &&
mod.resource.replace(/\\/g, '/').includes(key)
) {
// use original source to calculate hash since mod.hash
// includes the source map in development which changes
// every time for both server and client so we calculate
// the hash without the source map for the page module
const hash = require('crypto')
.createHash('sha256')
.update(mod.originalSource().buffer())
.digest()
.toString('hex')

chunksHash.add(hash)
} else {
// for non-pages we can use the module hash directly
const hash = stats.chunkGraph.getModuleHash(
mod,
chunk.runtime
)
chunksHash.add(hash)
}
})
const prevHash = pageHashMap.get(key)
const curHash = chunksHash.toString()

if (prevHash && prevHash !== curHash) {
changedItems.add(key)
}
pageHashMap.set(key, curHash)
}
pageHashMap.set(key, chunk.hash)
}
})
}
})
})
}
})
} catch (err) {
console.error(err)
}
}

multiCompiler.compilers[0].hooks.emit.tap(
Expand Down
1 change: 1 addition & 0 deletions packages/next/types/webpack.d.ts
Expand Up @@ -35,6 +35,7 @@ declare module 'next/dist/compiled/webpack/webpack' {
export let BasicEvaluatedExpression: any
export let GraphHelpers: any
export let sources: typeof webpackSources
export let StringXor: any
// TODO change this to webpack5
export { webpack4 as webpack, loader, webpack4, webpack5 }
}
Expand Down
@@ -0,0 +1,3 @@
{
"hello": "world"
}
@@ -1,4 +1,5 @@
import { useRouter } from 'next/router'
import data from '../lib/data.json'

export default function Gsp(props) {
if (useRouter().isFallback) {
Expand All @@ -19,6 +20,7 @@ export const getStaticProps = async () => {
return {
props: {
count,
data,
random: Math.random(),
},
}
Expand Down

0 comments on commit 59714db

Please sign in to comment.