Skip to content

Commit 45a105b

Browse files
authoredSep 28, 2022
fix(gatsby): Persist manifest on cached builds (#36693)
* fix(gatsby): Persist manifest on cached builds * Clarify manifest path * Error handling

File tree

3 files changed

+70
-11
lines changed

3 files changed

+70
-11
lines changed
 

‎packages/gatsby-cli/src/structured-errors/error-map.ts

+15
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,21 @@ const errors = {
783783
docsUrl: `https://gatsby.dev/partial-hydration-error`,
784784
category: ErrorCategory.USER,
785785
},
786+
"80001": {
787+
text: (): string =>
788+
stripIndents(
789+
`
790+
Failed to restore previous client module manifest.
791+
792+
This can happen if the manifest is corrupted or is not compatible with the current version of Gatsby.
793+
794+
Please run "gatsby clean" and try again. If the issue persists, please open an issue with a reproduction at https://github.com/gatsbyjs/gatsby/issues/new for more help.
795+
`
796+
),
797+
level: Level.ERROR,
798+
docsUrl: `https://gatsby.dev/partial-hydration-error`,
799+
category: ErrorCategory.USER,
800+
},
786801
}
787802

788803
export type ErrorId = string | keyof typeof errors

‎packages/gatsby/src/utils/webpack.config.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const { store } = require(`../redux`)
1010
const { actions } = require(`../redux/actions`)
1111
const { getPublicPath } = require(`./get-public-path`)
1212
const debug = require(`debug`)(`gatsby:webpack-config`)
13-
const report = require(`gatsby-cli/lib/reporter`)
13+
const reporter = require(`gatsby-cli/lib/reporter`)
1414
import { withBasePath, withTrailingSlash } from "./path"
1515
import { getGatsbyDependents } from "./gatsby-dependents"
1616
const apiRunnerNode = require(`./api-runner-node`)
@@ -74,7 +74,7 @@ module.exports = async (
7474
parsed = dotenv.parse(fs.readFileSync(envFile, { encoding: `utf8` }))
7575
} catch (err) {
7676
if (err.code !== `ENOENT`) {
77-
report.error(
77+
reporter.error(
7878
`There was a problem processing the .env file (${envFile})`,
7979
err
8080
)
@@ -232,7 +232,7 @@ module.exports = async (
232232
plugins.virtualModules(),
233233
new BabelConfigItemsCacheInvalidatorPlugin(),
234234
process.env.GATSBY_WEBPACK_LOGGING?.split(`,`)?.includes(stage) &&
235-
new WebpackLoggingPlugin(program.directory, report, program.verbose),
235+
new WebpackLoggingPlugin(program.directory, reporter, program.verbose),
236236
].filter(Boolean)
237237

238238
switch (stage) {
@@ -285,8 +285,13 @@ module.exports = async (
285285
new StaticQueryMapper(store),
286286
isPartialHydrationEnabled
287287
? new PartialHydrationPlugin(
288-
`../.cache/partial-hydration/manifest.json`,
289-
path.join(directory, `.cache`, `public-page-renderer-prod.js`)
288+
path.join(
289+
directory,
290+
`.cache`,
291+
`partial-hydration`,
292+
`manifest.json`
293+
),
294+
reporter
290295
)
291296
: null,
292297
])

‎packages/gatsby/src/utils/webpack/plugins/partial-hydration.ts

+45-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import * as path from "path"
2+
import fs from "fs-extra"
23
import Template from "webpack/lib/Template"
34
import ModuleDependency from "webpack/lib/dependencies/ModuleDependency"
45
import NullDependency from "webpack/lib/dependencies/NullDependency"
56
import { createNormalizedModuleKey } from "../utils/create-normalized-module-key"
67
import webpack, { Module, NormalModule, Dependency, javascript } from "webpack"
8+
import type Reporter from "gatsby-cli/lib/reporter"
79

810
interface IModuleExport {
911
id: string
@@ -33,14 +35,17 @@ class ClientReferenceDependency extends ModuleDependency {
3335
*/
3436
export class PartialHydrationPlugin {
3537
name = `PartialHydrationPlugin`
36-
_manifestPath: string
37-
_rootFilePath: string
38+
39+
_manifestPath: string // Absolute path to where the manifest file should be written
40+
_reporter: typeof Reporter
41+
3842
_references: Array<ClientReferenceDependency> = []
3943
_clientModules = new Set<webpack.NormalModule>()
44+
_previousManifest = {}
4045

41-
constructor(manifestPath: string, rootFilePath: string) {
46+
constructor(manifestPath: string, reporter: typeof Reporter) {
4247
this._manifestPath = manifestPath
43-
this._rootFilePath = rootFilePath
48+
this._reporter = reporter
4449
}
4550

4651
_generateClientReferenceChunk(
@@ -188,6 +193,27 @@ export class PartialHydrationPlugin {
188193
}
189194

190195
apply(compiler: webpack.Compiler): void {
196+
// Restore manifest from the previous compilation, otherwise it will be wiped since files aren't visited on cached builds
197+
compiler.hooks.beforeCompile.tap(this.name, () => {
198+
try {
199+
const previousManifest = fs.existsSync(this._manifestPath)
200+
201+
if (!previousManifest) {
202+
return
203+
}
204+
205+
this._previousManifest = JSON.parse(
206+
fs.readFileSync(this._manifestPath, `utf-8`)
207+
)
208+
} catch (error) {
209+
this._reporter.panic({
210+
id: `80001`,
211+
context: {},
212+
error,
213+
})
214+
}
215+
})
216+
191217
compiler.hooks.thisCompilation.tap(
192218
this.name,
193219
(compilation, { normalModuleFactory }) => {
@@ -298,10 +324,23 @@ export class PartialHydrationPlugin {
298324
compilation.options.context as string
299325
)
300326

327+
/**
328+
* `emitAsset` is unclear about what the path should be relative to and absolute paths don't work. This works so we'll go with that.
329+
* @see {@link https://webpack.js.org/api/compilation-object/#emitasset}
330+
*/
331+
const emitManifestPath = `..${this._manifestPath.replace(
332+
compiler.context,
333+
``
334+
)}`
335+
301336
compilation.emitAsset(
302-
this._manifestPath,
337+
emitManifestPath,
303338
new webpack.sources.RawSource(
304-
JSON.stringify(manifest, null, 2),
339+
JSON.stringify(
340+
{ ...this._previousManifest, ...manifest },
341+
null,
342+
2
343+
),
305344
false
306345
)
307346
)

0 commit comments

Comments
 (0)
Please sign in to comment.