diff --git a/CHANGELOG.md b/CHANGELOG.md index bdfa4d189221..f2b0fb81e514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,8 +27,9 @@ - `[babel-preset-jest]` [**BREAKING**] Export a function instead of an object for Babel 7 compatibility ([#7203](https://github.com/facebook/jest/pull/7203)) - `[expect]` Check constructor equality in .toStrictEqual() ([#7005](https://github.com/facebook/jest/pull/7005)) - `[jest-util]` Add `jest.getTimerCount()` to get the count of scheduled fake timers ([#7285](https://github.com/facebook/jest/pull/7285)) -- `[jest-config]` Add `dependencyExtractor` option to use a custom module to extract dependencies from files ([#7313](https://github.com/facebook/jest/pull/7313), [#7349](https://github.com/facebook/jest/pull/7349)). +- `[jest-config]` Add `dependencyExtractor` option to use a custom module to extract dependencies from files ([#7313](https://github.com/facebook/jest/pull/7313), [#7349](https://github.com/facebook/jest/pull/7349), [#7350](https://github.com/facebook/jest/pull/7350)) - `[jest-haste-map]` [**BREAKING**] Expose relative paths when getting the file iterator ([#7321](https://github.com/facebook/jest/pull/7321)) +- `[jest-haste-map]` Accept a `getCacheKey` method in `hasteImplModulePath` modules to reset the cache when the logic changes ([#7350](https://github.com/facebook/jest/pull/7350)) - `[jest-config]` Add `haste.computeSha1` option to compute the sha-1 of the files in the haste map ([#7345](https://github.com/facebook/jest/pull/7345)) ### Fixes diff --git a/docs/Configuration.md b/docs/Configuration.md index a74e757b5281..8cc5493f7864 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -265,6 +265,8 @@ This option allows the use of a custom dependency extractor. It must be a node m The function should return an iterable (`Array`, `Set`, etc.) with the dependencies found in the code. +That module can also contain a `getCacheKey` function to generate a cache key to determine if the logic has changed and any cached artifacts relying on it should be discarded. + ### `errorOnDeprecated` [boolean] Default: `false` diff --git a/packages/jest-haste-map/src/__tests__/dependencyExtractor.js b/packages/jest-haste-map/src/__tests__/dependencyExtractor.js index d8241394a89f..a1d8e5bbc6db 100644 --- a/packages/jest-haste-map/src/__tests__/dependencyExtractor.js +++ b/packages/jest-haste-map/src/__tests__/dependencyExtractor.js @@ -26,3 +26,13 @@ export function extract(code, defaultDependencyExtractor) { return dependencies; } + +let cacheKey; + +export function getCacheKey() { + return cacheKey; +} + +export function setCacheKey(key) { + cacheKey = key; +} diff --git a/packages/jest-haste-map/src/__tests__/haste_impl.js b/packages/jest-haste-map/src/__tests__/haste_impl.js index 368879354ac2..ce58fef46fee 100644 --- a/packages/jest-haste-map/src/__tests__/haste_impl.js +++ b/packages/jest-haste-map/src/__tests__/haste_impl.js @@ -7,8 +7,13 @@ 'use strict'; const path = require('path'); +let cacheKey; module.exports = { + getCacheKey() { + return cacheKey; + }, + getHasteName(filename) { if ( filename.includes('__mocks__') || @@ -23,4 +28,8 @@ module.exports = { .substr(filename.lastIndexOf(path.sep) + 1) .replace(/(\.(android|ios|native))?\.js$/, ''); }, + + setCacheKey(key) { + cacheKey = key; + }, }; diff --git a/packages/jest-haste-map/src/__tests__/index.test.js b/packages/jest-haste-map/src/__tests__/index.test.js index 0429634bdf42..79383443fff5 100644 --- a/packages/jest-haste-map/src/__tests__/index.test.js +++ b/packages/jest-haste-map/src/__tests__/index.test.js @@ -232,6 +232,31 @@ describe('HasteMap', () => { expect(hasteMap1.getCacheFilePath()).not.toBe(hasteMap2.getCacheFilePath()); }); + it('creates different cache file paths for different dependency extractor cache keys', () => { + jest.resetModuleRegistry(); + const HasteMap = require('../'); + const dependencyExtractor = require('./dependencyExtractor'); + const config = Object.assign({}, defaultConfig, { + dependencyExtractor: require.resolve('./dependencyExtractor'), + }); + dependencyExtractor.setCacheKey('foo'); + const hasteMap1 = new HasteMap(config); + dependencyExtractor.setCacheKey('bar'); + const hasteMap2 = new HasteMap(config); + expect(hasteMap1.getCacheFilePath()).not.toBe(hasteMap2.getCacheFilePath()); + }); + + it('creates different cache file paths for different hasteImplModulePath cache keys', () => { + jest.resetModuleRegistry(); + const HasteMap = require('../'); + const hasteImpl = require('./haste_impl'); + hasteImpl.setCacheKey('foo'); + const hasteMap1 = new HasteMap(defaultConfig); + hasteImpl.setCacheKey('bar'); + const hasteMap2 = new HasteMap(defaultConfig); + expect(hasteMap1.getCacheFilePath()).not.toBe(hasteMap2.getCacheFilePath()); + }); + it('creates different cache file paths for different projects', () => { jest.resetModuleRegistry(); const HasteMap = require('../'); diff --git a/packages/jest-haste-map/src/index.js b/packages/jest-haste-map/src/index.js index d31d8f2188ff..ef5392efd5fe 100644 --- a/packages/jest-haste-map/src/index.js +++ b/packages/jest-haste-map/src/index.js @@ -261,10 +261,30 @@ class HasteMap extends EventEmitter { 'deprecated. Provide a RegExp instead. See https://github.com/facebook/jest/pull/4063.', ); } + const rootDirHash = crypto .createHash('md5') .update(options.rootDir) .digest('hex'); + let hasteImplHash = ''; + let dependencyExtractorHash = ''; + + if (options.hasteImplModulePath) { + // $FlowFixMe: dynamic require + const hasteImpl = require(options.hasteImplModulePath); + if (hasteImpl.getCacheKey) { + hasteImplHash = String(hasteImpl.getCacheKey()); + } + } + + if (options.dependencyExtractor) { + // $FlowFixMe: dynamic require + const dependencyExtractor = require(options.dependencyExtractor); + if (dependencyExtractor.getCacheKey) { + dependencyExtractorHash = String(dependencyExtractor.getCacheKey()); + } + } + this._cachePath = HasteMap.getCacheFilePath( this._options.cacheDirectory, `haste-map-${this._options.name}-${rootDirHash}`, @@ -278,6 +298,8 @@ class HasteMap extends EventEmitter { this._options.computeSha1.toString(), options.mocksPattern || '', (options.ignorePattern || '').toString(), + hasteImplHash, + dependencyExtractorHash, ); this._whitelist = getWhiteList(options.providesModuleNodeModules); this._buildPromise = null;