diff --git a/CHANGELOG.md b/CHANGELOG.md index 79c62fb8261d..6391a1227bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - `[jest-snapshot]` Handle arrays when merging snapshots ([#7089](https://github.com/facebook/jest/pull/7089)) - `[expect]` Extract names of async and generator functions ([#8362](https://github.com/facebook/jest/pull/8362)) - `[jest-runtime]` Fix virtual mocks not being unmockable after previously being mocked ([#8396](https://github.com/facebook/jest/pull/8396)) +- `[jest-transform]` Replace special characters in transform cache filenames to support Windows ([#8353](https://github.com/facebook/jest/pull/8353)) ### Chore & Maintenance diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 3e0e7c4d8b93..7748d2cb2439 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -102,6 +102,7 @@ export default class ScriptTransformer { .update(fileData) .update(configString) .update(instrument ? 'instrument' : '') + .update(filename) .update(CACHE_VERSION) .digest('hex'); } @@ -121,11 +122,11 @@ export default class ScriptTransformer { // Create sub folders based on the cacheKey to avoid creating one // directory with many files. const cacheDir = path.join(baseCacheDir, cacheKey[0] + cacheKey[1]); + const cacheFilenamePrefix = path + .basename(filename, path.extname(filename)) + .replace(/\W/g, ''); const cachePath = slash( - path.join( - cacheDir, - path.basename(filename, path.extname(filename)) + '_' + cacheKey, - ), + path.join(cacheDir, cacheFilenamePrefix + '_' + cacheKey), ); createDirectory(cacheDir); diff --git a/packages/jest-transform/src/__tests__/script_transformer.test.js b/packages/jest-transform/src/__tests__/script_transformer.test.js index 994d28d7066c..bcacc14fb8ee 100644 --- a/packages/jest-transform/src/__tests__/script_transformer.test.js +++ b/packages/jest-transform/src/__tests__/script_transformer.test.js @@ -152,6 +152,7 @@ describe('ScriptTransformer', () => { mockFs = object({ '/fruits/banana.js': ['module.exports = "banana";'].join('\n'), + '/fruits/banana:colon.js': ['module.exports = "bananaColon";'].join('\n'), '/fruits/grapefruit.js': [ 'module.exports = function () { return "grapefruit"; }', ].join('\n'), @@ -532,6 +533,34 @@ describe('ScriptTransformer', () => { expect(writeFileAtomic.sync).toBeCalled(); }); + it('reads values from the cache when the file contains colons', () => { + const transformConfig = { + ...config, + transform: [['^.+\\.js$', 'test_preprocessor']], + }; + let scriptTransformer = new ScriptTransformer(transformConfig); + scriptTransformer.transform('/fruits/banana:colon.js', {}); + + const cachePath = getCachePath(mockFs, config); + expect(writeFileAtomic.sync).toBeCalled(); + expect(writeFileAtomic.sync.mock.calls[0][0]).toBe(cachePath); + + // Cache the state in `mockFsCopy` + const mockFsCopy = mockFs; + jest.resetModuleRegistry(); + reset(); + + // Restore the cached fs + mockFs = mockFsCopy; + scriptTransformer = new ScriptTransformer(transformConfig); + scriptTransformer.transform('/fruits/banana:colon.js', {}); + + expect(fs.readFileSync).toHaveBeenCalledTimes(2); + expect(fs.readFileSync).toBeCalledWith('/fruits/banana:colon.js', 'utf8'); + expect(fs.readFileSync).toBeCalledWith(cachePath, 'utf8'); + expect(writeFileAtomic.sync).not.toBeCalled(); + }); + it('does not reuse the in-memory cache between different projects', () => { const scriptTransformer = new ScriptTransformer({ ...config,