Skip to content

Commit

Permalink
fix: revert usage of @jest/create-cache-key-function (#2108)
Browse files Browse the repository at this point in the history
Closes #2080
Closes #2090
Closes #2104
  • Loading branch information
ahnpnl authored Nov 8, 2020
1 parent a42de7a commit dee8231
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 96 deletions.
6 changes: 3 additions & 3 deletions e2e/__tests__/__snapshots__/coverage.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`using template "default" should report coverages 1`] = `
exports[`Code coverage should pass using template "default" 1`] = `
√ jest
↳ exit code: 0
===[ STDOUT ]===================================================================
Expand All @@ -23,7 +23,7 @@ exports[`using template "default" should report coverages 1`] = `
================================================================================
`;

exports[`using template "with-babel-7" should report coverages 1`] = `
exports[`Code coverage should pass using template "with-babel-7" 1`] = `
√ jest
↳ exit code: 0
===[ STDOUT ]===================================================================
Expand All @@ -46,7 +46,7 @@ exports[`using template "with-babel-7" should report coverages 1`] = `
================================================================================
`;

exports[`using template "with-babel-7-string-config" should report coverages 1`] = `
exports[`Code coverage should pass using template "with-babel-7-string-config" 1`] = `
√ jest
↳ exit code: 0
===[ STDOUT ]===================================================================
Expand Down
14 changes: 7 additions & 7 deletions e2e/__tests__/coverage.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { allValidPackageSets } from '../__helpers__/templates'
import { configureTestCase } from '../__helpers__/test-case'

