Skip to content
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
279 changes: 183 additions & 96 deletions src/compiler/builder.ts

Large diffs are not rendered by default.

51 changes: 29 additions & 22 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,28 +453,28 @@ export function forEachEmittedFile<T>(
) {
const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile, forceDtsEmit);
const options = host.getCompilerOptions();
if (options.outFile) {
if (sourceFiles.length) {
const bundle = factory.createBundle(sourceFiles);
const result = action(getOutputPathsFor(bundle, host, forceDtsEmit), bundle);
if (result) {
return result;
if (!onlyBuildInfo) {
if (options.outFile) {
if (sourceFiles.length) {
const bundle = factory.createBundle(sourceFiles);
const result = action(getOutputPathsFor(bundle, host, forceDtsEmit), bundle);
if (result) {
return result;
}
}
}
}
else {
if (!onlyBuildInfo) {
else {
for (const sourceFile of sourceFiles) {
const result = action(getOutputPathsFor(sourceFile, host, forceDtsEmit), sourceFile);
if (result) {
return result;
}
}
}
if (includeBuildInfo) {
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options);
if (buildInfoPath) return action({ buildInfoPath }, /*sourceFileOrBundle*/ undefined);
}
}
if (includeBuildInfo) {
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options);
if (buildInfoPath) return action({ buildInfoPath }, /*sourceFileOrBundle*/ undefined);
}
}

Expand Down Expand Up @@ -506,8 +506,7 @@ export function getOutputPathsForBundle(options: CompilerOptions, forceDtsPaths:
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 = getTsBuildInfoEmitOutputFilePath(options);
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath };
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath };
}

/** @internal */
Expand All @@ -526,7 +525,7 @@ export function getOutputPathsFor(sourceFile: SourceFile | Bundle, host: EmitHos
const sourceMapFilePath = !jsFilePath || isJsonSourceFile(sourceFile) ? undefined : getSourceMapFilePath(jsFilePath, options);
const declarationFilePath = (forceDtsPaths || (getEmitDeclarations(options) && !isJsonFile)) ? getDeclarationEmitOutputFilePath(sourceFile.fileName, host) : undefined;
const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath: undefined };
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath };
}
}

Expand Down Expand Up @@ -601,12 +600,11 @@ function createAddOutput() {
}

function getSingleOutputFileNames(configFile: ParsedCommandLine, addOutput: ReturnType<typeof createAddOutput>["addOutput"]) {
const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
addOutput(jsFilePath);
addOutput(sourceMapFilePath);
addOutput(declarationFilePath);
addOutput(declarationMapPath);
addOutput(buildInfoPath);
}

function getOwnOutputFileNames(configFile: ParsedCommandLine, inputFileName: string, ignoreCase: boolean, addOutput: ReturnType<typeof createAddOutput>["addOutput"], getCommonSourceDirectory?: () => string) {
Expand Down Expand Up @@ -679,8 +677,8 @@ export function getAllProjectOutputs(configFile: ParsedCommandLine, ignoreCase:
for (const inputFileName of configFile.fileNames) {
getOwnOutputFileNames(configFile, inputFileName, ignoreCase, addOutput, getCommonSourceDirectory);
}
addOutput(getTsBuildInfoEmitOutputFilePath(configFile.options));
}
addOutput(getTsBuildInfoEmitOutputFilePath(configFile.options));
return getOutputs();
}

