diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 6c3b1fbb376..6f1a2345e8d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -20,7 +20,7 @@ namespace ts { * Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit. */ export function forEachEmittedFile( - host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) => T, + host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) => T, sourceFilesOrTargetSourceFile?: ReadonlyArray | SourceFile, emitOnlyDtsFiles = false) { const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile); @@ -42,11 +42,18 @@ namespace ts { return result; } } + const buildInfoPath = getOutputPathForBuildInfo(host.getCompilerOptions(), host.getProjectReferences()); + return action({ buildInfoPath }, /*sourceFileOrBundle*/ undefined); } } - function hasPrependReference(projectReferences: ReadonlyArray | undefined) { - return projectReferences && forEach(projectReferences, ref => ref.prepend); + /*@internal*/ + export function getOutputPathForBuildInfo(options: CompilerOptions, projectReferences: ReadonlyArray | undefined) { + if (!options.composite && !length(projectReferences)) return undefined; + const outPath = options.outFile || options.out; + if (outPath) return combinePaths(getDirectoryPath(outPath), infoFile); + if (options.outDir) return combinePaths(options.outDir, infoFile); + return options.configFilePath && combinePaths(getDirectoryPath(options.configFilePath), infoFile); } /*@internal*/ @@ -56,7 +63,7 @@ namespace ts { const sourceMapFilePath = jsFilePath && getSourceMapFilePath(jsFilePath, options); const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? removeFileExtension(outPath) + Extension.Dts : undefined; const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined; - const buildInfoPath = outPath && (options.composite || hasPrependReference(projectReferences)) ? combinePaths(getDirectoryPath(outPath), infoFile) : undefined; + const buildInfoPath = getOutputPathForBuildInfo(options, projectReferences); return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath }; } @@ -136,14 +143,11 @@ namespace ts { exportedModulesFromDeclarationEmit }; - function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { + function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) { if (buildInfoPath) buildInfo = { js: [], dts: [], commonSourceDirectory: host.getCommonSourceDirectory(), sources: {} }; emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, buildInfo && { sections: buildInfo.js, sources: buildInfo.sources }); emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath, buildInfo && { sections: buildInfo.dts, sources: buildInfo.sources }); - // Write bundled offset information if applicable - if (!emitOnlyDtsFiles && !emitSkipped && buildInfoPath) { - writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText(buildInfo!), /*writeByteOrderMark*/ false); - } + emitBuildInfo(buildInfo, buildInfoPath); if (!emitSkipped && emittedFilesList) { if (!emitOnlyDtsFiles) { @@ -166,8 +170,15 @@ namespace ts { } } - function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string | undefined, sourceMapFilePath: string | undefined, bundleFileInfo: BundleFileInfo | undefined) { - if (emitOnlyDtsFiles || !jsFilePath) { + function emitBuildInfo(buildInfo: BuildInfo | undefined, buildInfoPath: string | undefined) { + // Write build information if applicable + if (!buildInfo || !buildInfoPath || emitOnlyDtsFiles || emitSkipped) return; + + writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText(buildInfo), /*writeByteOrderMark*/ false); + } + + function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle | undefined, jsFilePath: string | undefined, sourceMapFilePath: string | undefined, bundleFileInfo: BundleFileInfo | undefined) { + if (!sourceFileOrBundle || emitOnlyDtsFiles || !jsFilePath) { return; } @@ -208,8 +219,8 @@ namespace ts { transform.dispose(); } - function emitDeclarationFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, declarationFilePath: string | undefined, declarationMapPath: string | undefined, bundleFileInfo: BundleFileInfo | undefined) { - if (!(declarationFilePath && !isInJSFile(sourceFileOrBundle))) { + function emitDeclarationFileOrBundle(sourceFileOrBundle: SourceFile | Bundle | undefined, declarationFilePath: string | undefined, declarationMapPath: string | undefined, bundleFileInfo: BundleFileInfo | undefined) { + if (!sourceFileOrBundle || !(declarationFilePath && !isInJSFile(sourceFileOrBundle))) { return; } const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index 69c5e17ad06..1d808740559 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -1300,6 +1300,11 @@ namespace ts { priorNewestUpdateTime = newer(priorNewestUpdateTime, host.getModifiedTime(file) || missingFileModifiedTime); } + // For info file, ignore if we cant update modified time + if (isInfoFile(file) && !host.fileExists(file)) { + continue; + } + host.setModifiedTime(file, now); if (proj.options.listEmittedFiles) { writeFileName(`TSFILE: ${file}`); @@ -1482,6 +1487,7 @@ namespace ts { for (const inputFile of project.fileNames) { outputs.push(...getOutputFileNames(inputFile, project)); } + if (!ignoreBuildInfo) outputs.push(Debug.assertDefined(getOutputPathForBuildInfo(project.options, project.projectReferences))); return outputs; } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f3a2d69b493..27d5df27596 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3361,11 +3361,11 @@ namespace ts { } export interface EmitFileNames { - jsFilePath: string | undefined; - sourceMapFilePath: string | undefined; - declarationFilePath: string | undefined; - declarationMapPath: string | undefined; - buildInfoPath: string | undefined; + jsFilePath?: string | undefined; + sourceMapFilePath?: string | undefined; + declarationFilePath?: string | undefined; + declarationMapPath?: string | undefined; + buildInfoPath?: string | undefined; } /** diff --git a/src/testRunner/unittests/tsbuild/sample.ts b/src/testRunner/unittests/tsbuild/sample.ts index 5a98c0b276c..85be9936f22 100644 --- a/src/testRunner/unittests/tsbuild/sample.ts +++ b/src/testRunner/unittests/tsbuild/sample.ts @@ -349,11 +349,14 @@ export class cNew {}`); "TSFILE: /src/core/index.js", "TSFILE: /src/core/index.d.ts", "TSFILE: /src/core/index.d.ts.map", + "TSFILE: /src/core/.tsbuildinfo", "TSFILE: /src/logic/index.js", "TSFILE: /src/logic/index.js.map", "TSFILE: /src/logic/index.d.ts", + "TSFILE: /src/logic/.tsbuildinfo", "TSFILE: /src/tests/index.js", "TSFILE: /src/tests/index.d.ts", + "TSFILE: /src/tests/.tsbuildinfo", ]); }); }); diff --git a/src/testRunner/unittests/tsbuildWatchMode.ts b/src/testRunner/unittests/tsbuildWatchMode.ts index ed779614ffe..62adc0386f2 100644 --- a/src/testRunner/unittests/tsbuildWatchMode.ts +++ b/src/testRunner/unittests/tsbuildWatchMode.ts @@ -512,7 +512,8 @@ let x: string = 10;`); changeExtension(fileWithError.path, Extension.Js), changeExtension(fileWithError.path, Extension.Dts), changeExtension(fileWithoutError.path, Extension.Js), - changeExtension(fileWithoutError.path, Extension.Dts) + changeExtension(fileWithoutError.path, Extension.Dts), + `${subProjectLocation}/${infoFile}` ]; function verifyDtsErrors(host: TsBuildWatchSystem, isIncremental: boolean, expectedErrors: ReadonlyArray) { @@ -572,9 +573,10 @@ let x: string = 10;`); it("when fixing errors only changed file is emitted", () => { const host = createSolutionWithIncrementalError(); fixError(host); - assert.equal(host.writtenFiles.size, 2, `Expected to write only changed files: ${arrayFrom(host.writtenFiles.keys())}`); + assert.equal(host.writtenFiles.size, 3, `Expected to write only changed files: ${arrayFrom(host.writtenFiles.keys())}`); verifyWrittenFile(host, outputs[0]); verifyWrittenFile(host, outputs[1]); + verifyWrittenFile(host, outputs[4]); }); it("when file with no error changes, declaration errors are reported", () => { diff --git a/tests/baselines/reference/tsbuild/sample1/initial-Build/buildInfo/sample.js b/tests/baselines/reference/tsbuild/sample1/initial-Build/buildInfo/sample.js index 9d327a427e5..22c9266b1d6 100644 --- a/tests/baselines/reference/tsbuild/sample1/initial-Build/buildInfo/sample.js +++ b/tests/baselines/reference/tsbuild/sample1/initial-Build/buildInfo/sample.js @@ -1,3 +1,11 @@ +//// [/src/core/.tsbuildinfo] +{ + "js": [], + "dts": [], + "commonSourceDirectory": "/src/core/", + "sources": {} +} + //// [/src/core/anotherModule.d.ts] export declare const World = "hello"; //# sourceMappingURL=anotherModule.d.ts.map @@ -180,6 +188,14 @@ function multiply(a, b) { return a * b; } exports.multiply = multiply; +//// [/src/logic/.tsbuildinfo] +{ + "js": [], + "dts": [], + "commonSourceDirectory": "/src/logic/", + "sources": {} +} + //// [/src/logic/index.d.ts] export declare function getSecondsInDay(): number; import * as mod from '../core/anotherModule'; @@ -325,6 +341,14 @@ sourceFile:index.ts --- >>>//# sourceMappingURL=index.js.map +//// [/src/tests/.tsbuildinfo] +{ + "js": [], + "dts": [], + "commonSourceDirectory": "/src/tests/", + "sources": {} +} + //// [/src/tests/index.d.ts] import * as mod from '../core/anotherModule'; export declare const m: typeof mod; diff --git a/tests/baselines/reference/tsbuild/sample1/initial-Build/buildInfo/when-logic-config-changes-declaration-dir.js b/tests/baselines/reference/tsbuild/sample1/initial-Build/buildInfo/when-logic-config-changes-declaration-dir.js index 9d327a427e5..22c9266b1d6 100644 --- a/tests/baselines/reference/tsbuild/sample1/initial-Build/buildInfo/when-logic-config-changes-declaration-dir.js +++ b/tests/baselines/reference/tsbuild/sample1/initial-Build/buildInfo/when-logic-config-changes-declaration-dir.js @@ -1,3 +1,11 @@ +//// [/src/core/.tsbuildinfo] +{ + "js": [], + "dts": [], + "commonSourceDirectory": "/src/core/", + "sources": {} +} + //// [/src/core/anotherModule.d.ts] export declare const World = "hello"; //# sourceMappingURL=anotherModule.d.ts.map @@ -180,6 +188,14 @@ function multiply(a, b) { return a * b; } exports.multiply = multiply; +//// [/src/logic/.tsbuildinfo] +{ + "js": [], + "dts": [], + "commonSourceDirectory": "/src/logic/", + "sources": {} +} + //// [/src/logic/index.d.ts] export declare function getSecondsInDay(): number; import * as mod from '../core/anotherModule'; @@ -325,6 +341,14 @@ sourceFile:index.ts --- >>>//# sourceMappingURL=index.js.map +//// [/src/tests/.tsbuildinfo] +{ + "js": [], + "dts": [], + "commonSourceDirectory": "/src/tests/", + "sources": {} +} + //// [/src/tests/index.d.ts] import * as mod from '../core/anotherModule'; export declare const m: typeof mod;