Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion integration-tests/ssr/__tests__/ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ describe(`SSR`, () => {

expect(html).toMatchSnapshot()
})

test(`dev & build outputs match`, async () => {
const childProcess = await execa(`yarn`, [`test-output`])

expect(childProcess.code).toEqual(0)
})
}, 15000)

test(`it generates an error page correctly`, async () => {
const src = path.join(__dirname, `/fixtures/bad-page.js`)
const dest = path.join(__dirname, `../src/pages/bad-page.js`)
Expand Down
9 changes: 9 additions & 0 deletions integration-tests/ssr/test-output.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@
)
)

// Fetch once to trigger re-compilation.
await fetch(`${devSiteBasePath}/${path}`)

// Then wait for 6 seconds to ensure it's ready to go.
// Otherwise, tests are flaky depending on the speed of the testing machine.
await new Promise(resolve => {
setTimeout(() => resolve(), 6000)
})

let devStatus = 200
const rawDevHtml = await fetch(`${devSiteBasePath}/${path}`).then(res => {
devStatus = res.status
Expand Down
35 changes: 34 additions & 1 deletion packages/gatsby/src/commands/build-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import telemetry from "gatsby-telemetry"
import { chunk } from "lodash"
import webpack from "webpack"

import { emitter } from "../redux"
import webpackConfig from "../utils/webpack.config"
import { structureWebpackErrors } from "../utils/webpack-error-utils"

Expand All @@ -14,6 +15,30 @@ import { IProgram, Stage } from "./types"
type IActivity = any // TODO
type IWorkerPool = any // TODO

export interface IWebpackWatchingPauseResume extends webpack.Watching {
suspend: () => void
resume: () => void
}

let devssrWebpackCompiler: webpack.Compiler
let devssrWebpackWatcher: IWebpackWatchingPauseResume
let needToRecompileSSRBundle = true
export const getDevSSRWebpack = (): Record<
IWebpackWatchingPauseResume,
webpack.Compiler,
needToRecompileSSRBundle
> => {
if (process.env.gatsby_executing_command !== `develop`) {
throw new Error(`This function can only be called in development`)
}

return {
devssrWebpackWatcher,
devssrWebpackCompiler,
needToRecompileSSRBundle,
}
}

let oldHash = ``
let newHash = ``
const runWebpack = (
Expand All @@ -34,11 +59,19 @@ const runWebpack = (
process.env.GATSBY_EXPERIMENTAL_DEV_SSR &&
stage === `develop-html`
) {
webpack(compilerConfig).watch(
devssrWebpackCompiler = webpack(compilerConfig)
devssrWebpackCompiler.hooks.invalid.tap(`ssr file invalidation`, file => {
needToRecompileSSRBundle = true
})
devssrWebpackWatcher = devssrWebpackCompiler.watch(
{
ignored: /node_modules/,
},
(err, stats) => {
needToRecompileSSRBundle = false
emitter.emit(`DEV_SSR_COMPILATION_DONE`)
devssrWebpackWatcher.suspend()

if (err) {
return reject(err)
} else {
Expand Down
37 changes: 37 additions & 0 deletions packages/gatsby/src/utils/dev-ssr/render-dev-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import report from "gatsby-cli/lib/reporter"
import { startListener } from "../../bootstrap/requires-writer"
import { findPageByPath } from "../find-page-by-path"
import { getPageData as getPageDataExperimental } from "../get-page-data"
import { getDevSSRWebpack } from "../../commands/build-html"
import { emitter } from "../../redux"

const startWorker = (): any => {
const newWorker = new JestWorker(require.resolve(`./render-dev-html-child`), {
Expand Down Expand Up @@ -144,6 +146,41 @@ export const renderDevHTML = ({
return reject(`404 page`)
}

// Resume the webpack watcher and wait for any compilation necessary to happen.
// We timeout after 1.5s as the user might not care per se about SSR.
//
// We pause and resume so there's no excess webpack activity during normal development.
const {
devssrWebpackCompiler,
devssrWebpackWatcher,
needToRecompileSSRBundle,
} = getDevSSRWebpack()
if (
devssrWebpackWatcher &&
devssrWebpackCompiler &&
needToRecompileSSRBundle
) {
let isResolved = false
await new Promise(resolve => {
function finish(stats: Stats): void {
emitter.off(`DEV_SSR_COMPILATION_DONE`, finish)
if (!isResolved) {
resolve(stats)
}
}
emitter.on(`DEV_SSR_COMPILATION_DONE`, finish)
devssrWebpackWatcher.resume()
// Suspending is just a flag, so it's safe to re-suspend right away
devssrWebpackWatcher.suspend()

// Timeout after 1.5s.
setTimeout(() => {
isResolved = true
resolve()
}, 1500)
})
}

// Wait for public/render-page.js to update w/ the page component.
const found = await ensurePathComponentInSSRBundle(pageObj, directory)

Expand Down