Skip to content

Commit

Permalink
Add support for tsconfig.json files that have an extends property (#1)
Browse files Browse the repository at this point in the history
* Fix issue where dangerjs did not support extended tsconfigs

* Add tests to verify extending tsconfigs
  • Loading branch information
Jtupiter committed Sep 22, 2022
1 parent b427e3e commit 5edfe63
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"target": "ES6",
"module": "ES2015"
}
}
3 changes: 3 additions & 0 deletions source/runner/runners/utils/_tests/_fixtures/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "./tsconfig.base.json"
}
32 changes: 21 additions & 11 deletions source/runner/runners/utils/_tests/_transpiler.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
jest.mock("fs", () => ({
readFileSync: jest.fn(),
realpathSync: {},
existsSync: jest.fn(),
}))
jest.mock("path", () => {
const path = jest.requireActual("path")
return { ...path, resolve: jest.fn(path.resolve) }
})

import { typescriptify, lookupTSConfig, dirContains } from "../transpiler"
import * as fs from "fs"
import fs from "fs"
import * as path from "path"
import ts from "typescript"

describe("typescriptify", () => {
it("removes the module option in a tsconfig", () => {
Expand All @@ -21,11 +17,25 @@ describe("typescriptify", () => {
module: "es2015",
},
}
const fsMock = fs.readFileSync as jest.Mock
fsMock.mockImplementationOnce(() => JSON.stringify(fakeTSConfig))

jest.spyOn(ts.sys, "readFile").mockImplementationOnce(() => JSON.stringify(fakeTSConfig))
expect(typescriptify(dangerfile, "/a/b")).not.toContain("import")
})

it("resolves extended tsconfigs", () => {
const actualPath = jest.requireActual("path") as typeof path
const resolve = path.resolve as jest.Mock
resolve.mockImplementation((p: string = "") => actualPath.resolve(__dirname, p))

const dangerfile = `import { a } from 'lodash'; (() => a())()`

const transpiledCode = typescriptify(dangerfile, actualPath.resolve(__dirname, "./_fixtures"))
console.log(transpiledCode)
expect(transpiledCode).not.toContain("import")

// Arrow functions (=>) are not compiled to functions when the target is ES6.
// The ES6 target is defined in the base tsconfig so it must be inheriting from it.
expect(transpiledCode).toContain("=>")
})
})

/** Normalizes path to platform-specific */
Expand All @@ -45,9 +55,9 @@ describe("lookupTSConfig", () => {
const resolve = path.resolve as jest.Mock
resolve.mockImplementation((p: string = "") => actualPath.resolve(cwd, p))

const existsSync = fs.existsSync as jest.Mock
const existsSync = jest.spyOn(fs, "existsSync")
const tsconfigPath = path.resolve(path.join(configDir, "tsconfig.json"))
existsSync.mockImplementation((f: string) => path.resolve(f) === tsconfigPath)
existsSync.mockImplementation((f: fs.PathLike) => path.resolve(f as string) === tsconfigPath)
}

it("can find in the same folder as dangerfile", () => {
Expand Down
47 changes: 24 additions & 23 deletions source/runner/runners/utils/transpiler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fs from "fs"
import * as path from "path"
import JSON5 from "json5"
import ts from "typescript";
import { debug } from "../../../debug"

const enum BabelPackagePrefix {
Expand Down Expand Up @@ -120,40 +120,41 @@ export const lookupTSConfig = (dir: string): string | null => {
}

export const typescriptify = (content: string, dir: string): string => {
const ts = require("typescript")

// Support custom TSC options, but also fallback to defaults
let compilerOptions: any
let compilerOptions: ts.CompilerOptions;
const tsConfigPath = lookupTSConfig(dir)
if (tsConfigPath) {
compilerOptions = JSON5.parse(fs.readFileSync(tsConfigPath, "utf8"))
compilerOptions = ts.parseJsonConfigFileContent(
ts.readConfigFile(tsConfigPath, ts.sys.readFile).config,
{
fileExists: ts.sys.fileExists,
readFile: ts.sys.readFile,
readDirectory: ts.sys.readDirectory,
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
},
dir,
).options;
} else {
compilerOptions = ts.getDefaultCompilerOptions()
}

let result = ts.transpileModule(content, sanitizeTSConfig(compilerOptions))
let result = ts.transpileModule(content, { compilerOptions: sanitizeTSConfig(compilerOptions)})
return result.outputText
}

const sanitizeTSConfig = (config: any) => {
if (!config.compilerOptions) {
return config
}

const safeConfig = config

// It can make sense to ship TS code with modules
// for `import`/`export` syntax, but as we're running
// the transpiled code on vanilla node - it'll need to
// be used with plain old commonjs
//
// @see https://github.com/apollographql/react-apollo/pull/1402#issuecomment-351810274
//
if (safeConfig.compilerOptions.module) {
safeConfig.compilerOptions.module = "commonjs"
const sanitizeTSConfig = (config: ts.CompilerOptions) => {
if (config.module) {
// It can make sense to ship TS code with modules
// for `import`/`export` syntax, but as we're running
// the transpiled code on vanilla node - it'll need to
// be used with plain old commonjs
//
// @see https://github.com/apollographql/react-apollo/pull/1402#issuecomment-351810274
//
config.module = ts.ModuleKind.CommonJS;
}

return safeConfig
return config
}

export const babelify = (content: string, filename: string, extraPlugins: string[]): string => {
Expand Down

0 comments on commit 5edfe63

Please sign in to comment.