Skip to content

Commit

Permalink
fix(gatsby): handle case of changing page path casing on case-insensi…
Browse files Browse the repository at this point in the history
…tive fs (#31071) (#31082)

* fix(gatsby): handle case of changing page path casing on case-insensitive fs

* figure out if platform is case insenstive once and not for every page separately

* add tests for casing ... cases

(cherry picked from commit 940eddf)

Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com>
  • Loading branch information
GatsbyJS Bot and pieh committed Apr 27, 2021
1 parent 21dc379 commit 3224803
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 3 deletions.
74 changes: 73 additions & 1 deletion packages/gatsby/src/commands/__tests__/build-utils.ts
Expand Up @@ -4,14 +4,21 @@ import {
IHtmlFileState,
IStaticQueryResultState,
} from "../../redux/types"
import { calcDirtyHtmlFiles } from "../build-utils"

const platformSpy = jest.spyOn(require(`os`), `platform`)
interface IMinimalStateSliceForTest {
html: IGatsbyState["html"]
pages: IGatsbyState["pages"]
}

describe(`calcDirtyHtmlFiles`, () => {
let calcDirtyHtmlFiles
beforeEach(() => {
jest.isolateModules(() => {
calcDirtyHtmlFiles = require(`../build-utils`).calcDirtyHtmlFiles
})
})

function generateStateToTestHelper(
pages: Record<
string,
Expand Down Expand Up @@ -224,6 +231,71 @@ describe(`calcDirtyHtmlFiles`, () => {
])
})

describe(`onCreatePage + deletePage + createPage that change path casing of a page`, () => {
afterAll(() => {
platformSpy.mockRestore()
})

it(`linux (case sensitive file system)`, () => {
let isolatedCalcDirtyHtmlFiles
jest.isolateModules(() => {
platformSpy.mockImplementation(() => `linux`)
isolatedCalcDirtyHtmlFiles = require(`../build-utils`)
.calcDirtyHtmlFiles
})

const state = generateStateToTestHelper({
// page was created, then deleted and similar page with slightly different path was created for it
// different artifacts would be created for them
"/TEST/": {
dirty: 0,
removedOrDeleted: `deleted`,
},
"/test/": {
dirty: 1,
},
})

const results = isolatedCalcDirtyHtmlFiles(state)

// on case sensitive file systems /test/ and /TEST/ are different files so we do need to delete a file
expect(results.toRegenerate.sort()).toEqual([`/test/`])
expect(results.toDelete.sort()).toEqual([`/TEST/`])
expect(Array.from(results.toCleanupFromTrackedState).sort()).toEqual([])
})

it(`windows / mac (case insensitive file system)`, () => {
let isolatedCalcDirtyHtmlFiles
jest.isolateModules(() => {
platformSpy.mockImplementation(() => `win32`)
isolatedCalcDirtyHtmlFiles = require(`../build-utils`)
.calcDirtyHtmlFiles
})

const state = generateStateToTestHelper({
// page was created, then deleted and similar page with slightly different path was created for it
// both page paths would result in same artifacts
"/TEST/": {
dirty: 0,
removedOrDeleted: `deleted`,
},
"/test/": {
dirty: 1,
},
})

const results = isolatedCalcDirtyHtmlFiles(state)

// on case insensitive file systems /test/ and /TEST/ are NOT different files so we should
// not delete files, but still we should cleanup tracked state
expect(results.toRegenerate.sort()).toEqual([`/test/`])
expect(results.toDelete.sort()).toEqual([])
expect(Array.from(results.toCleanupFromTrackedState).sort()).toEqual([
`/TEST/`,
])
})
})

// cases above are to be able to pinpoint exact failure, kitchen sink case is to test all of above in one go
// and make sure that various conditions mixed together are handled correctly
it(`kitchen sink`, () => {
Expand Down
13 changes: 11 additions & 2 deletions packages/gatsby/src/commands/build-utils.ts
@@ -1,6 +1,6 @@
import fs from "fs-extra"
import path from "path"

import { platform } from "os"
import reporter from "gatsby-cli/lib/reporter"

import {
Expand Down Expand Up @@ -66,10 +66,18 @@ export const removePageFiles = async (
})
}

const FSisCaseInsensitive = platform() === `win32` || platform() === `darwin`
function normalizePagePath(path: string): string {
if (path === `/`) {
return `/`
}

if (FSisCaseInsensitive) {
// e.g. /TEST/ and /test/ would produce "same" artifacts on case insensitive
// file systems
path = path.toLowerCase()
}

return path.endsWith(`/`) ? path.slice(0, -1) : path
}

Expand Down Expand Up @@ -105,7 +113,8 @@ export function calcDirtyHtmlFiles(
* to regenerate and more importantly - to delete (so we don't delete html and page-data file
* when path changes slightly but it would still result in same html and page-data filenames
* for example adding/removing trailing slash between builds or even mid build with plugins
* like `gatsby-plugin-remove-trailing-slashes`)
* like `gatsby-plugin-remove-trailing-slashes`). Additionally similar consideration need to
* be accounted for cases where page paths casing on case-insensitive file systems.
*/
function markActionForPage(path: string, action: PageGenerationAction): void {
const normalizedPagePath = normalizePagePath(path)
Expand Down

0 comments on commit 3224803

Please sign in to comment.