const testCase = configureTestCase('simple', {
jestConfig: { collectCoverage: true },
})
describe('Code coverage', () => {
const testCase = configureTestCase('simple', {
jestConfig: { collectCoverage: true },
})

testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { templateName }) => {
describe(`using template "${templateName}"`, () => {
const result = runTest()
testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { testLabel }) => {
it(testLabel, () => {
const result = runTest()

it(`should report coverages`, () => {
expect(result.status).toBe(0)
expect(result).toMatchSnapshot()
})
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
},
"homepage": "https://kulshekhar.github.io/ts-jest",
"dependencies": {
"@jest/create-cache-key-function": "^26.5.0",
"@types/jest": "26.x",
"bs-logger": "0.x",
"buffer-from": "1.x",
Expand Down
45 changes: 17 additions & 28 deletions src/ts-jest-transformer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LogLevels } from 'bs-logger'
import { sep } from 'path'

import { TsJestTransformer } from './ts-jest-transformer'
import { ConfigSet } from './config/config-set'
Expand All @@ -12,39 +13,24 @@ beforeEach(() => {
})

describe('TsJestTransformer', () => {
describe('createOrResolveTransformerCfg', () => {
it('should return the same config-set for same values with jest config string is not in cachedConfigSets', () => {
const obj1 = { cwd: '/foo/.' } as any
const tjT = new TsJestTransformer()

logTarget.clear()

tjT.getCacheKey('foo', 'foo', JSON.stringify(obj1), {
config: obj1,
} as any)
describe('configFor', () => {
it('should return the same config-set for same values with jest config string is not in configSetsIndex', () => {
const obj1 = { cwd: '/foo/.', rootDir: '/bar//dummy/..', globals: {} }
const cs3 = new TsJestTransformer().configsFor(obj1 as any)

expect(logTarget.lines[0]).toMatchInlineSnapshot(`
"[level:30] no matching config-set found, creating a new one
"
`)
expect(cs3.cwd).toBe(`${sep}foo`)
expect(cs3.rootDir).toBe(`${sep}bar`)
})

it('should return the same config-set for same values with jest config string in cachedConfigSets', () => {
it('should return the same config-set for same values with jest config string in configSetsIndex', () => {
const obj1 = { cwd: '/foo/.', rootDir: '/bar//dummy/..', globals: {} }
const obj2 = { ...obj1 }
const tjT1 = new TsJestTransformer()
const tjT2 = new TsJestTransformer()

logTarget.clear()
const cs1 = new TsJestTransformer().configsFor(obj1 as any)
const cs2 = new TsJestTransformer().configsFor(obj2 as any)

tjT1.getCacheKey('foo', 'foo', JSON.stringify(obj1), {
config: obj1,
} as any)
tjT2.getCacheKey('foo', 'foo', JSON.stringify(obj2), {
config: obj2,
} as any)

expect(logTarget.filteredLines(LogLevels.info)).toHaveLength(1)
expect(cs1.cwd).toBe(`${sep}foo`)
expect(cs1.rootDir).toBe(`${sep}bar`)
expect(cs2).toBe(cs1)
})
})

Expand All @@ -63,10 +49,13 @@ describe('TsJestTransformer', () => {
tr.getCacheKey(input.fileContent, input.fileName, '{}', { ...input.options, instrument: true }),
tr.getCacheKey(input.fileContent, input.fileName, '{}', { ...input.options, rootDir: '/bar' }),
]

// each key should have correct length
for (const key of keys) {
expect(key).toHaveLength(32)
expect(key).toHaveLength(40)
}
// unique array should have same length
expect(keys.filter((k, i, all) => all.indexOf(k) === i)).toHaveLength(keys.length)
})
})

Expand Down
132 changes: 75 additions & 57 deletions src/ts-jest-transformer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import createCacheKey from '@jest/create-cache-key-function'
import type { CacheKeyOptions, TransformedSource, Transformer, TransformOptions } from '@jest/transform'
import type { Config } from '@jest/types'
import type { Logger } from 'bs-logger'
Expand All @@ -9,6 +8,7 @@ import { stringify } from './utils/json'
import { JsonableValue } from './utils/jsonable-value'
import { rootLogger } from './utils/logger'
import { Errors, interpolate } from './utils/messages'
import { sha1 } from './utils/sha1'

interface CachedConfigSet {
configSet: ConfigSet
Expand All @@ -25,14 +25,65 @@ export class TsJestTransformer implements Transformer {
private static readonly _cachedConfigSets: CachedConfigSet[] = []
protected readonly logger: Logger
protected _transformCfgStr!: string
protected _configSet!: ConfigSet

constructor() {
this.logger = rootLogger.child({ namespace: 'ts-jest-transformer' })

this.logger.debug('created new transformer')
}

/**
* @public
*/
configsFor(jestConfig: Config.ProjectConfig): ConfigSet {
const ccs: CachedConfigSet | undefined = TsJestTransformer._cachedConfigSets.find(
(cs) => cs.jestConfig.value === jestConfig,
)
let configSet: ConfigSet
if (ccs) {
this._transformCfgStr = ccs.transformerCfgStr
configSet = ccs.configSet
} else {
// try to look-it up by stringified version
const serializedJestCfg = stringify(jestConfig)
const serializedCcs = TsJestTransformer._cachedConfigSets.find(
(cs) => cs.jestConfig.serialized === serializedJestCfg,
)
if (serializedCcs) {
// update the object so that we can find it later
// this happens because jest first calls getCacheKey with stringified version of
// the config, and then it calls the transformer with the proper object
serializedCcs.jestConfig.value = jestConfig
this._transformCfgStr = serializedCcs.transformerCfgStr
configSet = serializedCcs.configSet
} else {
// create the new record in the index
this.logger.info('no matching config-set found, creating a new one')

configSet = new ConfigSet(jestConfig)
this._transformCfgStr = new JsonableValue({
digest: configSet.tsJestDigest,
babel: configSet.babelConfig,
...jestConfig,
tsconfig: {
options: configSet.parsedTsConfig.options,
raw: configSet.parsedTsConfig.raw,
},
}).serialized
TsJestTransformer._cachedConfigSets.push({
jestConfig: new JsonableValue(jestConfig),
configSet,
transformerCfgStr: this._transformCfgStr,
})
}
}

return configSet
}

/**
* @public
*/
process(
input: string,
filePath: Config.Path,
Expand All @@ -43,9 +94,10 @@ export class TsJestTransformer implements Transformer {

let result: string | TransformedSource
const source: string = input
const { hooks } = this._configSet
const shouldStringifyContent = this._configSet.shouldStringifyContent(filePath)
const babelJest = shouldStringifyContent ? undefined : this._configSet.babelJestTransformer
const configs = this.configsFor(jestConfig)
const { hooks } = configs
const shouldStringifyContent = configs.shouldStringifyContent(filePath)
const babelJest = shouldStringifyContent ? undefined : configs.babelJestTransformer
const isDefinitionFile = filePath.endsWith(DECLARATION_TYPE_EXT)
const isJsFile = JS_JSX_REGEX.test(filePath)
const isTsFile = !isDefinitionFile && TS_TSX_REGEX.test(filePath)
Expand All @@ -55,15 +107,15 @@ export class TsJestTransformer implements Transformer {
} else if (isDefinitionFile) {
// do not try to compile declaration files
result = ''
} else if (!this._configSet.parsedTsConfig.options.allowJs && isJsFile) {
} else if (!configs.parsedTsConfig.options.allowJs && isJsFile) {
// we've got a '.js' but the compiler option `allowJs` is not set or set to false
this.logger.warn({ fileName: filePath }, interpolate(Errors.GotJsFileButAllowJsFalse, { path: filePath }))

result = source
} else if (isJsFile || isTsFile) {
// transpile TS code (source maps are included)
/* istanbul ignore if */
result = this._configSet.tsCompiler.compile(source, filePath)
result = configs.tsCompiler.compile(source, filePath)
} else {
// we should not get called for files with other extension than js[x], ts[x] and d.ts,
// TypeScript will bail if we try to compile, and if it was to call babel, users can
Expand Down Expand Up @@ -99,66 +151,32 @@ export class TsJestTransformer implements Transformer {
* Jest uses this to cache the compiled version of a file
*
* @see https://github.com/facebook/jest/blob/v23.5.0/packages/jest-runtime/src/script_transformer.js#L61-L90
*
* @public
*/
getCacheKey(
fileContent: string,
filePath: string,
_jestConfigStr: string,
transformOptions: CacheKeyOptions,
): string {
this.createOrResolveTransformerCfg(transformOptions.config)
const configs = this.configsFor(transformOptions.config)

this.logger.debug({ fileName: filePath, transformOptions }, 'computing cache key for', filePath)

return createCacheKey()(fileContent, filePath, this._transformCfgStr, {
config: transformOptions.config,
instrument: false,
})
}

/**
* Users can override this method and provide their own config class
*/
protected createOrResolveTransformerCfg(jestConfig: Config.ProjectConfig): void {
const ccs: CachedConfigSet | undefined = TsJestTransformer._cachedConfigSets.find(
(cs) => cs.jestConfig.value === jestConfig,
// we do not instrument, ensure it is false all the time
const { instrument = false, rootDir = configs.rootDir } = transformOptions

return sha1(
this._transformCfgStr,
'\x00',
rootDir,
'\x00',
`instrument:${instrument ? 'on' : 'off'}`,
'\x00',
fileContent,
'\x00',
filePath,
)
if (ccs) {
this._transformCfgStr = ccs.transformerCfgStr
this._configSet = ccs.configSet
} else {
// try to look-it up by stringified version
const serializedJestCfg = stringify(jestConfig)
const serializedCcs = TsJestTransformer._cachedConfigSets.find(
(cs) => cs.jestConfig.serialized === serializedJestCfg,
)
if (serializedCcs) {
// update the object so that we can find it later
// this happens because jest first calls getCacheKey with stringified version of
// the config, and then it calls the transformer with the proper object
serializedCcs.jestConfig.value = jestConfig
this._transformCfgStr = serializedCcs.transformerCfgStr
this._configSet = serializedCcs.configSet
} else {
// create the new record in the index
this.logger.info('no matching config-set found, creating a new one')

this._configSet = new ConfigSet(jestConfig)
this._transformCfgStr = new JsonableValue({
digest: this._configSet.tsJestDigest,
babel: this._configSet.babelConfig,
...jestConfig,
tsconfig: {
options: this._configSet.parsedTsConfig.options,
raw: this._configSet.parsedTsConfig.raw,
},
}).serialized
TsJestTransformer._cachedConfigSets.push({
jestConfig: new JsonableValue(jestConfig),
configSet: this._configSet,
transformerCfgStr: this._transformCfgStr,
})
}
}
}
}

0 comments on commit dee8231

Please sign in to comment.