Skip to content

Commit ba8e21c

Browse files
authored
feat(gatsby): Match node manifest pages by page context slug (#34790)
* Match node manifest pages by page context slug * add docs link
1 parent d846f89 commit ba8e21c

File tree

6 files changed

+75
-14
lines changed

6 files changed

+75
-14
lines changed

integration-tests/node-manifest/__tests__/create-node-manifest.test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ describe(`Node Manifest API in "gatsby ${gatsbyCommandName}"`, () => {
7070
expect(manifestFileContents.foundPageBy).toBe(`context.id`)
7171
})
7272

73+
it(`Creates an accurate node manifest when ownerNodeId isn't present but there's a matching "slug" in pageContext`, async () => {
74+
const manifestFileContents = await getManifestContents(5)
75+
76+
expect(manifestFileContents.node.id).toBe(`5`)
77+
expect(manifestFileContents.page.path).toBe(`/slug-test-path`)
78+
expect(manifestFileContents.foundPageBy).toBe(`context.slug`)
79+
})
80+
7381
if (gatsbyCommandName === `build`) {
7482
// this doesn't work in gatsby develop since query tracking
7583
// only runs when visiting a page in browser.

integration-tests/node-manifest/gatsby-node.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const createManifestId = nodeId => `${commandName}-${nodeId}`
44

55
exports.sourceNodes = ({ actions }) => {
66
// template nodes
7-
for (let id = 1; id < 5; id++) {
7+
for (let id = 1; id < 6; id++) {
88
const node = {
99
id: `${id}`,
1010
internal: {
@@ -13,6 +13,10 @@ exports.sourceNodes = ({ actions }) => {
1313
},
1414
}
1515

16+
if (id === 5) {
17+
node.slug = `test-slug`
18+
}
19+
1620
actions.createNode(node)
1721

1822
actions.unstable_createNodeManifest({
@@ -108,4 +112,12 @@ exports.createPages = ({ actions }) => {
108112
path: `three-alternative`,
109113
component: require.resolve(`./src/templates/three.js`),
110114
})
115+
116+
actions.createPage({
117+
path: `slug-test-path`,
118+
context: {
119+
slug: `test-slug`,
120+
},
121+
component: require.resolve(`./src/templates/four.js`),
122+
})
111123
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { graphql } from "gatsby"
2+
import React from "react"
3+
4+
export default function Four({ data }) {
5+
return <div>Template 4. Node by slug {data.testNode.slug}</div>
6+
}
7+
8+
export const query = graphql`
9+
query SLUG_TEST($slug: String) {
10+
testNode(slug: { eq: $slug }) {
11+
id
12+
}
13+
otherNode: testNode(id: { eq: "2" }) {
14+
id
15+
}
16+
}
17+
`

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -627,31 +627,37 @@ const errors = {
627627

628628
/** Node Manifest warnings */
629629
"11801": {
630-
// @todo add docs link to "using Preview" once it's updated with an explanation of ownerNodeId
631630
text: ({ inputManifest }): string => `${getSharedNodeManifestWarning(
632631
inputManifest
633632
)} but Gatsby couldn't find a page for this node.
634-
If you want a manifest to be created for this node (for previews or other purposes), ensure that a page was created (and that a ownerNodeId is added to createPage() if you're not using the Filesystem Route API).\n`,
633+
If you want a manifest to be created for this node (for previews or other purposes), ensure that a page was created (and that a ownerNodeId is added to createPage() if you're not using the Filesystem Route API). See https://www.gatsbyjs.com/docs/conceptual/content-sync for more info.\n`,
635634
level: Level.WARNING,
636635
category: ErrorCategory.USER,
637636
},
638637

639638
"11802": {
640-
// @todo add docs link to "using Preview" once it's updated with an explanation of ownerNodeId
641639
text: ({ inputManifest, pagePath }): string =>
642640
`${getSharedNodeManifestWarning(
643641
inputManifest
644-
)} but Gatsby didn't find a ownerNodeId for the page at ${pagePath}\nUsing the first page that was found with the node manifest id set in pageContext.id in createPage().\nThis may result in an inaccurate node manifest (for previews or other purposes).`,
642+
)} but Gatsby didn't find an ownerNodeId for the page at ${pagePath}\nUsing the first page that was found with the node manifest id set in pageContext.id in createPage().\nThis may result in an inaccurate node manifest (for previews or other purposes). See https://www.gatsbyjs.com/docs/conceptual/content-sync for more info.`,
643+
level: Level.WARNING,
644+
category: ErrorCategory.USER,
645+
},
646+
647+
"11805": {
648+
text: ({ inputManifest, pagePath }): string =>
649+
`${getSharedNodeManifestWarning(
650+
inputManifest
651+
)} but Gatsby didn't find an ownerNodeId for the page at ${pagePath}\nUsing the first page that was found with the node manifest id set in pageContext.slug in createPage().\nThis may result in an inaccurate node manifest (for previews or other purposes). See https://www.gatsbyjs.com/docs/conceptual/content-sync for more info.`,
645652
level: Level.WARNING,
646653
category: ErrorCategory.USER,
647654
},
648655

649656
"11803": {
650-
// @todo add docs link to "using Preview" once it's updated with an explanation of ownerNodeId
651657
text: ({ inputManifest, pagePath }): string =>
652658
`${getSharedNodeManifestWarning(
653659
inputManifest
654-
)} but Gatsby didn't find a ownerNodeId for the page at ${pagePath}\nUsing the first page where this node is queried.\nThis may result in an inaccurate node manifest (for previews or other purposes).`,
660+
)} but Gatsby didn't find an ownerNodeId for the page at ${pagePath}\nUsing the first page where this node is queried.\nThis may result in an inaccurate node manifest (for previews or other purposes). See https://www.gatsbyjs.com/docs/conceptual/content-sync for more info.`,
655661
level: Level.WARNING,
656662
category: ErrorCategory.USER,
657663
},

packages/gatsby-plugin-gatsby-cloud/src/utils/use-poll-for-node-manifest/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type FoundPageBy =
1212
| `ownerNodeId`
1313
| `filesystem-route-api`
1414
| `context.id`
15+
| `context.slug`
1516
| `queryTracking`
1617
| `none`
1718

packages/gatsby/src/utils/node-manifest.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ type FoundPageBy =
2828
| `filesystem-route-api`
2929
// for these three we warn to use ownerNodeId instead
3030
| `context.id`
31+
| `context.slug`
3132
| `queryTracking`
3233
| `none`
3334

3435
/**
35-
* Finds a final built page by nodeId
36+
* Finds a final built page by nodeId or by node.slug as a fallback.
3637
*
3738
* Note that this function wont work properly in `gatsby develop`
3839
* since develop no longer runs all page queries when creating pages.
@@ -41,7 +42,13 @@ type FoundPageBy =
4142
* When this fn is being used for routing to previews the user wont necessarily have
4243
* visited the page in the browser yet.
4344
*/
44-
async function findPageOwnedByNodeId({ nodeId }: { nodeId: string }): Promise<{
45+
async function findPageOwnedByNode({
46+
nodeId,
47+
slug,
48+
}: {
49+
nodeId: string
50+
slug?: string
51+
}): Promise<{
4552
page: INodeManifestPage
4653
foundPageBy: FoundPageBy
4754
}> {
@@ -78,10 +85,10 @@ async function findPageOwnedByNodeId({ nodeId }: { nodeId: string }): Promise<{
7885
// if we haven't found a page with this nodeId
7986
// set as page.ownerNodeId then run this logic.
8087
// this condition is on foundOwnerNodeId instead of ownerPagePath
81-
// in case we find a page with the nodeId in page.context.id
88+
// in case we find a page with the nodeId in page.context.id/context.slug
8289
// and then later in the loop there's a page with this nodeId
8390
// set on page.ownerNodeId.
84-
// We always want to prefer ownerPagePath over context.id
91+
// We always want to prefer ownerPagePath over context.id/context.slug
8592
if (foundOwnerNodeId) {
8693
break
8794
}
@@ -98,7 +105,10 @@ async function findPageOwnedByNodeId({ nodeId }: { nodeId: string }): Promise<{
98105

99106
foundOwnerNodeId = fullPage?.ownerNodeId === nodeId
100107

101-
const foundPageIdInContext = fullPage?.context.id === nodeId
108+
const foundPageIdInContext = fullPage?.context?.id === nodeId
109+
110+
// querying by node.slug in GraphQL queries is common enough that we can search for it as a fallback after ownerNodeId, filesystem routes, and context.id
111+
const foundPageSlugInContext = slug && fullPage?.context?.slug === slug
102112

103113
if (foundOwnerNodeId) {
104114
foundPageBy = `ownerNodeId`
@@ -113,6 +123,8 @@ async function findPageOwnedByNodeId({ nodeId }: { nodeId: string }): Promise<{
113123
foundPageBy = pageCreatedByFilesystemPlugin
114124
? `filesystem-route-api`
115125
: `context.id`
126+
} else if (foundPageSlugInContext && fullPage) {
127+
foundPageBy = `context.slug`
116128
}
117129

118130
if (
@@ -128,7 +140,8 @@ async function findPageOwnedByNodeId({ nodeId }: { nodeId: string }): Promise<{
128140
// that's mapped to this node id.
129141
// this also makes this work with the filesystem Route API without
130142
// changing that API.
131-
foundPageIdInContext)
143+
foundPageIdInContext ||
144+
foundPageSlugInContext)
132145
) {
133146
// save this path to use in our manifest!
134147
ownerPagePath = fullPage.path
@@ -153,6 +166,7 @@ async function findPageOwnedByNodeId({ nodeId }: { nodeId: string }): Promise<{
153166
export const foundPageByToLogIds = {
154167
none: `11801`,
155168
[`context.id`]: `11802`,
169+
[`context.slug`]: `11805`,
156170
queryTracking: `11803`,
157171
[`filesystem-route-api`]: `success`,
158172
ownerNodeId: `success`,
@@ -177,6 +191,7 @@ export function warnAboutNodeManifestMappingProblems({
177191
switch (foundPageBy) {
178192
case `none`:
179193
case `context.id`:
194+
case `context.slug`:
180195
case `queryTracking`: {
181196
logId = foundPageByToLogIds[foundPageBy]
182197
if (verbose) {
@@ -236,8 +251,10 @@ export async function processNodeManifest(
236251
}
237252

238253
// map the node to a page that was created
239-
const { page: nodeManifestPage, foundPageBy } = await findPageOwnedByNodeId({
254+
const { page: nodeManifestPage, foundPageBy } = await findPageOwnedByNode({
240255
nodeId,
256+
// querying by node.slug in GraphQL queries is common enough that we can search for it as a fallback after ownerNodeId, filesystem routes, and context.id
257+
slug: fullNode?.slug as string,
241258
})
242259

243260
const nodeManifestMappingProblemsContext = {

0 commit comments

Comments
 (0)