From 6cacbea296c06fb3a8884b7b0fbc6647d88c5f07 Mon Sep 17 00:00:00 2001 From: Huafu Gandon Date: Sat, 6 Oct 2018 06:52:15 +0200 Subject: [PATCH] fix(cache): adds project's dep versions to cache key Includes the real version of each dependency of the containing project in the cache key digest calculation. Closes #785 --- src/config/config-set.spec.ts | 49 ++++++++++++++++++++++++++++++++--- src/config/config-set.ts | 42 ++++++++++++++++++++++++++++-- src/util/messages.ts | 1 + 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/config/config-set.spec.ts b/src/config/config-set.spec.ts index 9d3c183c82..045980b65c 100644 --- a/src/config/config-set.spec.ts +++ b/src/config/config-set.spec.ts @@ -489,23 +489,63 @@ describe('babelJestTransformer', () => { }) }) // babelJestTransformer +describe('projectDependencies', () => { + const pkg = { + optionalDependencies: { opt: '1.2.3' }, + peerDependencies: { peer: '1.2.4' }, + devDependencies: { dev: '1.2.5' }, + dependencies: { std: '1.2.6' }, + } + const realVersions: any = { + peer: '0.1.0', + dev: '4.3.2', + std: '9.10.2', + } + it('should list all deps with their real version', () => { + jest.doMock('peer/package.json', () => ({ version: realVersions.peer }), { virtual: true }) + jest.doMock('dev/package.json', () => ({ version: realVersions.dev }), { virtual: true }) + jest.doMock('std/package.json', () => ({ version: realVersions.std }), { virtual: true }) + const cs = createConfigSet({ + tsJestConfig: { tsConfig: false } as any, + projectPackageJson: pkg, + }) + expect(cs.projectDependencies).toEqual(realVersions) + jest.dontMock('peer/package.json') + jest.dontMock('dev/package.json') + jest.dontMock('std/package.json') + }) +}) // projectDependencies + describe('cacheKey', () => { it('should be a string', () => { - const cs = createConfigSet({ tsJestConfig: { tsConfig: false } as any }) + const cs = createConfigSet({ + tsJestConfig: { tsConfig: false } as any, + projectDependencies: { + opt: '1.2.3', + peer: '1.2.4', + dev: '1.2.5', + std: '1.2.6', + }, + }) // we tested those and don't want the snapshot to change all the time we upgrade const val = cs.jsonValue.value delete val.versions cs.jsonValue.value = val // digest is mocked in src/__mocks__/index.ts expect(cs.cacheKey).toMatchInlineSnapshot( - `"{\\"digest\\":\\"a0d51ca854194df8191d0e65c0ca4730f510f332\\",\\"jest\\":{\\"__backported\\":true,\\"globals\\":{}},\\"transformers\\":[\\"hoisting-jest-mock@1\\"],\\"tsJest\\":{\\"compiler\\":\\"typescript\\",\\"diagnostics\\":{\\"ignoreCodes\\":[6059,18002,18003],\\"pretty\\":true,\\"throws\\":true},\\"isolatedModules\\":false,\\"transformers\\":[]},\\"tsconfig\\":{\\"declaration\\":false,\\"inlineSourceMap\\":false,\\"inlineSources\\":true,\\"module\\":1,\\"noEmit\\":false,\\"outDir\\":\\"$$ts-jest$$\\",\\"removeComments\\":false,\\"sourceMap\\":true,\\"target\\":1}}"`, + '"{\\"digest\\":\\"a0d51ca854194df8191d0e65c0ca4730f510f332\\",\\"jest\\":{\\"__backported\\":true,\\"globals\\":{}},\\"projectDepVersions\\":{\\"dev\\":\\"1.2.5\\",\\"opt\\":\\"1.2.3\\",\\"peer\\":\\"1.2.4\\",\\"std\\":\\"1.2.6\\"},\\"transformers\\":[\\"hoisting-jest-mock@1\\"],\\"tsJest\\":{\\"compiler\\":\\"typescript\\",\\"diagnostics\\":{\\"ignoreCodes\\":[6059,18002,18003],\\"pretty\\":true,\\"throws\\":true},\\"isolatedModules\\":false,\\"transformers\\":[]},\\"tsconfig\\":{\\"declaration\\":false,\\"inlineSourceMap\\":false,\\"inlineSources\\":true,\\"module\\":1,\\"noEmit\\":false,\\"outDir\\":\\"$$ts-jest$$\\",\\"removeComments\\":false,\\"sourceMap\\":true,\\"target\\":1}}"', ) }) }) // cacheKey describe('jsonValue', () => { it('should create jsonValue based on each config and version', () => { - const cs = createConfigSet({ tsJestConfig: { tsConfig: false } as any }) + const cs = createConfigSet({ + tsJestConfig: { tsConfig: false } as any, + projectDependencies: { + 'some-module': '1.2.3', + }, + }) const val = cs.jsonValue.valueOf() expect(cs.toJSON()).toEqual(val) // it will change each time we upgrade – we tested those in the `version` block @@ -521,6 +561,9 @@ Object { "__backported": true, "globals": Object {}, }, + "projectDepVersions": Object { + "some-module": "1.2.3", + }, "transformers": Array [ "hoisting-jest-mock@1", ], diff --git a/src/config/config-set.ts b/src/config/config-set.ts index 4331144fa4..3b53e14be1 100644 --- a/src/config/config-set.ts +++ b/src/config/config-set.ts @@ -9,7 +9,7 @@ * with the complete, object version of it. */ import { LogContexts, Logger } from 'bs-logger' -import { existsSync, readFileSync } from 'fs' +import { existsSync, readFileSync, realpathSync } from 'fs' import json5 from 'json5' import { dirname, isAbsolute, join, normalize, resolve } from 'path' import semver from 'semver' @@ -112,6 +112,41 @@ const toDiagnosticCodeList = (items: any, into: number[] = []): number[] => { } export class ConfigSet { + @Memoize() + get projectPackageJson(): Record { + const tsJestRoot = resolve(__dirname, '..', '..') + let pkgPath = resolve(tsJestRoot, '..', '..', 'package.json') + let exists = existsSync(pkgPath) + if (!exists) { + if (realpathSync(this.rootDir) === realpathSync(tsJestRoot)) { + pkgPath = resolve(tsJestRoot, 'package.json') + exists = true + } else { + this.logger.warn(Errors.UnableToFindProjectRoot) + } + } + return exists ? require(pkgPath) : {} + } + + @Memoize() + get projectDependencies(): Record { + const { projectPackageJson: pkg } = this + const names = Object.keys({ + ...pkg.optionalDependencies, + ...pkg.peerDependencies, + ...pkg.devDependencies, + ...pkg.dependencies, + }) + return names.reduce( + (map, name) => { + const version = getPackageVersion(name) + if (version) map[name] = version + return map + }, + {} as Record, + ) + } + @Memoize() get jest(): jest.ProjectConfig { const config = backportJestConfig(this.logger, this._jestConfig) @@ -440,6 +475,8 @@ export class ConfigSet { const cacheSuffix = sha1( stringify({ version: this.compilerModule.version, + digest: this.tsJestDigest, + dependencies: this.projectDependencies, compiler: this.tsJest.compiler, compilerOptions: this.typescript.options, isolatedModules: this.tsJest.isolatedModules, @@ -447,7 +484,7 @@ export class ConfigSet { }), ) const res = join(this.jest.cacheDirectory, 'ts-jest', cacheSuffix.substr(0, 2), cacheSuffix.substr(2)) - logger.debug({ cacheDirectory: res }, `will use file caching`) + logger.debug({ cacheDirectory: res }, 'will use file caching') return res } @@ -519,6 +556,7 @@ export class ConfigSet { return new JsonableValue({ versions: this.versions, + projectDepVersions: this.projectDependencies, digest: this.tsJestDigest, transformers: this.astTransformers.map(t => `${t.name}@${t.version}`), jest, diff --git a/src/util/messages.ts b/src/util/messages.ts index 8f371158bc..1033e7bfcf 100644 --- a/src/util/messages.ts +++ b/src/util/messages.ts @@ -21,6 +21,7 @@ export enum Errors { GotUnknownFileTypeWithoutBabel = 'Got a unknown file type to compile (file: {{path}}). To fix this, in your Jest config change the `transform` key which value is `ts-jest` so that it does not match this kind of files anymore.', GotUnknownFileTypeWithBabel = 'Got a unknown file type to compile (file: {{path}}). To fix this, in your Jest config change the `transform` key which value is `ts-jest` so that it does not match this kind of files anymore. If you still want Babel to process it, add another entry to the `transform` option with value `babel-jest` which key matches this type of files.', ConfigNoModuleInterop = 'If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.', + UnableToFindProjectRoot = 'Unable to find the root of the project where ts-jest has been installed.', } /**