Skip to content

Commit

Permalink
handle static query result changes
Browse files Browse the repository at this point in the history
  • Loading branch information
pieh committed Jan 26, 2021
1 parent e6afe73 commit 72eddde
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 10 deletions.
3 changes: 3 additions & 0 deletions integration-tests/artifacts/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,15 @@ describe(`Second run (different pages created, data changed)`, () => {
`/stale-pages/only-not-in-first`,
`/page-query-changing-data-but-not-id/`,
`/page-query-dynamic-2/`,
`/static-query-result-tracking/should-invalidate/`,
]

const expectedPagesToRemainFromPreviousBuild = [
`/stale-pages/stable/`,
`/page-query-stable/`,
`/page-query-changing-but-not-invalidating-html/`,
`/static-query-result-tracking/stable/`,
`/static-query-result-tracking/rerun-query-but-dont-recreate-html/`,
]

const expectedPages = [
Expand Down
19 changes: 18 additions & 1 deletion integration-tests/artifacts/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ exports.onPreInit = ({ reporter, emitter }) => {
}

exports.sourceNodes = ({ actions, createContentDigest }) => {
// used to create pages and queried by them
function createNodeHelper(type, nodePartial) {
actions.createNode({
...nodePartial,
Expand All @@ -32,6 +31,7 @@ exports.sourceNodes = ({ actions, createContentDigest }) => {
})
}

// used to create pages and queried by them
createNodeHelper(`DepPageQuery`, {
id: `page-query-stable`,
label: `Stable (always created)`,
Expand All @@ -52,6 +52,23 @@ exports.sourceNodes = ({ actions, createContentDigest }) => {
id: `page-query-dynamic-${runNumber}`, // this should cause different page path
label: `This is run number {$runNumber}`,
})

// used by static queries
createNodeHelper(`DepStaticQuery`, {
id: `static-query-stable`,
label: `Stable (always created)`,
})

createNodeHelper(`DepStaticQuery`, {
id: `static-query-changing-but-not-invalidating-html`,
label: `Stable (always created)`,
buildRun: runNumber, // important for test setup - this will invalidate static query, but shouldn't invalidate html (if it's not queried)
})

createNodeHelper(`DepStaticQuery`, {
id: `static-query-changing-data-but-not-id`,
label: `This is${isFirstRun ? `` : ` not`} a first run`, // this will be queried - we want to invalidate html here
})
}

exports.createPages = ({ actions }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react"
import { graphql, useStaticQuery } from "gatsby"

export default function DepStaticQueryPageRerunQueryButDontRecreateHtml() {
const { data } = useStaticQuery(graphql`
{
depStaticQuery(
id: { eq: "static-query-changing-but-not-invalidating-html" }
) {
label
}
}
`)

return <pre>{JSON.stringify(data, null, 2)}</pre>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react"
import { graphql, useStaticQuery } from "gatsby"

export default function DepStaticQueryPageShouldInvalidate() {
const { data } = useStaticQuery(graphql`
{
depStaticQuery(id: { eq: "static-query-changing-data-but-not-id" }) {
label
}
}
`)

return <pre>{JSON.stringify(data, null, 2)}</pre>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react"
import { graphql, useStaticQuery } from "gatsby"

export default function DepStaticQueryPageStable() {
const { data } = useStaticQuery(graphql`
{
depStaticQuery(id: { eq: "static-query-stable" }) {
label
}
}
`)

return <pre>{JSON.stringify(data, null, 2)}</pre>
}
7 changes: 6 additions & 1 deletion packages/gatsby/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import fs from "fs-extra"
import telemetry from "gatsby-telemetry"

import { doBuildPages, buildRenderer, deleteRenderer } from "./build-html"
import { calcDirtyHtmlFiles } from "../utils/page-html"
import {
calcDirtyHtmlFiles,
markHtmlDirtyIfResultOfUsedStaticQueryChanged,
} from "../utils/page-html"
import { buildProductionBundle } from "./build-javascript"
import { bootstrap } from "../bootstrap"
import apiRunnerNode from "../utils/api-runner-node"
Expand Down Expand Up @@ -203,6 +206,8 @@ module.exports = async function build(program: IBuildArgs): Promise<void> {
buildSSRBundleActivityProgress.end()
}

markHtmlDirtyIfResultOfUsedStaticQueryChanged()

const { toRegenerate, toDelete } = process.env
.GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES
? calcDirtyHtmlFiles(store.getState())
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby/src/query/query-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ export async function queryRunner(
componentPath: queryJob.componentPath,
isPage: queryJob.isPage,
resultHash,
queryHash: queryJob.hash,
})

return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Object {
"pageQueryHash": "",
},
},
"trackedStaticQueryResults": Map {},
},
"jobsV2": Object {
"complete": Map {},
Expand Down
10 changes: 3 additions & 7 deletions packages/gatsby/src/redux/actions/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,16 +225,12 @@ export const setProgramStatus = (
* @private
*/
export const pageQueryRun = (
{
path,
componentPath,
isPage,
resultHash,
}: {
payload: {
path: string
componentPath: string
isPage: boolean
resultHash: string
queryHash: string
},
plugin: IGatsbyPlugin,
traceId?: string
Expand All @@ -243,7 +239,7 @@ export const pageQueryRun = (
type: `PAGE_QUERY_RUN`,
plugin,
traceId,
payload: { path, componentPath, isPage, resultHash },
payload,
}
}

Expand Down
51 changes: 50 additions & 1 deletion packages/gatsby/src/redux/reducers/html.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ActionsUnion, IGatsbyState, IHtmlFileState } from "../types"
import {
ActionsUnion,
IGatsbyState,
IHtmlFileState,
IStaticQueryResultState,
} from "../types"

// TODO: once all cases for marking as dirty are implemented - reorder flags and their values to tidy them up
const FLAG_DIRTY_NEW_PAGE = 0b00001
Expand All @@ -7,13 +12,17 @@ const FLAG_DIRTY_BROWSER_COMPILATION_HASH = 0b00100
const FLAG_DIRTY_SSR_COMPILATION_HASH = 0b10000
const FLAG_DIRTY_CLEARED_CACHE = 0b01000

const FLAG_DIRTY_STATIC_QUERY_FIRST_RUN = 0b100000
const FLAG_DIRTY_STATIC_QUERY_RESULT_CHANGED = 0b1000000

type PagePath = string

function initialState(): IGatsbyState["html"] {
return {
trackedHtmlFiles: new Map<PagePath, IHtmlFileState>(),
browserCompilationHash: ``,
ssrCompilationHash: ``,
trackedStaticQueryResults: new Map<string, IStaticQueryResultState>(),
}
}

Expand Down Expand Up @@ -90,6 +99,25 @@ export function htmlReducer(
htmlFile.pageQueryHash = action.payload.resultHash
htmlFile.dirty |= FLAG_DIRTY_PAGE_QUERY
}
} else {
// static query case
let staticQueryResult = state.trackedStaticQueryResults.get(
action.payload.queryHash
)
if (!staticQueryResult) {
staticQueryResult = {
dirty: FLAG_DIRTY_STATIC_QUERY_FIRST_RUN,
staticQueryResultHash: action.payload.resultHash,
}
state.trackedStaticQueryResults.set(
action.payload.queryHash,
staticQueryResult
)
} else if (
staticQueryResult.staticQueryResultHash !== action.payload.resultHash
) {
staticQueryResult.dirty |= FLAG_DIRTY_STATIC_QUERY_RESULT_CHANGED
}
}

return state
Expand Down Expand Up @@ -130,6 +158,27 @@ export function htmlReducer(

return state
}

case `HTML_MARK_DIRTY_BECAUSE_STATIC_QUERY_RESULT_CHANGED`: {
// mark pages as dirty
for (const path of action.payload.pages) {
const htmlFile = state.trackedHtmlFiles.get(path)
if (htmlFile) {
htmlFile.dirty |= FLAG_DIRTY_STATIC_QUERY_RESULT_CHANGED
}
}

// mark static queries as not dirty anymore (we flushed their dirtiness into pages)
for (const staticQueryHash of action.payload.staticQueryHashes) {
const staticQueryResult = state.trackedStaticQueryResults.get(
staticQueryHash
)
if (staticQueryResult) {
staticQueryResult.dirty = 0
}
}
return state
}
}
return state
}
16 changes: 16 additions & 0 deletions packages/gatsby/src/redux/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ export interface IHtmlFileState {
pageQueryHash: string // TODO: change to page-data hash
}

export interface IStaticQueryResultState {
dirty: number
staticQueryResultHash: string
}

export type GatsbyNodeAPI =
| "onPreBoostrap"
| "onPostBoostrap"
Expand Down Expand Up @@ -286,6 +291,7 @@ export interface IGatsbyState {
trackedHtmlFiles: Map<Identifier, IHtmlFileState>
browserCompilationHash: string
ssrCompilationHash: string
trackedStaticQueryResults: Map<string, IStaticQueryResultState>
}
}

Expand Down Expand Up @@ -366,6 +372,7 @@ export type ActionsUnion =
| ISetProgramExtensions
| IRemovedHtml
| IGeneratedHtml
| IMarkHtmlDirty

export interface IApiFinishedAction {
type: `API_FINISHED`
Expand Down Expand Up @@ -539,6 +546,7 @@ export interface IPageQueryRunAction {
componentPath: string
isPage: boolean
resultHash: string
queryHash: string
}
}

Expand Down Expand Up @@ -838,3 +846,11 @@ interface IGeneratedHtml {
type: `HTML_GENERATED`
payload: Array<string>
}

interface IMarkHtmlDirty {
type: `HTML_MARK_DIRTY_BECAUSE_STATIC_QUERY_RESULT_CHANGED`
payload: {
pages: Set<string>
staticQueryHashes: Set<string>
}
}
46 changes: 46 additions & 0 deletions packages/gatsby/src/utils/page-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from "fs-extra"
import path from "path"

import { IGatsbyState } from "../redux/types"
import { store } from "../redux"

const checkForHtmlSuffix = (pagePath: string): boolean =>
!/\.(html?)$/i.test(pagePath)
Expand Down Expand Up @@ -52,3 +53,48 @@ export function calcDirtyHtmlFiles(
toDelete,
}
}

export function markHtmlDirtyIfResultOfUsedStaticQueryChanged(): void {
const state = store.getState()

const dirtyStaticQueryResults = new Set<string>()
state.html.trackedStaticQueryResults.forEach(function (
staticQueryResultState,
staticQueryHash
) {
if (staticQueryResultState.dirty) {
dirtyStaticQueryResults.add(staticQueryHash)
}
})

// we have dirty static query hashes - now we need to find templates that use them
const dirtyTemplates = new Set<string>()
state.staticQueriesByTemplate.forEach(function (
staticQueryHashes,
componentPath
) {
for (const dirtyStaticQueryHash of dirtyStaticQueryResults) {
if (staticQueryHashes.includes(dirtyStaticQueryHash)) {
dirtyTemplates.add(componentPath)
break // we already know this template need to rebuild, no need to check rest of queries
}
}
})

// mark html as dirty
const dirtyPages = new Set<string>()
for (const dirtyTemplate of dirtyTemplates) {
const component = state.components.get(dirtyTemplate)
for (const page of component.pages) {
dirtyPages.add(page)
}
}

store.dispatch({
type: `HTML_MARK_DIRTY_BECAUSE_STATIC_QUERY_RESULT_CHANGED`,
payload: {
pages: dirtyPages,
staticQueryHashes: dirtyStaticQueryResults,
},
})
}

0 comments on commit 72eddde

Please sign in to comment.