Skip to content

Commit

Permalink
Fix the output file names api to use the correct common source directory
Browse files Browse the repository at this point in the history
Fixes #41801 and #41780
  • Loading branch information
sheetalkamat committed Dec 3, 2020
1 parent 319d835 commit aefccb6
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 50 deletions.
74 changes: 57 additions & 17 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,36 +130,32 @@ namespace ts {
return Extension.Js;
}

function rootDirOfOptions(configFile: ParsedCommandLine) {
return configFile.options.rootDir || getDirectoryPath(Debug.checkDefined(configFile.options.configFilePath));
}

function getOutputPathWithoutChangingExt(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, outputDir: string | undefined) {
function getOutputPathWithoutChangingExt(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, outputDir: string | undefined, getCommonSourceDirectory?: () => string) {
return outputDir ?
resolvePath(
outputDir,
getRelativePathFromDirectory(rootDirOfOptions(configFile), inputFileName, ignoreCase)
getRelativePathFromDirectory(getCommonSourceDirectory ? getCommonSourceDirectory() : getCommonSourceDirectoryOfConfig(configFile, ignoreCase), inputFileName, ignoreCase)
) :
inputFileName;
}

/* @internal */
export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, getCommonSourceDirectory?: () => string) {
Debug.assert(!fileExtensionIs(inputFileName, Extension.Dts) && !fileExtensionIs(inputFileName, Extension.Json));
return changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.declarationDir || configFile.options.outDir),
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.declarationDir || configFile.options.outDir, getCommonSourceDirectory),
Extension.Dts
);
}

function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, getCommonSourceDirectory?: () => string) {
if (configFile.options.emitDeclarationOnly) return undefined;
const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
const outputFileName = changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir),
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir, getCommonSourceDirectory),
isJsonFile ?
Extension.Json :
fileExtensionIs(inputFileName, Extension.Tsx) && configFile.options.jsx === JsxEmit.Preserve ?
configFile.options.jsx === JsxEmit.Preserve && (fileExtensionIs(inputFileName, Extension.Tsx) || fileExtensionIs(inputFileName, Extension.Jsx)) ?
Extension.Jsx :
Extension.Js
);
Expand Down Expand Up @@ -190,32 +186,75 @@ namespace ts {
addOutput(buildInfoPath);
}

function getOwnOutputFileNames(configFile: ParsedCommandLine, inputFileName: string, ignoreCase: boolean, addOutput: ReturnType<typeof createAddOutput>["addOutput"]) {
function getOwnOutputFileNames(configFile: ParsedCommandLine, inputFileName: string, ignoreCase: boolean, addOutput: ReturnType<typeof createAddOutput>["addOutput"], getCommonSourceDirectory?: () => string) {
if (fileExtensionIs(inputFileName, Extension.Dts)) return;
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase);
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory);
addOutput(js);
if (fileExtensionIs(inputFileName, Extension.Json)) return;
if (js && configFile.options.sourceMap) {
addOutput(`${js}.map`);
}
if (getEmitDeclarations(configFile.options)) {
const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory);
addOutput(dts);
if (configFile.options.declarationMap) {
addOutput(`${dts}.map`);
}
}
}

/*@internal*/
export function getCommonSourceDirectory(
options: CompilerOptions,
emittedFiles: () => readonly string[],
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName,
checkSourceFilesBelongToPath?: (commonSourceDirectory: string) => void
): string {
let commonSourceDirectory;
if (options.rootDir) {
// If a rootDir is specified use it as the commonSourceDirectory
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
checkSourceFilesBelongToPath?.(commonSourceDirectory);
}
else if (options.composite && options.configFilePath) {
// Project compilations never infer their root from the input source paths
commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath));
checkSourceFilesBelongToPath?.(commonSourceDirectory);
}
else {
commonSourceDirectory = computeCommonSourceDirectoryOfFilenames(emittedFiles(), currentDirectory, getCanonicalFileName);
}

if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
// Make sure directory path ends with directory separator so this string can directly
// used to replace with "" to get the relative path of the source file and the relative path doesn't
// start with / making it rooted path
commonSourceDirectory += directorySeparator;
}
return commonSourceDirectory;
}

/*@internal*/
export function getCommonSourceDirectoryOfConfig({ options, fileNames }: ParsedCommandLine, ignoreCase: boolean): string {
return getCommonSourceDirectory(
options,
() => filter(fileNames, file => !(options.noEmitForJsFiles && fileExtensionIsOneOf(file, supportedJSExtensions)) && !fileExtensionIs(file, Extension.Dts)),
getDirectoryPath(normalizeSlashes(Debug.checkDefined(options.configFilePath))),
createGetCanonicalFileName(!ignoreCase)
);
}

