Skip to content

Commit dfe9fb0

Browse files
authored
fix(build): remove stale page-data files (#26937)
* test(artifacts): assert (un)expected page-data/html files * fix(gatsby): delete stale page-data on builds * remove deepFilter option it could cause very weird edge cases if user actually have pages with `/sq/d/` prefix and we already check for `page-data.json` file (static query will have [hash].json names) * handle fs traversal errors (kind of)
1 parent c726f98 commit dfe9fb0

File tree

8 files changed

+421
-184
lines changed

8 files changed

+421
-184
lines changed
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
const { spawn } = require(`child_process`)
2+
const path = require(`path`)
3+
const { murmurhash } = require(`babel-plugin-remove-graphql-queries`)
4+
const { readPageData } = require(`gatsby/dist/utils/page-data`)
5+
const { stripIgnoredCharacters } = require(`gatsby/graphql`)
6+
const fs = require(`fs`)
7+
8+
jest.setTimeout(100000)
9+
10+
const publicDir = path.join(process.cwd(), `public`)
11+
12+
const gatsbyBin = path.join(`node_modules`, `.bin`, `gatsby`)
13+
14+
const titleQuery = `
15+
{
16+
site {
17+
siteMetadata {
18+
title
19+
}
20+
}
21+
}
22+
`
23+
24+
const authorQuery = `
25+
{
26+
site {
27+
siteMetadata {
28+
author
29+
}
30+
}
31+
}
32+
`
33+
34+
const githubQuery = `
35+
{
36+
site {
37+
siteMetadata {
38+
github
39+
}
40+
}
41+
}
42+
`
43+
44+
const moreInfoQuery = `
45+
{
46+
site {
47+
siteMetadata {
48+
moreInfo
49+
}
50+
}
51+
}
52+
`
53+
54+
function hashQuery(query) {
55+
const text = stripIgnoredCharacters(query)
56+
const hash = murmurhash(text, `abc`)
57+
return String(hash)
58+
}
59+
60+
const globalQueries = [githubQuery, moreInfoQuery]
61+
62+
const pagePathToFilePath = {
63+
html: pagePath => path.join(`public`, pagePath, `index.html`),
64+
"page-data": pagePath =>
65+
path.join(
66+
`public`,
67+
`page-data`,
68+
pagePath === `/` ? `index` : pagePath,
69+
`page-data.json`
70+
),
71+
}
72+
73+
function assertFileExistenceForPagePaths({ pagePaths, type, shouldExist }) {
74+
if (![`html`, `page-data`].includes(type)) {
75+
throw new Error(`Unexpected type`)
76+
}
77+
78+
test.each(pagePaths)(
79+
`${type} file for "%s" ${shouldExist ? `exists` : `DOESN'T exist`}`,
80+
async pagePath => {
81+
const filePath = pagePathToFilePath[type](pagePath)
82+
const exists = await new Promise(resolve => {
83+
fs.stat(filePath, err => {
84+
resolve(err === null)
85+
})
86+
})
87+
88+
expect(exists).toBe(shouldExist)
89+
}
90+
)
91+
}
92+
93+
beforeAll(async done => {
94+
const gatsbyCleanProcess = spawn(gatsbyBin, [`clean`], {
95+
stdio: [`inherit`, `inherit`, `inherit`, `inherit`],
96+
env: {
97+
...process.env,
98+
NODE_ENV: `production`,
99+
},
100+
})
101+
102+
gatsbyCleanProcess.on(`exit`, exitCode => {
103+
done()
104+
})
105+
})
106+
107+
describe(`First run`, () => {
108+
beforeAll(async done => {
109+
const gatsbyProcess = spawn(gatsbyBin, [`build`], {
110+
stdio: [`inherit`, `inherit`, `inherit`, `inherit`],
111+
env: {
112+
...process.env,
113+
NODE_ENV: `production`,
114+
RUN_FOR_STALE_PAGE_ARTIFICATS: `1`,
115+
},
116+
})
117+
118+
gatsbyProcess.on(`exit`, exitCode => {
119+
done()
120+
})
121+
})
122+
123+
describe(`Static Queries`, () => {
124+
test(`are written correctly when inline`, async () => {
125+
const queries = [titleQuery, ...globalQueries]
126+
const pagePath = `/inline/`
127+
128+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
129+
130+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
131+
})
132+
133+
test(`are written correctly when imported`, async () => {
134+
const queries = [titleQuery, ...globalQueries]
135+
const pagePath = `/import/`
136+
137+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
138+
139+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
140+
})
141+
142+
test(`are written correctly when dynamically imported`, async () => {
143+
const queries = [titleQuery, ...globalQueries]
144+
const pagePath = `/dynamic/`
145+
146+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
147+
148+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
149+
})
150+
151+
test(`are written correctly in jsx`, async () => {
152+
const queries = [titleQuery, ...globalQueries]
153+
const pagePath = `/jsx/`
154+
155+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
156+
157+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
158+
})
159+
160+
test(`are written correctly in tsx`, async () => {
161+
const queries = [titleQuery, ...globalQueries]
162+
const pagePath = `/tsx/`
163+
164+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
165+
166+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
167+
})
168+
169+
test(`are written correctly in typescript`, async () => {
170+
const queries = [titleQuery, ...globalQueries]
171+
const pagePath = `/typescript/`
172+
173+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
174+
175+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
176+
})
177+
178+
test(`are written correctly when nesting imports`, async () => {
179+
const queries = [titleQuery, authorQuery, ...globalQueries]
180+
const pagePath = `/import-import/`
181+
182+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
183+
184+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
185+
})
186+
187+
test(`are written correctly when nesting dynamic imports`, async () => {
188+
const queries = [titleQuery, ...globalQueries]
189+
const pagePath = `/dynamic-dynamic/`
190+
191+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
192+
193+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
194+
})
195+
196+
test(`are written correctly when nesting a dynamic import in a regular import`, async () => {
197+
const queries = [titleQuery, authorQuery, ...globalQueries]
198+
const pagePath = `/import-dynamic/`
199+
200+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
201+
202+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
203+
})
204+
205+
test(`are written correctly when nesting a regular import in a dynamic import`, async () => {
206+
const queries = [titleQuery, ...globalQueries]
207+
const pagePath = `/dynamic-import/`
208+
209+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
210+
211+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
212+
})
213+
214+
test("are written correctly with circular dependency", async () => {
215+
const queries = [titleQuery, ...globalQueries]
216+
const pagePath = `/circular-dep/`
217+
218+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
219+
220+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
221+
})
222+
223+
test(`are written correctly when using gatsby-browser`, async () => {
224+
const queries = [...globalQueries]
225+
const pagePath = `/gatsby-browser/`
226+
227+
const { staticQueryHashes } = await readPageData(publicDir, pagePath)
228+
229+
expect(staticQueryHashes.sort()).toEqual(queries.map(hashQuery).sort())
230+
})
231+
})
232+
233+
const expectedPages = [`stale-pages/stable`, `stale-pages/only-in-first`]
234+
const unexpectedPages = [`stale-pages/only-in-second`]
235+
236+
describe(`html files`, () => {
237+
const type = `html`
238+
239+
describe(`should have expected html files`, () => {
240+
assertFileExistenceForPagePaths({
241+
pagePaths: expectedPages,
242+
type,
243+
shouldExist: true,
244+
})
245+
})
246+
247+
describe(`shouldn't have unexpected html files`, () => {
248+
assertFileExistenceForPagePaths({
249+
pagePaths: unexpectedPages,
250+
type,
251+
shouldExist: false,
252+
})
253+
})
254+
})
255+
256+
describe(`page-data files`, () => {
257+
const type = `page-data`
258+
259+
describe(`should have expected page-data files`, () => {
260+
assertFileExistenceForPagePaths({
261+
pagePaths: expectedPages,
262+
type,
263+
shouldExist: true,
264+
})
265+
})
266+
267+
describe(`shouldn't have unexpected page-data files`, () => {
268+
assertFileExistenceForPagePaths({
269+
pagePaths: unexpectedPages,
270+
type,
271+
shouldExist: false,
272+
})
273+
})
274+
})
275+
})
276+
277+
describe(`Second run`, () => {
278+
const expectedPages = [`stale-pages/stable`, `stale-pages/only-in-second`]
279+
const unexpectedPages = [`stale-pages/only-in-first`]
280+
281+
beforeAll(async done => {
282+
const gatsbyProcess = spawn(gatsbyBin, [`build`], {
283+
stdio: [`inherit`, `inherit`, `inherit`, `inherit`],
284+
env: {
285+
...process.env,
286+
NODE_ENV: `production`,
287+
RUN_FOR_STALE_PAGE_ARTIFICATS: `2`,
288+
},
289+
})
290+
291+
gatsbyProcess.on(`exit`, exitCode => {
292+
done()
293+
})
294+
})
295+
296+
describe(`html files`, () => {
297+
const type = `html`
298+
299+
describe(`should have expected html files`, () => {
300+
assertFileExistenceForPagePaths({
301+
pagePaths: expectedPages,
302+
type,
303+
shouldExist: true,
304+
})
305+
})
306+
307+
describe(`shouldn't have unexpected html files`, () => {
308+
assertFileExistenceForPagePaths({
309+
pagePaths: unexpectedPages,
310+
type,
311+
shouldExist: false,
312+
})
313+
})
314+
})
315+
316+
describe(`page-data files`, () => {
317+
const type = `page-data`
318+
319+
describe(`should have expected page-data files`, () => {
320+
assertFileExistenceForPagePaths({
321+
pagePaths: expectedPages,
322+
type,
323+
shouldExist: true,
324+
})
325+
})
326+
327+
describe(`shouldn't have unexpected page-data files`, () => {
328+
assertFileExistenceForPagePaths({
329+
pagePaths: unexpectedPages,
330+
type,
331+
shouldExist: false,
332+
})
333+
})
334+
})
335+
})

0 commit comments

Comments
 (0)