From 8c489bfdf8bd141089c45d888c4ad4b02da53b4b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 9 May 2019 15:30:16 -0700 Subject: [PATCH] UpdateBundleProject to contain emit method --- src/compiler/emitter.ts | 9 +- src/compiler/tsbuild.ts | 378 +++++++++++++++++++++------------------- 2 files changed, 203 insertions(+), 184 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8f91e3d25c1..2b1e1309609 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -638,7 +638,12 @@ namespace ts { } /*@internal*/ - export function emitUsingBuildInfo(config: ParsedCommandLine, host: EmitUsingBuildInfoHost, getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined): EmitUsingBuildInfoResult { + export function emitUsingBuildInfo( + config: ParsedCommandLine, + host: EmitUsingBuildInfoHost, + getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined, + customTransformers?: CustomTransformers + ): EmitUsingBuildInfoResult { const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false); const buildInfoText = host.readFile(Debug.assertDefined(buildInfoPath)); if (!buildInfoText) return buildInfoPath!; @@ -723,7 +728,7 @@ namespace ts { useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(), getProgramBuildInfo: returnUndefined }; - emitFiles(notImplementedResolver, emitHost, /*targetSourceFile*/ undefined, /*emitOnlyDtsFiles*/ false, getTransformers(config.options)); + emitFiles(notImplementedResolver, emitHost, /*targetSourceFile*/ undefined, /*emitOnlyDtsFiles*/ false, getTransformers(config.options, customTransformers)); return outputFiles; } diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index 5498c710983..d66874f3a41 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -709,7 +709,7 @@ namespace ts { interface UpdateBundleProject extends InvalidatedProjectBase { readonly kind: InvalidatedProjectKind.UpdateBundle; - updateBundle(): BuildResultFlags | BuildInvalidedProject; + emit(writeFile?: WriteFileCallback, customTransformers?: CustomTransformers): EmitResult | BuildInvalidedProject | undefined; } type InvalidatedProject = UpdateOutputFileStampsProject | BuildInvalidedProject | UpdateBundleProject; @@ -733,91 +733,109 @@ namespace ts { }; } - function createBuildInvalidedProject( + function createBuildOrUpdateInvalidedProject( + kind: InvalidatedProjectKind.Build | InvalidatedProjectKind.UpdateBundle, state: SolutionBuilderState, project: ResolvedConfigFileName, projectPath: ResolvedConfigFilePath, projectIndex: number, config: ParsedCommandLine, - buildOrder: readonly ResolvedConfigFileName[] - ): BuildInvalidedProject { + buildOrder: readonly ResolvedConfigFileName[], + ): BuildInvalidedProject | UpdateBundleProject { enum Step { CreateProgram, SyntaxDiagnostics, SemanticDiagnostics, Emit, + EmitBundle, + BuildInvalidatedProjectOfBundle, QueueReferencingProjects, Done } - let step = Step.CreateProgram; + let step = kind === InvalidatedProjectKind.Build ? Step.CreateProgram : Step.EmitBundle; let program: T | undefined; let buildResult: BuildResultFlags | undefined; + let invalidatedProjectOfBundle: BuildInvalidedProject | undefined; - return { - kind: InvalidatedProjectKind.Build, - project, - projectPath, - getBuilderProgram: () => withProgramOrUndefined(identity), - getProgram: () => - withProgramOrUndefined( - program => program.getProgramOrUndefined() - ), - getCompilerOptions: () => config.options, - getSourceFile: fileName => - withProgramOrUndefined( - program => program.getSourceFile(fileName) - ), - getSourceFiles: () => - withProgramOrEmptyArray( - program => program.getSourceFiles() - ), - getOptionsDiagnostics: cancellationToken => - withProgramOrEmptyArray( - program => program.getOptionsDiagnostics(cancellationToken) - ), - getGlobalDiagnostics: cancellationToken => - withProgramOrEmptyArray( - program => program.getGlobalDiagnostics(cancellationToken) - ), - getConfigFileParsingDiagnostics: () => - withProgramOrEmptyArray( - program => program.getConfigFileParsingDiagnostics() - ), - getSyntacticDiagnostics: (sourceFile, cancellationToken) => - withProgramOrEmptyArray( - program => program.getSyntacticDiagnostics(sourceFile, cancellationToken) - ), - getAllDependencies: sourceFile => - withProgramOrEmptyArray( - program => program.getAllDependencies(sourceFile) - ), - getSemanticDiagnostics: (sourceFile, cancellationToken) => - withProgramOrEmptyArray( - program => program.getSemanticDiagnostics(sourceFile, cancellationToken) - ), - getSemanticDiagnosticsOfNextAffectedFile: (cancellationToken, ignoreSourceFile) => - withProgramOrUndefined( - program => - ((program as any as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile) && - (program as any as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile(cancellationToken, ignoreSourceFile) - ), - emit: (targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) => { - if (targetSourceFile || emitOnlyDtsFiles) { - return withProgramOrUndefined( - program => program.emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) - ); + return kind === InvalidatedProjectKind.Build ? + { + kind, + project, + projectPath, + getBuilderProgram: () => withProgramOrUndefined(identity), + getProgram: () => + withProgramOrUndefined( + program => program.getProgramOrUndefined() + ), + getCompilerOptions: () => config.options, + getSourceFile: fileName => + withProgramOrUndefined( + program => program.getSourceFile(fileName) + ), + getSourceFiles: () => + withProgramOrEmptyArray( + program => program.getSourceFiles() + ), + getOptionsDiagnostics: cancellationToken => + withProgramOrEmptyArray( + program => program.getOptionsDiagnostics(cancellationToken) + ), + getGlobalDiagnostics: cancellationToken => + withProgramOrEmptyArray( + program => program.getGlobalDiagnostics(cancellationToken) + ), + getConfigFileParsingDiagnostics: () => + withProgramOrEmptyArray( + program => program.getConfigFileParsingDiagnostics() + ), + getSyntacticDiagnostics: (sourceFile, cancellationToken) => + withProgramOrEmptyArray( + program => program.getSyntacticDiagnostics(sourceFile, cancellationToken) + ), + getAllDependencies: sourceFile => + withProgramOrEmptyArray( + program => program.getAllDependencies(sourceFile) + ), + getSemanticDiagnostics: (sourceFile, cancellationToken) => + withProgramOrEmptyArray( + program => program.getSemanticDiagnostics(sourceFile, cancellationToken) + ), + getSemanticDiagnosticsOfNextAffectedFile: (cancellationToken, ignoreSourceFile) => + withProgramOrUndefined( + program => + ((program as any as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile) && + (program as any as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile(cancellationToken, ignoreSourceFile) + ), + emit: (targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) => { + if (targetSourceFile || emitOnlyDtsFiles) { + return withProgramOrUndefined( + program => program.emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) + ); + } + executeSteps(Step.SemanticDiagnostics, cancellationToken); + if (step !== Step.Emit) return undefined; + return emit(writeFile, cancellationToken, customTransformers); + }, + getCurrentDirectory: () => state.currentDirectory, + done: cancellationToken => { + executeSteps(Step.Done, cancellationToken); + state.projectPendingBuild.delete(projectPath); } - executeSteps(Step.SemanticDiagnostics, cancellationToken); - if (step !== Step.Emit) return undefined; - return emit(writeFile, cancellationToken, customTransformers); - }, - getCurrentDirectory: () => state.currentDirectory, - done: cancellationToken => { - executeSteps(Step.Done, cancellationToken); - state.projectPendingBuild.delete(projectPath); - } - }; + } : + { + kind, + project, + projectPath, + emit: (writeFile: WriteFileCallback | undefined, customTransformers: CustomTransformers | undefined) => { + if (step !== Step.EmitBundle) return invalidatedProjectOfBundle; + return emitBundle(writeFile, customTransformers); + }, + done: cancellationToken => { + executeSteps(Step.Done, cancellationToken); + state.projectPendingBuild.delete(projectPath); + } + }; function withProgramOrUndefined(action: (program: T) => U | undefined): U | undefined { executeSteps(Step.CreateProgram); @@ -941,7 +959,7 @@ namespace ts { } // Actual Emit - const { host, compilerHost, diagnostics, projectStatus } = state; + const { host, compilerHost } = state; let resultFlags = BuildResultFlags.DeclarationOutputUnchanged; let newestDeclarationFileContentChangedTime = minimumDate; let anyDtsChanged = false; @@ -967,6 +985,25 @@ namespace ts { } }); + finishEmit( + emitterDiagnostics, + emittedOutputs, + newestDeclarationFileContentChangedTime, + /*newestDeclarationFileContentChangedTimeIsMaximumDate*/ anyDtsChanged, + outputFiles.length ? outputFiles[0].name : getFirstProjectOutput(config, !host.useCaseSensitiveFileNames()), + resultFlags + ); + return emitResult; + } + + function finishEmit( + emitterDiagnostics: DiagnosticCollection, + emittedOutputs: FileMap, + priorNewestUpdateTime: Date, + newestDeclarationFileContentChangedTimeIsMaximumDate: boolean, + oldestOutputFileName: string, + resultFlags: BuildResultFlags + ) { const emitDiagnostics = emitterDiagnostics.getDiagnostics(); if (emitDiagnostics.length) { buildResult = buildErrors( @@ -978,27 +1015,87 @@ namespace ts { "Emit" ); step = Step.QueueReferencingProjects; - return emitResult; + return emitDiagnostics; } if (state.writeFileName) { emittedOutputs.forEach(name => listEmittedFile(state, config, name)); - listFiles(program!, state.writeFileName); + if (program) listFiles(program, state.writeFileName); } // Update time stamps for rest of the outputs - newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker(state, config, newestDeclarationFileContentChangedTime, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs); - diagnostics.delete(projectPath); - projectStatus.set(projectPath, { + const newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker(state, config, priorNewestUpdateTime, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs); + state.diagnostics.delete(projectPath); + state.projectStatus.set(projectPath, { type: UpToDateStatusType.UpToDate, - newestDeclarationFileContentChangedTime: anyDtsChanged ? maximumDate : newestDeclarationFileContentChangedTime, - oldestOutputFileName: outputFiles.length ? outputFiles[0].name : getFirstProjectOutput(config, !host.useCaseSensitiveFileNames()) + newestDeclarationFileContentChangedTime: newestDeclarationFileContentChangedTimeIsMaximumDate ? + maximumDate : + newestDeclarationFileContentChangedTime, + oldestOutputFileName }); - afterProgramCreate(state, projectPath, program!); + if (program) afterProgramCreate(state, projectPath, program); state.projectCompilerOptions = state.baseCompilerOptions; - step++; + step = Step.QueueReferencingProjects; buildResult = resultFlags; - return emitResult; + return emitDiagnostics; + } + + function emitBundle(writeFileCallback?: WriteFileCallback, customTransformers?: CustomTransformers): EmitResult | BuildInvalidedProject | undefined { + Debug.assert(kind === InvalidatedProjectKind.UpdateBundle); + if (state.options.dry) { + reportStatus(state, Diagnostics.A_non_dry_build_would_update_output_of_project_0, project); + buildResult = BuildResultFlags.Success; + step = Step.QueueReferencingProjects; + return undefined; + } + + if (state.options.verbose) reportStatus(state, Diagnostics.Updating_output_of_project_0, project); + + // Update js, and source map + const { compilerHost } = state; + state.projectCompilerOptions = config.options; + const outputFiles = emitUsingBuildInfo( + config, + compilerHost, + ref => { + const refName = resolveProjectName(state, ref.path); + return parseConfigFile(state, refName, toResolvedConfigFilePath(state, refName)); + }, + customTransformers + ); + + if (isString(outputFiles)) { + reportStatus(state, Diagnostics.Cannot_update_output_of_project_0_because_there_was_error_reading_file_1, project, relName(state, outputFiles)); + step = Step.BuildInvalidatedProjectOfBundle; + return invalidatedProjectOfBundle = createBuildOrUpdateInvalidedProject( + InvalidatedProjectKind.Build, + state, + project, + projectPath, + projectIndex, + config, + buildOrder + ) as BuildInvalidedProject; + } + + // Actual Emit + Debug.assert(!!outputFiles.length); + const emitterDiagnostics = createDiagnosticCollection(); + const emittedOutputs = createMap() as FileMap; + outputFiles.forEach(({ name, text, writeByteOrderMark }) => { + emittedOutputs.set(toPath(state, name), name); + writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark); + }); + + const emitDiagnostics = finishEmit( + emitterDiagnostics, + emittedOutputs, + minimumDate, + /*newestDeclarationFileContentChangedTimeIsMaximumDate*/ false, + outputFiles[0].name, + BuildResultFlags.DeclarationOutputUnchanged + ); + return { emitSkipped: false, diagnostics: emitDiagnostics }; } function executeSteps(till: Step, cancellationToken?: CancellationToken) { @@ -1021,6 +1118,15 @@ namespace ts { emit(/*writeFileCallback*/ undefined, cancellationToken); break; + case Step.EmitBundle: + emitBundle(); + break; + + case Step.BuildInvalidatedProjectOfBundle: + Debug.assertDefined(invalidatedProjectOfBundle).done(cancellationToken); + step = Step.Done; + break; + case Step.QueueReferencingProjects: queueReferencingProjects(state, project, projectPath, projectIndex, config, buildOrder, Debug.assertDefined(buildResult)); step++; @@ -1037,42 +1143,6 @@ namespace ts { } } - function createUpdateBundleProject( - state: SolutionBuilderState, - project: ResolvedConfigFileName, - projectPath: ResolvedConfigFilePath, - projectIndex: number, - config: ParsedCommandLine, - buildOrder: readonly ResolvedConfigFileName[] - ): UpdateBundleProject { - let updatePending = true; - return { - kind: InvalidatedProjectKind.UpdateBundle, - project, - projectPath, - updateBundle: update, - done: cancellationToken => { - if (updatePending) { - const result = update(); - if ((result as BuildInvalidedProject).project) { - return (result as BuildInvalidedProject).done(cancellationToken); - } - } - state.projectPendingBuild.delete(projectPath); - } - }; - - function update() { - const buildResult = updateBundle(state, project, projectPath, config); - if (isString(buildResult)) { - return createBuildInvalidedProject(state, project, projectPath, projectIndex, config, buildOrder); - } - queueReferencingProjects(state, project, projectPath, projectIndex, config, buildOrder, buildResult); - updatePending = false; - return buildResult; - } - } - function needsBuild({ options }: SolutionBuilderState, status: UpToDateStatus, config: ParsedCommandLine) { if (status.type !== UpToDateStatusType.OutOfDateWithPrepend || options.force) return true; return config.fileNames.length === 0 || @@ -1149,9 +1219,17 @@ namespace ts { continue; } - return needsBuild(state, status, config) ? - createBuildInvalidedProject(state, project, projectPath, projectIndex, config, buildOrder) : - createUpdateBundleProject(state, project, projectPath, projectIndex, config, buildOrder); + return createBuildOrUpdateInvalidedProject( + needsBuild(state, status, config) ? + InvalidatedProjectKind.Build : + InvalidatedProjectKind.UpdateBundle, + state, + project, + projectPath, + projectIndex, + config, + buildOrder, + ); } return undefined; @@ -1228,70 +1306,6 @@ namespace ts { moduleResolutionCache.moduleNameToDirectoryMap.setOwnOptions(config.options); } - function updateBundle( - state: SolutionBuilderState, - proj: ResolvedConfigFileName, - resolvedPath: ResolvedConfigFilePath, - config: ParsedCommandLine - ): BuildResultFlags | string { - if (state.options.dry) { - reportStatus(state, Diagnostics.A_non_dry_build_would_update_output_of_project_0, proj); - return BuildResultFlags.Success; - } - - if (state.options.verbose) reportStatus(state, Diagnostics.Updating_output_of_project_0, proj); - - // Update js, and source map - const { projectStatus, diagnostics, compilerHost } = state; - state.projectCompilerOptions = config.options; - const outputFiles = emitUsingBuildInfo( - config, - compilerHost, - ref => { - const refName = resolveProjectName(state, ref.path); - return parseConfigFile(state, refName, toResolvedConfigFilePath(state, refName)); - }); - if (isString(outputFiles)) { - reportStatus(state, Diagnostics.Cannot_update_output_of_project_0_because_there_was_error_reading_file_1, proj, relName(state, outputFiles)); - return outputFiles; - } - - // Actual Emit - Debug.assert(!!outputFiles.length); - const emitterDiagnostics = createDiagnosticCollection(); - const emittedOutputs = createMap() as FileMap; - outputFiles.forEach(({ name, text, writeByteOrderMark }) => { - emittedOutputs.set(toPath(state, name), name); - writeFile(compilerHost, emitterDiagnostics, name, text, writeByteOrderMark); - }); - const emitDiagnostics = emitterDiagnostics.getDiagnostics(); - if (emitDiagnostics.length) { - return buildErrors( - state, - resolvedPath, - /*program*/ undefined, - emitDiagnostics, - BuildResultFlags.EmitErrors, - "Emit" - ); - } - - if (state.writeFileName) { - emittedOutputs.forEach(name => listEmittedFile(state, config, name)); - } - - // Update timestamps for dts - const newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker(state, config, minimumDate, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs); - diagnostics.delete(resolvedPath); - projectStatus.set(resolvedPath, { - type: UpToDateStatusType.UpToDate, - newestDeclarationFileContentChangedTime, - oldestOutputFileName: outputFiles[0].name - }); - state.projectCompilerOptions = state.baseCompilerOptions; - return BuildResultFlags.DeclarationOutputUnchanged; - } - function checkConfigFileUpToDateStatus(state: SolutionBuilderState, configFile: string, oldestOutputFileTime: Date, oldestOutputFileName: string): Status.OutOfDateWithSelf | undefined { // Check tsconfig time const tsconfigTime = state.host.getModifiedTime(configFile) || missingFileModifiedTime;