Expand Down Expand Up @@ -726,7 +724,16 @@ export function emitResolverSkipsTypeChecking(emitOnly: boolean | EmitOnly | und

/** @internal */
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnly?: boolean | EmitOnly, onlyBuildInfo?: boolean, forceDtsEmit?: boolean): EmitResult {
export function emitFiles(
resolver: EmitResolver,
host: EmitHost,
targetSourceFile: SourceFile | undefined,
{ scriptTransformers, declarationTransformers }: EmitTransformers,
emitOnly: boolean | EmitOnly | undefined,
onlyBuildInfo: boolean,
forceDtsEmit?: boolean,
skipBuildInfo?: boolean,
): EmitResult {
// Why var? It avoids TDZ checks in the runtime which can be costly.
// See: https://github.com/microsoft/TypeScript/issues/52924
/* eslint-disable no-var */
Expand All @@ -748,7 +755,7 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
getSourceFilesToEmit(host, targetSourceFile, forceDtsEmit),
forceDtsEmit,
onlyBuildInfo,
!targetSourceFile,
!targetSourceFile && !skipBuildInfo,
);
exit();

Expand All @@ -775,7 +782,7 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi

function emitBuildInfo(buildInfoPath: string | undefined) {
// Write build information if applicable
if (!buildInfoPath || targetSourceFile || emitSkipped) return;
if (!buildInfoPath || targetSourceFile) return;
if (host.isEmitBlocked(buildInfoPath)) {
emitSkipped = true;
return;
Expand Down
40 changes: 34 additions & 6 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2788,7 +2788,6 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}

function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult {
Debug.assert(!options.outFile);
tracing?.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true);
performance.mark("beforeEmit");
const emitResult = emitFiles(
Expand Down Expand Up @@ -2846,9 +2845,28 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
return typeChecker || (typeChecker = createTypeChecker(program));
}

function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnly?: boolean | EmitOnly, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
function emit(
sourceFile?: SourceFile,
writeFileCallback?: WriteFileCallback,
cancellationToken?: CancellationToken,
emitOnly?: boolean | EmitOnly,
transformers?: CustomTransformers,
forceDtsEmit?: boolean,
skipBuildInfo?: boolean,
): EmitResult {
tracing?.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true);
const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnly, transformers, forceDtsEmit));
const result = runWithCancellationToken(() =>
emitWorker(
program,
sourceFile,
writeFileCallback,
cancellationToken,
emitOnly,
transformers,
forceDtsEmit,
skipBuildInfo,
)
);
tracing?.pop();
return result;
}
Expand All @@ -2857,7 +2875,16 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
return hasEmitBlockingDiagnostics.has(toPath(emitFileName));
}

function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnly?: boolean | EmitOnly, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
function emitWorker(
program: Program,
sourceFile: SourceFile | undefined,
writeFileCallback: WriteFileCallback | undefined,
cancellationToken: CancellationToken | undefined,
emitOnly: boolean | EmitOnly | undefined,
customTransformers: CustomTransformers | undefined,
forceDtsEmit: boolean | undefined,
skipBuildInfo: boolean | undefined,
): EmitResult {
if (!forceDtsEmit) {
const result = handleNoEmitOptions(program, sourceFile, writeFileCallback, cancellationToken);
if (result) return result;
Expand Down Expand Up @@ -2891,6 +2918,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
emitOnly,
/*onlyBuildInfo*/ false,
forceDtsEmit,
skipBuildInfo,
),
);

