From 387964faed14ce24d2cf8170a04eee244d69b8b9 Mon Sep 17 00:00:00 2001 From: Ahn Date: Tue, 26 Jan 2021 00:09:04 +0100 Subject: [PATCH] feat(compiler): allow custom transformers to access internal `Program` (#2299) BREAKING CHANGE `ts-jest` custom AST transformer function signature has changed to ``` import type { TsCompilerInstance } from 'ts-jest/dist/types' export function factory(compilerInstance: TsCompilerInstance) { //... } ``` --- .../with-extra-options/foo.js | 6 +- src/__mocks__/dummy-transformer.js | 8 +- src/compiler/ts-compiler.spec.ts | 54 +++++++++++- src/compiler/ts-compiler.ts | 49 ++++++++--- .../__snapshots__/config-set.spec.ts.snap | 55 ++++++++++-- src/config/config-set.spec.ts | 2 +- src/config/config-set.ts | 86 +++++++++---------- src/transformers/README.md | 6 +- src/transformers/hoist-jest.spec.ts | 7 +- src/transformers/hoist-jest.ts | 8 +- src/transformers/index.spec.ts | 16 ---- src/transformers/index.ts | 8 -- src/transformers/path-mapping.spec.ts | 15 ++-- src/transformers/path-mapping.ts | 12 +-- src/ts-jest-transformer.spec.ts | 19 ++-- src/types.ts | 7 +- 16 files changed, 231 insertions(+), 127 deletions(-) delete mode 100644 src/transformers/index.spec.ts delete mode 100644 src/transformers/index.ts diff --git a/e2e/__cases__/ast-transformers/with-extra-options/foo.js b/e2e/__cases__/ast-transformers/with-extra-options/foo.js index 2fae9f1050..e0cbf7889a 100644 --- a/e2e/__cases__/ast-transformers/with-extra-options/foo.js +++ b/e2e/__cases__/ast-transformers/with-extra-options/foo.js @@ -1,8 +1,8 @@ const { LogContexts, LogLevels } = require('bs-logger') -function factory(cs, extraOpts = Object.create(null)) { - const logger = cs.logger.child({ namespace: 'dummy-transformer' }) - const ts = cs.compilerModule +function factory({ configSet }, extraOpts = Object.create(null)) { + const logger = configSet.logger.child({ namespace: 'dummy-transformer' }) + const ts = configSet.compilerModule logger.debug('Dummy transformer with extra options', JSON.stringify(extraOpts)) function createVisitor(_ctx, _sf) { diff --git a/src/__mocks__/dummy-transformer.js b/src/__mocks__/dummy-transformer.js index e84abdace0..28406fe70c 100644 --- a/src/__mocks__/dummy-transformer.js +++ b/src/__mocks__/dummy-transformer.js @@ -1,8 +1,10 @@ const { LogContexts, LogLevels } = require('bs-logger') -function factory(cs) { - const logger = cs.logger.child({ namespace: 'dummy-transformer' }) - const ts = cs.compilerModule +function factory(tsCompiler) { + const logger = tsCompiler.configSet.logger.child({ namespace: 'dummy-transformer' }) + const ts = tsCompiler.configSet.compilerModule + // eslint-disable-next-line no-console + console.log(tsCompiler.program) function createVisitor(_ctx, _) { return (node) => node diff --git a/src/compiler/ts-compiler.spec.ts b/src/compiler/ts-compiler.spec.ts index 2aa82b6836..f474213ac1 100644 --- a/src/compiler/ts-compiler.spec.ts +++ b/src/compiler/ts-compiler.spec.ts @@ -30,7 +30,7 @@ describe('TsCompiler', () => { expect(new ProcessedSource(compiledOutput, fileName).outputCodeWithoutMaps).toMatchSnapshot() }) - it('should compile js file for allowJs true', () => { + test('should compile js file for allowJs true', () => { const fileName = 'foo.js' const compiler = makeCompiler({ tsJestConfig: { ...baseTsJestConfig, tsconfig: { allowJs: true, outDir: TS_JEST_OUT_DIR } }, @@ -179,6 +179,32 @@ const t: string = f(5) ).not.toThrowError() }) }) + + test('should use correct custom AST transformers', () => { + // eslint-disable-next-line no-console + console.log = jest.fn() + const fileName = 'foo.js' + const compiler = makeCompiler({ + tsJestConfig: { + ...baseTsJestConfig, + tsconfig: { + allowJs: true, + outDir: TS_JEST_OUT_DIR, + }, + astTransformers: { + before: ['dummy-transformer'], + after: ['dummy-transformer'], + afterDeclarations: ['dummy-transformer'], + }, + }, + }) + const source = 'export default 42' + + compiler.getCompiledOutput(source, fileName, false) + + // eslint-disable-next-line no-console + expect(console.log).toHaveBeenCalledTimes(3) + }) }) describe('isolatedModule false', () => { @@ -452,5 +478,31 @@ const t: string = f(5) expect(() => compiler.getCompiledOutput(source, fileName, false)).toThrowErrorMatchingSnapshot() }) }) + + test('should pass Program instance into custom transformers', () => { + // eslint-disable-next-line no-console + console.log = jest.fn() + const fileName = join(mockFolder, 'thing.spec.ts') + const compiler = makeCompiler( + { + tsJestConfig: { + ...baseTsJestConfig, + astTransformers: { + before: ['dummy-transformer'], + after: ['dummy-transformer'], + afterDeclarations: ['dummy-transformer'], + }, + }, + }, + jestCacheFS, + ) + + compiler.getCompiledOutput(readFileSync(fileName, 'utf-8'), fileName, false) + + // eslint-disable-next-line no-console + expect(console.log).toHaveBeenCalled() + // eslint-disable-next-line no-console + expect(((console.log as any) as jest.MockInstance).mock.calls[0][0].emit).toBeDefined() + }) }) }) diff --git a/src/compiler/ts-compiler.ts b/src/compiler/ts-compiler.ts index aa5efe2e6a..b35c89e3e1 100644 --- a/src/compiler/ts-compiler.ts +++ b/src/compiler/ts-compiler.ts @@ -10,11 +10,16 @@ import type { ResolvedModuleFull, TranspileOutput, CompilerOptions, + SourceFile, + Program, + TransformerFactory, + Bundle, + CustomTransformerFactory, } from 'typescript' import type { ConfigSet } from '../config/config-set' import { LINE_FEED } from '../constants' -import type { CompilerInstance, ResolvedModulesMap, StringMap, TTypeScript } from '../types' +import type { ResolvedModulesMap, StringMap, TsCompilerInstance, TTypeScript } from '../types' import { rootLogger } from '../utils/logger' import { Errors, interpolate } from '../utils/messages' @@ -23,23 +28,22 @@ import { updateOutput } from './compiler-utils' /** * @internal */ -export class TsCompiler implements CompilerInstance { +export class TsCompiler implements TsCompilerInstance { private readonly _logger: Logger private readonly _ts: TTypeScript private readonly _parsedTsConfig: ParsedCommandLine private readonly _compilerCacheFS: Map = new Map() - private readonly _jestCacheFS: StringMap private readonly _initialCompilerOptions: CompilerOptions private _compilerOptions: CompilerOptions private _cachedReadFile: ((fileName: string) => string | undefined) | undefined private _projectVersion = 1 private _languageService: LanguageService | undefined + program: Program | undefined constructor(readonly configSet: ConfigSet, readonly jestCacheFS: StringMap) { this._ts = configSet.compilerModule this._logger = rootLogger.child({ namespace: 'ts-compiler' }) this._parsedTsConfig = this.configSet.parsedTsConfig as ParsedCommandLine - this._jestCacheFS = jestCacheFS this._initialCompilerOptions = { ...this._parsedTsConfig.options } this._compilerOptions = { ...this._initialCompilerOptions } if (!this.configSet.isolatedModules) { @@ -96,13 +100,13 @@ export class TsCompiler implements CompilerInstance { // Read contents from TypeScript memory cache. if (!hit) { const fileContent = - this._jestCacheFS.get(normalizedFileName) ?? this._cachedReadFile?.(normalizedFileName) ?? undefined + this.jestCacheFS.get(normalizedFileName) ?? this._cachedReadFile?.(normalizedFileName) ?? undefined if (fileContent) { - this._jestCacheFS.set(normalizedFileName, fileContent) + this.jestCacheFS.set(normalizedFileName, fileContent) this._compilerCacheFS.set(normalizedFileName, 1) } } - const contents = this._jestCacheFS.get(normalizedFileName) + const contents = this.jestCacheFS.get(normalizedFileName) if (contents === undefined) return @@ -118,7 +122,17 @@ export class TsCompiler implements CompilerInstance { getCurrentDirectory: () => this.configSet.cwd, getCompilationSettings: () => this._compilerOptions, getDefaultLibFileName: () => this._ts.getDefaultLibFilePath(this._compilerOptions), - getCustomTransformers: () => this.configSet.customTransformers, + getCustomTransformers: () => ({ + before: this.configSet.resolvedTransformers.before.map((beforeTransformer) => + beforeTransformer.factory(this, beforeTransformer.options), + ) as (TransformerFactory | CustomTransformerFactory)[], + after: this.configSet.resolvedTransformers.after.map((afterTransformer) => + afterTransformer.factory(this, afterTransformer.options), + ) as (TransformerFactory | CustomTransformerFactory)[], + afterDeclarations: this.configSet.resolvedTransformers.afterDeclarations.map((afterDeclarations) => + afterDeclarations.factory(this, afterDeclarations.options), + ) as TransformerFactory[], + }), resolveModuleNames: (moduleNames: string[], containingFile: string): (ResolvedModuleFull | undefined)[] => moduleNames.map((moduleName) => { const { resolvedModule } = this._ts.resolveModuleName( @@ -136,6 +150,7 @@ export class TsCompiler implements CompilerInstance { this._logger.debug('created language service') this._languageService = this._ts.createLanguageService(serviceHost, this._ts.createDocumentRegistry()) + this.program = this._languageService.getProgram() } getResolvedModulesMap(fileContent: string, fileName: string): ResolvedModulesMap { @@ -197,7 +212,17 @@ export class TsCompiler implements CompilerInstance { const result: TranspileOutput = this._ts.transpileModule(fileContent, { fileName, - transformers: this.configSet.customTransformers, + transformers: { + before: this.configSet.resolvedTransformers.before.map((beforeTransformer) => + beforeTransformer.factory(this, beforeTransformer.options), + ) as (TransformerFactory | CustomTransformerFactory)[], + after: this.configSet.resolvedTransformers.after.map((afterTransformer) => + afterTransformer.factory(this, afterTransformer.options), + ) as (TransformerFactory | CustomTransformerFactory)[], + afterDeclarations: this.configSet.resolvedTransformers.afterDeclarations.map((afterDeclarations) => + afterDeclarations.factory(this, afterDeclarations.options), + ) as TransformerFactory[], + }, compilerOptions: this._compilerOptions, reportDiagnostics: this.configSet.shouldReportDiagnostics(fileName), }) @@ -212,9 +237,7 @@ export class TsCompiler implements CompilerInstance { private _isFileInCache(fileName: string): boolean { return ( - this._jestCacheFS.has(fileName) && - this._compilerCacheFS.has(fileName) && - this._compilerCacheFS.get(fileName) !== 0 + this.jestCacheFS.has(fileName) && this._compilerCacheFS.has(fileName) && this._compilerCacheFS.get(fileName) !== 0 ) } @@ -229,7 +252,7 @@ export class TsCompiler implements CompilerInstance { shouldIncrementProjectVersion = true } else { const prevVersion = this._compilerCacheFS.get(fileName) ?? 0 - const previousContents = this._jestCacheFS.get(fileName) + const previousContents = this.jestCacheFS.get(fileName) // Avoid incrementing cache when nothing has changed. if (previousContents !== contents) { this._compilerCacheFS.set(fileName, prevVersion + 1) diff --git a/src/config/__snapshots__/config-set.spec.ts.snap b/src/config/__snapshots__/config-set.spec.ts.snap index b675ef0992..6619771d30 100644 --- a/src/config/__snapshots__/config-set.spec.ts.snap +++ b/src/config/__snapshots__/config-set.spec.ts.snap @@ -54,17 +54,31 @@ Array [ exports[`customTransformers should return an object containing all resolved transformers 1`] = ` Object { + "after": Array [], + "afterDeclarations": Array [], "before": Array [ - [Function], + Object { + "factory": [Function], + "name": "hoisting-jest-mock", + "version": 4, + }, ], } `; exports[`customTransformers should return an object containing all resolved transformers 2`] = ` Object { + "after": Array [], + "afterDeclarations": Array [], "before": Array [ - [Function], - [Function], + Object { + "factory": [Function], + "name": "hoisting-jest-mock", + "version": 4, + }, + Object { + "factory": [Function], + }, ], } `; @@ -72,30 +86,53 @@ Object { exports[`customTransformers should return an object containing all resolved transformers 3`] = ` Object { "after": Array [ - [Function], + Object { + "factory": [Function], + }, ], + "afterDeclarations": Array [], "before": Array [ - [Function], + Object { + "factory": [Function], + "name": "hoisting-jest-mock", + "version": 4, + }, ], } `; exports[`customTransformers should return an object containing all resolved transformers 4`] = ` Object { + "after": Array [], "afterDeclarations": Array [ - [Function], + Object { + "factory": [Function], + }, ], "before": Array [ - [Function], + Object { + "factory": [Function], + "name": "hoisting-jest-mock", + "version": 4, + }, ], } `; exports[`customTransformers should return an object containing all resolved transformers 5`] = ` Object { + "after": Array [], + "afterDeclarations": Array [], "before": Array [ - [Function], - [Function], + Object { + "factory": [Function], + "name": "hoisting-jest-mock", + "version": 4, + }, + Object { + "factory": [Function], + "options": Object {}, + }, ], } `; diff --git a/src/config/config-set.spec.ts b/src/config/config-set.spec.ts index b15f6061f0..fe90079b1a 100644 --- a/src/config/config-set.spec.ts +++ b/src/config/config-set.spec.ts @@ -169,7 +169,7 @@ describe('customTransformers', () => { resolve: null, }) - expect(cs.customTransformers).toMatchSnapshot() + expect(cs.resolvedTransformers).toMatchSnapshot() }) }) diff --git a/src/config/config-set.ts b/src/config/config-set.ts index 8e2d6ec95f..cac020b79b 100644 --- a/src/config/config-set.ts +++ b/src/config/config-set.ts @@ -15,26 +15,17 @@ import type { Config } from '@jest/types' import { LogContexts, Logger } from 'bs-logger' import { globsToMatcher } from 'jest-util' import json5 from 'json5' -import { - CompilerOptions, - CustomTransformers, - Diagnostic, - FormatDiagnosticsHost, - ParsedCommandLine, - DiagnosticCategory, - ModuleKind, - ScriptTarget, -} from 'typescript' +import type { CompilerOptions, Diagnostic, FormatDiagnosticsHost, ParsedCommandLine } from 'typescript' import { DEFAULT_JEST_TEST_MATCH, JS_JSX_EXTENSIONS } from '../constants' -import { factory as hoisting } from '../transformers/hoist-jest' import type { AstTransformer, + AstTransformerDesc, BabelConfig, BabelJestTransformer, + ProjectConfigTsJest, TsJestDiagnosticsCfg, TsJestGlobalOptions, - ProjectConfigTsJest, TTypeScript, } from '../types' import { backportJestConfig } from '../utils/backports' @@ -64,13 +55,6 @@ export const IGNORE_DIAGNOSTIC_CODES = [ */ export const TS_JEST_OUT_DIR = '$$ts-jest$$' -const TARGET_TO_VERSION_MAPPING: Record = { - [ScriptTarget.ES2018]: 'es2018', - [ScriptTarget.ES2019]: 'es2019', - [ScriptTarget.ES2020]: 'es2020', - [ScriptTarget.ESNext]: 'ESNext', -} - /** * @internal */ @@ -106,6 +90,12 @@ const toDiagnosticCodeList = (items: (string | number)[], into: number[] = []): return into } +interface TsJestAstTransformer { + before: AstTransformerDesc[] + after: AstTransformerDesc[] + afterDeclarations: AstTransformerDesc[] +} + export class ConfigSet { /** * Use by e2e, don't mark as internal @@ -118,7 +108,11 @@ export class ConfigSet { readonly rootDir: string tsCacheDir: string | undefined parsedTsConfig!: ParsedCommandLine | Record - customTransformers: CustomTransformers = Object.create(null) + resolvedTransformers: TsJestAstTransformer = { + before: [], + after: [], + afterDeclarations: [], + } useESM = false /** * @internal @@ -305,44 +299,40 @@ export class ConfigSet { this.logger.debug({ tsconfig: this.parsedTsConfig }, 'normalized typescript config via ts-jest option') // transformers + this.resolvedTransformers.before = [require('../transformers/hoist-jest')] const { astTransformers } = options - this.customTransformers = { - before: [hoisting(this)], - } if (astTransformers) { - const resolveTransformers = (transformers: (string | AstTransformer)[]) => + const resolveTransformers = (transformers: (string | AstTransformer)[]): AstTransformerDesc[] => transformers.map((transformer) => { - let transformerPath: string if (typeof transformer === 'string') { - transformerPath = this.resolvePath(transformer, { nodeResolve: true }) - - return require(transformerPath).factory(this) + return require(this.resolvePath(transformer, { nodeResolve: true })) } else { - transformerPath = this.resolvePath(transformer.path, { nodeResolve: true }) - - return require(transformerPath).factory(this, transformer.options) + return { + ...require(this.resolvePath(transformer.path, { nodeResolve: true })), + options: transformer.options, + } } }) if (astTransformers.before) { /* istanbul ignore next (already covered in unit test) */ - this.customTransformers.before?.push(...resolveTransformers(astTransformers.before)) + this.resolvedTransformers.before?.push(...resolveTransformers(astTransformers.before)) } if (astTransformers.after) { - this.customTransformers = { - ...this.customTransformers, + this.resolvedTransformers = { + ...this.resolvedTransformers, after: resolveTransformers(astTransformers.after), } } if (astTransformers.afterDeclarations) { - this.customTransformers = { - ...this.customTransformers, + this.resolvedTransformers = { + ...this.resolvedTransformers, afterDeclarations: resolveTransformers(astTransformers.afterDeclarations), } } } this.logger.debug( - { customTransformers: this.customTransformers }, + { customTransformers: this.resolvedTransformers }, 'normalized custom AST transformers via ts-jest option', ) @@ -394,25 +384,25 @@ export class ConfigSet { const finalOptions = result.options // Target ES2015 output by default (instead of ES3). if (finalOptions.target === undefined) { - finalOptions.target = ScriptTarget.ES2015 + finalOptions.target = this.compilerModule.ScriptTarget.ES2015 } // check the module interoperability const target = finalOptions.target // compute the default if not set - const defaultModule = [ScriptTarget.ES3, ScriptTarget.ES5].includes(target) - ? ModuleKind.CommonJS - : ModuleKind.ESNext + const defaultModule = [this.compilerModule.ScriptTarget.ES3, this.compilerModule.ScriptTarget.ES5].includes(target) + ? this.compilerModule.ModuleKind.CommonJS + : this.compilerModule.ModuleKind.ESNext const moduleValue = finalOptions.module ?? defaultModule if ( !this.babelConfig && - moduleValue !== ModuleKind.CommonJS && + moduleValue !== this.compilerModule.ModuleKind.CommonJS && !(finalOptions.esModuleInterop || finalOptions.allowSyntheticDefaultImports) ) { result.errors.push({ code: DiagnosticCodes.ConfigModuleOption, messageText: Errors.ConfigNoModuleInterop, - category: DiagnosticCategory.Message, + category: this.compilerModule.DiagnosticCategory.Message, file: undefined, start: undefined, length: undefined, @@ -443,12 +433,18 @@ export class ConfigSet { */ const nodeJsVer = process.version const compilationTarget = result.options.target + const TARGET_TO_VERSION_MAPPING: Record = { + [this.compilerModule.ScriptTarget.ES2018]: 'es2018', + [this.compilerModule.ScriptTarget.ES2019]: 'es2019', + [this.compilerModule.ScriptTarget.ES2020]: 'es2020', + [this.compilerModule.ScriptTarget.ESNext]: 'ESNext', + } /* istanbul ignore next (cover by e2e) */ if ( compilationTarget && !this.babelConfig && - ((nodeJsVer.startsWith('v10') && compilationTarget > ScriptTarget.ES2018) || - (nodeJsVer.startsWith('v12') && compilationTarget > ScriptTarget.ES2019)) + ((nodeJsVer.startsWith('v10') && compilationTarget > this.compilerModule.ScriptTarget.ES2018) || + (nodeJsVer.startsWith('v12') && compilationTarget > this.compilerModule.ScriptTarget.ES2019)) ) { const message = interpolate(Errors.MismatchNodeTargetMapping, { nodeJsVer: process.version, diff --git a/src/transformers/README.md b/src/transformers/README.md index 4230e35f97..b5bb598a60 100644 --- a/src/transformers/README.md +++ b/src/transformers/README.md @@ -7,15 +7,15 @@ See https://dev.doctorevidence.com/how-to-write-a-typescript-transform-plugin-fc ```ts import { SourceFile, TransformationContext, Transformer, Visitor } from 'typescript' -import { ConfigSet } from '../config/config-set' +import type { TsCompilerInstance } from 'ts-jest/dist/types' // this is a unique identifier for your transformer export const name = 'my-transformer' // increment this each time you change the behavior of your transformer export const version = 1 -export function factory(cs: ConfigSet) { - const ts = cs.compilerModule +export function factory(compilerInstance: TsCompilerInstance) { + const ts = compilerInstance.configSet.compilerModule function createVisitor(ctx: TransformationContext, sf: SourceFile) { const visitor: Visitor = node => { // here we can check each node and potentially return diff --git a/src/transformers/hoist-jest.spec.ts b/src/transformers/hoist-jest.spec.ts index 6dbacb0add..80a01c78bc 100644 --- a/src/transformers/hoist-jest.spec.ts +++ b/src/transformers/hoist-jest.spec.ts @@ -1,6 +1,8 @@ -import { testing } from 'bs-logger' import ts from 'typescript' +import { createConfigSet } from '../__helpers__/fakers' +import { TsCompiler } from '../compiler/ts-compiler' + import * as hoist from './hoist-jest' const CODE_WITH_HOISTING_NO_JEST_GLOBALS = ` @@ -115,8 +117,7 @@ const CODE_WITH_HOISTING_HAS_JEST_GLOBALS = ` console.log(it) ` -const logger = testing.createLoggerMock() -const createFactory = () => hoist.factory({ logger, compilerModule: ts } as any) +const createFactory = () => hoist.factory(new TsCompiler(createConfigSet(), new Map())) const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } }) describe('hoisting', () => { diff --git a/src/transformers/hoist-jest.ts b/src/transformers/hoist-jest.ts index ab07d7a192..30c3492b68 100644 --- a/src/transformers/hoist-jest.ts +++ b/src/transformers/hoist-jest.ts @@ -11,7 +11,7 @@ import type { Visitor, } from 'typescript' -import type { ConfigSet } from '../config/config-set' +import type { TsCompilerInstance } from '../types' /** * What methods of `jest` we should hoist @@ -36,13 +36,13 @@ export const version = 4 * * @internal */ -export function factory(cs: ConfigSet): (ctx: TransformationContext) => Transformer { - const logger = cs.logger.child({ namespace: 'ts-hoisting' }) +export function factory({ configSet }: TsCompilerInstance): (ctx: TransformationContext) => Transformer { + const logger = configSet.logger.child({ namespace: 'ts-hoisting' }) /** * Our compiler (typescript, or a module with typescript-like interface) * To access Program or TypeChecker, do: cs.tsCompiler.program or cs.tsCompiler.program.getTypeChecker() */ - const ts = cs.compilerModule + const ts = configSet.compilerModule const importNames: string[] = [] function shouldHoistExpression(node: Node): boolean { diff --git a/src/transformers/index.spec.ts b/src/transformers/index.spec.ts deleted file mode 100644 index f0c3ad8d79..0000000000 --- a/src/transformers/index.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { internals } from '.' - -describe('transformers', () => { - it('should return internal transformer', () => { - expect(internals).toHaveLength(1) - expect(internals).toMatchInlineSnapshot(` - Array [ - Object { - "factory": [Function], - "name": "hoisting-jest-mock", - "version": 4, - }, - ] - `) - }) -}) diff --git a/src/transformers/index.ts b/src/transformers/index.ts deleted file mode 100644 index dc6c760ffb..0000000000 --- a/src/transformers/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { AstTransformerDesc } from '../types' - -import * as hoisting from './hoist-jest' - -/** - * @internal - */ -export const internals: AstTransformerDesc[] = [hoisting] diff --git a/src/transformers/path-mapping.spec.ts b/src/transformers/path-mapping.spec.ts index 5de3dc6c46..951f988d7b 100644 --- a/src/transformers/path-mapping.spec.ts +++ b/src/transformers/path-mapping.spec.ts @@ -1,9 +1,10 @@ import { join } from 'path' import { testing } from 'bs-logger' -import tsc from 'typescript' +import ts from 'typescript' import { createConfigSet } from '../__helpers__/fakers' +import { TsCompiler } from '../compiler/ts-compiler' import { normalizeSlashes } from '../utils/normalize-slashes' import * as pathMapping from './path-mapping' @@ -59,9 +60,9 @@ describe('path-mapping', () => { }, logger, }) - const createFactory = () => pathMapping.factory(configSet) - const transpile = (source: string) => tsc.transpileModule(source, { transformers: { before: [createFactory()] } }) - jest.spyOn(tsc, 'resolveModuleName').mockReturnValue({ + const createFactory = () => pathMapping.factory(new TsCompiler(configSet, new Map())) + const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } }) + jest.spyOn(ts, 'resolveModuleName').mockReturnValue({ resolvedModule: { resolvedFileName: require.resolve('../utils/json'), extension: 'ts', @@ -114,10 +115,10 @@ describe('path-mapping', () => { }, logger, }) - const createFactory = () => pathMapping.factory(configSet) - const transpile = (source: string) => tsc.transpileModule(source, { transformers: { before: [createFactory()] } }) + const createFactory = () => pathMapping.factory(new TsCompiler(configSet, new Map())) + const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } }) const resolvedFileNameStub = join('..', `utils/json.${extension}`) - jest.spyOn(tsc, 'resolveModuleName').mockReturnValue({ + jest.spyOn(ts, 'resolveModuleName').mockReturnValue({ resolvedModule: { resolvedFileName: resolvedFileNameStub, extension, diff --git a/src/transformers/path-mapping.ts b/src/transformers/path-mapping.ts index 7f56ec7296..6d4971d5c7 100644 --- a/src/transformers/path-mapping.ts +++ b/src/transformers/path-mapping.ts @@ -9,7 +9,7 @@ import { basename, dirname, isAbsolute, join, normalize, relative } from 'path' import { LogContexts, LogLevels } from 'bs-logger' import type * as _ts from 'typescript' -import type { ConfigSet } from '../config/config-set' +import type { TsCompilerInstance } from '../types' /** * @internal @@ -26,10 +26,12 @@ const isBaseDir = (base: string, dir: string) => !relative(base, dir)?.startsWit /** * The factory of import path alias transformer factory. */ -export function factory(cs: ConfigSet): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile> { - const logger = cs.logger.child({ namespace: 'ts-path-mapping' }) - const ts = cs.compilerModule - const compilerOptions = cs.parsedTsConfig.options +export function factory({ + configSet, +}: TsCompilerInstance): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile> { + const logger = configSet.logger.child({ namespace: 'ts-path-mapping' }) + const ts = configSet.compilerModule + const compilerOptions = configSet.parsedTsConfig.options const rootDirs = compilerOptions.rootDirs?.filter(isAbsolute) const isDynamicImport = (node: _ts.Node): node is _ts.CallExpression => diff --git a/src/ts-jest-transformer.spec.ts b/src/ts-jest-transformer.spec.ts index 7195416716..1a1089bc75 100644 --- a/src/ts-jest-transformer.spec.ts +++ b/src/ts-jest-transformer.spec.ts @@ -37,6 +37,7 @@ describe('TsJestTransformer', () => { () => { const obj1 = { config: { cwd: process.cwd(), extensionsToTreatAsEsm: [], globals: {}, testMatch: [], testRegex: [] }, + cacheFS: new Map(), } const obj2 = { ...obj1, config: { ...obj1.config, globals: Object.create(null) } } // @ts-expect-error testing purpose @@ -51,6 +52,7 @@ describe('TsJestTransformer', () => { test('should return the same config set for same values with jest config objects', () => { const obj1 = { config: { cwd: process.cwd(), extensionsToTreatAsEsm: [], globals: {}, testMatch: [], testRegex: [] }, + cacheFS: new Map(), } const obj2 = { ...obj1 } // @ts-expect-error testing purpose @@ -135,6 +137,7 @@ describe('TsJestTransformer', () => { config: { foo: 'bar', testMatch: [], testRegex: [], extensionsToTreatAsEsm: [] } as any, instrument: false, rootDir: '/foo', + cacheFS: new Map(), }, } as any const transformOptionsWithCache = { @@ -256,6 +259,7 @@ describe('TsJestTransformer', () => { testRegex: [], extensionsToTreatAsEsm: [], }, + cacheFS: new Map(), } as any let tr!: TsJestTransformer @@ -268,6 +272,7 @@ describe('TsJestTransformer', () => { const filePath = 'foo.html' const fileContent = '

Hello World

' const transformOptions = { + ...baseTransformOptions, config: { ...baseTransformOptions.config, globals: { @@ -276,7 +281,7 @@ describe('TsJestTransformer', () => { }, }, }, - } as any + } tr.getCacheKey(fileContent, filePath, transformOptions) const result = tr.process(fileContent, filePath, transformOptions) @@ -297,13 +302,14 @@ describe('TsJestTransformer', () => { const fileContent = 'const foo = 1' const filePath = 'foo.js' const transformOptions = { + ...baseTransformOptions, config: { ...baseTransformOptions.config, globals: { 'ts-jest': { tsconfig: { allowJs: false } }, }, }, - } as any + } tr.getCacheKey(fileContent, filePath, transformOptions) logTarget.clear() @@ -333,13 +339,14 @@ describe('TsJestTransformer', () => { const fileContent = 'const foo = 1' const output = 'var foo = 1' const transformOptions = { + ...baseTransformOptions, config: { ...baseTransformOptions.config, globals: { 'ts-jest': { tsconfig: { allowJs: true } }, }, }, - } as any + } tr.getCacheKey(fileContent, filePath, transformOptions) logTarget.clear() jest.spyOn(TsJestCompiler.prototype, 'getCompiledOutput').mockReturnValueOnce(output) @@ -353,13 +360,14 @@ describe('TsJestTransformer', () => { const fileContent = 'foo' const filePath = 'foo.bar' const transformOptions = { + ...baseTransformOptions, config: { ...baseTransformOptions.config, globals: { 'ts-jest': { tsconfig: { allowJs: true } }, }, }, - } as any + } tr.getCacheKey(fileContent, filePath, transformOptions) logTarget.clear() @@ -375,13 +383,14 @@ describe('TsJestTransformer', () => { test.each(['foo.bar', 'foo.js'])('should process file with babel-jest', (filePath) => { const fileContent = 'foo' const transformOptions = { + ...baseTransformOptions, config: { ...baseTransformOptions.config, globals: { 'ts-jest': { babelConfig: true }, }, }, - } as any + } tr.getCacheKey(fileContent, filePath, transformOptions) logTarget.clear() diff --git a/src/types.ts b/src/types.ts index 942028b9da..bf0b3eb80d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -198,6 +198,10 @@ export interface CompilerInstance { getResolvedModulesMap(fileContent: string, fileName: string): ResolvedModulesMap getCompiledOutput(fileContent: string, fileName: string, supportsStaticESM: boolean): string } +export interface TsCompilerInstance extends CompilerInstance { + configSet: ConfigSet + program: _ts.Program | undefined +} /** * @internal */ @@ -205,7 +209,8 @@ export interface AstTransformerDesc> { name: string version: number factory( - cs: ConfigSet, + tsCompiler: TsCompilerInstance, opts?: T, ): _ts.TransformerFactory<_ts.SourceFile> | _ts.TransformerFactory<_ts.Bundle | _ts.SourceFile> + options?: T }