From 8b3c446c1c3a59b7c54d5a2548d8bde2e7963692 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 16 May 2018 14:02:22 -0700 Subject: [PATCH] [jest/ts] modify `ts-jest.tsConfigFile` config based on filePath --- src/dev/file.js | 46 -------------------------- src/dev/file.ts | 58 ++++++++++++++++++++++++++++++++ src/dev/jest/config.js | 4 +-- src/dev/jest/ts_transform.js | 2 ++ src/dev/jest/ts_transform.ts | 44 +++++++++++++++++++++++++ src/dev/typescript/index.ts | 1 + src/dev/typescript/project.ts | 62 +++++++++++++++++++++++++++++++++++ 7 files changed, 168 insertions(+), 49 deletions(-) delete mode 100644 src/dev/file.js create mode 100644 src/dev/file.ts create mode 100644 src/dev/jest/ts_transform.js create mode 100644 src/dev/jest/ts_transform.ts create mode 100644 src/dev/typescript/index.ts create mode 100644 src/dev/typescript/project.ts diff --git a/src/dev/file.js b/src/dev/file.js deleted file mode 100644 index 970b7a7c4df65b2..000000000000000 --- a/src/dev/file.js +++ /dev/null @@ -1,46 +0,0 @@ -import { dirname, join, resolve, relative, extname } from 'path'; - -import { REPO_ROOT } from './constants'; - -export class File { - constructor(path) { - this._path = resolve(path); - this._relativePath = relative(REPO_ROOT, this._path); - this._ext = extname(this._path); - } - - getRelativePath() { - return this._relativePath; - } - - isJs() { - return this._ext === '.js'; - } - - getRelativeParentDirs() { - const parents = []; - - while (true) { - const parent = parents.length - // NOTE: resolve() produces absolute paths, so we have to use join() - ? join(parents[parents.length - 1], '..') - : dirname(this._relativePath); - - if (parent === '..' || parent === '.') { - break; - } else { - parents.push(parent); - } - } - - return parents; - } - - toString() { - return this._relativePath; - } - - toJSON() { - return this._relativePath; - } -} diff --git a/src/dev/file.ts b/src/dev/file.ts new file mode 100644 index 000000000000000..ece3ab44f73cb13 --- /dev/null +++ b/src/dev/file.ts @@ -0,0 +1,58 @@ +import { dirname, extname, join, relative, resolve } from 'path'; + +import { REPO_ROOT } from './constants'; + +export class File { + private path: string; + private relativePath: string; + private ext: string; + + constructor(path: string) { + this.path = resolve(path); + this.relativePath = relative(REPO_ROOT, this.path); + this.ext = extname(this.path); + } + + public getAbsolutePath() { + return this.path; + } + + public getRelativePath() { + return this.relativePath; + } + + public isJs() { + return this.ext === '.js'; + } + + public isTypescript() { + return this.ext === '.ts' || this.ext === '.tsx'; + } + + public getRelativeParentDirs() { + const parents: string[] = []; + + while (true) { + // NOTE: resolve() produces absolute paths, so we have to use join() + const parent = parents.length + ? join(parents[parents.length - 1], '..') + : dirname(this.relativePath); + + if (parent === '..' || parent === '.') { + break; + } else { + parents.push(parent); + } + } + + return parents; + } + + public toString() { + return this.relativePath; + } + + public toJSON() { + return this.relativePath; + } +} diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 7595dc8fabf1c81..dfec96a6ffa10ff 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -42,7 +42,6 @@ export default { 'js', 'json', 'ts', - 'tsx', ], modulePathIgnorePatterns: [ '__fixtures__/', @@ -51,7 +50,6 @@ export default { testMatch: [ '**/*.test.js', '**/*.test.ts', - '**/*.test.tsx', ], testPathIgnorePatterns: [ '/packages/kbn-ui-framework/(dist|doc_site|generator-kui)/', @@ -60,7 +58,7 @@ export default { ], transform: { '^.+\\.js$': '/src/dev/jest/babel_transform.js', - '^.+\\.tsx?$': 'ts-jest', + '^.+\\.tsx?$': '/src/dev/jest/ts_transform.js', }, transformIgnorePatterns: [ '[/\\\\]node_modules[/\\\\].+\\.js$', diff --git a/src/dev/jest/ts_transform.js b/src/dev/jest/ts_transform.js new file mode 100644 index 000000000000000..e03e6636ba281b7 --- /dev/null +++ b/src/dev/jest/ts_transform.js @@ -0,0 +1,2 @@ +require('../../babel-register'); +module.exports = require('./ts_transform.ts'); diff --git a/src/dev/jest/ts_transform.ts b/src/dev/jest/ts_transform.ts new file mode 100644 index 000000000000000..b312e0b62199b2d --- /dev/null +++ b/src/dev/jest/ts_transform.ts @@ -0,0 +1,44 @@ +import { getCacheKey, install, process } from 'ts-jest'; +import { JestConfig, TransformOptions } from 'ts-jest/dist/jest-types'; + +import { TS_PROJECTS, Project } from '../typescript' +import { transform } from 'typescript'; + +function extendJestConfigJSON(jestConfigJSON: string, filePath: string) { + const jestConfig = JSON.parse(jestConfigJSON) as JestConfig; + return JSON.stringify(extendJestConfig(jestConfig, filePath)) +} + +function extendJestConfig(jestConfig: JestConfig, filePath: string) { + const project = TS_PROJECTS.find(p => p.isAbsolutePathSelected(path)); + + if (!project) { + throw new Error('Unable to find tsconfig.json file selecting file "${filePath}". Ensure one exists and it is listed in "src/dev/typescript/projects.ts"') + } + + return { + ...jestConfig, + globals: { + ...jestConfig.globals || {}, + 'ts-jest': { + tsConfigFile: project.getTsConfigPath(), + skipBabel: true + } + } + } +} + + +module.exports = { + process(src: string, filePath: string, jestConfig: JestConfig, transformOptions: TransformOptions) { + const extendedConfig = extendJestConfig(jestConfig, filePath); + return process(src, filePath, extendedConfig, transformOptions); + }, + + getCacheKey(src: string, filePath: string, jestConfigJSON: string, transformOptions: TransformOptions) { + const extendedConfigJSON = extendJestConfigJSON(jestConfigJSON, filePath); + return getCacheKey(src, filePath, extendedConfigJSON, transformOptions); + }, + + install, +}; diff --git a/src/dev/typescript/index.ts b/src/dev/typescript/index.ts new file mode 100644 index 000000000000000..6e7b0a90a729cb5 --- /dev/null +++ b/src/dev/typescript/index.ts @@ -0,0 +1 @@ +export { TS_PROJECTS, Project } from './project'; diff --git a/src/dev/typescript/project.ts b/src/dev/typescript/project.ts new file mode 100644 index 000000000000000..62ad5630e551cb2 --- /dev/null +++ b/src/dev/typescript/project.ts @@ -0,0 +1,62 @@ +import { readFileSync } from 'fs'; +import { basename, dirname, relative, resolve } from 'path'; + +import { IMinimatch, Minimatch } from 'minimatch'; +import { parseConfigFileTextToJson } from 'typescript'; + +import { File } from '../file'; + +const ROOT_DIR = resolve(__dirname, '../../../'); + +function makeMatchers(directory: string, patterns: string[]) { + return patterns.map(pattern => new Minimatch(resolve(directory, pattern), { + dot: true, + })); +} + +export class Project { + public static fromConfig(path: string) { + const { error, config } = parseConfigFileTextToJson(path, readFileSync(path, 'utf8')); + + if (error) { + throw error; + } + + return new Project(path, config.files, config.include, config.exclude); + } + + private include: IMinimatch[]; + private exclude: IMinimatch[]; + + constructor( + private tsConfigPath: string, + files?: string[], + include?: string[], + exclude: string[] = [] + ) { + if (files || !include) { + throw new Error( + 'tsconfig.json files in the Kibana repo must use "include" keys and not "files"' + ); + } + + this.include = makeMatchers(dirname(tsConfigPath), include); + this.exclude = makeMatchers(dirname(tsConfigPath), exclude); + } + + public getTsConfigPath() { + return this.tsConfigPath; + } + + public isAbsolutePathSelected(path: string) { + return this.exclude.some(exc => exc.match(path)) + ? false + : this.include.some(inc => inc.match(path)); + } +} + +export const TS_PROJECTS = [ + Project.fromConfig(require.resolve('../../../tsconfig.json')), + Project.fromConfig(require.resolve('../../../packages/kbn-pm/tsconfig.json')), + Project.fromConfig(require.resolve('../../../packages/kbn-system-loader/tsconfig.json')), +];