Skip to content

Commit

Permalink
fix(compiler): don't resolve files from build folder for `projectRefe…
Browse files Browse the repository at this point in the history
…rences` (#1614)
  • Loading branch information
ahnpnl committed May 9, 2020
1 parent 8a29aaa commit 74b92d3
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 369 deletions.
@@ -1,3 +1,6 @@
/** @type {import('@jest/types').Config.InitialOptions} */
/** @typedef {import('ts-jest')} */

module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
Expand All @@ -7,6 +10,7 @@ module.exports = {
globals: {
'ts-jest': {
isolatedModules: true,
tsConfig: 'tsconfig-tests.json'
},
},
}
@@ -1,8 +1,7 @@
{
"private": true,
"scripts": {
"build": "yarn tsc -b -f tsconfig.json",
"test": "yarn build && jest"
"test": "jest"
},
"devDependencies": {
"@types/jest": "^25.2.1",
Expand Down
Expand Up @@ -6,7 +6,7 @@
"packages/*"
],
"scripts": {
"test": "yarn tsc -b packages/my-app/tsconfig.json && jest --no-cache"
"test": "jest --no-cache"
},
"devDependencies": {
"@types/jest": "^25.2.1",
Expand Down
154 changes: 2 additions & 152 deletions src/compiler/compiler-utils.ts
@@ -1,12 +1,10 @@
import { Logger } from 'bs-logger'
import { writeFileSync } from 'fs'
import micromatch = require('micromatch')
import { dirname, join, normalize, relative, resolve } from 'path'
import { join, normalize } from 'path'
import * as _ts from 'typescript'

import { ConfigSet } from '../config/config-set'
import { EXTENSION_REGEX, JSON_REGEX, TS_TSX_REGEX } from '../constants'
import { MemoryCache, SourceOutput, TSFiles } from '../types'
import { MemoryCache } from '../types'
import { sha1 } from '../util/sha1'

/**
Expand Down Expand Up @@ -60,151 +58,3 @@ export function isTestFile(testMatchPatterns: (string | RegExp)[], fileName: str
typeof pattern === 'string' ? micromatch.isMatch(fileName, pattern) : pattern.test(fileName),
)
}

/* istanbul ignore next (we leave this for e2e) */
function isUsingProjectReferences(
program: _ts.Program,
projectReferences: readonly _ts.ProjectReference[] | undefined,
) {
if (projectReferences && !!program.getProjectReferences) {
return Boolean(program && program.getProjectReferences())
}

return false
}

/* istanbul ignore next (we leave this for e2e) */
function getResolvedProjectReferences(
program: _ts.Program,
): readonly (_ts.ResolvedProjectReference | undefined)[] | undefined {
const getProjectReferences = program.getResolvedProjectReferences ?? program.getProjectReferences
if (getProjectReferences) {
return getProjectReferences()
}

return
}

/* istanbul ignore next (we leave this for e2e) */
function getProjectReferenceForFile(
filePath: string,
program: _ts.Program,
projectReferences: readonly _ts.ProjectReference[] | undefined,
) {
if (isUsingProjectReferences(program, projectReferences)) {
return (
program &&
getResolvedProjectReferences(program)!.find(
ref => (ref && ref.commandLine.fileNames.some(file => normalize(file) === filePath)) || false,
)
)
}

return
}

/**
* @internal
*/
/* istanbul ignore next (we leave this for e2e) */
export function getAndCacheProjectReference(
filePath: string,
program: _ts.Program,
files: TSFiles,
projectReferences: readonly _ts.ProjectReference[] | undefined,
) {
const file = files.get(filePath)
if (file?.projectReference) {
return file.projectReference.project
}

const projectReference = getProjectReferenceForFile(filePath, program, projectReferences)
if (file !== undefined) {
file.projectReference = { project: projectReference }
}

return projectReference
}

// Adapted from https://github.com/Microsoft/TypeScript/blob/45101491c0b077c509b25830ef0ee5f85b293754/src/compiler/tsbuild.ts#L305
/* istanbul ignore next (we leave this for e2e) */
function getOutputJavaScriptFileName(inputFileName: string, projectReference: _ts.ResolvedProjectReference) {
const { options } = projectReference.commandLine
const projectDirectory = options.rootDir || dirname(projectReference.sourceFile.fileName)
const relativePath = relative(projectDirectory, inputFileName)
const outputPath = resolve(options.outDir || projectDirectory, relativePath)
const newExtension = JSON_REGEX.test(inputFileName)
? '.json'
: TS_TSX_REGEX.test(inputFileName) && options.jsx === _ts.JsxEmit.Preserve
? '.jsx'
: '.js'

return outputPath.replace(EXTENSION_REGEX, newExtension)
}

/**
* Gets the output JS file path for an input file governed by a composite project.
* Pulls from the cache if it exists; computes and caches the result otherwise.
*/
/* istanbul ignore next (we leave this for e2e) */
function getAndCacheOutputJSFileName(
inputFileName: string,
projectReference: _ts.ResolvedProjectReference,
files: TSFiles,
) {
const file = files.get(inputFileName)
if (file?.projectReference?.outputFileName) {
return file.projectReference.outputFileName
}

const outputFileName = getOutputJavaScriptFileName(inputFileName, projectReference)
if (file !== undefined) {
file.projectReference = file.projectReference ?? {
project: projectReference,
}
file.projectReference.outputFileName = outputFileName
}

return outputFileName
}

/**
* @internal
*/
/* istanbul ignore next (we leave this for e2e) */
export function getCompileResultFromReferencedProject(
fileName: string,
configs: ConfigSet,
files: TSFiles,
referencedProject: _ts.ResolvedProjectReference,
): SourceOutput {
const [relativeProjectConfigPath, relativeFilePath] = [
configs.resolvePath(referencedProject.sourceFile.fileName),
configs.resolvePath(fileName),
]
if (referencedProject.commandLine.options.outFile !== undefined) {
throw new Error(
`The referenced project at ${relativeProjectConfigPath} is using ` +
"the outFile' option, which is not supported with ts-jest.",
)
}

const jsFileName = getAndCacheOutputJSFileName(fileName, referencedProject, files)
const relativeJSFileName = configs.resolvePath(jsFileName)
if (!configs.compilerModule.sys.fileExists(jsFileName)) {
throw new Error(
'Could not find output JavaScript file for input ' +
`${relativeFilePath} (looked at ${relativeJSFileName}).\n` +
'The input file is part of a project reference located at ' +
`${relativeProjectConfigPath}, so ts-jest is looking for the ` +
'project’s pre-built output on disk. Try running `tsc --build` ' +
'to build project references.',
)
}

const mapFileName = `${jsFileName}.map`
const outputText = configs.compilerModule.sys.readFile(jsFileName)
const sourceMapText = configs.compilerModule.sys.readFile(mapFileName)

return [outputText!, sourceMapText!]
}
51 changes: 0 additions & 51 deletions src/compiler/language-service.spec.ts
Expand Up @@ -17,57 +17,6 @@ describe('Language service', () => {
logTarget.clear()
})

it('should get compile result from referenced project when there is a built reference project', () => {
const tmp = tempDir('compiler')
const compiler = makeCompiler({
jestConfig: { cache: true, cacheDirectory: tmp },
tsJestConfig: { tsConfig: false },
})
const source = 'console.log("hello")'
const fileName = 'test-reference-project.ts'
const getAndCacheProjectReferenceSpy = jest
.spyOn(compilerUtils, 'getAndCacheProjectReference')
.mockReturnValueOnce({} as any)
jest
.spyOn(compilerUtils, 'getCompileResultFromReferencedProject')
.mockImplementationOnce(() => [
source,
'{"version":3,"file":"test-reference-project.js","sourceRoot":"","sources":["test-reference-project.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA","sourcesContent":["console.log(\\"hello\\")"]}',
])
writeFileSync(fileName, source, 'utf8')

compiler.compile(source, fileName)

expect(getAndCacheProjectReferenceSpy).toHaveBeenCalled()
expect(compilerUtils.getCompileResultFromReferencedProject).toHaveBeenCalled()

jest.restoreAllMocks()
removeSync(fileName)
})

it('should get compile result from language service when there is no referenced project', () => {
const tmp = tempDir('compiler')
const compiler = makeCompiler({
jestConfig: { cache: true, cacheDirectory: tmp },
tsJestConfig: { tsConfig: false },
})
const source = 'console.log("hello")'
const fileName = 'test-no-reference-project.ts'
const getAndCacheProjectReferenceSpy = jest
.spyOn(compilerUtils, 'getAndCacheProjectReference')
.mockReturnValueOnce(undefined)
jest.spyOn(compilerUtils, 'getCompileResultFromReferencedProject')
writeFileSync(fileName, source, 'utf8')

compiler.compile(source, fileName)

expect(getAndCacheProjectReferenceSpy).toHaveBeenCalled()
expect(compilerUtils.getCompileResultFromReferencedProject).not.toHaveBeenCalled()

jest.restoreAllMocks()
removeSync(fileName)
})

it('should cache resolved modules for test file with testMatchPatterns from jest config when match', () => {
const spy = jest.spyOn(compilerUtils, 'cacheResolvedModules').mockImplementationOnce(() => {})
const tmp = tempDir('compiler')
Expand Down

0 comments on commit 74b92d3

Please sign in to comment.