diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index dbc6fcd91da..e208ab44abc 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3992,14 +3992,26 @@ "category": "Message", "code": 6371 }, - "Project '{0}' is out of date because output to prepend from its dependency '{1}' has changed": { + "Project '{0}' is out of date because output javascript and source map (if specified) of its dependency '{1}' has changed": { "category": "Message", "code": 6372 }, - "Updating output of project '{0}'...": { + "Updating output javascript and javascript source map (if specified) of project '{0}'...": { "category": "Message", "code": 6373 }, + "A non-dry build would update timestamps for output of project '{0}'": { + "category": "Message", + "code": 6374 + }, + "A non-dry build would update output javascript and javascript source map (if specified) of project '{0}'": { + "category": "Message", + "code": 6375 + }, + "Cannot update output javascript and javascript source map (if specified) of project '{0}' because there was error reading file '{1}'": { + "category": "Message", + "code": 6376 + }, "The expected type comes from property '{0}' which is declared here on type '{1}'": { "category": "Message", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 2a89c8a4740..034392db24b 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -21,8 +21,9 @@ namespace ts { const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile); const options = host.getCompilerOptions(); if (options.outFile || options.out) { - if (sourceFiles.length) { - const bundle = createBundle(sourceFiles, host.getPrependNodes()); + const prepends = host.getPrependNodes(); + if (sourceFiles.length || prepends.length) { + const bundle = createBundle(sourceFiles, prepends); const result = action(getOutputPathsFor(bundle, host, emitOnlyDtsFiles), bundle); if (result) { return result; @@ -104,7 +105,7 @@ namespace ts { /*@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, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[], declarationTransformers?: TransformerFactory[]): EmitResult { + export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[], declarationTransformers?: TransformerFactory[]): EmitResult { const compilerOptions = host.getCompilerOptions(); const sourceMapDataList: SourceMapEmitResult[] | undefined = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined; const emittedFilesList: string[] | undefined = compilerOptions.listEmittedFiles ? [] : undefined; @@ -136,7 +137,7 @@ namespace ts { 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, JSON.stringify(buildInfo, undefined, 2), /*writeByteOrderMark*/ false); + writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText(buildInfo!), /*writeByteOrderMark*/ false); } if (!emitSkipped && emittedFilesList) { @@ -409,6 +410,131 @@ namespace ts { } } + function getBuildInfoText(buildInfo: BuildInfo) { + return JSON.stringify(buildInfo, undefined, 2); + } + + const notImplementedResolver: EmitResolver = { + hasGlobalName: notImplemented, + getReferencedExportContainer: notImplemented, + getReferencedImportDeclaration: notImplemented, + getReferencedDeclarationWithCollidingName: notImplemented, + isDeclarationWithCollidingName: notImplemented, + isValueAliasDeclaration: notImplemented, + isReferencedAliasDeclaration: notImplemented, + isTopLevelValueImportEqualsWithEntityName: notImplemented, + getNodeCheckFlags: notImplemented, + isDeclarationVisible: notImplemented, + isLateBound: (_node): _node is LateBoundDeclaration => false, + collectLinkedAliases: notImplemented, + isImplementationOfOverload: notImplemented, + isRequiredInitializedParameter: notImplemented, + isOptionalUninitializedParameterProperty: notImplemented, + isExpandoFunctionDeclaration: notImplemented, + getPropertiesOfContainerFunction: notImplemented, + createTypeOfDeclaration: notImplemented, + createReturnTypeOfSignatureDeclaration: notImplemented, + createTypeOfExpression: notImplemented, + createLiteralConstValue: notImplemented, + isSymbolAccessible: notImplemented, + isEntityNameVisible: notImplemented, + // Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant + getConstantValue: notImplemented, + getReferencedValueDeclaration: notImplemented, + getTypeReferenceSerializationKind: notImplemented, + isOptionalParameter: notImplemented, + moduleExportsSomeValue: notImplemented, + isArgumentsLocalBinding: notImplemented, + getExternalModuleFileFromDeclaration: notImplemented, + getTypeReferenceDirectivesForEntityName: notImplemented, + getTypeReferenceDirectivesForSymbol: notImplemented, + isLiteralConstDeclaration: notImplemented, + getJsxFactoryEntity: notImplemented, + getAllAccessorDeclarations: notImplemented, + getSymbolOfExternalModuleSpecifier: notImplemented, + isBindingCapturedByNode: notImplemented, + }; + + /*@internal*/ + /** File that isnt present resulting in error or output files */ + export type EmitUsingBuildInfoResult = string | ReadonlyArray; + + /*@internal*/ + export interface EmitUsingBuildInfoHost extends ModuleResolutionHost { + getCurrentDirectory(): string; + getCanonicalFileName(fileName: string): string; + useCaseSensitiveFileNames(): boolean; + getNewLine(): string; + } + + /*@internal*/ + export function emitUsingBuildInfo(config: ParsedCommandLine, host: EmitUsingBuildInfoHost, getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined): EmitUsingBuildInfoResult { + const { buildInfoPath, jsFilePath, sourceMapFilePath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false, config.projectReferences); + const buildInfoText = host.readFile(Debug.assertDefined(buildInfoPath)); + if (!buildInfoText) return buildInfoPath!; + const jsFileText = host.readFile(Debug.assertDefined(jsFilePath)); + if (!jsFileText) return jsFilePath!; + const sourceMapText = sourceMapFilePath && host.readFile(sourceMapFilePath); + // error if no source map or for now if inline sourcemap + if ((sourceMapFilePath && !sourceMapText) || config.options.inlineSourceMap) return sourceMapFilePath || "inline sourcemap decoding"; + + const buildInfo = JSON.parse(buildInfoText) as BuildInfo; + const ownPrependInput = createInputFiles( + jsFileText, + /*declarationText*/ undefined!, + sourceMapFilePath, + sourceMapText, + /*declarationMapPath*/ undefined!, + /*declarationMaptext*/ undefined!, + jsFilePath, + /*declarationPath*/ undefined, + buildInfoPath, + buildInfo, + /*onlyOwnText*/ true + ); + const optionsWithoutDeclaration = clone(config.options); + optionsWithoutDeclaration.declaration = false; + optionsWithoutDeclaration.composite = false; + const outputFiles: OutputFile[] = []; + const emitHost: EmitHost = { + getPrependNodes: () => createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f)). concat(ownPrependInput), + getProjectReferences: () => config.projectReferences, + getCanonicalFileName: host.getCanonicalFileName, + getCommonSourceDirectory: () => buildInfo.commonSourceDirectory, + getCompilerOptions: () => optionsWithoutDeclaration, + getCurrentDirectory: () => host.getCurrentDirectory(), + getNewLine: () => host.getNewLine(), + getSourceFile: notImplemented, + getSourceFileByPath: notImplemented, + getSourceFiles: () => emptyArray, + getLibFileFromReference: notImplemented, + isSourceFileFromExternalLibrary: notImplemented, + writeFile: (name, text, writeByteOrderMark) => { + if (name === buildInfoPath) { + // Add dts and sources build info since we are not touching that file + const newBuildInfo = JSON.parse(text) as BuildInfo; + newBuildInfo.dts = buildInfo.dts; + newBuildInfo.sources = buildInfo.sources; + text = getBuildInfoText(newBuildInfo); + } + outputFiles.push({ name, text, writeByteOrderMark }); + }, + isEmitBlocked: returnFalse, + readFile: f => host.readFile(f), + fileExists: f => host.fileExists(f), + ...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}), + useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(), + }; + emitFiles( + notImplementedResolver, + emitHost, + /*targetSourceFile*/ undefined, + /*emitOnlyDtsFiles*/ false, + getTransformers(optionsWithoutDeclaration) + ); + return outputFiles; + } + const enum PipelinePhase { Notification, Substitution, @@ -562,14 +688,17 @@ namespace ts { writeLine(); const pos = getTextPosWithWriteLine(); print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); - if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prepend, data: (prepend as UnparsedSource).fileName }); + if (bundleFileInfo) { + if (prepend.oldFileOfCurrentEmit) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Text }); + else bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prepend, data: (prepend as UnparsedSource).fileName }); + } } const pos = getTextPosWithWriteLine(); for (const sourceFile of bundle.sourceFiles) { print(EmitHint.SourceFile, sourceFile, sourceFile); } - if (bundleFileInfo) { + if (bundleFileInfo && bundle.sourceFiles.length) { bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Text }); // Store prologues const prologues = getPrologueDirectivesFromBundledSourceFiles(bundle); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 7d6f22025ef..5b06f81eae3 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2666,6 +2666,7 @@ namespace ts { let texts: UnparsedSourceText[] | undefined; if (!isString(textOrInputFiles)) { Debug.assert(mapPathOrType === "js" || mapPathOrType === "dts"); + node.oldFileOfCurrentEmit = textOrInputFiles.oldFileOfCurrentEmit; node.fileName = (mapPathOrType === "js" ? textOrInputFiles.javascriptPath : textOrInputFiles.declarationPath) || ""; node.sourceMapPath = mapPathOrType === "js" ? textOrInputFiles.javascriptMapPath : textOrInputFiles.declarationMapPath; Object.defineProperties(node, { @@ -2676,6 +2677,7 @@ namespace ts { if (textOrInputFiles.buildInfo) { const sections = mapPathOrType === "js" ? textOrInputFiles.buildInfo.js : textOrInputFiles.buildInfo.dts; for (const section of sections) { + if (textOrInputFiles.oldFileOfCurrentEmit && section.kind !== BundleFileSectionKind.Text) continue; switch (section.kind) { case BundleFileSectionKind.Prologue: (prologues || (prologues = [])).push(createUnparsedNode(section, node) as UnparsedPrologue); @@ -2766,7 +2768,12 @@ namespace ts { javascriptMapPath: string | undefined, javascriptMapText: string | undefined, declarationMapPath: string | undefined, - declarationMapText: string | undefined + declarationMapText: string | undefined, + javascriptPath: string | undefined, + declarationPath: string | undefined, + buildInfoPath?: string | undefined, + buildInfo?: BuildInfo, + oldFileOfCurrentEmit?: boolean ): InputFiles; export function createInputFiles( javascriptTextOrReadFileText: string | ((path: string) => string | undefined), @@ -2774,17 +2781,14 @@ namespace ts { javascriptMapPath?: string, javascriptMapTextOrDeclarationPath?: string, declarationMapPath?: string, - declarationMapTextOrBuildInfoPath?: string + declarationMapTextOrBuildInfoPath?: string, + javascriptPath?: string | undefined, + declarationPath?: string | undefined, + buildInfoPath?: string | undefined, + buildInfo?: BuildInfo, + oldFileOfCurrentEmit?: boolean ): InputFiles { const node = createNode(SyntaxKind.InputFiles); - let buildInfo: BuildInfo | false; - const getBuildInfo = (getText: () => string | undefined) => { - if (buildInfo === undefined) { - const result = getText(); - buildInfo = result !== undefined ? JSON.parse(result) as BuildInfo : false; - } - return buildInfo || undefined; - }; if (!isString(javascriptTextOrReadFileText)) { const cache = createMap(); const textGetter = (path: string | undefined) => { @@ -2800,6 +2804,14 @@ namespace ts { const result = textGetter(path); return result !== undefined ? result : `/* Input file ${path} was missing */\r\n`; }; + let buildInfo: BuildInfo | false; + const getBuildInfo = (getText: () => string | undefined) => { + if (buildInfo === undefined) { + const result = getText(); + buildInfo = result !== undefined ? JSON.parse(result) as BuildInfo : false; + } + return buildInfo || undefined; + }; node.javascriptPath = declarationTextOrJavascriptPath; node.javascriptMapPath = javascriptMapPath; node.declarationPath = Debug.assertDefined(javascriptMapTextOrDeclarationPath); @@ -2820,6 +2832,11 @@ namespace ts { node.declarationText = declarationTextOrJavascriptPath; node.declarationMapPath = declarationMapPath; node.declarationMapText = declarationMapTextOrBuildInfoPath; + node.javascriptPath = javascriptPath; + node.declarationPath = declarationPath, + node.buildInfoPath = buildInfoPath; + node.buildInfo = buildInfo; + node.oldFileOfCurrentEmit = oldFileOfCurrentEmit; } return node; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a6903bc8940..b3957496766 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1443,30 +1443,16 @@ namespace ts { return projectReferences; } - function getPrependNodes(): InputFiles[] { - if (!projectReferences) { - return emptyArray; - } - - const nodes: InputFiles[] = []; - for (let i = 0; i < projectReferences.length; i++) { - const ref = projectReferences[i]; - const resolvedRefOpts = resolvedProjectReferences![i]!.commandLine; - if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) { - const out = resolvedRefOpts.options.outFile || resolvedRefOpts.options.out; - // Upstream project didn't have outFile set -- skip (error will have been issued earlier) - if (!out) continue; - - const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(resolvedRefOpts.options, /*forceDtsPaths*/ true, resolvedRefOpts.projectReferences); - const node = createInputFiles(fileName => { - const path = toPath(fileName); - const sourceFile = getSourceFileByPath(path); - return sourceFile ? sourceFile.text : filesByName.has(path) ? undefined : host.readFile(path); - }, jsFilePath! , sourceMapFilePath, declarationFilePath! , declarationMapPath, buildInfoPath); - nodes.push(node); + function getPrependNodes() { + return createPrependNodes( + projectReferences, + (_ref, index) => resolvedProjectReferences![index]!.commandLine, + fileName => { + const path = toPath(fileName); + const sourceFile = getSourceFileByPath(path); + return sourceFile ? sourceFile.text : filesByName.has(path) ? undefined : host.readFile(path); } - } - return nodes; + ); } function isSourceFileFromExternalLibrary(file: SourceFile): boolean { @@ -1563,7 +1549,7 @@ namespace ts { const emitResult = emitFiles( emitResolver, getEmitHost(writeFileCallback), - sourceFile!, // TODO: GH#18217 + sourceFile, emitOnlyDtsFiles, transformers, customTransformers && customTransformers.afterDeclarations @@ -3141,6 +3127,25 @@ namespace ts { fileExists(fileName: string): boolean; } + /* @internal */ + export function createPrependNodes(projectReferences: ReadonlyArray | undefined, getCommandLine: (ref: ProjectReference, index: number) => ParsedCommandLine | undefined, readFile: (path: string) => string | undefined) { + if (!projectReferences) return emptyArray; + let nodes: InputFiles[] | undefined; + for (let i = 0; i < projectReferences.length; i++) { + const ref = projectReferences[i]; + const resolvedRefOpts = getCommandLine(ref, i); + if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) { + const out = resolvedRefOpts.options.outFile || resolvedRefOpts.options.out; + // Upstream project didn't have outFile set -- skip (error will have been issued earlier) + if (!out) continue; + + const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(resolvedRefOpts.options, /*forceDtsPaths*/ true, resolvedRefOpts.projectReferences); + const node = createInputFiles(readFile, jsFilePath!, sourceMapFilePath, declarationFilePath!, declarationMapPath, buildInfoPath); + (nodes || (nodes = [])).push(node); + } + } + return nodes || emptyArray; + } /** * Returns the target config filename of a project reference. * Note: The file might not exist. diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index f6d7d5b115c..69c5e17ad06 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -1007,13 +1007,9 @@ namespace ts { return; } - //if (status.type === UpToDateStatusType.OutOfDateWithPrepend) { - // // Fake that files have been built by manipulating prepend and existing output - // //updateOutputTimestamps(proj); - // return; - //} - - const buildResult = buildSingleProject(resolved); + const buildResult = status.type === UpToDateStatusType.OutOfDateWithPrepend ? + updateBundle(resolved) : // Fake that files have been built by manipulating prepend and existing output + buildSingleProject(resolved); // Actual build if (buildResult & BuildResultFlags.AnyErrors) return; const { referencingProjectsMap, buildQueue } = getGlobalDependencyGraph(); @@ -1109,8 +1105,7 @@ namespace ts { if (options.verbose) reportStatus(Diagnostics.Building_project_0, proj); - let resultFlags = BuildResultFlags.None; - resultFlags |= BuildResultFlags.DeclarationOutputUnchanged; + let resultFlags = BuildResultFlags.DeclarationOutputUnchanged; const configFile = parseConfigFile(proj); if (!configFile) { @@ -1136,7 +1131,6 @@ namespace ts { configFile.errors, configFile.projectReferences ); - projectCompilerOptions = baseCompilerOptions; // Don't emit anything in the presence of syntactic errors or options diagnostics const syntaxDiagnostics = [ @@ -1208,6 +1202,7 @@ namespace ts { diagnostics.removeKey(proj); projectStatus.setValue(proj, status); afterProgramCreate(proj, program); + projectCompilerOptions = baseCompilerOptions; return resultFlags; function buildErrors(diagnostics: ReadonlyArray, errorFlags: BuildResultFlags, errorType: string) { @@ -1215,6 +1210,7 @@ namespace ts { reportAndStoreErrors(proj, diagnostics); projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: `${errorType} errors` }); afterProgramCreate(proj, program); + projectCompilerOptions = baseCompilerOptions; return resultFlags; } } @@ -1229,9 +1225,60 @@ namespace ts { } } + function updateBundle(proj: ResolvedConfigFileName): BuildResultFlags { + if (options.dry) { + reportStatus(Diagnostics.A_non_dry_build_would_update_output_javascript_and_javascript_source_map_if_specified_of_project_0, proj); + return BuildResultFlags.Success; + } + + if (options.verbose) reportStatus(Diagnostics.Updating_output_javascript_and_javascript_source_map_if_specified_of_project_0, proj); + + // Update js, and source map + const config = Debug.assertDefined(parseConfigFile(proj)); + projectCompilerOptions = config.options; + const outputFiles = emitUsingBuildInfo( + config, + compilerHost, + ref => parseConfigFile(resolveProjectName(ref.path))); + if (isString(outputFiles)) { + reportStatus(Diagnostics.Cannot_update_output_javascript_and_javascript_source_map_if_specified_of_project_0_because_there_was_error_reading_file_1, proj, relName(outputFiles)); + return buildSingleProject(proj); + } + + // Actual Emit + Debug.assert(!!outputFiles.length); + const emitterDiagnostics = createDiagnosticCollection(); + const emittedOutputs = createFileMap(toPath as ToPath); + outputFiles.forEach(({ name, text, writeByteOrderMark }) => { + emittedOutputs.setValue(name, true); + writeFile(compilerHost, emitterDiagnostics, name, text, writeByteOrderMark); + }); + const emitDiagnostics = emitterDiagnostics.getDiagnostics(); + if (emitDiagnostics.length) { + reportAndStoreErrors(proj, emitDiagnostics); + projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Emit errors" }); + projectCompilerOptions = baseCompilerOptions; + return BuildResultFlags.DeclarationOutputUnchanged | BuildResultFlags.EmitErrors; + } + + // Update timestamps for dts + const newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker(config, minimumDate, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs); + + const status: UpToDateStatus = { + type: UpToDateStatusType.UpToDate, + newestDeclarationFileContentChangedTime, + oldestOutputFileName: outputFiles[0].name + }; + + diagnostics.removeKey(proj); + projectStatus.setValue(proj, status); + projectCompilerOptions = baseCompilerOptions; + return BuildResultFlags.DeclarationOutputUnchanged; + } + function updateOutputTimestamps(proj: ParsedCommandLine) { if (options.dry) { - return reportStatus(Diagnostics.A_non_dry_build_would_build_project_0, proj.options.configFilePath!); + return reportStatus(Diagnostics.A_non_dry_build_would_update_timestamps_for_output_of_project_0, proj.options.configFilePath!); } const priorNewestUpdateTime = updateOutputTimestampsWorker(proj, minimumDate, Diagnostics.Updating_output_timestamps_of_project_0); projectStatus.setValue(proj.options.configFilePath as ResolvedConfigFilePath, { type: UpToDateStatusType.UpToDate, newestDeclarationFileContentChangedTime: priorNewestUpdateTime } as UpToDateStatus); @@ -1354,13 +1401,6 @@ namespace ts { continue; } - //if (status.type === UpToDateStatusType.OutOfDateWithPrepend && !options.force) { - // reportAndStoreErrors(next, errors); - // // Fake that files have been built by manipulating prepend and existing output - // // updateOutputTimestamps(proj); - // continue; - //} - if (status.type === UpToDateStatusType.UpstreamBlocked) { reportAndStoreErrors(next, errors); if (options.verbose) reportStatus(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, projName, status.upstreamProjectName); @@ -1373,7 +1413,9 @@ namespace ts { continue; } - const buildResult = buildSingleProject(next); + const buildResult = status.type === UpToDateStatusType.OutOfDateWithPrepend && !options.force ? + updateBundle(next) : // Fake that files have been built by manipulating prepend and existing output + buildSingleProject(next); // Actual build anyFailed = anyFailed || !!(buildResult & BuildResultFlags.AnyErrors); } reportErrorSummary(); @@ -1484,7 +1526,7 @@ namespace ts { // Don't report anything for "up to date because it was already built" -- too verbose break; case UpToDateStatusType.OutOfDateWithPrepend: - return formatMessage(Diagnostics.Project_0_is_out_of_date_because_output_to_prepend_from_its_dependency_1_has_changed, + return formatMessage(Diagnostics.Project_0_is_out_of_date_because_output_javascript_and_source_map_if_specified_of_its_dependency_1_has_changed, relName(configFileName), relName(status.newerProjectName)); case UpToDateStatusType.UpToDateWithUpstreamTypes: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9dbd948b6cd..33cda79bf4a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2775,8 +2775,9 @@ namespace ts { declarationText: string; declarationMapPath?: string; declarationMapText?: string; - buildInfoPath?: string; + /*@internal*/ buildInfoPath?: string; /*@internal*/ buildInfo?: BuildInfo; + /*@internal*/ oldFileOfCurrentEmit?: boolean; } export interface UnparsedSource extends Node { @@ -2785,13 +2786,17 @@ namespace ts { text: string; prologues: ReadonlyArray; helpers: ReadonlyArray | undefined; + + // References and noDefaultLibAre Dts only referencedFiles: ReadonlyArray; typeReferenceDirectives: ReadonlyArray | undefined; libReferenceDirectives: ReadonlyArray; hasNoDefaultLib?: boolean; + sourceMapPath?: string; sourceMapText?: string; texts: ReadonlyArray; + /*@internal*/ oldFileOfCurrentEmit?: boolean; /*@internal*/ parsedSourceMap?: RawSourceMap | false | undefined; // Adding this to satisfy services, fix later /*@internal*/ diff --git a/src/testRunner/unittests/tsbuild/outFile.ts b/src/testRunner/unittests/tsbuild/outFile.ts index 308d02c2db1..22194a0124d 100644 --- a/src/testRunner/unittests/tsbuild/outFile.ts +++ b/src/testRunner/unittests/tsbuild/outFile.ts @@ -241,38 +241,7 @@ Mismatch Actual: ${JSON.stringify(mapDefined(arrayFrom(actualReadFileMap.entries incrementalBuild( "incremental declaration doesnt change", - [ - // Configs - sources[project.first][source.config], - sources[project.second][source.config], - sources[project.third][source.config], - - // Source files - ...sources[project.first][source.ts], - ...sources[project.third][source.ts], - - // Additional source Files - ...(additionalSourceFiles || emptyArray), - - // outputs - ...outputFiles[project.first], - ...outputFiles[project.second], - outputFiles[project.third][ext.dts], - ], - fs => appendFileContent(fs, relSources[project.first][source.ts][part.one], "console.log(s);"), - [ - getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]), - [Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.first][source.config], relOutputFiles[project.first][ext.js], relSources[project.first][source.ts][part.one]], - [Diagnostics.Building_project_0, sources[project.first][source.config]], - [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, relSources[project.second][source.config], relSources[project.second][source.ts][part.one], relOutputFiles[project.second][ext.js]], - [Diagnostics.Project_0_is_out_of_date_because_output_to_prepend_from_its_dependency_1_has_changed, relSources[project.third][source.config], "src/first"], - [Diagnostics.Building_project_0, sources[project.third][source.config]] - ] - ); - - if (modifyAgainFs) { - incrementalBuild( - "incremental headers change", + withoutBuildInfo ? [ // Configs sources[project.first][source.config], @@ -290,15 +259,122 @@ Mismatch Actual: ${JSON.stringify(mapDefined(arrayFrom(actualReadFileMap.entries ...outputFiles[project.first], ...outputFiles[project.second], outputFiles[project.third][ext.dts], + + // To prepend:: checked to see if we can do prepend manipulation + outputFiles[project.third][ext.buildinfo], + ] : + [ + // Configs + sources[project.first][source.config], + sources[project.second][source.config], + sources[project.third][source.config], + + // Source files + ...sources[project.first][source.ts], + + // Additional source Files + ...(additionalSourceFiles && additionalSourceFiles.length === 3 ? [additionalSourceFiles[project.first]] : emptyArray), + + // outputs without d.ts since we just update js + outputFiles[project.first][ext.js], + outputFiles[project.first][ext.jsmap], + outputFiles[project.first][ext.dts], + outputFiles[project.first][ext.buildinfo], + outputFiles[project.second][ext.js], + outputFiles[project.second][ext.jsmap], + outputFiles[project.second][ext.buildinfo], + + // To prepend:: + outputFiles[project.third][ext.buildinfo], + outputFiles[project.third][ext.js], + outputFiles[project.third][ext.jsmap] ], + fs => appendFileContent(fs, relSources[project.first][source.ts][part.one], "console.log(s);"), + [ + getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]), + [Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.first][source.config], relOutputFiles[project.first][ext.js], relSources[project.first][source.ts][part.one]], + [Diagnostics.Building_project_0, sources[project.first][source.config]], + [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, relSources[project.second][source.config], relSources[project.second][source.ts][part.one], relOutputFiles[project.second][ext.js]], + [Diagnostics.Project_0_is_out_of_date_because_output_javascript_and_source_map_if_specified_of_its_dependency_1_has_changed, relSources[project.third][source.config], "src/first"], + [Diagnostics.Updating_output_javascript_and_javascript_source_map_if_specified_of_project_0, sources[project.third][source.config]], + ...>(!withoutBuildInfo ? + [ + [Diagnostics.Updating_unchanged_output_timestamps_of_project_0, sources[project.third][source.config]] + ] : + [ + [Diagnostics.Cannot_update_output_javascript_and_javascript_source_map_if_specified_of_project_0_because_there_was_error_reading_file_1, sources[project.third][source.config], relOutputFiles[project.third][ext.buildinfo]], + [Diagnostics.Building_project_0, sources[project.third][source.config]] + ]) + ] + ); + + if (modifyAgainFs) { + incrementalBuild( + "incremental headers change", + withoutBuildInfo ? + [ + // Configs + sources[project.first][source.config], + sources[project.second][source.config], + sources[project.third][source.config], + + // Source files + ...sources[project.first][source.ts], + ...sources[project.third][source.ts], + + // Additional source Files + ...(additionalSourceFiles || emptyArray), + + // outputs + ...outputFiles[project.first], + ...outputFiles[project.second], + outputFiles[project.third][ext.dts], + + // To prepend:: checked to see if we can do prepend manipulation + outputFiles[project.third][ext.buildinfo], + ] : + [ + // Configs + sources[project.first][source.config], + sources[project.second][source.config], + sources[project.third][source.config], + + // Source files + ...sources[project.first][source.ts], + + // Additional source Files + ...(additionalSourceFiles || emptyArray), + + // outputs without d.ts since we just update js + outputFiles[project.first][ext.js], + outputFiles[project.first][ext.jsmap], + outputFiles[project.first][ext.dts], + outputFiles[project.first][ext.buildinfo], + outputFiles[project.second][ext.js], + outputFiles[project.second][ext.jsmap], + outputFiles[project.second][ext.buildinfo], + + // To prepend:: + outputFiles[project.third][ext.buildinfo], + outputFiles[project.third][ext.js], + outputFiles[project.third][ext.jsmap] + ], fs => modifyAgainFs(fs), [ getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]), [Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.first][source.config], relOutputFiles[project.first][ext.js], relSources[project.first][source.ts][part.one]], [Diagnostics.Building_project_0, sources[project.first][source.config]], [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, relSources[project.second][source.config], relSources[project.second][source.ts][part.one], relOutputFiles[project.second][ext.js]], - [Diagnostics.Project_0_is_out_of_date_because_output_to_prepend_from_its_dependency_1_has_changed, relSources[project.third][source.config], "src/first"], - [Diagnostics.Building_project_0, sources[project.third][source.config]] + [Diagnostics.Project_0_is_out_of_date_because_output_javascript_and_source_map_if_specified_of_its_dependency_1_has_changed, relSources[project.third][source.config], "src/first"], + [Diagnostics.Updating_output_javascript_and_javascript_source_map_if_specified_of_project_0, sources[project.third][source.config]], + ...>(!withoutBuildInfo ? + [ + [Diagnostics.Updating_unchanged_output_timestamps_of_project_0, sources[project.third][source.config]] + ] : + [ + [Diagnostics.Cannot_update_output_javascript_and_javascript_source_map_if_specified_of_project_0_because_there_was_error_reading_file_1, sources[project.third][source.config], relOutputFiles[project.third][ext.buildinfo]], + [Diagnostics.Building_project_0, sources[project.third][source.config]] + ]) ] ); }