/*@internal*/
export function getAllProjectOutputs(configFile: ParsedCommandLine, ignoreCase: boolean): readonly string[] {
const { addOutput, getOutputs } = createAddOutput();
if (outFile(configFile.options)) {
getSingleOutputFileNames(configFile, addOutput);
}
else {
const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(configFile, ignoreCase));
for (const inputFileName of configFile.fileNames) {
getOwnOutputFileNames(configFile, inputFileName, ignoreCase, addOutput);
getOwnOutputFileNames(configFile, inputFileName, ignoreCase, addOutput, getCommonSourceDirectory);
}
addOutput(getTsBuildInfoEmitOutputFilePath(configFile.options));
}
Expand All @@ -242,13 +281,14 @@ namespace ts {
return Debug.checkDefined(jsFilePath, `project ${configFile.options.configFilePath} expected to have at least one output`);
}

const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(configFile, ignoreCase));
for (const inputFileName of configFile.fileNames) {
if (fileExtensionIs(inputFileName, Extension.Dts)) continue;
const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase);
const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory);
if (jsFilePath) return jsFilePath;
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
if (getEmitDeclarations(configFile.options)) {
return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase, getCommonSourceDirectory);
}
}
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(configFile.options);
Expand Down
45 changes: 17 additions & 28 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace ts {
}

/* @internal */
export function computeCommonSourceDirectoryOfFilenames(fileNames: string[], currentDirectory: string, getCanonicalFileName: GetCanonicalFileName): string {
export function computeCommonSourceDirectoryOfFilenames(fileNames: readonly string[], currentDirectory: string, getCanonicalFileName: GetCanonicalFileName): string {
let commonPathComponents: string[] | undefined;
const failed = forEach(fileNames, sourceFile => {
// Each file contributes into common source file path
Expand Down Expand Up @@ -899,9 +899,15 @@ namespace ts {
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
}
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(parsedRef.commandLine, !host.useCaseSensitiveFileNames()));
for (const fileName of parsedRef.commandLine.fileNames) {
if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
processSourceFile(
getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory),
/*isDefaultLib*/ false,
/*ignoreNoDefaultLib*/ false,
/*packageId*/ undefined
);
}
}
}
Expand Down Expand Up @@ -1127,26 +1133,13 @@ namespace ts {
function getCommonSourceDirectory() {
if (commonSourceDirectory === undefined) {
const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, program));
if (options.rootDir) {
// If a rootDir is specified use it as the commonSourceDirectory
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory);
}
else if (options.composite && options.configFilePath) {
// Project compilations never infer their root from the input source paths
commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath));
checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory);
}
else {
commonSourceDirectory = computeCommonSourceDirectory(emittedFiles);
}

if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
// Make sure directory path ends with directory separator so this string can directly
// used to replace with "" to get the relative path of the source file and the relative path doesn't
// start with / making it rooted path
commonSourceDirectory += directorySeparator;
}
commonSourceDirectory = ts.getCommonSourceDirectory(
options,
() => mapDefined(emittedFiles, file => file.isDeclarationFile ? undefined : file.fileName),
currentDirectory,
getCanonicalFileName,
commonSourceDirectory => checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory)
);
}
return commonSourceDirectory;
}
Expand Down Expand Up @@ -2708,9 +2701,10 @@ namespace ts {
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
}
else {
const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(resolvedRef.commandLine, !host.useCaseSensitiveFileNames()));
forEach(resolvedRef.commandLine.fileNames, fileName => {
if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, host.useCaseSensitiveFileNames());
const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory);
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
}
});
Expand Down Expand Up @@ -2974,11 +2968,6 @@ namespace ts {
}
}

function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
const fileNames = mapDefined(sourceFiles, file => file.isDeclarationFile ? undefined : file.fileName);
return computeCommonSourceDirectoryOfFilenames(fileNames, currentDirectory, getCanonicalFileName);
}

function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
let allFilesBelongToPath = true;
const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Output::
[12:01:00 AM] Projects in this build:
* src/tsconfig.json

[[90m12:01:00 AM[0m] Project 'src/tsconfig.json' is out of date because output file 'src/dist/src/index.js' does not exist
[[90m12:01:00 AM[0m] Project 'src/tsconfig.json' is out of date because output file 'src/dist/index.js' does not exist

[12:01:00 AM] Building project '/src/tsconfig.json'...

Expand All @@ -52,14 +52,11 @@ Output::
[12:04:00 AM] Projects in this build:
* src/tsconfig.json

[12:04:00 AM] Project 'src/tsconfig.json' is out of date because output file 'src/dist/src/index.js' does not exist

[12:04:00 AM] Building project '/src/tsconfig.json'...
[12:04:00 AM] Project 'src/tsconfig.json' is up to date because newest input 'src/src/index.ts' is older than oldest output 'src/dist/index.js'

exitCode:: ExitStatus.Success


//// [/src/dist/index.js] file written with same contents


Change:: Normal build without change, that does not block emit on error to show files that get emitted
Expand Down

0 comments on commit aefccb6

Please sign in to comment.