Skip to content

Commit d302879

Browse files
piehLekoArtstyhopp
authored
fix(gatsby-plugin-page-creator): update pages when nodes change (#36623)
Co-authored-by: Lennart <[email protected]> Co-authored-by: tyhopp <[email protected]>
1 parent c073bbe commit d302879

19 files changed

+778
-116
lines changed

e2e-tests/development-runtime/cypress/integration/unified-routing/collection-routing.js

+85-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,51 @@
1-
describe(`collection-routing`, () => {
2-
beforeEach(() => {
3-
cy.visit(`/collection-routing`).waitForRouteChange()
1+
const prefixes = [``, `child-`, `computed-`, `child-computed-`]
2+
3+
function assert404(slug) {
4+
for (const prefix of prefixes) {
5+
cy.visit(`/collection-routing/mutations/${prefix}${slug}/`, {
6+
failOnStatusCode: false,
7+
}).waitForRouteChange()
8+
9+
// page doesn't exist yet
10+
cy.get(`h1`).invoke(`text`).should(`eq`, `Gatsby.js development 404 page`)
11+
}
12+
}
13+
14+
function assertPageExist(slug, content) {
15+
for (const prefix of prefixes) {
16+
cy.visit(
17+
`/collection-routing/mutations/${prefix}${slug}/`
18+
).waitForRouteChange()
19+
cy.contains(content)
20+
}
21+
}
22+
23+
function refresh(setup) {
24+
cy.then(() => {
25+
return fetch(
26+
`http://localhost:8000/__refresh/gatsby-source-fs-route-mutations`,
27+
{
28+
method: `POST`,
29+
headers: {
30+
"Content-Type": "application/json",
31+
},
32+
body: JSON.stringify({ setup }),
33+
}
34+
)
435
})
536

37+
cy.wait(5000)
38+
}
39+
40+
describe(`collection-routing`, () => {
641
it(`can create simplest collection route that also has a number as an identifier`, () => {
742
cy.visit(`/collection-routing/1/`).waitForRouteChange()
843
cy.findByTestId(`slug`).should(`have.text`, `/preview/1`)
944
cy.findByTestId(`pagecontext`).should(`have.text`, `1`)
1045
})
1146

1247
it(`can navigate to a collection route and see its content rendered`, () => {
48+
cy.visit(`/collection-routing`).waitForRouteChange()
1349
// this test depends on the alphabetical sorting of markdown files
1450
cy.findByTestId(`collection-routing-blog-0`)
1551
.should(`have.attr`, `data-testslug`, `/2018-12-14-hello-world/`)
@@ -24,6 +60,7 @@ describe(`collection-routing`, () => {
2460
})
2561

2662
it(`can navigate to a collection route that uses unions and see its content rendered`, () => {
63+
cy.visit(`/collection-routing`).waitForRouteChange()
2764
// this test depends on the alphabetical sorting of image files
2865
cy.findByTestId(`collection-routing-image-0`)
2966
.should(`have.attr`, `data-testimagename`, `citrus-fruits`)
@@ -61,4 +98,49 @@ describe(`collection-routing`, () => {
6198
cy.findByTestId(`title`)
6299
cy.should(`have.text`, `Named SPLAT Nested with Collection Route!`)
63100
})
101+
102+
describe(`data updates`, () => {
103+
before(() => {
104+
refresh(`reset`)
105+
})
106+
after(() => {
107+
refresh(`reset`)
108+
})
109+
110+
it(`creates a page when new node is created`, () => {
111+
assert404(`new-node`)
112+
assert404(`updated-node`)
113+
114+
refresh(`create`)
115+
116+
assertPageExist(`new-node`, `This is node that was just created`)
117+
assert404(`updated-node`)
118+
})
119+
120+
it(`remove previous page and add a new one when slug changes`, () => {
121+
assertPageExist(`new-node`, `This is node that was just created`)
122+
assert404(`updated-node`)
123+
124+
refresh(`update`)
125+
126+
assertPageExist(
127+
`updated-node`,
128+
`This is node that had slug and content updated`
129+
)
130+
assert404(`new-node`)
131+
})
132+
133+
it(`remove a page when node is deleted`, () => {
134+
assertPageExist(
135+
`updated-node`,
136+
`This is node that had slug and content updated`
137+
)
138+
assert404(`new-node`)
139+
140+
refresh(`delete`)
141+
142+
assert404(`new-node`)
143+
assert404(`updated-node`)
144+
})
145+
})
64146
})

e2e-tests/development-runtime/gatsby-config.js

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ module.exports = {
3737
`gatsby-source-fake-data`,
3838
`gatsby-source-pinc-data`,
3939
`gatsby-source-query-on-demand-data`,
40+
`gatsby-source-fs-route-mutations`,
4041
`gatsby-browser-tsx`,
4142
`gatsby-node-typegen`,
4243
`gatsby-transformer-sharp`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { GatsbyNode } from "gatsby"
2+
3+
let createdNodes = new Set<string>()
4+
5+
export const sourceNodes: GatsbyNode["sourceNodes"] = ({
6+
actions,
7+
webhookBody,
8+
getNode,
9+
createContentDigest,
10+
reporter,
11+
}) => {
12+
const handledNodes = new Set(createdNodes)
13+
function addNode(data: { id: string; slug: string; content: string }): void {
14+
const node = {
15+
...data,
16+
parent: null,
17+
children: [],
18+
internal: {
19+
type: `FilesystemRoutesMutation`,
20+
contentDigest: createContentDigest(data),
21+
},
22+
}
23+
24+
createdNodes.add(node.id)
25+
handledNodes.delete(node.id)
26+
27+
actions.createNode(node)
28+
29+
const childNode = {
30+
...data,
31+
id: `${node.id} << childNode`,
32+
parent: node.id,
33+
internal: {
34+
type: `FilesystemRoutesMutationChild`,
35+
contentDigest: node.internal.contentDigest,
36+
},
37+
}
38+
actions.createNode(childNode)
39+
const parent = getNode(node.id)
40+
41+
if (!parent) {
42+
throw new Error(`Could not find parent node`)
43+
}
44+
45+
actions.createParentChildLink({
46+
parent: parent,
47+
child: childNode,
48+
})
49+
}
50+
51+
if (webhookBody?.setup === `create`) {
52+
reporter.verbose(`[gatsby-source-fs-route-mutation] create a new node`)
53+
addNode({
54+
id: `fs-route-mutation-test`,
55+
slug: `new-node`,
56+
content: `This is node that was just created`,
57+
})
58+
} else if (webhookBody?.setup === `update`) {
59+
reporter.verbose(`[gatsby-source-fs-route-mutation] update a node`)
60+
addNode({
61+
id: `fs-route-mutation-test`,
62+
slug: `updated-node`,
63+
content: `This is node that had slug and content updated`,
64+
})
65+
} else if (webhookBody?.setup === `delete`) {
66+
reporter.verbose(`[gatsby-source-fs-route-mutation] delete a node`)
67+
} else {
68+
reporter.verbose(`[gatsby-source-fs-route-mutation] initial setup`)
69+
}
70+
71+
addNode({
72+
id: `fs-route-mutation-stable`,
73+
slug: `stable`,
74+
content: `This is stable node`,
75+
})
76+
77+
for (const nodeIdToDelete of handledNodes) {
78+
const nodeToDelete = getNode(nodeIdToDelete)
79+
if (nodeToDelete) {
80+
createdNodes.delete(nodeIdToDelete)
81+
actions.deleteNode(nodeToDelete)
82+
}
83+
}
84+
}
85+
86+
export const createResolvers: GatsbyNode["createResolvers"] = ({
87+
createResolvers,
88+
}) => {
89+
createResolvers({
90+
FilesystemRoutesMutation: {
91+
computed: {
92+
type: `String`,
93+
resolve(source: { slug?: string }): string {
94+
return `computed-${source.slug}`
95+
},
96+
},
97+
},
98+
FilesystemRoutesMutationChild: {
99+
computed: {
100+
type: `String`,
101+
resolve(source: { slug?: string }): string {
102+
return `computed-${source.slug}`
103+
},
104+
},
105+
},
106+
})
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// noop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react"
2+
import { graphql } from "gatsby"
3+
4+
export default function FSRoutesMutationTemplate({ data }) {
5+
return (
6+
<>
7+
<pre>{JSON.stringify(data, null, 2)}</pre>
8+
</>
9+
)
10+
}
11+
12+
export const query = graphql`
13+
query($id: String!) {
14+
filesystemRoutesMutationChild(id: { eq: $id }) {
15+
content
16+
}
17+
}
18+
`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react"
2+
import { graphql } from "gatsby"
3+
4+
export default function FSRoutesMutationTemplate({ data }) {
5+
return (
6+
<>
7+
<pre>{JSON.stringify(data, null, 2)}</pre>
8+
</>
9+
)
10+
}
11+
12+
export const query = graphql`
13+
query($id: String!) {
14+
filesystemRoutesMutationChild(id: { eq: $id }) {
15+
content
16+
}
17+
}
18+
`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react"
2+
import { graphql } from "gatsby"
3+
4+
export default function FSRoutesMutationTemplate({ data }) {
5+
return (
6+
<>
7+
<pre>{JSON.stringify(data, null, 2)}</pre>
8+
</>
9+
)
10+
}
11+
12+
export const query = graphql`
13+
query($id: String!) {
14+
filesystemRoutesMutation(id: { eq: $id }) {
15+
content
16+
}
17+
}
18+
`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react"
2+
import { graphql } from "gatsby"
3+
4+
export default function FSRoutesMutationTemplate({ data }) {
5+
return (
6+
<>
7+
<pre>{JSON.stringify(data, null, 2)}</pre>
8+
</>
9+
)
10+
}
11+
12+
export const query = graphql`
13+
query($id: String!) {
14+
filesystemRoutesMutation(id: { eq: $id }) {
15+
content
16+
}
17+
}
18+
`

packages/gatsby-plugin-page-creator/src/__tests__/extract-query.ts

+12
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,18 @@ describe(`extract query`, () => {
223223
)
224224
})
225225
})
226+
227+
it(`supports limiting collection query to specified node ids if provided`, () => {
228+
expect(
229+
generateQueryFromString(
230+
`Thing`,
231+
compatiblePath(`/foo/bar/{Thing.id}/{Thing.fields__name}.js`),
232+
[`id-1`, `id-2`]
233+
)
234+
).toMatchInlineSnapshot(
235+
`"{allThing(filter: { id: { in: [\\"id-1\\",\\"id-2\\"] } }){nodes{id,fields{name},internal{contentFilePath}}}}"`
236+
)
237+
})
226238
})
227239

228240
describe(`reverseLookupParams`, () => {

packages/gatsby-plugin-page-creator/src/collection-extract-query-string.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { CODES, prefixId } from "./error-utils"
77
// This Function opens up the actual collection file and extracts the queryString used in the
88
export function collectionExtractQueryString(
99
absolutePath: string,
10-
reporter: Reporter
10+
reporter: Reporter,
11+
nodeIds?: Array<string>
1112
): string | null {
1213
let queryString: string | null = null
1314

@@ -38,7 +39,7 @@ export function collectionExtractQueryString(
3839

3940
// 3 This is important, we get the model or query, but we have to create a real graphql
4041
// query from it. This generateQueryFromString call does all of that magic
41-
queryString = generateQueryFromString(modelType, absolutePath)
42+
queryString = generateQueryFromString(modelType, absolutePath, nodeIds)
4243

4344
return queryString
4445
}

packages/gatsby-plugin-page-creator/src/create-page-wrapper.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export function createPage(
2929
graphql: CreatePagesArgs["graphql"],
3030
reporter: Reporter,
3131
trailingSlash: TrailingSlash,
32+
pagesPath: string,
3233
ignore?: IPathIgnoreOptions | string | Array<string> | null,
3334
slugifyOptions?: ISlugifyOptions
3435
): void {
@@ -48,15 +49,16 @@ export function createPage(
4849
// If the page includes a `{}` in it, then we create it as a collection builder
4950
if (pathIsCollectionBuilder(absolutePath)) {
5051
trackFeatureIsUsed(`UnifiedRoutes:collection-page-builder`)
51-
createPagesFromCollectionBuilder(
52+
createPagesFromCollectionBuilder({
5253
filePath,
5354
absolutePath,
55+
pagesPath,
5456
actions,
5557
graphql,
5658
reporter,
5759
trailingSlash,
58-
slugifyOptions
59-
)
60+
slugifyOptions,
61+
})
6062
return
6163
}
6264

0 commit comments

Comments
 (0)