From 885bc44784f12ddc87cfcd5ed78ba44a28b3e0e0 Mon Sep 17 00:00:00 2001 From: Huafu Gandon Date: Wed, 5 Sep 2018 09:18:44 +0200 Subject: [PATCH] feat(config): exposes custom transformers to config for presets --- src/__helpers__/fakers.ts | 1 + src/compiler.ts | 5 +-- .../__snapshots__/config-set.spec.ts.snap | 1 + src/config/config-set.ts | 45 +++++++++++++++++-- src/transformers/README.md | 5 +++ src/transformers/hoisting.ts | 4 ++ src/transformers/index.ts | 12 ++--- src/types.ts | 16 ++++++- 8 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/__helpers__/fakers.ts b/src/__helpers__/fakers.ts index add2e24037..5a904cdccc 100644 --- a/src/__helpers__/fakers.ts +++ b/src/__helpers__/fakers.ts @@ -57,6 +57,7 @@ export function tsJestConfig(options?: Partial): TsJestConfig { return { isolatedModules: false, compiler: 'typescript', + transformers: [], babelConfig: undefined, tsConfig: undefined, stringifyContentPathRegex: undefined, diff --git a/src/compiler.ts b/src/compiler.ts index 2f89579315..c77be9b795 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -35,10 +35,8 @@ import stableStringify = require('fast-json-stable-stringify') import { readFileSync, writeFileSync } from 'fs' import mkdirp = require('mkdirp') import { basename, extname, join, relative } from 'path' -import { CustomTransformers } from 'typescript' import { ConfigSet } from './config/config-set' -import { factory as customTransformersFactory } from './transformers' import { MemoryCache, TsCompiler, TypeInfo } from './types' import { Errors, interpolate } from './util/messages' import { sha1 } from './util/sha1' @@ -88,8 +86,7 @@ export function createCompiler(configs: ConfigSet): TsCompiler { ? (path: string) => (/\.[tj]sx$/.test(path) ? '.jsx' : '.js') : (_: string) => '.js' - // TODO: grab internal transformers - const transformers: CustomTransformers = customTransformersFactory(configs) + const transformers = configs.tsCustomTransformers /** * Create the basic required function using transpile mode. diff --git a/src/config/__snapshots__/config-set.spec.ts.snap b/src/config/__snapshots__/config-set.spec.ts.snap index e9be81cad6..fa934720b6 100644 --- a/src/config/__snapshots__/config-set.spec.ts.snap +++ b/src/config/__snapshots__/config-set.spec.ts.snap @@ -32,6 +32,7 @@ Object { }, "isolatedModules": false, "stringifyContentPathRegex": undefined, + "transformers": Array [], "tsConfig": Object { "kind": "file", "value": undefined, diff --git a/src/config/config-set.ts b/src/config/config-set.ts index 30d822e9bd..8701f9f25e 100644 --- a/src/config/config-set.ts +++ b/src/config/config-set.ts @@ -13,11 +13,13 @@ import { existsSync, readFileSync } from 'fs' import json5 from 'json5' import { dirname, isAbsolute, join, resolve } from 'path' import semver from 'semver' -import { Diagnostic, FormatDiagnosticsHost, ParsedCommandLine } from 'typescript' +import { CustomTransformers, Diagnostic, FormatDiagnosticsHost, ParsedCommandLine } from 'typescript' import { version as myVersion } from '..' import { createCompiler } from '../compiler' +import { internals as internalAstTransformers } from '../transformers' import { + AstTransformerDesc, BabelConfig, BabelJestTransformer, TTypeScript, @@ -160,6 +162,9 @@ export class ConfigSet { } } + // transformers + const transformers = (options.astTransformers || []).map(mod => this.resolvePath(mod, { nodeResolve: true })) + // babel jest const { babelConfig: babelConfigOpt } = options let babelConfig: TsJestConfig['babelConfig'] @@ -210,6 +215,7 @@ export class ConfigSet { diagnostics, isolatedModules: !!options.isolatedModules, compiler: options.compiler || 'typescript', + transformers, stringifyContentPathRegex, } this.logger.debug({ tsJestConfig: res }, 'normalized ts-jest config') @@ -316,6 +322,18 @@ export class ConfigSet { return createCompiler(this) } + @Memoize() + get astTransformers(): AstTransformerDesc[] { + return [...internalAstTransformers, ...this.tsJest.transformers.map(m => require(m))] + } + + @Memoize() + get tsCustomTransformers(): CustomTransformers { + return { + before: this.astTransformers.map(t => t.factory(this)), + } + } + @Memoize() get hooks(): TsJestHooksMap { let hooksFile = process.env.TS_JEST_HOOKS @@ -487,14 +505,32 @@ export class ConfigSet { return { input, resolved: result } } - resolvePath(inputPath: string, noFailIfMissing = false): string { + resolvePath( + inputPath: string, + { throwIfMissing = true, nodeResolve = false }: { throwIfMissing?: boolean; nodeResolve?: boolean } = {}, + ): string { let path: string = inputPath + let nodeResolved = false if (path.startsWith('')) { path = resolve(this.rootDir, path.substr(9)) } else if (!isAbsolute(path)) { - path = resolve(this.cwd, path) + if (!path.startsWith('.') && nodeResolve) { + try { + path = require.resolve(path) + nodeResolved = true + } catch (_) {} + } + if (!nodeResolved) { + path = resolve(this.cwd, path) + } + } + if (!nodeResolved && nodeResolve) { + try { + path = require.resolve(path) + nodeResolved = true + } catch (_) {} } - if (!noFailIfMissing && !existsSync(path)) { + if (throwIfMissing && !existsSync(path)) { throw new Error(interpolate(Errors.FileNotFound, { inputPath, resolvedPath: path })) } this.logger.debug({ fromPath: inputPath, toPath: path }, 'resolved path from', inputPath, 'to', path) @@ -514,6 +550,7 @@ export class ConfigSet { return new JsonableValue({ versions: this.versions, + transformers: this.astTransformers.map(t => `${t.name}@${t.version}`), jest, tsJest: this.tsJest, babel: this.babel, diff --git a/src/transformers/README.md b/src/transformers/README.md index 6f4738249b..7f273588ed 100644 --- a/src/transformers/README.md +++ b/src/transformers/README.md @@ -13,6 +13,11 @@ import { } from 'typescript'; import { ConfigSet } from '../config-set' +// 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 function createVisitor(ctx: TransformationContext, sf: SourceFile) { diff --git a/src/transformers/hoisting.ts b/src/transformers/hoisting.ts index 124cc3dee4..ec8dbc56a8 100644 --- a/src/transformers/hoisting.ts +++ b/src/transformers/hoisting.ts @@ -19,6 +19,10 @@ import { ConfigSet } from '../config/config-set' */ const HOIST_METHODS = ['mock', 'unmock'] +export const name = 'hoisting-jest-mock' +// increment this each time the code is modified +export const version = 1 + /** * The factory of hoisting transformer factory * @param cs Current jest configuration-set diff --git a/src/transformers/index.ts b/src/transformers/index.ts index 43b9326481..644c689f0b 100644 --- a/src/transformers/index.ts +++ b/src/transformers/index.ts @@ -1,11 +1,5 @@ -import { CustomTransformers } from 'typescript' +import { AstTransformerDesc } from '../types' -import { ConfigSet } from '../config/config-set' +import * as hoisting from './hoisting' -import { factory as hoistingFactory } from './hoisting' - -export function factory(cs: ConfigSet): CustomTransformers { - return { - before: [hoistingFactory(cs)], - } -} +export const internals: AstTransformerDesc[] = [hoisting] diff --git a/src/types.ts b/src/types.ts index d403ef4048..a60f595666 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,7 @@ import * as _babel from 'babel__core' -import _ts, { CompilerOptions } from 'typescript' +import _ts, { CompilerOptions, SourceFile, TransformerFactory } from 'typescript' + +import { ConfigSet } from './config/config-set' export type TBabelCore = typeof _babel export type TTypeScript = typeof _ts @@ -29,6 +31,11 @@ export interface TsJestGlobalOptions { */ compiler?: string + /** + * Custom transformers (mostly used by jest presets) + */ + astTransformers?: string[] + /** * TS diagnostics - less to be reported if `isolatedModules` is `true`. It can be: * - `true` (or `undefined`, it's the default): show all diagnostics @@ -92,6 +99,7 @@ export interface TsJestConfig { compiler: string diagnostics: TsJestConfig$diagnostics babelConfig: TsJestConfig$babelConfig + transformers: string[] // to deprecate / deprecated === === === stringifyContentPathRegex: TsJestConfig$stringifyContentPathRegex @@ -158,3 +166,9 @@ export interface TsCompiler { compile(code: string, fileName: string, lineOffset?: number): string getTypeInfo(code: string, fileName: string, position: number): TypeInfo } + +export interface AstTransformerDesc { + name: string + version: number + factory(cs: ConfigSet): TransformerFactory +}