Skip to content
57 changes: 57 additions & 0 deletions packages/gatsby/src/utils/__tests__/node-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
processNodeManifests,
} from "../node-manifest"

function itWindows(name: string, fn: () => void): void {
return process.platform === `win32` ? it(name, fn) : xit(name, fn)
}

jest.mock(`fs-extra`, () => {
return {
ensureDir: jest.fn(),
Expand Down Expand Up @@ -342,4 +346,57 @@ describe(`processNodeManifests`, () => {
await testProcessNodeManifests()
process.env.NODE_ENV = `test`
})

itWindows(`replaces reserved Windows characters with a dash`, async () => {
const nodes = [
{ id: `1`, usePageContextId: true },
{ id: `2`, useOwnerNodeId: true },
{ id: `3`, useQueryTracking: true },
]
mockGetNodes(
new Map(nodes.map(node => [`${node.id}`, { id: `${node.id}` }]))
)

store.getState.mockImplementation(() => {
return {
...storeState,
pages: new Map([
[
`/test`,
{
path: `/test`,
context: { id: `1` },
},
],
[
`/test-2`,
{
path: `/test-2`,
context: { id: `2` },
},
],
]),
nodeManifests: [
{
pluginName: `test`,
node: { id: `1` },
// A manifest id containing all of the reserved windows characters that we check
// for and replace
manifestId: `\\*"<>/:?|`,
},
],
queries: {
byNode: new Map([
[`1`, new Set([`/test`, `/test-2`])],
[`2`, new Set([`/test`, `/test-2`])],
]),
},
}
})

await processNodeManifests()
const nodeManifestFileName = path.basename(fs.writeJSON.mock.calls[0][0])

expect(nodeManifestFileName).toEqual(`---------.json`)
})
})
22 changes: 21 additions & 1 deletion packages/gatsby/src/utils/node-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,33 @@ export async function processNodeManifest(

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

let fileNameBase = inputManifest.manifestId

/**
* Windows has a handful of special/reserved characters that are not valid in a file path
* @reference https://superuser.com/questions/358855/what-characters-are-safe-in-cross-platform-file-names-for-linux-windows-and-os
*
* The two exceptions to the list linked above are
* - the colon that is part of the hard disk partition name at the beginning of a file path (i.e. C:)
* - backslashes. We don't want to replace backslashes because those are used to delineate what the actual file path is
*
* During local development, node manifests can be written to disk but are generally unused as they are only used
* for Content Sync which runs in Gatsby Cloud. Gatsby cloud is a Linux environment in which these special chars are valid in
* filepaths. To avoid errors on Windows, we replace all instances of the special chars in the filepath (with the exception of the
* hard disk partition name) with "-" to ensure that local Windows development setups do not break when attempting
* to write one of these manifests to disk.
*/
if (process.platform === `win32`) {
fileNameBase = fileNameBase.replace(/:|\/|\*|\?|"|<|>|\||\\/g, `-`)
}

// write out the manifest file
const manifestFilePath = path.join(
gatsbySiteDirectory,
`public`,
`__node-manifests`,
inputManifest.pluginName,
`${inputManifest.manifestId}.json`
`${fileNameBase}.json`
)

const manifestFileDir = path.dirname(manifestFilePath)
Expand Down