Skip to content

Commit 1589bd8

Browse files
GatsbyJS Botveryspry
GatsbyJS Bot
andauthored
fix(gatsby): ensure that writing node manifests to disk does not break on Windows (#33853) (#34020)
* fix: ensure that writing node manifests to disk does not break on Windows * refactor: use joinPath helper when creating node manifest file name * feat: replace all invalid Windows file path chars when creating node manifest files * test: use joinPath helper in node manifest tests * feat: only replace special windows chars in the manifest filename * feat: do not use negative look behind for Windows hard drive name * test: add tests for windows node manifest file name character replacing (cherry picked from commit f525ce0) Co-authored-by: Matt Ehlinger <[email protected]>
1 parent 9694010 commit 1589bd8

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

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

+57
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import {
1212
processNodeManifests,
1313
} from "../node-manifest"
1414

15+
function itWindows(name: string, fn: () => void): void {
16+
return process.platform === `win32` ? it(name, fn) : xit(name, fn)
17+
}
18+
1519
jest.mock(`fs-extra`, () => {
1620
return {
1721
ensureDir: jest.fn(),
@@ -342,4 +346,57 @@ describe(`processNodeManifests`, () => {
342346
await testProcessNodeManifests()
343347
process.env.NODE_ENV = `test`
344348
})
349+
350+
itWindows(`replaces reserved Windows characters with a dash`, async () => {
351+
const nodes = [
352+
{ id: `1`, usePageContextId: true },
353+
{ id: `2`, useOwnerNodeId: true },
354+
{ id: `3`, useQueryTracking: true },
355+
]
356+
mockGetNodes(
357+
new Map(nodes.map(node => [`${node.id}`, { id: `${node.id}` }]))
358+
)
359+
360+
store.getState.mockImplementation(() => {
361+
return {
362+
...storeState,
363+
pages: new Map([
364+
[
365+
`/test`,
366+
{
367+
path: `/test`,
368+
context: { id: `1` },
369+
},
370+
],
371+
[
372+
`/test-2`,
373+
{
374+
path: `/test-2`,
375+
context: { id: `2` },
376+
},
377+
],
378+
]),
379+
nodeManifests: [
380+
{
381+
pluginName: `test`,
382+
node: { id: `1` },
383+
// A manifest id containing all of the reserved windows characters that we check
384+
// for and replace
385+
manifestId: `\\*"<>/:?|`,
386+
},
387+
],
388+
queries: {
389+
byNode: new Map([
390+
[`1`, new Set([`/test`, `/test-2`])],
391+
[`2`, new Set([`/test`, `/test-2`])],
392+
]),
393+
},
394+
}
395+
})
396+
397+
await processNodeManifests()
398+
const nodeManifestFileName = path.basename(fs.writeJSON.mock.calls[0][0])
399+
400+
expect(nodeManifestFileName).toEqual(`---------.json`)
401+
})
345402
})

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

+21-1
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,33 @@ export async function processNodeManifest(
278278

279279
const gatsbySiteDirectory = store.getState().program.directory
280280

281+
let fileNameBase = inputManifest.manifestId
282+
283+
/**
284+
* Windows has a handful of special/reserved characters that are not valid in a file path
285+
* @reference https://superuser.com/questions/358855/what-characters-are-safe-in-cross-platform-file-names-for-linux-windows-and-os
286+
*
287+
* The two exceptions to the list linked above are
288+
* - the colon that is part of the hard disk partition name at the beginning of a file path (i.e. C:)
289+
* - backslashes. We don't want to replace backslashes because those are used to delineate what the actual file path is
290+
*
291+
* During local development, node manifests can be written to disk but are generally unused as they are only used
292+
* for Content Sync which runs in Gatsby Cloud. Gatsby cloud is a Linux environment in which these special chars are valid in
293+
* filepaths. To avoid errors on Windows, we replace all instances of the special chars in the filepath (with the exception of the
294+
* hard disk partition name) with "-" to ensure that local Windows development setups do not break when attempting
295+
* to write one of these manifests to disk.
296+
*/
297+
if (process.platform === `win32`) {
298+
fileNameBase = fileNameBase.replace(/:|\/|\*|\?|"|<|>|\||\\/g, `-`)
299+
}
300+
281301
// write out the manifest file
282302
const manifestFilePath = path.join(
283303
gatsbySiteDirectory,
284304
`public`,
285305
`__node-manifests`,
286306
inputManifest.pluginName,
287-
`${inputManifest.manifestId}.json`
307+
`${fileNameBase}.json`
288308
)
289309

290310
const manifestFileDir = path.dirname(manifestFilePath)

0 commit comments

Comments
 (0)