Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FD-101194 - Add support for tsconfig.json files that have an extends property #1

Merged
merged 2 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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