Expand Down Expand Up @@ -5410,7 +5438,7 @@ export function handleNoEmitOptions<T extends BuilderProgram>(
if (options.noEmit) {
// Cache the semantic diagnostics
program.getSemanticDiagnostics(sourceFile, cancellationToken);
return sourceFile || options.outFile ?
return sourceFile ?
emitSkippedWithNoDiagnostics :
program.emitBuildInfo(writeFile, cancellationToken);
}
Expand All @@ -5432,7 +5460,7 @@ export function handleNoEmitOptions<T extends BuilderProgram>(

if (!diagnostics.length) return undefined;
let emittedFiles: string[] | undefined;
if (!sourceFile && !options.outFile) {
if (!sourceFile) {
const emitResult = program.emitBuildInfo(writeFile, cancellationToken);
if (emitResult.diagnostics) diagnostics = [...diagnostics, ...emitResult.diagnostics];
emittedFiles = emitResult.emittedFiles;
Expand Down
23 changes: 6 additions & 17 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import {
PollingInterval,
Program,
ProgramBuildInfo,
ProgramBundleEmitBuildInfo,
ProgramHost,
ProgramMultiFileEmitBuildInfo,
ProgramUpdateLevel,
Expand Down Expand Up @@ -1074,8 +1075,6 @@ function createBuildOrUpdateInvalidedProject<T extends BuilderProgram>(
({ buildResult, step } = buildErrors(
state,
projectPath,
program,
config,
diagnostics,
errorFlags,
errorType,
Expand Down Expand Up @@ -1132,8 +1131,6 @@ function createBuildOrUpdateInvalidedProject<T extends BuilderProgram>(
({ buildResult, step } = buildErrors(
state,
projectPath,
program,
config,
declDiagnostics,
BuildResultFlags.DeclarationEmitErrors,
"Declaration file",
Expand Down Expand Up @@ -1208,8 +1205,6 @@ function createBuildOrUpdateInvalidedProject<T extends BuilderProgram>(
({ buildResult, step } = buildErrors(
state,
projectPath,
program,
config,
emitDiagnostics,
BuildResultFlags.EmitErrors,
"Emit",
Expand Down Expand Up @@ -1453,20 +1448,13 @@ function afterProgramDone<T extends BuilderProgram>(
function buildErrors<T extends BuilderProgram>(
state: SolutionBuilderState<T>,
resolvedPath: ResolvedConfigFilePath,
program: T | undefined,
config: ParsedCommandLine,
diagnostics: readonly Diagnostic[],
buildResult: BuildResultFlags,
errorType: string,
) {
// Since buildinfo has changeset and diagnostics when doing multi file emit, only --out cannot emit buildinfo if it has errors
const canEmitBuildInfo = program && !program.getCompilerOptions().outFile;

reportAndStoreErrors(state, resolvedPath, diagnostics);
state.projectStatus.set(resolvedPath, { type: UpToDateStatusType.Unbuildable, reason: `${errorType} errors` });
if (canEmitBuildInfo) return { buildResult, step: BuildStep.EmitBuildInfo };
afterProgramDone(state, program);
return { buildResult, step: BuildStep.QueueReferencingProjects };
return { buildResult, step: BuildStep.EmitBuildInfo };
}

function isFileWatcherWithModifiedTime(value: FileWatcherWithModifiedTime | Date): value is FileWatcherWithModifiedTime {
Expand Down Expand Up @@ -1692,11 +1680,12 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
// But if noEmit is true, affectedFilesPendingEmit will have file list even if there are no semantic errors to preserve list of files to be emitted when running with noEmit false
// So with noEmit set to true, check on semantic diagnostics needs to be explicit as oppose to when it is false when only files pending emit is sufficient
if (
(buildInfo.program as ProgramMultiFileEmitBuildInfo).changeFileSet?.length ||
buildInfo.program.changeFileSet?.length ||
(!project.options.noEmit ?
(buildInfo.program as ProgramMultiFileEmitBuildInfo).affectedFilesPendingEmit?.length ||
(buildInfo.program as ProgramMultiFileEmitBuildInfo).emitDiagnosticsPerFile?.length :
(buildInfo.program as ProgramMultiFileEmitBuildInfo).semanticDiagnosticsPerFile?.length)
buildInfo.program.emitDiagnosticsPerFile?.length ||
(buildInfo.program as ProgramBundleEmitBuildInfo).pendingEmit !== undefined :
buildInfo.program.semanticDiagnosticsPerFile?.length)
) {
return {
type: UpToDateStatusType.OutOfDateBuildInfo,
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4707,7 +4707,7 @@ export interface Program extends ScriptReferenceHost {
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
/** @internal */
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnly?: boolean | EmitOnly, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnly?: boolean | EmitOnly, customTransformers?: CustomTransformers, forceDtsEmit?: boolean, skipBuildInfo?: boolean): EmitResult;

getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
Expand Down
Loading