Enables passing --declaration, --emitDeclarationOnly, --declarationMap, --soureMap and --inlineSourceMap to tsc --build (#51241)

* Add different tests for baselining before behavior

* Fix assert for first project output

* Add ability to pass declaration, declarationMap, emitDeclarationOnly, sourceMap and inlineSourceMap on commandline of --build

* Store these options in --out scenario

* Store buildInfo program for --out even if not composite

* Changes to make these commanline options work

* Do not include sourceMapUrl text in the sourceFile version

* Emit complete program in --out scenario as well since we need to determine uptodate ness

* Copy js or dts bundle from old build if emitting only js or dts files

* Dont emit dts if we want to emit only js files even though options suggest emitting d.ts files as well

* Adding comments and refactoring some of the code
This commit is contained in:
Sheetal Nandi
2022-11-07 10:53:22 -08:00
committed by GitHub
parent 896b922871
commit eac566b8c3
336 changed files with 38579 additions and 2748 deletions

View File

@@ -20,6 +20,9 @@ namespace ts {
export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
/** Signature (Hash of d.ts emitted), is string if it was emitted using same d.ts.map option as what compilerOptions indicate, otherwise tuple of string */
export type EmitSignature = string | [signature: string];
export interface ReusableBuilderProgramState extends BuilderState {
/**
* Cache of bind and check diagnostics for files with their Path being the key
@@ -40,15 +43,11 @@ namespace ts {
/**
* Files pending to be emitted
*/
affectedFilesPendingEmit?: readonly Path[] | undefined;
affectedFilesPendingEmit?: ReadonlyESMap<Path, BuilderFileEmit>;
/**
* Files pending to be emitted kind.
* emitKind pending for a program with --out
*/
affectedFilesPendingEmitKind?: ESMap<Path, BuilderFileEmit> | undefined;
/**
* Current index to retrieve pending affected file
*/
affectedFilesPendingEmitIndex?: number | undefined;
programEmitPending?: BuilderFileEmit;
/*
* true if semantic diagnostics are ReusableDiagnostic instead of Diagnostic
*/
@@ -56,20 +55,32 @@ namespace ts {
/**
* Hash of d.ts emitted for the file, use to track when emit of d.ts changes
*/
emitSignatures?: ESMap<Path, string>;
emitSignatures?: ESMap<Path, EmitSignature>;
/**
* Hash of d.ts emit with --out
*/
outSignature?: string;
outSignature?: EmitSignature;
/**
* Name of the file whose dts was the latest to change
*/
latestChangedDtsFile: string | undefined;
/**
* Bundle information either from oldState or current one so it can be used to complete the information in buildInfo when emitting only js or dts files
*/
bundle?: BundleBuildInfo;
}
export const enum BuilderFileEmit {
DtsOnly,
Full
None = 0,
Js = 1 << 0, // emit js file
JsMap = 1 << 1, // emit js.map file
JsInlineMap = 1 << 2, // emit inline source map in js file
Dts = 1 << 3, // emit d.ts file
DtsMap = 1 << 4, // emit d.ts.map file
AllJs = Js | JsMap | JsInlineMap,
AllDts = Dts | DtsMap,
All = AllJs | AllDts,
}
/**
@@ -116,7 +127,7 @@ namespace ts {
/**
* Files pending to be emitted
*/
affectedFilesPendingEmit: Path[] | undefined;
affectedFilesPendingEmit?: ESMap<Path, BuilderFileEmit>;
/**
* true if build info is emitted
*/
@@ -125,26 +136,46 @@ namespace ts {
* Already seen emitted files
*/
seenEmittedFiles: ESMap<Path, BuilderFileEmit> | undefined;
/**
* true if program has been emitted
*/
programEmitComplete?: true;
/** Stores list of files that change signature during emit - test only */
filesChangingSignature?: Set<Path>;
}
export type SavedBuildProgramEmitState = Pick<BuilderProgramState,
"affectedFilesPendingEmit" |
"affectedFilesPendingEmitIndex" |
"affectedFilesPendingEmitKind" |
"seenEmittedFiles" |
"programEmitComplete" |
"programEmitPending" |
"emitSignatures" |
"outSignature" |
"latestChangedDtsFile" |
"hasChangedEmitSignature"
> & { changedFilesSet: BuilderProgramState["changedFilesSet"] | undefined };
/** Get flags determining what all needs to be emitted */
export function getBuilderFileEmit(options: CompilerOptions) {
let result = BuilderFileEmit.Js;
if (options.sourceMap) result = result | BuilderFileEmit.JsMap;
if (options.inlineSourceMap) result = result | BuilderFileEmit.JsInlineMap;
if (getEmitDeclarations(options)) result = result | BuilderFileEmit.Dts;
if (options.declarationMap) result = result | BuilderFileEmit.DtsMap;
if (options.emitDeclarationOnly) result = result & BuilderFileEmit.AllDts;
return result;
}
/** Determing what all is pending to be emitted based on previous options or previous file emit flags */
export function getPendingEmitKind(optionsOrEmitKind: CompilerOptions | BuilderFileEmit, oldOptionsOrEmitKind: CompilerOptions | BuilderFileEmit | undefined): BuilderFileEmit {
const oldEmitKind = oldOptionsOrEmitKind && (isNumber(oldOptionsOrEmitKind) ? oldOptionsOrEmitKind : getBuilderFileEmit(oldOptionsOrEmitKind));
const emitKind = isNumber(optionsOrEmitKind) ? optionsOrEmitKind : getBuilderFileEmit(optionsOrEmitKind);
if (oldEmitKind === emitKind) return BuilderFileEmit.None;
if (!oldEmitKind || !emitKind) return emitKind;
const diff = oldEmitKind ^ emitKind;
let result = BuilderFileEmit.None;
// If there is diff in Js emit, pending emit is js emit flags
if (diff & BuilderFileEmit.AllJs) result = emitKind & BuilderFileEmit.AllJs;
// If there is diff in Dts emit, pending emit is dts emit flags
if (diff & BuilderFileEmit.AllDts) result = result | (emitKind & BuilderFileEmit.AllDts);
return result;
}
function hasSameKeys(map1: ReadonlyCollection<string> | undefined, map2: ReadonlyCollection<string> | undefined): boolean {
// Has same size and every key is present in both maps
return map1 === map2 || map1 !== undefined && map2 !== undefined && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key));
@@ -164,7 +195,7 @@ namespace ts {
state.semanticDiagnosticsPerFile = new Map();
}
else if (compilerOptions.composite && oldState?.outSignature && outFilePath === outFile(oldState?.compilerOptions)) {
state.outSignature = oldState?.outSignature;
state.outSignature = oldState.outSignature && getEmitSignatureFromOldSignature(compilerOptions, oldState.compilerOptions, oldState.outSignature);
}
state.changedFilesSet = new Set();
state.latestChangedDtsFile = compilerOptions.composite ? oldState?.latestChangedDtsFile : undefined;
@@ -186,12 +217,15 @@ namespace ts {
if (useOldState) {
// Copy old state's changed files set
oldState!.changedFilesSet?.forEach(value => state.changedFilesSet.add(value));
if (!outFilePath && oldState!.affectedFilesPendingEmit) {
state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit.slice();
state.affectedFilesPendingEmitKind = oldState!.affectedFilesPendingEmitKind && new Map(oldState!.affectedFilesPendingEmitKind);
state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex;
if (!outFilePath && oldState!.affectedFilesPendingEmit?.size) {
state.affectedFilesPendingEmit = new Map(oldState!.affectedFilesPendingEmit);
state.seenAffectedFiles = new Set();
}
state.programEmitPending = oldState!.programEmitPending;
}
else {
// We arent using old state, so atleast emit buildInfo with current information
state.buildInfoEmitPending = true;
}
// Update changed files and copy semantic diagnostics if we can
@@ -216,7 +250,7 @@ namespace ts {
// Referenced file was deleted in the new program
newReferences && forEachKey(newReferences, path => !state.fileInfos.has(path) && oldState!.fileInfos.has(path))) {
// Register file as changed file and do not copy semantic diagnostics, since all changed files need to be re-evaluated
state.changedFilesSet.add(sourceFilePath);
addFileToChangeSet(state, sourceFilePath);
}
else if (canCopySemanticDiagnostics) {
const sourceFile = newProgram.getSourceFileByPath(sourceFilePath)!;
@@ -236,26 +270,75 @@ namespace ts {
}
if (canCopyEmitSignatures) {
const oldEmitSignature = oldState.emitSignatures.get(sourceFilePath);
if (oldEmitSignature) (state.emitSignatures ||= new Map()).set(sourceFilePath, oldEmitSignature);
if (oldEmitSignature) {
(state.emitSignatures ??= new Map()).set(sourceFilePath, getEmitSignatureFromOldSignature(compilerOptions, oldState.compilerOptions, oldEmitSignature));
}
}
});
// If the global file is removed, add all files as changed
if (useOldState && forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => info.affectsGlobalScope && !state.fileInfos.has(sourceFilePath))) {
if (useOldState && forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => (outFilePath || info.affectsGlobalScope) && !state.fileInfos.has(sourceFilePath))) {
BuilderState.getAllFilesExcludingDefaultLibraryFile(state, newProgram, /*firstSourceFile*/ undefined)
.forEach(file => state.changedFilesSet.add(file.resolvedPath));
.forEach(file => addFileToChangeSet(state, file.resolvedPath));
}
else if (oldCompilerOptions && !outFilePath && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) {
// Add all files to affectedFilesPendingEmit since emit changed
newProgram.getSourceFiles().forEach(f => addToAffectedFilesPendingEmit(state, f.resolvedPath, BuilderFileEmit.Full));
Debug.assert(!state.seenAffectedFiles || !state.seenAffectedFiles.size);
state.seenAffectedFiles = state.seenAffectedFiles || new Set();
else if (oldCompilerOptions) {
// If options affect emit, then we need to do complete emit per compiler options
// otherwise only the js or dts that needs to emitted because its different from previously emitted options
const pendingEmitKind = compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions) ?
getBuilderFileEmit(compilerOptions) :
getPendingEmitKind(compilerOptions, oldCompilerOptions);
if (pendingEmitKind !== BuilderFileEmit.None) {
if (!outFilePath) {
// Add all files to affectedFilesPendingEmit since emit changed
newProgram.getSourceFiles().forEach(f => {
// Add to affectedFilesPending emit only if not changed since any changed file will do full emit
if (!state.changedFilesSet.has(f.resolvedPath)) {
addToAffectedFilesPendingEmit(
state,
f.resolvedPath,
pendingEmitKind
);
}
});
Debug.assert(!state.seenAffectedFiles || !state.seenAffectedFiles.size);
state.seenAffectedFiles = state.seenAffectedFiles || new Set();
state.buildInfoEmitPending = true;
}
else {
state.programEmitPending = state.programEmitPending ?
state.programEmitPending | pendingEmitKind :
pendingEmitKind;
}
}
}
if (outFilePath && !state.changedFilesSet.size) {
// Copy the bundle information from old state so we can patch it later if we are doing partial emit
if (useOldState) state.bundle = oldState!.bundle;
// If this program has prepend references, always emit since we wont know if files on disk are correct unless we check file hash for correctness
if (some(newProgram.getProjectReferences(), ref => !!ref.prepend)) state.programEmitPending = getBuilderFileEmit(compilerOptions);
}
// Since old states change files set is copied, any additional change means we would need to emit build info
state.buildInfoEmitPending = !useOldState || state.changedFilesSet.size !== (oldState!.changedFilesSet?.size || 0);
return state;
}
function addFileToChangeSet(state: BuilderProgramState, path: Path) {
state.changedFilesSet.add(path);
state.buildInfoEmitPending = true;
// Setting this to undefined as changed files means full emit so no need to track emit explicitly
state.programEmitPending = undefined;
}
/**
* Covert to Emit signature based on oldOptions and EmitSignature format
* If d.ts map options differ then swap the format, otherwise use as is
*/
function getEmitSignatureFromOldSignature(options: CompilerOptions, oldOptions: CompilerOptions, oldEmitSignature: EmitSignature): EmitSignature {
return !!options.declarationMap === !!oldOptions.declarationMap ?
// Use same format of signature
oldEmitSignature :
// Convert to different format
isString(oldEmitSignature) ? [oldEmitSignature] : oldEmitSignature[0];
}
function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newProgram: Program, getCanonicalFileName: GetCanonicalFileName): readonly Diagnostic[] {
if (!diagnostics.length) return emptyArray;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory()));
@@ -300,11 +383,9 @@ namespace ts {
// Only in --out changeFileSet is kept around till emit
Debug.assert(!state.changedFilesSet.size || outFilePath);
return {
affectedFilesPendingEmit: state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice(),
affectedFilesPendingEmitKind: state.affectedFilesPendingEmitKind && new Map(state.affectedFilesPendingEmitKind),
affectedFilesPendingEmitIndex: state.affectedFilesPendingEmitIndex,
affectedFilesPendingEmit: state.affectedFilesPendingEmit && new Map(state.affectedFilesPendingEmit),
seenEmittedFiles: state.seenEmittedFiles && new Map(state.seenEmittedFiles),
programEmitComplete: state.programEmitComplete,
programEmitPending: state.programEmitPending,
emitSignatures: state.emitSignatures && new Map(state.emitSignatures),
outSignature: state.outSignature,
latestChangedDtsFile: state.latestChangedDtsFile,
@@ -315,10 +396,8 @@ namespace ts {
function restoreBuilderProgramEmitState(state: BuilderProgramState, savedEmitState: SavedBuildProgramEmitState) {
state.affectedFilesPendingEmit = savedEmitState.affectedFilesPendingEmit;
state.affectedFilesPendingEmitKind = savedEmitState.affectedFilesPendingEmitKind;
state.affectedFilesPendingEmitIndex = savedEmitState.affectedFilesPendingEmitIndex;
state.seenEmittedFiles = savedEmitState.seenEmittedFiles;
state.programEmitComplete = savedEmitState.programEmitComplete;
state.programEmitPending = savedEmitState.programEmitPending;
state.emitSignatures = savedEmitState.emitSignatures;
state.outSignature = savedEmitState.outSignature;
state.latestChangedDtsFile = savedEmitState.latestChangedDtsFile;
@@ -356,6 +435,7 @@ namespace ts {
if (!seenAffectedFiles.has(affectedFile.resolvedPath)) {
// Set the next affected file as seen and remove the cached semantic diagnostics
state.affectedFilesIndex = affectedFilesIndex;
addToAffectedFilesPendingEmit(state, affectedFile.resolvedPath, getBuilderFileEmit(state.compilerOptions));
handleDtsMayChangeOfAffectedFile(
state,
affectedFile,
@@ -409,34 +489,33 @@ namespace ts {
}
}
function clearAffectedFilesPendingEmit(state: BuilderProgramState) {
state.affectedFilesPendingEmit = undefined;
state.affectedFilesPendingEmitKind = undefined;
state.affectedFilesPendingEmitIndex = undefined;
function clearAffectedFilesPendingEmit(state: BuilderProgramState, emitOnlyDtsFiles: boolean | undefined) {
if (!state.affectedFilesPendingEmit?.size) return;
if (!emitOnlyDtsFiles) return state.affectedFilesPendingEmit = undefined;
state.affectedFilesPendingEmit.forEach((emitKind, path) => {
// Mark the files as pending only if they are pending on js files, remove the dts emit pending flag
const pending = emitKind & BuilderFileEmit.AllJs;
if (!pending) state.affectedFilesPendingEmit!.delete(path);
else state.affectedFilesPendingEmit!.set(path, pending);
});
}
/**
* Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
*/
function getNextAffectedFilePendingEmit(state: BuilderProgramState) {
const { affectedFilesPendingEmit } = state;
if (affectedFilesPendingEmit) {
const seenEmittedFiles = (state.seenEmittedFiles || (state.seenEmittedFiles = new Map()));
for (let i = state.affectedFilesPendingEmitIndex!; i < affectedFilesPendingEmit.length; i++) {
const affectedFile = Debug.checkDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[i]);
if (affectedFile && sourceFileMayBeEmitted(affectedFile, state.program!)) {
const seenKind = seenEmittedFiles.get(affectedFile.resolvedPath);
const emitKind = Debug.checkDefined(Debug.checkDefined(state.affectedFilesPendingEmitKind).get(affectedFile.resolvedPath));
if (seenKind === undefined || seenKind < emitKind) {
// emit this file
state.affectedFilesPendingEmitIndex = i;
return { affectedFile, emitKind };
}
}
function getNextAffectedFilePendingEmit(state: BuilderProgramState, emitOnlyDtsFiles: boolean | undefined) {
if (!state.affectedFilesPendingEmit?.size) return undefined;
return forEachEntry(state.affectedFilesPendingEmit, (emitKind, path) => {
const affectedFile = state.program!.getSourceFileByPath(path);
if (!affectedFile || !sourceFileMayBeEmitted(affectedFile, state.program!)) {
state.affectedFilesPendingEmit!.delete(path);
return undefined;
}
clearAffectedFilesPendingEmit(state);
}
return undefined;
const seenKind = state.seenEmittedFiles?.get(affectedFile.resolvedPath);
let pendingKind = getPendingEmitKind(emitKind, seenKind);
if (emitOnlyDtsFiles) pendingKind = pendingKind & BuilderFileEmit.AllDts;
if (pendingKind) return { affectedFile, emitKind: pendingKind };
});
}
function removeDiagnosticsOfLibraryFiles(state: BuilderProgramState) {
@@ -527,7 +606,7 @@ namespace ts {
);
// If not dts emit, nothing more to do
if (getEmitDeclarations(state.compilerOptions)) {
addToAffectedFilesPendingEmit(state, path, BuilderFileEmit.DtsOnly);
addToAffectedFilesPendingEmit(state, path, state.compilerOptions.declarationMap ? BuilderFileEmit.AllDts : BuilderFileEmit.Dts);
}
}
}
@@ -677,63 +756,6 @@ namespace ts {
return undefined;
}
/**
* This is called after completing operation on the next affected file.
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
*/
function doneWithAffectedFile(
state: BuilderProgramState,
affected: SourceFile | Program,
emitKind?: BuilderFileEmit,
isPendingEmit?: boolean,
isBuildInfoEmit?: boolean
) {
if (isBuildInfoEmit) {
state.buildInfoEmitPending = false;
}
else if (affected === state.program) {
state.changedFilesSet.clear();
state.programEmitComplete = true;
}
else {
state.seenAffectedFiles!.add((affected as SourceFile).resolvedPath);
// Change in changeSet/affectedFilesPendingEmit, buildInfo needs to be emitted
state.buildInfoEmitPending = true;
if (emitKind !== undefined) {
(state.seenEmittedFiles || (state.seenEmittedFiles = new Map())).set((affected as SourceFile).resolvedPath, emitKind);
}
if (isPendingEmit) {
state.affectedFilesPendingEmitIndex!++;
}
else {
state.affectedFilesIndex!++;
}
}
}
/**
* Returns the result with affected file
*/
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult<T> {
doneWithAffectedFile(state, affected);
return { result, affected };
}
/**
* Returns the result with affected file
*/
function toAffectedFileEmitResult(
state: BuilderProgramState,
result: EmitResult,
affected: SourceFile | Program,
emitKind: BuilderFileEmit,
isPendingEmit?: boolean,
isBuildInfoEmit?: boolean
): AffectedFileResult<EmitResult> {
doneWithAffectedFile(state, affected, emitKind, isPendingEmit, isBuildInfoEmit);
return { result, affected };
}
/**
* Gets semantic diagnostics for the file which are
* bindAndCheckDiagnostics (from cache) and program diagnostics
@@ -770,9 +792,14 @@ namespace ts {
export type ProgramBuildInfoFileId = number & { __programBuildInfoFileIdBrand: any };
export type ProgramBuildInfoFileIdListId = number & { __programBuildInfoFileIdListIdBrand: any };
export type ProgramBuildInfoDiagnostic = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId, diagnostics: readonly ReusableDiagnostic[]];
export type ProgramBuilderInfoFilePendingEmit = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId];
/**
* fileId if pending emit is same as what compilerOptions suggest
* [fileId] if pending emit is only dts file emit
* [fileId, emitKind] if any other type emit is pending
*/
export type ProgramBuilderInfoFilePendingEmit = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId] | [fileId: ProgramBuildInfoFileId, emitKind: BuilderFileEmit];
export type ProgramBuildInfoReferencedMap = [fileId: ProgramBuildInfoFileId, fileIdListId: ProgramBuildInfoFileIdListId][];
export type ProgramBuildInfoBuilderStateFileInfo = Omit<BuilderState.FileInfo, "signature"> & {
export type ProgramMultiFileEmitBuildInfoBuilderStateFileInfo = Omit<BuilderState.FileInfo, "signature"> & {
/**
* Signature is
* - undefined if FileInfo.version === FileInfo.signature
@@ -785,14 +812,14 @@ namespace ts {
* [fileId, signature] if different from file's signature
* fileId if file wasnt emitted
*/
export type ProgramBuildInfoEmitSignature = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId, signature: string];
export type ProgramBuildInfoEmitSignature = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId, signature: EmitSignature | []];
/**
* ProgramBuildInfoFileInfo is string if FileInfo.version === FileInfo.signature && !FileInfo.affectsGlobalScope otherwise encoded FileInfo
* ProgramMultiFileEmitBuildInfoFileInfo is string if FileInfo.version === FileInfo.signature && !FileInfo.affectsGlobalScope otherwise encoded FileInfo
*/
export type ProgramBuildInfoFileInfo = string | ProgramBuildInfoBuilderStateFileInfo;
export type ProgramMultiFileEmitBuildInfoFileInfo = string | ProgramMultiFileEmitBuildInfoBuilderStateFileInfo;
export interface ProgramMultiFileEmitBuildInfo {
fileNames: readonly string[];
fileInfos: readonly ProgramBuildInfoFileInfo[];
fileInfos: readonly ProgramMultiFileEmitBuildInfoFileInfo[];
options: CompilerOptions | undefined;
fileIdsList: readonly (readonly ProgramBuildInfoFileId[])[] | undefined;
referencedMap: ProgramBuildInfoReferencedMap | undefined;
@@ -804,13 +831,22 @@ namespace ts {
// Because this is only output file in the program, we dont need fileId to deduplicate name
latestChangedDtsFile?: string | undefined;
}
/**
* ProgramBundleEmitBuildInfoFileInfo is string if !FileInfo.impliedFormat otherwise encoded FileInfo
*/
export type ProgramBundleEmitBuildInfoFileInfo = string | BuilderState.FileInfo;
/**
* false if it is the emit corresponding to compilerOptions
* value otherwise
*/
export type ProgramBuildInfoBundlePendingEmit = BuilderFileEmit | false;
export interface ProgramBundleEmitBuildInfo {
fileNames: readonly string[];
fileInfos: readonly string[];
fileInfos: readonly ProgramBundleEmitBuildInfoFileInfo[];
options: CompilerOptions | undefined;
outSignature: string | undefined;
outSignature: EmitSignature | undefined;
latestChangedDtsFile: string | undefined;
pendingEmit: ProgramBuildInfoBundlePendingEmit | undefined;
}
export type ProgramBuildInfo = ProgramMultiFileEmitBuildInfo | ProgramBundleEmitBuildInfo;
@@ -822,38 +858,50 @@ namespace ts {
/**
* Gets the program information to be emitted in buildInfo so that we can use it to create new program
*/
function getProgramBuildInfo(state: BuilderProgramState, getCanonicalFileName: GetCanonicalFileName): ProgramBuildInfo | undefined {
const outFilePath = outFile(state.compilerOptions);
if (outFilePath && !state.compilerOptions.composite) return;
function getBuildInfo(state: BuilderProgramState, getCanonicalFileName: GetCanonicalFileName, bundle: BundleBuildInfo | undefined): BuildInfo {
const currentDirectory = Debug.checkDefined(state.program).getCurrentDirectory();
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory));
// Convert the file name to Path here if we set the fileName instead to optimize multiple d.ts file emits and having to compute Canonical path
const latestChangedDtsFile = state.latestChangedDtsFile ? relativeToBuildInfoEnsuringAbsolutePath(state.latestChangedDtsFile) : undefined;
if (outFilePath) {
const fileNames: string[] = [];
const fileInfos: string[] = [];
state.program!.getRootFileNames().forEach(f => {
const sourceFile = state.program!.getSourceFile(f);
if (!sourceFile) return;
fileNames.push(relativeToBuildInfo(sourceFile.resolvedPath));
fileInfos.push(sourceFile.version);
});
const result: ProgramBundleEmitBuildInfo = {
fileNames,
fileInfos,
options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions, "affectsBundleEmitBuildInfo"),
outSignature: state.outSignature,
latestChangedDtsFile,
};
return result;
}
const fileNames: string[] = [];
const fileNameToFileId = new Map<string, ProgramBuildInfoFileId>();
if (outFile(state.compilerOptions)) {
// Copy all fileInfo, version and impliedFormat
// Affects global scope and signature doesnt matter because with --out they arent calculated or needed to determine upto date ness
const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramBundleEmitBuildInfoFileInfo => {
// Ensure fileId
toFileId(key);
return value.impliedFormat ?
{ version: value.version, impliedFormat: value.impliedFormat, signature: undefined, affectsGlobalScope: undefined } :
value.version;
});
const program: ProgramBundleEmitBuildInfo = {
fileNames,
fileInfos,
options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions),
outSignature: state.outSignature,
latestChangedDtsFile,
pendingEmit: !state.programEmitPending ?
undefined : // Pending is undefined or None is encoded as undefined
state.programEmitPending === getBuilderFileEmit(state.compilerOptions) ?
false : // Pending emit is same as deteremined by compilerOptions
state.programEmitPending, // Actual value
};
// Complete the bundle information if we are doing partial emit (only js or only dts)
const { js, dts, commonSourceDirectory, sourceFiles } = bundle!;
state.bundle = bundle = {
commonSourceDirectory,
sourceFiles,
js: js || (!state.compilerOptions.emitDeclarationOnly ? state.bundle?.js : undefined),
dts: dts || (getEmitDeclarations(state.compilerOptions) ? state.bundle?.dts : undefined),
};
return createBuildInfo(program, bundle);
}
let fileIdsList: (readonly ProgramBuildInfoFileId[])[] | undefined;
let fileNamesToFileIdListId: ESMap<string, ProgramBuildInfoFileIdListId> | undefined;
let emitSignatures: ProgramBuildInfoEmitSignature[] | undefined;
const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramBuildInfoFileInfo => {
const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramMultiFileEmitBuildInfoFileInfo => {
// Ensure fileId
const fileId = toFileId(key);
Debug.assert(fileNames[fileId - 1] === relativeToBuildInfo(key));
@@ -864,7 +912,10 @@ namespace ts {
if (!isJsonSourceFile(file) && sourceFileMayBeEmitted(file, state.program!)) {
const emitSignature = state.emitSignatures?.get(key);
if (emitSignature !== actualSignature) {
(emitSignatures ||= []).push(emitSignature === undefined ? fileId : [fileId, emitSignature]);
(emitSignatures ||= []).push(emitSignature === undefined ?
fileId : // There is no emit, encode as false
// fileId, signature: emptyArray if signature only differs in dtsMap option than our own compilerOptions otherwise EmitSignature
[fileId, !isString(emitSignature) && emitSignature[0] === actualSignature ? emptyArray as [] : emitSignature]);
}
}
}
@@ -919,15 +970,20 @@ namespace ts {
}
let affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
if (state.affectedFilesPendingEmit) {
if (state.affectedFilesPendingEmit?.size) {
const fullEmitForOptions = getBuilderFileEmit(state.compilerOptions);
const seenFiles = new Set<Path>();
for (const path of state.affectedFilesPendingEmit.slice(state.affectedFilesPendingEmitIndex).sort(compareStringsCaseSensitive)) {
for (const path of arrayFrom(state.affectedFilesPendingEmit.keys()).sort(compareStringsCaseSensitive)) {
if (tryAddToSet(seenFiles, path)) {
const file = state.program!.getSourceFileByPath(path)!;
if (!sourceFileMayBeEmitted(file, state.program!)) continue;
const fileId = toFileId(path), emitKind = state.affectedFilesPendingEmitKind!.get(path)!;
const fileId = toFileId(path), pendingEmit = state.affectedFilesPendingEmit.get(path)!;
(affectedFilesPendingEmit ||= []).push(
emitKind === BuilderFileEmit.Full ? fileId : [fileId]
pendingEmit === fullEmitForOptions ?
fileId : // Pending full emit per options
pendingEmit === BuilderFileEmit.Dts ?
[fileId] : // Pending on Dts only
[fileId, pendingEmit] // Anything else
);
}
}
@@ -940,10 +996,10 @@ namespace ts {
}
}
const result: ProgramMultiFileEmitBuildInfo = {
const program: ProgramMultiFileEmitBuildInfo = {
fileNames,
fileInfos,
options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions, "affectsMultiFileEmitBuildInfo"),
options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions),
fileIdsList,
referencedMap,
exportedModulesMap,
@@ -953,7 +1009,7 @@ namespace ts {
emitSignatures,
latestChangedDtsFile,
};
return result;
return createBuildInfo(program, bundle);
function relativeToBuildInfoEnsuringAbsolutePath(path: string) {
return relativeToBuildInfo(getNormalizedAbsolutePath(path, currentDirectory));
@@ -986,12 +1042,12 @@ namespace ts {
/**
* @param optionKey key of CommandLineOption to use to determine if the option should be serialized in tsbuildinfo
*/
function convertToProgramBuildInfoCompilerOptions(options: CompilerOptions, optionKey: "affectsBundleEmitBuildInfo" | "affectsMultiFileEmitBuildInfo") {
function convertToProgramBuildInfoCompilerOptions(options: CompilerOptions) {
let result: CompilerOptions | undefined;
const { optionsNameMap } = getOptionsNameMap();
for (const name of getOwnKeys(options).sort(compareStringsCaseSensitive)) {
const optionInfo = optionsNameMap.get(name.toLowerCase());
if (optionInfo?.[optionKey]) {
if (optionInfo?.affectsBuildInfo) {
(result ||= {})[name] = convertToReusableCompilerOptionValue(
optionInfo,
options[name] as CompilerOptionsValue,
@@ -1149,7 +1205,7 @@ namespace ts {
*/
const computeHash = maybeBind(host, host.createHash);
const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState, host.disableUseFileVersionAsSignature);
newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state, getCanonicalFileName);
newProgram.getBuildInfo = bundle => getBuildInfo(state, getCanonicalFileName, bundle);
// To ensure that we arent storing any references to old program or new program without state
newProgram = undefined!; // TODO: GH#18217
@@ -1197,58 +1253,76 @@ namespace ts {
*/
function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult> {
let affected = getNextAffectedFile(state, cancellationToken, computeHash, getCanonicalFileName, host);
let emitKind = BuilderFileEmit.Full;
let isPendingEmitFile = false;
const programEmitKind = getBuilderFileEmit(state.compilerOptions);
let emitKind: BuilderFileEmit = emitOnlyDtsFiles ?
programEmitKind & BuilderFileEmit.AllDts : programEmitKind;
if (!affected) {
if (!outFile(state.compilerOptions)) {
const pendingAffectedFile = getNextAffectedFilePendingEmit(state);
const pendingAffectedFile = getNextAffectedFilePendingEmit(state, emitOnlyDtsFiles);
if (!pendingAffectedFile) {
if (!state.buildInfoEmitPending) {
return undefined;
}
const affected = Debug.checkDefined(state.program);
return toAffectedFileEmitResult(
state,
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
// Otherwise just affected file
affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken),
affected,
/*emitKind*/ BuilderFileEmit.Full,
/*isPendingEmitFile*/ false,
/*isBuildInfoEmit*/ true
);
// Emit buildinfo if pending
if (!state.buildInfoEmitPending) return undefined;
const affected = state.program!;
const result = affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken);
state.buildInfoEmitPending = false;
return { result, affected };
}
// Emit pending affected file
({ affectedFile: affected, emitKind } = pendingAffectedFile);
isPendingEmitFile = true;
}
else {
const program = Debug.checkDefined(state.program);
if (state.programEmitComplete) return undefined;
affected = program;
// Emit program if it was pending emit
if (!state.programEmitPending) return undefined;
emitKind = state.programEmitPending;
if (emitOnlyDtsFiles) emitKind = emitKind & BuilderFileEmit.AllDts;
if (!emitKind) return undefined;
affected = state.program!;
}
}
return toAffectedFileEmitResult(
state,
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
// Otherwise just affected file
Debug.checkDefined(state.program).emit(
affected === state.program ? undefined : affected as SourceFile,
getEmitDeclarations(state.compilerOptions) ?
getWriteFileCallback(writeFile, customTransformers) :
writeFile || maybeBind(host, host.writeFile),
cancellationToken,
emitOnlyDtsFiles || emitKind === BuilderFileEmit.DtsOnly,
customTransformers
),
affected,
emitKind,
isPendingEmitFile,
// Determine if we can do partial emit
let emitOnly: EmitOnly | undefined;
if (emitKind & BuilderFileEmit.AllJs) emitOnly = EmitOnly.Js;
if (emitKind & BuilderFileEmit.AllDts) emitOnly = emitOnly === undefined ? EmitOnly.Dts : undefined;
if (affected === state.program) {
// Set up programEmit before calling emit so that its set in buildInfo
state.programEmitPending = state.changedFilesSet.size ?
getPendingEmitKind(programEmitKind, emitKind) :
state.programEmitPending ?
getPendingEmitKind(state.programEmitPending, emitKind) :
undefined;
}
// Actual emit
const result = state.program!.emit(
affected === state.program ? undefined : affected as SourceFile,
getWriteFileCallback(writeFile, customTransformers),
cancellationToken,
emitOnly,
customTransformers
);
if (affected !== state.program) {
// update affected files
const affectedSourceFile = affected as SourceFile;
state.seenAffectedFiles!.add(affectedSourceFile.resolvedPath);
if (state.affectedFilesIndex !== undefined) state.affectedFilesIndex++;
// Change in changeSet/affectedFilesPendingEmit, buildInfo needs to be emitted
state.buildInfoEmitPending = true;
// Update the pendingEmit for the file
const existing = state.seenEmittedFiles?.get(affectedSourceFile.resolvedPath) || BuilderFileEmit.None;
(state.seenEmittedFiles ??= new Map()).set(affectedSourceFile.resolvedPath, emitKind | existing);
const existingPending = state.affectedFilesPendingEmit?.get(affectedSourceFile.resolvedPath) || programEmitKind;
const pendingKind = getPendingEmitKind(existingPending, emitKind | existing);
if (pendingKind) (state.affectedFilesPendingEmit ??= new Map()).set(affectedSourceFile.resolvedPath, pendingKind);
else state.affectedFilesPendingEmit?.delete(affectedSourceFile.resolvedPath);
}
else {
// In program clear our changed files since any emit handles all changes
state.changedFilesSet.clear();
}
return { result, affected };
}
function getWriteFileCallback(writeFile: WriteFileCallback | undefined, customTransformers: CustomTransformers | undefined): WriteFileCallback {
function getWriteFileCallback(writeFile: WriteFileCallback | undefined, customTransformers: CustomTransformers | undefined): WriteFileCallback | undefined {
if (!getEmitDeclarations(state.compilerOptions)) return writeFile || maybeBind(host, host.writeFile);
return (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
if (isDeclarationFileName(fileName)) {
if (!outFile(state.compilerOptions)) {
@@ -1290,27 +1364,43 @@ namespace ts {
// and would need their d.ts change time in --build mode
if (state.compilerOptions.composite) {
const filePath = sourceFiles[0].resolvedPath;
const oldSignature = state.emitSignatures?.get(filePath);
emitSignature ??= computeSignature(text, computeHash, data);
// Dont write dts files if they didn't change
if (emitSignature === oldSignature) return;
emitSignature = handleNewSignature(state.emitSignatures?.get(filePath), emitSignature);
if (!emitSignature) return;
(state.emitSignatures ??= new Map()).set(filePath, emitSignature);
state.hasChangedEmitSignature = true;
state.latestChangedDtsFile = fileName;
}
}
else if (state.compilerOptions.composite) {
const newSignature = computeSignature(text, computeHash, data);
// Dont write dts files if they didn't change
if (newSignature === state.outSignature) return;
const newSignature = handleNewSignature(state.outSignature, /*newSignature*/ undefined);
if (!newSignature) return;
state.outSignature = newSignature;
state.hasChangedEmitSignature = true;
state.latestChangedDtsFile = fileName;
}
}
if (writeFile) writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
else if (host.writeFile) host.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
else state.program!.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
/**
* Compare to existing computed signature and store it or handle the changes in d.ts map option from before
* returning undefined means that, we dont need to emit this d.ts file since its contents didnt change
*/
function handleNewSignature(oldSignatureFormat: EmitSignature | undefined, newSignature: string | undefined) {
const oldSignature = !oldSignatureFormat || isString(oldSignatureFormat) ? oldSignatureFormat : oldSignatureFormat[0];
newSignature ??= computeSignature(text, computeHash, data);
// Dont write dts files if they didn't change
if (newSignature === oldSignature) {
// If the signature was encoded as string the dts map options match so nothing to do
if (oldSignatureFormat === oldSignature) return undefined;
// Mark as differsOnlyInMap so that --build can reverse the timestamp so that
// the downstream projects dont detect this as change in d.ts file
else if (data) data.differsOnlyInMap = true;
else data = { differsOnlyInMap: true };
}
else {
state.hasChangedEmitSignature = true;
state.latestChangedDtsFile = fileName;
}
return newSignature;
}
};
}
@@ -1356,23 +1446,13 @@ namespace ts {
};
}
// In non Emit builder, clear affected files pending emit
else if (state.affectedFilesPendingEmitKind?.size) {
Debug.assert(kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram);
// State can clear affected files pending emit if
if (!emitOnlyDtsFiles // If we are doing complete emit, affected files pending emit can be cleared
// If every file pending emit is pending on only dts emit
|| every(state.affectedFilesPendingEmit, (path, index) =>
index < state.affectedFilesPendingEmitIndex! ||
state.affectedFilesPendingEmitKind!.get(path) === BuilderFileEmit.DtsOnly)) {
clearAffectedFilesPendingEmit(state);
}
else {
clearAffectedFilesPendingEmit(state, emitOnlyDtsFiles);
}
}
return Debug.checkDefined(state.program).emit(
targetSourceFile,
getEmitDeclarations(state.compilerOptions) ?
getWriteFileCallback(writeFile, customTransformers) :
writeFile || maybeBind(host, host.writeFile),
getWriteFileCallback(writeFile, customTransformers),
cancellationToken,
emitOnlyDtsFiles,
customTransformers
@@ -1386,37 +1466,27 @@ namespace ts {
function getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<readonly Diagnostic[]> {
while (true) {
const affected = getNextAffectedFile(state, cancellationToken, computeHash, getCanonicalFileName, host);
if (!affected) {
// Done
return undefined;
let result;
if (!affected) return undefined; // Done
else if (affected !== state.program) {
// Get diagnostics for the affected file if its not ignored
const affectedSourceFile = affected as SourceFile;
if (!ignoreSourceFile || !ignoreSourceFile(affectedSourceFile)) {
result = getSemanticDiagnosticsOfFile(state, affectedSourceFile, cancellationToken);
}
state.seenAffectedFiles!.add(affectedSourceFile.resolvedPath);
state.affectedFilesIndex!++;
// Change in changeSet, buildInfo needs to be emitted
state.buildInfoEmitPending = true;
if (!result) continue;
}
else if (affected === state.program) {
else {
// When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified)
return toAffectedFileResult(
state,
state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken),
affected
);
result = state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken);
state.changedFilesSet.clear();
state.programEmitPending = getBuilderFileEmit(state.compilerOptions);
}
// Add file to affected file pending emit to handle for later emit time
// Apart for emit builder do this for tsbuildinfo, do this for non emit builder when noEmit is set as tsbuildinfo is written and reused between emitters
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram || state.compilerOptions.noEmit || state.compilerOptions.noEmitOnError) {
addToAffectedFilesPendingEmit(state, (affected as SourceFile).resolvedPath, BuilderFileEmit.Full);
}
// Get diagnostics for the affected file if its not ignored
if (ignoreSourceFile && ignoreSourceFile(affected as SourceFile)) {
// Get next affected file
doneWithAffectedFile(state, affected);
continue;
}
return toAffectedFileResult(
state,
getSemanticDiagnosticsOfFile(state, affected as SourceFile, cancellationToken),
affected
);
return { result, affected };
}
}
@@ -1456,23 +1526,11 @@ namespace ts {
}
function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) {
if (!state.affectedFilesPendingEmit) state.affectedFilesPendingEmit = [];
if (!state.affectedFilesPendingEmitKind) state.affectedFilesPendingEmitKind = new Map();
const existingKind = state.affectedFilesPendingEmitKind.get(affectedFilePendingEmit);
state.affectedFilesPendingEmit.push(affectedFilePendingEmit);
state.affectedFilesPendingEmitKind.set(affectedFilePendingEmit, existingKind || kind);
// affectedFilesPendingEmitIndex === undefined
// - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
// so start from 0 as array would be affectedFilesPendingEmit
// else, continue to iterate from existing index, the current set is appended to existing files
if (state.affectedFilesPendingEmitIndex === undefined) {
state.affectedFilesPendingEmitIndex = 0;
}
const existingKind = state.affectedFilesPendingEmit?.get(affectedFilePendingEmit) || BuilderFileEmit.None;
(state.affectedFilesPendingEmit ??= new Map()).set(affectedFilePendingEmit, existingKind | kind);
}
export function toBuilderStateFileInfo(fileInfo: ProgramBuildInfoFileInfo): BuilderState.FileInfo {
export function toBuilderStateFileInfoForMultiEmit(fileInfo: ProgramMultiFileEmitBuildInfoFileInfo): BuilderState.FileInfo {
return isString(fileInfo) ?
{ version: fileInfo, signature: fileInfo, affectsGlobalScope: undefined, impliedFormat: undefined } :
isString(fileInfo.signature) ?
@@ -1480,41 +1538,60 @@ namespace ts {
{ version: fileInfo.version, signature: fileInfo.signature === false ? undefined : fileInfo.version, affectsGlobalScope: fileInfo.affectsGlobalScope, impliedFormat: fileInfo.impliedFormat };
}
export function toBuilderFileEmit(value: ProgramBuilderInfoFilePendingEmit): BuilderFileEmit{
return isNumber(value) ? BuilderFileEmit.Full : BuilderFileEmit.DtsOnly;
export function toBuilderFileEmit(value: ProgramBuilderInfoFilePendingEmit, fullEmitForOptions: BuilderFileEmit): BuilderFileEmit{
return isNumber(value) ? fullEmitForOptions : value[1] || BuilderFileEmit.Dts;
}
export function createBuilderProgramUsingProgramBuildInfo(program: ProgramBuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram {
export function toProgramEmitPending(value: ProgramBuildInfoBundlePendingEmit, options: CompilerOptions | undefined): BuilderFileEmit | undefined {
return !value ? getBuilderFileEmit(options || {}) : value;
}
export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram {
const program = buildInfo.program!;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
let state: ReusableBuilderProgramState;
let filePaths: Path[] | undefined;
const filePaths = program.fileNames?.map(toPath);
let filePathsSetList: Set<Path>[] | undefined;
const latestChangedDtsFile = program.latestChangedDtsFile ? toAbsolutePath(program.latestChangedDtsFile) : undefined;
if (isProgramBundleEmitBuildInfo(program)) {
const fileInfos = new Map<Path, BuilderState.FileInfo>();
program.fileInfos.forEach((fileInfo, index) => {
const path = toFilePath(index + 1 as ProgramBuildInfoFileId);
fileInfos.set(path, isString(fileInfo) ? { version: fileInfo, signature: undefined, affectsGlobalScope: undefined, impliedFormat: undefined } : fileInfo);
});
state = {
fileInfos: new Map(),
fileInfos,
compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {},
latestChangedDtsFile,
outSignature: program.outSignature,
programEmitPending: program.pendingEmit === undefined ? undefined : toProgramEmitPending(program.pendingEmit, program.options),
bundle: buildInfo.bundle,
};
}
else {
filePaths = program.fileNames?.map(toPath);
filePathsSetList = program.fileIdsList?.map(fileIds => new Set(fileIds.map(toFilePath)));
const fileInfos = new Map<Path, BuilderState.FileInfo>();
const emitSignatures = program.options?.composite && !outFile(program.options) ? new Map<Path, string>() : undefined;
const emitSignatures = program.options?.composite && !outFile(program.options) ? new Map<Path, EmitSignature>() : undefined;
program.fileInfos.forEach((fileInfo, index) => {
const path = toFilePath(index + 1 as ProgramBuildInfoFileId);
const stateFileInfo = toBuilderStateFileInfo(fileInfo);
const stateFileInfo = toBuilderStateFileInfoForMultiEmit(fileInfo);
fileInfos.set(path, stateFileInfo);
if (emitSignatures && stateFileInfo.signature) emitSignatures.set(path, stateFileInfo.signature);
});
program.emitSignatures?.forEach(value => {
if (isNumber(value)) emitSignatures!.delete(toFilePath(value));
else emitSignatures!.set(toFilePath(value[0]), value[1]);
else {
const key = toFilePath(value[0]);
emitSignatures!.set(key, !isString(value[1]) && !value[1].length ?
// File signature is emit signature but differs in map
[emitSignatures!.get(key)! as string] :
value[1]
);
}
});
const fullEmitForOptions = program.affectedFilesPendingEmit ? getBuilderFileEmit(program.options || {}) : undefined;
state = {
fileInfos,
compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {},
@@ -1522,9 +1599,7 @@ namespace ts {
exportedModulesMap: toManyToManyPathMap(program.exportedModulesMap),
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toFilePath(isNumber(value) ? value : value[0]), value => isNumber(value) ? emptyArray : value[1]),
hasReusableDiagnostic: true,
affectedFilesPendingEmit: map(program.affectedFilesPendingEmit, value => toFilePath(isNumber(value) ? value : value[0])),
affectedFilesPendingEmitKind: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toFilePath(isNumber(value) ? value : value[0]), toBuilderFileEmit),
affectedFilesPendingEmitIndex: program.affectedFilesPendingEmit && 0,
affectedFilesPendingEmit: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toFilePath(isNumber(value) ? value : value[0]), value => toBuilderFileEmit(value, fullEmitForOptions!)),
changedFilesSet: new Set(map(program.changeFileSet, toFilePath)),
latestChangedDtsFile,
emitSignatures: emitSignatures?.size ? emitSignatures : undefined,
@@ -1566,7 +1641,7 @@ namespace ts {
}
function toFilePath(fileId: ProgramBuildInfoFileId) {
return filePaths![fileId - 1];
return filePaths[fileId - 1];
}
function toFilePathsSet(fileIdsListId: ProgramBuildInfoFileIdListId) {
@@ -1596,7 +1671,7 @@ namespace ts {
const fileInfos = new Map<Path, string>();
program.fileInfos.forEach((fileInfo, index) => {
const path = toPath(program.fileNames[index], buildInfoDirectory, getCanonicalFileName);
const version = isString(fileInfo) ? fileInfo : (fileInfo as ProgramBuildInfoBuilderStateFileInfo).version; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
const version = isString(fileInfo) ? fileInfo : fileInfo.version;
fileInfos.set(path, version);
});
return fileInfos;

View File

@@ -265,7 +265,8 @@ namespace ts {
export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly<BuilderState>, disableUseFileVersionAsSignature?: boolean): BuilderState {
const fileInfos = new Map<Path, FileInfo>();
const options = newProgram.getCompilerOptions();
const referencedMap = options.module !== ModuleKind.None && !outFile(options) ?
const isOutFile = outFile(options);
const referencedMap = options.module !== ModuleKind.None && !isOutFile ?
createManyToManyPathMap() : undefined;
const exportedModulesMap = referencedMap ? createManyToManyPathMap() : undefined;
const useOldState = canReuseOldState(referencedMap, oldState);
@@ -299,7 +300,8 @@ namespace ts {
fileInfos.set(sourceFile.resolvedPath, {
version,
signature,
affectsGlobalScope: isFileAffectingGlobalScope(sourceFile) || undefined,
// No need to calculate affectsGlobalScope with --out since its not used at all
affectsGlobalScope: !isOutFile ? isFileAffectingGlobalScope(sourceFile) || undefined : undefined,
impliedFormat: sourceFile.impliedNodeFormat
});
}

View File

@@ -9,6 +9,6 @@ namespace ts {
name: string;
writeByteOrderMark: boolean;
text: string;
/* @internal */ buildInfo?: BuildInfo
/* @internal */ data?: WriteFileCallbackData;
}
}

View File

@@ -300,12 +300,65 @@ namespace ts {
transpileOptionValue: undefined,
defaultValueDescription: Diagnostics.false_unless_composite_is_set
},
{
name: "declaration",
shortName: "d",
type: "boolean",
// Not setting affectsEmit because we calculate this flag might not affect full emit
affectsBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
transpileOptionValue: undefined,
description: Diagnostics.Generate_d_ts_files_from_TypeScript_and_JavaScript_files_in_your_project,
defaultValueDescription: Diagnostics.false_unless_composite_is_set,
},
{
name: "declarationMap",
type: "boolean",
// Not setting affectsEmit because we calculate this flag might not affect full emit
affectsBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
transpileOptionValue: undefined,
defaultValueDescription: false,
description: Diagnostics.Create_sourcemaps_for_d_ts_files
},
{
name: "emitDeclarationOnly",
type: "boolean",
// Not setting affectsEmit because we calculate this flag might not affect full emit
affectsBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
description: Diagnostics.Only_output_d_ts_files_and_not_JavaScript_files,
transpileOptionValue: undefined,
defaultValueDescription: false,
},
{
name: "sourceMap",
type: "boolean",
// Not setting affectsEmit because we calculate this flag might not affect full emit
affectsBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
defaultValueDescription: false,
description: Diagnostics.Create_source_map_files_for_emitted_JavaScript_files,
},
{
name: "inlineSourceMap",
type: "boolean",
// Not setting affectsEmit because we calculate this flag might not affect full emit
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Include_sourcemap_files_inside_the_emitted_JavaScript,
defaultValueDescription: false,
},
{
name: "assumeChangesOnlyAffectDirectDependencies",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Watch_and_Build_Modes,
description: Diagnostics.Have_recompiles_in_projects_that_use_incremental_and_watch_mode_assume_that_changes_within_a_file_will_only_affect_files_directly_depending_on_it,
defaultValueDescription: false,
@@ -341,7 +394,7 @@ namespace ts {
affectsSourceFile: true,
affectsModuleResolution: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
paramType: Diagnostics.VERSION,
showInSimplifiedHelpView: true,
category: Diagnostics.Language_and_Environment,
@@ -369,7 +422,7 @@ namespace ts {
})),
affectsModuleResolution: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
category: Diagnostics.Modules,
@@ -480,7 +533,7 @@ namespace ts {
type: jsxOptionMap,
affectsSourceFile: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
affectsModuleResolution: true,
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
@@ -488,58 +541,12 @@ namespace ts {
description: Diagnostics.Specify_what_JSX_code_is_generated,
defaultValueDescription: undefined,
},
{
name: "declaration",
shortName: "d",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
transpileOptionValue: undefined,
description: Diagnostics.Generate_d_ts_files_from_TypeScript_and_JavaScript_files_in_your_project,
defaultValueDescription: Diagnostics.false_unless_composite_is_set,
},
{
name: "declarationMap",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
transpileOptionValue: undefined,
defaultValueDescription: false,
description: Diagnostics.Create_sourcemaps_for_d_ts_files
},
{
name: "emitDeclarationOnly",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
description: Diagnostics.Only_output_d_ts_files_and_not_JavaScript_files,
transpileOptionValue: undefined,
defaultValueDescription: false,
},
{
name: "sourceMap",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
defaultValueDescription: false,
description: Diagnostics.Create_source_map_files_for_emitted_JavaScript_files,
},
{
name: "outFile",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
affectsDeclarationPath: true,
affectsBundleEmitBuildInfo: true,
isFilePath: true,
paramType: Diagnostics.FILE,
showInSimplifiedHelpView: true,
@@ -551,7 +558,7 @@ namespace ts {
name: "outDir",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
affectsDeclarationPath: true,
isFilePath: true,
paramType: Diagnostics.DIRECTORY,
@@ -563,7 +570,7 @@ namespace ts {
name: "rootDir",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
affectsDeclarationPath: true,
isFilePath: true,
paramType: Diagnostics.LOCATION,
@@ -574,9 +581,8 @@ namespace ts {
{
name: "composite",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBundleEmitBuildInfo: true,
// Not setting affectsEmit because we calculate this flag might not affect full emit
affectsBuildInfo: true,
isTSConfigOnly: true,
category: Diagnostics.Projects,
transpileOptionValue: undefined,
@@ -587,8 +593,7 @@ namespace ts {
name: "tsBuildInfoFile",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBundleEmitBuildInfo: true,
affectsBuildInfo: true,
isFilePath: true,
paramType: Diagnostics.FILE,
category: Diagnostics.Projects,
@@ -600,7 +605,7 @@ namespace ts {
name: "removeComments",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
defaultValueDescription: false,
@@ -619,7 +624,7 @@ namespace ts {
name: "importHelpers",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Allow_importing_helper_functions_from_tslib_once_per_project_instead_of_including_them_per_file,
defaultValueDescription: false,
@@ -633,7 +638,7 @@ namespace ts {
})),
affectsEmit: true,
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Specify_emit_Slashchecking_behavior_for_imports_that_are_only_used_for_types,
defaultValueDescription: ImportsNotUsedAsValues.Remove,
@@ -642,7 +647,7 @@ namespace ts {
name: "downlevelIteration",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Emit_more_compliant_but_verbose_and_less_performant_JavaScript_for_iteration,
defaultValueDescription: false,
@@ -664,7 +669,7 @@ namespace ts {
// The value of each strictFlag depends on own strictFlag value or this and never accessed directly.
// But we need to store `strict` in builf info, even though it won't be examined directly, so that the
// flags it controls (e.g. `strictNullChecks`) will be retrieved correctly
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_all_strict_type_checking_options,
@@ -674,7 +679,7 @@ namespace ts {
name: "noImplicitAny",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_error_reporting_for_expressions_and_declarations_with_an_implied_any_type,
@@ -684,7 +689,7 @@ namespace ts {
name: "strictNullChecks",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.When_type_checking_take_into_account_null_and_undefined,
@@ -694,7 +699,7 @@ namespace ts {
name: "strictFunctionTypes",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.When_assigning_functions_check_to_ensure_parameters_and_the_return_values_are_subtype_compatible,
@@ -704,7 +709,7 @@ namespace ts {
name: "strictBindCallApply",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Check_that_the_arguments_for_bind_call_and_apply_methods_match_the_original_function,
@@ -714,7 +719,7 @@ namespace ts {
name: "strictPropertyInitialization",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Check_for_class_properties_that_are_declared_but_not_set_in_the_constructor,
@@ -724,7 +729,7 @@ namespace ts {
name: "noImplicitThis",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_error_reporting_when_this_is_given_the_type_any,
@@ -734,7 +739,7 @@ namespace ts {
name: "useUnknownInCatchVariables",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Default_catch_clause_variables_as_unknown_instead_of_any,
@@ -745,7 +750,7 @@ namespace ts {
type: "boolean",
affectsSourceFile: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Ensure_use_strict_is_always_emitted,
@@ -757,7 +762,7 @@ namespace ts {
name: "noUnusedLocals",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_error_reporting_when_local_variables_aren_t_read,
defaultValueDescription: false,
@@ -766,7 +771,7 @@ namespace ts {
name: "noUnusedParameters",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Raise_an_error_when_a_function_parameter_isn_t_read,
defaultValueDescription: false,
@@ -775,7 +780,7 @@ namespace ts {
name: "exactOptionalPropertyTypes",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Interpret_optional_property_types_as_written_rather_than_adding_undefined,
defaultValueDescription: false,
@@ -784,7 +789,7 @@ namespace ts {
name: "noImplicitReturns",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_error_reporting_for_codepaths_that_do_not_explicitly_return_in_a_function,
defaultValueDescription: false,
@@ -794,7 +799,7 @@ namespace ts {
type: "boolean",
affectsBindDiagnostics: true,
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_error_reporting_for_fallthrough_cases_in_switch_statements,
defaultValueDescription: false,
@@ -803,7 +808,7 @@ namespace ts {
name: "noUncheckedIndexedAccess",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Add_undefined_to_a_type_when_accessed_using_an_index,
defaultValueDescription: false,
@@ -812,7 +817,7 @@ namespace ts {
name: "noImplicitOverride",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Ensure_overriding_members_in_derived_classes_are_marked_with_an_override_modifier,
defaultValueDescription: false,
@@ -821,7 +826,7 @@ namespace ts {
name: "noPropertyAccessFromIndexSignature",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
showInSimplifiedHelpView: false,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enforces_using_indexed_accessors_for_keys_declared_using_an_indexed_type,
@@ -908,7 +913,7 @@ namespace ts {
name: "allowSyntheticDefaultImports",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Interop_Constraints,
description: Diagnostics.Allow_import_x_from_y_when_a_module_doesn_t_have_a_default_export,
defaultValueDescription: Diagnostics.module_system_or_esModuleInterop
@@ -918,7 +923,7 @@ namespace ts {
type: "boolean",
affectsSemanticDiagnostics: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Interop_Constraints,
description: Diagnostics.Emit_additional_JavaScript_to_ease_support_for_importing_CommonJS_modules_This_enables_allowSyntheticDefaultImports_for_type_compatibility,
@@ -935,7 +940,7 @@ namespace ts {
name: "allowUmdGlobalAccess",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Modules,
description: Diagnostics.Allow_accessing_UMD_globals_from_modules,
defaultValueDescription: false,
@@ -958,7 +963,7 @@ namespace ts {
name: "sourceRoot",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
paramType: Diagnostics.LOCATION,
category: Diagnostics.Emit,
description: Diagnostics.Specify_the_root_path_for_debuggers_to_find_the_reference_source_code,
@@ -967,25 +972,16 @@ namespace ts {
name: "mapRoot",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
paramType: Diagnostics.LOCATION,
category: Diagnostics.Emit,
description: Diagnostics.Specify_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations,
},
{
name: "inlineSourceMap",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Include_sourcemap_files_inside_the_emitted_JavaScript,
defaultValueDescription: false,
},
{
name: "inlineSources",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Include_source_code_in_the_sourcemaps_inside_the_emitted_JavaScript,
defaultValueDescription: false,
@@ -996,7 +992,7 @@ namespace ts {
name: "experimentalDecorators",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Language_and_Environment,
description: Diagnostics.Enable_experimental_support_for_TC39_stage_2_draft_decorators,
defaultValueDescription: false,
@@ -1006,7 +1002,7 @@ namespace ts {
type: "boolean",
affectsSemanticDiagnostics: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Language_and_Environment,
description: Diagnostics.Emit_design_type_metadata_for_decorated_declarations_in_source_files,
defaultValueDescription: false,
@@ -1032,7 +1028,7 @@ namespace ts {
type: "string",
affectsSemanticDiagnostics: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
affectsModuleResolution: true,
category: Diagnostics.Language_and_Environment,
description: Diagnostics.Specify_module_specifier_used_to_import_the_JSX_factory_functions_when_using_jsx_Colon_react_jsx_Asterisk,
@@ -1051,9 +1047,8 @@ namespace ts {
name: "out",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
affectsDeclarationPath: true,
affectsBundleEmitBuildInfo: true,
isFilePath: false, // This is intentionally broken to support compatability with existing tsconfig files
// for correct behaviour, please use outFile
category: Diagnostics.Backwards_Compatibility,
@@ -1065,7 +1060,7 @@ namespace ts {
name: "reactNamespace",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Language_and_Environment,
description: Diagnostics.Specify_the_object_invoked_for_createElement_This_only_applies_when_targeting_react_JSX_emit,
defaultValueDescription: "`React`",
@@ -1074,7 +1069,7 @@ namespace ts {
name: "skipDefaultLibCheck",
type: "boolean",
// We need to store these to determine whether `lib` files need to be rechecked
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Completeness,
description: Diagnostics.Skip_type_checking_d_ts_files_that_are_included_with_TypeScript,
defaultValueDescription: false,
@@ -1090,7 +1085,7 @@ namespace ts {
name: "emitBOM",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Emit_a_UTF_8_Byte_Order_Mark_BOM_in_the_beginning_of_output_files,
defaultValueDescription: false,
@@ -1102,7 +1097,7 @@ namespace ts {
lf: NewLineKind.LineFeed
})),
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
paramType: Diagnostics.NEWLINE,
category: Diagnostics.Emit,
description: Diagnostics.Set_the_newline_character_for_emitting_files,
@@ -1112,7 +1107,7 @@ namespace ts {
name: "noErrorTruncation",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Output_Formatting,
description: Diagnostics.Disable_truncating_types_in_error_messages,
defaultValueDescription: false,
@@ -1143,7 +1138,7 @@ namespace ts {
name: "stripInternal",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Disable_emitting_declarations_that_have_internal_in_their_JSDoc_comments,
defaultValueDescription: false,
@@ -1184,7 +1179,7 @@ namespace ts {
name: "noImplicitUseStrict",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Disable_adding_use_strict_directives_in_emitted_JavaScript_files,
defaultValueDescription: false,
@@ -1193,7 +1188,7 @@ namespace ts {
name: "noEmitHelpers",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Disable_generating_custom_helper_functions_like_extends_in_compiled_output,
defaultValueDescription: false,
@@ -1202,7 +1197,7 @@ namespace ts {
name: "noEmitOnError",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
transpileOptionValue: undefined,
description: Diagnostics.Disable_emitting_files_if_any_type_checking_errors_are_reported,
@@ -1212,7 +1207,7 @@ namespace ts {
name: "preserveConstEnums",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Disable_erasing_const_enum_declarations_in_generated_code,
defaultValueDescription: false,
@@ -1221,7 +1216,7 @@ namespace ts {
name: "declarationDir",
type: "string",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
affectsDeclarationPath: true,
isFilePath: true,
paramType: Diagnostics.DIRECTORY,
@@ -1233,7 +1228,7 @@ namespace ts {
name: "skipLibCheck",
type: "boolean",
// We need to store these to determine whether `lib` files need to be rechecked
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Completeness,
description: Diagnostics.Skip_type_checking_all_d_ts_files,
defaultValueDescription: false,
@@ -1243,7 +1238,7 @@ namespace ts {
type: "boolean",
affectsBindDiagnostics: true,
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Disable_error_reporting_for_unused_labels,
defaultValueDescription: undefined,
@@ -1253,7 +1248,7 @@ namespace ts {
type: "boolean",
affectsBindDiagnostics: true,
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Disable_error_reporting_for_unreachable_code,
defaultValueDescription: undefined,
@@ -1262,7 +1257,7 @@ namespace ts {
name: "suppressExcessPropertyErrors",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Disable_reporting_of_excess_property_errors_during_the_creation_of_object_literals,
defaultValueDescription: false,
@@ -1271,7 +1266,7 @@ namespace ts {
name: "suppressImplicitAnyIndexErrors",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Suppress_noImplicitAny_errors_when_indexing_objects_that_lack_index_signatures,
defaultValueDescription: false,
@@ -1296,7 +1291,7 @@ namespace ts {
name: "noStrictGenericChecks",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Disable_strict_checking_of_generic_signatures_in_function_types,
defaultValueDescription: false,
@@ -1306,7 +1301,7 @@ namespace ts {
type: "boolean",
affectsSemanticDiagnostics: true,
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Language_and_Environment,
description: Diagnostics.Emit_ECMAScript_standard_compliant_class_fields,
defaultValueDescription: Diagnostics.true_for_ES2022_and_above_including_ESNext
@@ -1315,7 +1310,7 @@ namespace ts {
name: "preserveValueImports",
type: "boolean",
affectsEmit: true,
affectsMultiFileEmitBuildInfo: true,
affectsBuildInfo: true,
category: Diagnostics.Emit,
description: Diagnostics.Preserve_unused_imported_values_in_the_JavaScript_output_that_would_otherwise_be_removed,
defaultValueDescription: false,

View File

@@ -5426,6 +5426,10 @@
"category": "Message",
"code": 6405
},
"Project '{0}' is out of date because buildinfo file '{1}' indicates there is change in compilerOptions": {
"category": "Message",
"code": 6406
},
"The expected type comes from property '{0}' which is declared here on type '{1}'": {
"category": "Message",

View File

@@ -257,8 +257,8 @@ namespace ts {
/*@internal*/
export function getFirstProjectOutput(configFile: ParsedCommandLine, ignoreCase: boolean): string {
if (outFile(configFile.options)) {
const { jsFilePath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
return Debug.checkDefined(jsFilePath, `project ${configFile.options.configFilePath} expected to have at least one output`);
const { jsFilePath, declarationFilePath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
return Debug.checkDefined(jsFilePath || declarationFilePath, `project ${configFile.options.configFilePath} expected to have at least one output`);
}
const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(configFile, ignoreCase));
@@ -278,7 +278,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 | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean, forceDtsEmit?: boolean): EmitResult {
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnly?: boolean | EmitOnly, onlyBuildInfo?: boolean, forceDtsEmit?: boolean): EmitResult {
const compilerOptions = host.getCompilerOptions();
const sourceMapDataList: SourceMapEmitResult[] | undefined = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined;
const emittedFilesList: string[] | undefined = compilerOptions.listEmittedFiles ? [] : undefined;
@@ -331,7 +331,7 @@ namespace ts {
tracing?.pop();
if (!emitSkipped && emittedFilesList) {
if (!emitOnlyDtsFiles) {
if (!emitOnly) {
if (jsFilePath) {
emittedFilesList.push(jsFilePath);
}
@@ -342,11 +342,13 @@ namespace ts {
emittedFilesList.push(buildInfoPath);
}
}
if (declarationFilePath) {
emittedFilesList.push(declarationFilePath);
}
if (declarationMapPath) {
emittedFilesList.push(declarationMapPath);
if (emitOnly !== EmitOnly.Js) {
if (declarationFilePath) {
emittedFilesList.push(declarationFilePath);
}
if (declarationMapPath) {
emittedFilesList.push(declarationMapPath);
}
}
}
@@ -358,13 +360,11 @@ namespace ts {
function emitBuildInfo(bundle: BundleBuildInfo | undefined, buildInfoPath: string | undefined) {
// Write build information if applicable
if (!buildInfoPath || targetSourceFile || emitSkipped) return;
const program = host.getProgramBuildInfo();
if (host.isEmitBlocked(buildInfoPath)) {
emitSkipped = true;
return;
}
const version = ts.version; // Extracted into a const so the form is stable between namespace and module
const buildInfo: BuildInfo = { bundle, program, version };
const buildInfo = host.getBuildInfo(bundle) || createBuildInfo(/*program*/ undefined, bundle);
// Pass buildinfo as additional data to avoid having to reparse
writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText(buildInfo), /*writeByteOrderMark*/ false, /*sourceFiles*/ undefined, { buildInfo });
}
@@ -374,7 +374,7 @@ namespace ts {
jsFilePath: string | undefined,
sourceMapFilePath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle || emitOnlyDtsFiles || !jsFilePath) {
if (!sourceFileOrBundle || emitOnly || !jsFilePath) {
return;
}
@@ -424,16 +424,16 @@ namespace ts {
declarationFilePath: string | undefined,
declarationMapPath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle) return;
if (!sourceFileOrBundle || emitOnly === EmitOnly.Js) return;
if (!declarationFilePath) {
if (emitOnlyDtsFiles || compilerOptions.emitDeclarationOnly) emitSkipped = true;
if (emitOnly || compilerOptions.emitDeclarationOnly) emitSkipped = true;
return;
}
const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles;
const filesForEmit = forceDtsEmit ? sourceFiles : filter(sourceFiles, isSourceFileNotJson);
// Setup and perform the transformation to retrieve declarations from the input files
const inputListOrBundle = outFile(compilerOptions) ? [factory.createBundle(filesForEmit, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : filesForEmit;
if (emitOnlyDtsFiles && !getEmitDeclarations(compilerOptions)) {
if (emitOnly && !getEmitDeclarations(compilerOptions)) {
// Checker wont collect the linked aliases since thats only done when declaration is enabled.
// Do that here when emitting only dts files
filesForEmit.forEach(collectLinkedAliases);
@@ -646,6 +646,12 @@ namespace ts {
}
}
/*@internal*/
export function createBuildInfo(program: ProgramBuildInfo | undefined, bundle: BundleBuildInfo | undefined): BuildInfo {
const version = ts.version; // Extracted into a const so the form is stable between namespace and module
return { bundle, program, version };
}
/*@internal*/
export function getBuildInfoText(buildInfo: BuildInfo) {
return JSON.stringify(buildInfo);
@@ -822,21 +828,7 @@ namespace ts {
if (sourceMapText === text) return;
break;
case buildInfoPath:
const newBuildInfo = data!.buildInfo!;
newBuildInfo.program = buildInfo.program;
if (newBuildInfo.program && changedDtsText !== undefined && config.options.composite) {
// Update the output signature
(newBuildInfo.program as ProgramBundleEmitBuildInfo).outSignature = computeSignature(changedDtsText, createHash, changedDtsData);
}
// Update sourceFileInfo
const { js, dts, sourceFiles } = buildInfo.bundle!;
newBuildInfo.bundle!.js!.sources = js!.sources;
if (dts) {
newBuildInfo.bundle!.dts!.sources = dts.sources;
}
newBuildInfo.bundle!.sourceFiles = sourceFiles;
outputFiles.push({ name, text: getBuildInfoText(newBuildInfo), writeByteOrderMark, buildInfo: newBuildInfo });
return;
break;
case declarationFilePath:
if (declarationText === text) return;
changedDtsText = text;
@@ -848,13 +840,27 @@ namespace ts {
default:
Debug.fail(`Unexpected path: ${name}`);
}
outputFiles.push({ name, text, writeByteOrderMark });
outputFiles.push({ name, text, writeByteOrderMark, data });
},
isEmitBlocked: returnFalse,
readFile: f => host.readFile(f),
fileExists: f => host.fileExists(f),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
getProgramBuildInfo: returnUndefined,
getBuildInfo: bundle => {
const program = buildInfo.program;
if (program && changedDtsText !== undefined && config.options.composite) {
// Update the output signature
(program as ProgramBundleEmitBuildInfo).outSignature = computeSignature(changedDtsText, createHash, changedDtsData);
}
// Update sourceFileInfo
const { js, dts, sourceFiles } = buildInfo.bundle!;
bundle!.js!.sources = js!.sources;
if (dts) {
bundle!.dts!.sources = dts.sources;
}
bundle!.sourceFiles = sourceFiles;
return createBuildInfo(program, bundle);
},
getSourceFileFromReference: returnUndefined,
redirectTargetsMap: createMultiMap(),
getFileIncludeReasons: notImplemented,

View File

@@ -1967,7 +1967,7 @@ namespace ts {
return host.fileExists(f);
},
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo(),
getBuildInfo: bundle => program.getBuildInfo?.(bundle),
getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref),
redirectTargetsMap,
getFileIncludeReasons: program.getFileIncludeReasons,
@@ -2058,9 +2058,9 @@ namespace ts {
return typeChecker || (typeChecker = createTypeChecker(program));
}
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnly?: boolean | EmitOnly, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
tracing?.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true);
const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit));
const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnly, transformers, forceDtsEmit));
tracing?.pop();
return result;
}
@@ -2069,7 +2069,7 @@ namespace ts {
return hasEmitBlockingDiagnostics.has(toPath(emitFileName));
}
function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnly?: boolean | EmitOnly, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
if (!forceDtsEmit) {
const result = handleNoEmitOptions(program, sourceFile, writeFileCallback, cancellationToken);
if (result) return result;
@@ -2091,8 +2091,8 @@ namespace ts {
emitResolver,
getEmitHost(writeFileCallback),
sourceFile,
getTransformers(options, customTransformers, emitOnlyDtsFiles),
emitOnlyDtsFiles,
getTransformers(options, customTransformers, emitOnly),
emitOnly,
/*onlyBuildInfo*/ false,
forceDtsEmit
);

View File

@@ -322,9 +322,10 @@ namespace ts {
}
// Sometimes tools can see the following line as a source mapping url comment, so we mangle it a bit (the [M])
const sourceMapCommentRegExp = /^\/\/[@#] source[M]appingURL=(.+)\r?\n?$/;
export const sourceMapCommentRegExpDontCareLineStart = /\/\/[@#] source[M]appingURL=(.+)\r?\n?$/;
export const sourceMapCommentRegExp = /^\/\/[@#] source[M]appingURL=(.+)\r?\n?$/;
const whitespaceOrMapCommentRegExp = /^\s*(\/\/[@#] .*)?$/;
export const whitespaceOrMapCommentRegExp = /^\s*(\/\/[@#] .*)?$/;
export interface LineInfo {
getLineCount(): number;

View File

@@ -31,15 +31,15 @@ namespace ts {
export const noTransformers: EmitTransformers = { scriptTransformers: emptyArray, declarationTransformers: emptyArray };
export function getTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnlyDtsFiles?: boolean): EmitTransformers {
export function getTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnly?: boolean | EmitOnly): EmitTransformers {
return {
scriptTransformers: getScriptTransformers(compilerOptions, customTransformers, emitOnlyDtsFiles),
scriptTransformers: getScriptTransformers(compilerOptions, customTransformers, emitOnly),
declarationTransformers: getDeclarationTransformers(customTransformers),
};
}
function getScriptTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnlyDtsFiles?: boolean) {
if (emitOnlyDtsFiles) return emptyArray;
function getScriptTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnly?: boolean | EmitOnly) {
if (emitOnly) return emptyArray;
const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);

View File

@@ -20,6 +20,7 @@ namespace ts {
OutOfDateWithSelf,
OutOfDateWithUpstream,
OutOfDateBuildInfo,
OutOfDateOptions,
UpstreamOutOfDate,
UpstreamBlocked,
ComputingUpstream,
@@ -116,7 +117,7 @@ namespace ts {
* Buildinfo indicates that build is out of date
*/
export interface OutOfDateBuildInfo {
type: UpToDateStatusType.OutOfDateBuildInfo,
type: UpToDateStatusType.OutOfDateBuildInfo | UpToDateStatusType.OutOfDateOptions,
buildInfoFile: string;
}

View File

@@ -18,6 +18,11 @@ namespace ts {
/*@internal*/ pretty?: boolean;
incremental?: boolean;
assumeChangesOnlyAffectDirectDependencies?: boolean;
declaration?: boolean;
declarationMap?: boolean;
emitDeclarationOnly?: boolean;
sourceMap?: boolean;
inlineSourceMap?: boolean;
traceResolution?: boolean;
/* @internal */ diagnostics?: boolean;
@@ -964,7 +969,7 @@ namespace ts {
reportDeclarationDiagnostics,
/*write*/ undefined,
/*reportSummary*/ undefined,
(name, text, writeByteOrderMark, _onError, _sourceFiles, data) => outputFiles.push({ name, text, writeByteOrderMark, buildInfo: data?.buildInfo }),
(name, text, writeByteOrderMark, _onError, _sourceFiles, data) => outputFiles.push({ name, text, writeByteOrderMark, data }),
cancellationToken,
/*emitOnlyDts*/ false,
customTransformers || state.host.getCustomTransformers?.(project)
@@ -996,12 +1001,15 @@ namespace ts {
const isIncremental = isIncrementalCompilation(options);
let outputTimeStampMap: ESMap<Path, Date> | undefined;
let now: Date | undefined;
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
outputFiles.forEach(({ name, text, writeByteOrderMark, data }) => {
const path = toPath(state, name);
emittedOutputs.set(toPath(state, name), name);
if (buildInfo) setBuildInfo(state, buildInfo, projectPath, options, resultFlags);
if (data?.buildInfo) setBuildInfo(state, data.buildInfo, projectPath, options, resultFlags);
const modifiedTime = data?.differsOnlyInMap ? ts.getModifiedTime(state.host, name) : undefined;
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
if (!isIncremental && state.watch) {
// Revert the timestamp for the d.ts that is same
if (data?.differsOnlyInMap) state.host.setModifiedTime(name, modifiedTime!);
else if (!isIncremental && state.watch) {
(outputTimeStampMap ||= getOutputTimeStampMap(state, projectPath)!).set(path, now ||= getCurrentTime(state.host));
}
});
@@ -1119,13 +1127,13 @@ namespace ts {
const emittedOutputs = new Map<Path, string>();
let resultFlags = BuildResultFlags.DeclarationOutputUnchanged;
const existingBuildInfo = state.buildInfoCache.get(projectPath)!.buildInfo || undefined;
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
outputFiles.forEach(({ name, text, writeByteOrderMark, data }) => {
emittedOutputs.set(toPath(state, name), name);
if (buildInfo) {
if ((buildInfo.program as ProgramBundleEmitBuildInfo)?.outSignature !== (existingBuildInfo?.program as ProgramBundleEmitBuildInfo)?.outSignature) {
if (data?.buildInfo) {
if ((data.buildInfo.program as ProgramBundleEmitBuildInfo)?.outSignature !== (existingBuildInfo?.program as ProgramBundleEmitBuildInfo)?.outSignature) {
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
}
setBuildInfo(state, buildInfo, projectPath, config.options, resultFlags);
setBuildInfo(state, data.buildInfo, projectPath, config.options, resultFlags);
}
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
});
@@ -1626,6 +1634,13 @@ namespace ts {
buildInfoFile: buildInfoPath
};
}
if (!project.options.noEmit && getPendingEmitKind(project.options, buildInfo.program.options || {})) {
return {
type: UpToDateStatusType.OutOfDateOptions,
buildInfoFile: buildInfoPath
};
}
buildInfoProgram = buildInfo.program;
}
@@ -1657,7 +1672,7 @@ namespace ts {
if (!buildInfoVersionMap) buildInfoVersionMap = getBuildInfoFileVersionMap(buildInfoProgram, buildInfoPath!, host);
version = buildInfoVersionMap.get(toPath(state, inputFile));
const text = version ? state.readFileWithCache(inputFile) : undefined;
currentVersion = text && (host.createHash || generateDjb2Hash)(text);
currentVersion = text && getSourceFileVersionAsHashFromText(host, text);
if (version && version === currentVersion) pseudoInputUpToDate = true;
}
@@ -2394,6 +2409,13 @@ namespace ts {
relName(state, configFileName),
relName(state, status.buildInfoFile)
);
case UpToDateStatusType.OutOfDateOptions:
return reportStatus(
state,
Diagnostics.Project_0_is_out_of_date_because_buildinfo_file_1_indicates_there_is_change_in_compilerOptions,
relName(state, configFileName),
relName(state, status.buildInfoFile)
);
case UpToDateStatusType.UpToDate:
if (status.newestInputFileTime !== undefined) {
return reportStatus(

View File

@@ -4261,6 +4261,7 @@ namespace ts {
/*@internal*/ sourceMapUrlPos?: number;
/*@internal*/ buildInfo?: BuildInfo;
/*@internal*/ diagnostics?: readonly DiagnosticWithLocation[];
/*@internal*/ differsOnlyInMap?: true;
}
export type WriteFileCallback = (
fileName: string,
@@ -4369,6 +4370,11 @@ namespace ts {
/*@internal*/
export type FilePreprocessingDiagnostics = FilePreprocessingReferencedDiagnostic | FilePreprocessingFileExplainingDiagnostic;
/*@internal*/
export const enum EmitOnly{
Js,
Dts,
}
export interface Program extends ScriptReferenceHost {
getCurrentDirectory(): string;
/**
@@ -4404,7 +4410,7 @@ namespace ts {
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
/*@internal*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult; // eslint-disable-line @typescript-eslint/unified-signatures
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnly?: boolean | EmitOnly, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult; // eslint-disable-line @typescript-eslint/unified-signatures
getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
@@ -4468,7 +4474,7 @@ namespace ts {
/*@internal*/ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
/*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
/*@internal*/ isSourceOfProjectReferenceRedirect(fileName: string): boolean;
/*@internal*/ getProgramBuildInfo?(): ProgramBuildInfo | undefined;
/*@internal*/ getBuildInfo?(bundle: BundleBuildInfo | undefined): BuildInfo;
/*@internal*/ emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
/**
* This implementation handles file exists to be true if file is source of project reference redirect when program is created using useSourceOfProjectReferenceRedirect
@@ -6856,8 +6862,7 @@ namespace ts {
affectsEmit?: true; // true if the options affects emit
affectsProgramStructure?: true; // true if program should be reconstructed from root files if option changes and does not affect module resolution as affectsModuleResolution indirectly means program needs to reconstructed
affectsDeclarationPath?: true; // true if the options affects declaration file path computed
affectsMultiFileEmitBuildInfo?: true; // true if this options should be emitted in buildInfo without --out
affectsBundleEmitBuildInfo?: true; // true if this options should be emitted in buildInfo with --out
affectsBuildInfo?: true; // true if this options should be emitted in buildInfo
transpileOptionValue?: boolean | undefined; // If set this means that the option should be set to this value when transpiling
extraValidation?: (value: CompilerOptionsValue) => [DiagnosticMessage, ...string[]] | undefined; // Additional validation to be performed for the value to be valid
}
@@ -7534,7 +7539,7 @@ namespace ts {
getPrependNodes(): readonly (InputFiles | UnparsedSource)[];
writeFile: WriteFileCallback;
getProgramBuildInfo(): ProgramBuildInfo | undefined;
getBuildInfo(bundle: BundleBuildInfo | undefined): BuildInfo | undefined;
getSourceFileFromReference: Program["getSourceFileFromReference"];
readonly redirectTargetsMap: RedirectTargetsMap;
createHash?(data: string): string;

View File

@@ -626,13 +626,50 @@ namespace ts {
};
}
export function getSourceFileVersionAsHashFromText(host: Pick<CompilerHost, "createHash">, text: string) {
// If text can contain the sourceMapUrl ignore sourceMapUrl for calcualting hash
if (text.match(sourceMapCommentRegExpDontCareLineStart)) {
let lineEnd = text.length;
let lineStart = lineEnd;
for (let pos = lineEnd - 1; pos >= 0; pos--) {
const ch = text.charCodeAt(pos);
switch (ch) {
case CharacterCodes.lineFeed:
if (pos && text.charCodeAt(pos - 1) === CharacterCodes.carriageReturn) {
pos--;
}
// falls through
case CharacterCodes.carriageReturn:
break;
default:
if (ch < CharacterCodes.maxAsciiCharacter || !isLineBreak(ch)) {
lineStart = pos;
continue;
}
break;
}
// This is start of the line
const line = text.substring(lineStart, lineEnd);
if (line.match(sourceMapCommentRegExp)) {
text = text.substring(0, lineStart);
break;
}
// If we see a non-whitespace/map comment-like line, break, to avoid scanning up the entire file
else if (!line.match(whitespaceOrMapCommentRegExp)){
break;
}
lineEnd = lineStart;
}
}
return (host.createHash || generateDjb2Hash)(text);
}
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) {
const originalGetSourceFile = compilerHost.getSourceFile;
const computeHash = maybeBind(host, host.createHash) || generateDjb2Hash;
compilerHost.getSourceFile = (...args) => {
const result = originalGetSourceFile.call(compilerHost, ...args);
if (result) {
result.version = computeHash(result.text);
result.version = getSourceFileVersionAsHashFromText(host, result.text);
}
return result;
};

View File

@@ -20,7 +20,7 @@ namespace ts {
buildInfo = getBuildInfo(buildInfoPath, content);
}
if (!buildInfo || buildInfo.version !== version || !buildInfo.program) return undefined;
return createBuilderProgramUsingProgramBuildInfo(buildInfo.program, buildInfoPath, host);
return createBuilderProgramUsingProgramBuildInfo(buildInfo, buildInfoPath, host);
}
export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {

View File

@@ -119,6 +119,7 @@
"unittests/services/transpile.ts",
"unittests/tsbuild/amdModulesWithOut.ts",
"unittests/tsbuild/clean.ts",
"unittests/tsbuild/commandLine.ts",
"unittests/tsbuild/configFileErrors.ts",
"unittests/tsbuild/configFileExtends.ts",
"unittests/tsbuild/containerOnlyReferenced.ts",

View File

@@ -220,7 +220,7 @@ namespace ts {
assertParseResult("parse build with --incremental", ["--incremental", "tests"]);
assertParseResult("parse build with --locale en-us", ["--locale", "en-us", "src"]);
assertParseResult("parse build with --tsBuildInfoFile", ["--tsBuildInfoFile", "build.tsbuildinfo", "tests"]);
assertParseResult("reports other common may not be used with --build flags", ["--declaration", "--strict"]);
assertParseResult("reports other common may not be used with --build flags", ["--strict"]);
describe("Combining options that make no sense together", () => {
function verifyInvalidCombination(flag1: keyof BuildOptions, flag2: keyof BuildOptions) {

View File

@@ -0,0 +1,427 @@
namespace ts {
describe("unittests:: tsbuild:: commandLine::", () => {
describe("different options::", () => {
function withOptionChange(subScenario: string, ...options: readonly string[]): TestTscEdit {
return {
subScenario,
modifyFs: noop,
commandLineArgs: ["--b", "/src/project", "--verbose", ...options]
};
}
function noChangeWithSubscenario(subScenario: string): TestTscEdit {
return { ...noChangeRun, subScenario };
}
function withOptionChangeAndDiscrepancyExplanation(subScenario: string, option: string): TestTscEdit {
return {
...withOptionChange(subScenario, option),
discrepancyExplanation: () => [
`Clean build tsbuildinfo will have compilerOptions with composite and ${option.replace(/\-/g, "")}`,
`Incremental build will detect that it doesnt need to rebuild so tsbuild info is from before which has option composite only`,
]
};
}
function withEmitDeclarationOnlyChangeAndDiscrepancyExplanation(subScenario: string): TestTscEdit {
const edit = withOptionChangeAndDiscrepancyExplanation(subScenario, "--emitDeclarationOnly");
const discrepancyExplanation = edit.discrepancyExplanation!;
edit.discrepancyExplanation = () => [
...discrepancyExplanation(),
`Clean build info does not have js section because its fresh build`,
`Incremental build info has js section from old build`
];
return edit;
}
function withOptionChangeAndExportExplanation(subScenario: string, ...options: readonly string[]): TestTscEdit {
return {
...withOptionChange(subScenario, ...options),
discrepancyExplanation: noChangeWithExportsDiscrepancyRun.discrepancyExplanation,
};
}
function nochangeWithIncrementalDeclarationFromBeforeExplaination(): TestTscEdit {
return {
...noChangeRun,
discrepancyExplanation: () => [
`Clean build tsbuildinfo will have compilerOptions {}`,
`Incremental build will detect that it doesnt need to rebuild so tsbuild info is from before which has option declaration and declarationMap`,
],
};
}
function nochangeWithIncrementalOutDeclarationFromBeforeExplaination(): TestTscEdit {
const edit = nochangeWithIncrementalDeclarationFromBeforeExplaination();
const discrepancyExplanation = edit.discrepancyExplanation!;
edit.discrepancyExplanation = () => [
...discrepancyExplanation(),
`Clean build does not have dts bundle section`,
`Incremental build contains the dts build section from before`,
];
return edit;
}
function localChange(): TestTscEdit {
return {
subScenario: "local change",
modifyFs: fs => replaceText(fs, "/src/project/a.ts", "Local = 1", "Local = 10"),
};
}
function fs(options: CompilerOptions) {
return loadProjectFromFiles({
"/src/project/tsconfig.json": JSON.stringify({ compilerOptions: compilerOptionsToConfigJson(options) }),
"/src/project/a.ts": `export const a = 10;const aLocal = 10;`,
"/src/project/b.ts": `export const b = 10;const bLocal = 10;`,
"/src/project/c.ts": `import { a } from "./a";export const c = a;`,
"/src/project/d.ts": `import { b } from "./b";export const d = b;`,
});
}
verifyTscWithEdits({
scenario: "commandLine",
subScenario: "different options",
fs: () => fs({ composite: true }),
commandLineArgs: ["--b", "/src/project", "--verbose"],
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
withOptionChangeAndDiscrepancyExplanation("with declaration should not emit anything", "--declaration"),
noChangeRun,
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
noChangeWithSubscenario("should re-emit only dts so they dont contain sourcemap"),
withOptionChangeAndDiscrepancyExplanation("with emitDeclarationOnly should not emit anything", "--emitDeclarationOnly"),
noChangeRun,
localChange(),
withOptionChangeAndDiscrepancyExplanation("with declaration should not emit anything", "--declaration"),
withOptionChange("with inlineSourceMap", "--inlineSourceMap"),
withOptionChange("with sourceMap", "--sourceMap"),
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "commandLine",
subScenario: "different options with outFile",
fs: () => fs({ composite: true, outFile: "../outFile.js", module: ModuleKind.AMD }),
commandLineArgs: ["--b", "/src/project", "--verbose"],
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
withOptionChangeAndDiscrepancyExplanation("with declaration should not emit anything", "--declaration"),
noChangeRun,
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
noChangeWithSubscenario("should re-emit only dts so they dont contain sourcemap"),
withEmitDeclarationOnlyChangeAndDiscrepancyExplanation("with emitDeclarationOnly should not emit anything"),
noChangeRun,
localChange(),
withOptionChangeAndDiscrepancyExplanation("with declaration should not emit anything", "--declaration"),
withOptionChange("with inlineSourceMap", "--inlineSourceMap"),
withOptionChange("with sourceMap", "--sourceMap"),
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "commandLine",
subScenario: "different options with incremental",
fs: () => fs({ incremental: true }),
commandLineArgs: ["--b", "/src/project", "--verbose"],
edits: [
withOptionChangeAndExportExplanation("with sourceMap", "--sourceMap"),
withOptionChangeAndExportExplanation("should re-emit only js so they dont contain sourcemap"),
withOptionChange("with declaration, emit Dts and should not emit js", "--declaration"),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
nochangeWithIncrementalDeclarationFromBeforeExplaination(),
localChange(),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
nochangeWithIncrementalDeclarationFromBeforeExplaination(),
withOptionChange("with inlineSourceMap", "--inlineSourceMap"),
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("emit js files"),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
withOptionChange("with declaration and declarationMap, should not re-emit", "--declaration", "--declarationMap"),
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "commandLine",
subScenario: "different options with incremental with outFile",
fs: () => fs({ incremental: true, outFile: "../outFile.js", module: ModuleKind.AMD }),
commandLineArgs: ["--b", "/src/project", "--verbose"],
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
withOptionChange("with declaration, emit Dts and should not emit js", "--declaration"),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
nochangeWithIncrementalOutDeclarationFromBeforeExplaination(),
localChange(),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
nochangeWithIncrementalOutDeclarationFromBeforeExplaination(),
withOptionChange("with inlineSourceMap", "--inlineSourceMap"),
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("emit js files"),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
withOptionChange("with declaration and declarationMap, should not re-emit", "--declaration", "--declarationMap"),
],
baselinePrograms: true,
});
});
describe("emitDeclarationOnly::", () => {
function fs(options: CompilerOptions) {
return loadProjectFromFiles({
"/src/project1/src/tsconfig.json": JSON.stringify({
compilerOptions: compilerOptionsToConfigJson(options),
}),
"/src/project1/src/a.ts": `export const a = 10;const aLocal = 10;`,
"/src/project1/src/b.ts": `export const b = 10;const bLocal = 10;`,
"/src/project1/src/c.ts": `import { a } from "./a";export const c = a;`,
"/src/project1/src/d.ts": `import { b } from "./b";export const d = b;`,
"/src/project2/src/tsconfig.json": JSON.stringify({
compilerOptions: compilerOptionsToConfigJson(options),
references: [{ path: "../../project1/src" }],
}),
"/src/project2/src/e.ts": `export const e = 10;`,
"/src/project2/src/f.ts": `import { a } from "${options.outFile ? "a" : "../../project1/src/a"}"; export const f = a;`,
"/src/project2/src/g.ts": `import { b } from "${options.outFile ? "b" : "../../project1/src/b"}"; export const g = b;`,
});
}
function verifyWithIncremental(options: CompilerOptions) {
verifyTscWithEdits({
scenario: "commandLine",
subScenario: subScenario("emitDeclarationOnly on commandline"),
fs: () => fs(options),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly"],
edits: [
noChangeRun,
{
subScenario: "local change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
},
{
subScenario: "non local change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "export const aaa = 10;"),
},
{
subScenario: "emit js files",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
{
...noChangeRun,
discrepancyExplanation: () => [
`Clean build tsbuildinfo for both projects will have compilerOptions with composite and emitDeclarationOnly`,
`Incremental build will detect that it doesnt need to rebuild so tsbuild info for projects is from before which has option composite only`,
]
},
{
subScenario: "js emit with change without emitDeclarationOnly",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const alocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
{
subScenario: "local change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const aaaa = 10;"),
// --out without composite doesnt emit buildInfo without emitting program so it wouldnt have project2 tsbuildInfo so no mismatch
discrepancyExplanation: options.incremental && options.outFile ? undefined : () => [
`Clean build tsbuildinfo for project2 will have compilerOptions with composite and emitDeclarationOnly`,
`Incremental build will detect that it doesnt need to rebuild project2 so tsbuildinfo for it is from before which has option composite only`,
],
},
{
subScenario: "non local change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "export const aaaaa = 10;"),
},
{
subScenario: "js emit with change without emitDeclarationOnly",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "export const a2 = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "commandLine",
subScenario: subScenario("emitDeclarationOnly false on commandline"),
fs: () => fs({ ...options, emitDeclarationOnly: true }),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
edits: [
noChangeRun,
{
subScenario: "change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
},
{
subScenario: "emit js files",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
{
...noChangeRun,
discrepancyExplanation: () => [
`Clean build tsbuildinfo for both projects will have compilerOptions with composite and emitDeclarationOnly`,
`Incremental build will detect that it doesnt need to rebuild so tsbuild info for projects is from before which has option composite as true but emitDeclrationOnly as false`,
]
},
{
subScenario: "no change run with js emit",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
{
subScenario: "js emit with change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const blocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
],
baselinePrograms: true,
});
function subScenario(text: string) {
return `${text}${options.composite ? "" : " with declaration and incremental"}${options.outFile ? " with outFile" : ""}`;
}
}
verifyWithIncremental({ composite: true });
verifyWithIncremental({ incremental: true, declaration: true });
verifyWithIncremental({ composite: true, outFile: "../outFile.js", module: ModuleKind.AMD });
verifyWithIncremental({ incremental: true, declaration: true, outFile: "../outFile.js", module: ModuleKind.AMD });
verifyTscWithEdits({
scenario: "commandLine",
subScenario: "emitDeclarationOnly on commandline with declaration",
fs: () => fs({ declaration: true }),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly"],
edits: [
noChangeRun,
{
subScenario: "local change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
},
{
subScenario: "non local change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "export const aaa = 10;"),
},
{
subScenario: "emit js files",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
noChangeRun,
{
subScenario: "js emit with change without emitDeclarationOnly",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const alocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
{
subScenario: "local change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const aaaa = 10;"),
},
{
subScenario: "non local change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "export const aaaaa = 10;"),
},
{
subScenario: "js emit with change without emitDeclarationOnly",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "export const a2 = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "commandLine",
subScenario: "emitDeclarationOnly false on commandline with declaration",
fs: () => fs({ declaration: true, emitDeclarationOnly: true }),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
edits: [
noChangeRun,
{
subScenario: "change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
},
{
subScenario: "emit js files",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
noChangeRun,
{
subScenario: "no change run with js emit",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
{
subScenario: "js emit with change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const blocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "commandLine",
subScenario: "emitDeclarationOnly on commandline with declaration with outFile",
fs: () => fs({ declaration: true, outFile: "../outFile.js", module: ModuleKind.AMD }),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly"],
edits: [
noChangeRun,
{
subScenario: "local change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
},
{
subScenario: "non local change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "export const aaa = 10;"),
},
{
subScenario: "emit js files",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
noChangeRun,
{
subScenario: "js emit with change without emitDeclarationOnly",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const alocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
{
subScenario: "local change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const aaaa = 10;"),
},
{
subScenario: "non local change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "export const aaaaa = 10;"),
},
{
subScenario: "js emit with change without emitDeclarationOnly",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "export const a2 = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
},
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "commandLine",
subScenario: "emitDeclarationOnly false on commandline with declaration with outFile",
fs: () => fs({ declaration: true, emitDeclarationOnly: true, outFile: "../outFile.js", module: ModuleKind.AMD }),
commandLineArgs: ["--b", "/src/project2/src", "--verbose"],
edits: [
noChangeRun,
{
subScenario: "change",
modifyFs: fs => appendText(fs, "/src/project1/src/a.ts", "const aa = 10;"),
},
{
subScenario: "emit js files",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
noChangeRun,
{
subScenario: "no change run with js emit",
modifyFs: noop,
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
{
subScenario: "js emit with change",
modifyFs: fs => appendText(fs, "/src/project1/src/b.ts", "const blocal = 10;"),
commandLineArgs: ["--b", "/src/project2/src", "--verbose", "--emitDeclarationOnly", "false"],
},
],
baselinePrograms: true,
});
});
});
}

View File

@@ -9,13 +9,6 @@ namespace ts {
});
describe("unittests:: tsbuild:: configFileErrors:: reports syntax errors in config file", () => {
function discrepancyExplanation() {
return [
"During incremental build, tsbuildinfo is not emitted, so declaration option is not present",
"Clean build has declaration option in tsbuildinfo",
];
}
verifyTscWithEdits({
scenario: "configFileErrors",
subScenario: "reports syntax errors in config file",
@@ -39,14 +32,16 @@ namespace ts {
modifyFs: fs => replaceText(fs, "/src/tsconfig.json", ",", `,
"declaration": true,`),
subScenario: "reports syntax errors after change to config file",
discrepancyExplanation
discrepancyExplanation: () => [
"During incremental build, tsbuildinfo is not emitted, so declaration option is not present",
"Clean build has declaration option in tsbuildinfo",
],
},
{
modifyFs: fs => appendText(fs, "/src/a.ts", "export function fooBar() { }"),
subScenario: "reports syntax errors after change to ts file",
discrepancyExplanation,
},
{ ...noChangeRun, discrepancyExplanation },
noChangeRun,
{
modifyFs: fs => fs.writeFileSync(
"/src/tsconfig.json",

View File

@@ -198,11 +198,12 @@ interface Symbol {
}
type ReadableProgramBuildInfoDiagnostic = string | [string, readonly ReusableDiagnostic[]];
type ReadableProgramBuilderInfoFilePendingEmit = [original: string | [string], emitKind: "DtsOnly" | "Full"];
type ReadableProgramBuildInfoEmitSignature = string | [string, string];
type ReadableProgramBuildInfoFileInfo = Omit<BuilderState.FileInfo, "impliedFormat"> & {
type ReadableBuilderFileEmit = string & { __readableBuilderFileEmit: any; };
type ReadableProgramBuilderInfoFilePendingEmit = [original: string | [string], emitKind: ReadableBuilderFileEmit];
type ReadableProgramBuildInfoEmitSignature = string | [string, EmitSignature | []];
type ReadableProgramBuildInfoFileInfo<T> = Omit<BuilderState.FileInfo, "impliedFormat"> & {
impliedFormat: string | undefined;
original: ProgramBuildInfoBuilderStateFileInfo | undefined;
original: T | undefined;
};
type ReadableProgramMultiFileEmitBuildInfo = Omit<ProgramMultiFileEmitBuildInfo,
"fileIdsList" | "fileInfos" |
@@ -210,7 +211,7 @@ interface Symbol {
"affectedFilesPendingEmit" | "changeFileSet" | "emitSignatures"
> & {
fileNamesList: readonly (readonly string[])[] | undefined;
fileInfos: MapLike<ReadableProgramBuildInfoFileInfo>;
fileInfos: MapLike<ReadableProgramBuildInfoFileInfo<ProgramMultiFileEmitBuildInfoFileInfo>>;
referencedMap: MapLike<string[]> | undefined;
exportedModulesMap: MapLike<string[]> | undefined;
semanticDiagnosticsPerFile: readonly ReadableProgramBuildInfoDiagnostic[] | undefined;
@@ -218,8 +219,10 @@ interface Symbol {
changeFileSet: readonly string[] | undefined;
emitSignatures: readonly ReadableProgramBuildInfoEmitSignature[] | undefined;
};
type ReadableProgramBundleEmitBuildInfo = Omit<ProgramBundleEmitBuildInfo, "fileInfos"> & {
fileInfos: MapLike<string>;
type ReadableProgramBuildInfoBundlePendingEmit = [emitKind: ReadableBuilderFileEmit, original: ProgramBuildInfoBundlePendingEmit];
type ReadableProgramBundleEmitBuildInfo = Omit<ProgramBundleEmitBuildInfo, "fileInfos" | "pendingEmit"> & {
fileInfos: MapLike<string | ReadableProgramBuildInfoFileInfo<BuilderState.FileInfo>>;
pendingEmit: ReadableProgramBuildInfoBundlePendingEmit | undefined;
};
type ReadableProgramBuildInfo = ReadableProgramMultiFileEmitBuildInfo | ReadableProgramBundleEmitBuildInfo;
@@ -233,16 +236,28 @@ interface Symbol {
let fileNamesList: string[][] | undefined;
if (buildInfo.program && isProgramBundleEmitBuildInfo(buildInfo.program)) {
const fileInfos: ReadableProgramBundleEmitBuildInfo["fileInfos"] = {};
buildInfo.program?.fileInfos?.forEach((fileInfo, index) => fileInfos[toFileName(index + 1 as ProgramBuildInfoFileId)] = fileInfo);
buildInfo.program?.fileInfos?.forEach((fileInfo, index) =>
fileInfos[toFileName(index + 1 as ProgramBuildInfoFileId)] = isString(fileInfo) ?
fileInfo :
toReadableFileInfo(fileInfo, identity)
);
const pendingEmit = buildInfo.program.pendingEmit;
program = {
...buildInfo.program,
fileInfos
fileInfos,
pendingEmit: pendingEmit === undefined ?
undefined :
[
toReadableBuilderFileEmit(toProgramEmitPending(pendingEmit, buildInfo.program.options)),
pendingEmit
],
};
}
else if (buildInfo.program) {
const fileInfos: ReadableProgramMultiFileEmitBuildInfo["fileInfos"] = {};
buildInfo.program?.fileInfos?.forEach((fileInfo, index) => fileInfos[toFileName(index + 1 as ProgramBuildInfoFileId)] = toReadableFileInfo(fileInfo));
buildInfo.program?.fileInfos?.forEach((fileInfo, index) => fileInfos[toFileName(index + 1 as ProgramBuildInfoFileId)] = toReadableFileInfo(fileInfo, toBuilderStateFileInfoForMultiEmit));
fileNamesList = buildInfo.program.fileIdsList?.map(fileIdsListId => fileIdsListId.map(toFileName));
const fullEmitForOptions = buildInfo.program.affectedFilesPendingEmit ? getBuilderFileEmit(buildInfo.program.options || {}) : undefined;
program = buildInfo.program && {
fileNames: buildInfo.program.fileNames,
fileNamesList,
@@ -255,7 +270,7 @@ interface Symbol {
toFileName(d) :
[toFileName(d[0]), d[1]]
),
affectedFilesPendingEmit: buildInfo.program.affectedFilesPendingEmit?.map(toReadableProgramBuilderInfoFilePendingEmit),
affectedFilesPendingEmit: buildInfo.program.affectedFilesPendingEmit?.map(value => toReadableProgramBuilderInfoFilePendingEmit(value, fullEmitForOptions!)),
changeFileSet: buildInfo.program.changeFileSet?.map(toFileName),
emitSignatures: buildInfo.program.emitSignatures?.map(s =>
isNumber(s) ?
@@ -298,10 +313,10 @@ interface Symbol {
return fileNamesList![fileIdsListId - 1];
}
function toReadableFileInfo(fileInfo: ProgramBuildInfoFileInfo): ReadableProgramBuildInfoFileInfo {
const info = toBuilderStateFileInfo(fileInfo);
function toReadableFileInfo<T>(original: T, toFileInfo: (fileInfo: T) => BuilderState.FileInfo): ReadableProgramBuildInfoFileInfo<T> {
const info = toFileInfo(original);
return {
original: isString(fileInfo) ? undefined : fileInfo,
original: isString(original) ? undefined : original,
...info,
impliedFormat: info.impliedFormat && getNameOfCompilerOptionValue(info.impliedFormat, moduleOptionDeclaration.type),
};
@@ -316,15 +331,27 @@ interface Symbol {
return result;
}
function toReadableProgramBuilderInfoFilePendingEmit(value: ProgramBuilderInfoFilePendingEmit): ReadableProgramBuilderInfoFilePendingEmit {
const emitKind = toBuilderFileEmit(value);
function toReadableProgramBuilderInfoFilePendingEmit(value: ProgramBuilderInfoFilePendingEmit, fullEmitForOptions: BuilderFileEmit): ReadableProgramBuilderInfoFilePendingEmit {
return [
isNumber(value) ? toFileName(value) : [toFileName(value[0])],
emitKind === BuilderFileEmit.DtsOnly ? "DtsOnly" :
emitKind === BuilderFileEmit.Full ? "Full" :
Debug.assertNever(emitKind),
toReadableBuilderFileEmit(toBuilderFileEmit(value, fullEmitForOptions)),
];
}
function toReadableBuilderFileEmit(emit: BuilderFileEmit | undefined): ReadableBuilderFileEmit {
let result = "";
if (emit) {
if (emit & BuilderFileEmit.Js) addFlags("Js");
if (emit & BuilderFileEmit.JsMap) addFlags("JsMap");
if (emit & BuilderFileEmit.JsInlineMap) addFlags("JsInlineMap");
if (emit & BuilderFileEmit.Dts) addFlags("Dts");
if (emit & BuilderFileEmit.DtsMap) addFlags("DtsMap");
}
return (result || "None") as ReadableBuilderFileEmit;
function addFlags(flag: string) {
result = result ? `${result} | ${flag}` : flag;
}
}
}
export function toPathWithSystem(sys: System, fileName: string): Path {
@@ -538,7 +565,7 @@ interface Symbol {
} {
if (!text) return { buildInfo: text };
const readableBuildInfo = JSON.parse(text) as ReadableBuildInfo;
let sanitizedFileInfos: MapLike<ReadableProgramBuildInfoFileInfo | string> | undefined;
let sanitizedFileInfos: MapLike<string | Omit<ReadableProgramBuildInfoFileInfo<ProgramMultiFileEmitBuildInfoFileInfo> | ReadableProgramBuildInfoFileInfo<BuilderState.FileInfo>, "signature" | "original"> & { signature: undefined; original: undefined; }> | undefined;
if (readableBuildInfo.program?.fileInfos) {
sanitizedFileInfos = {};
for (const id in readableBuildInfo.program.fileInfos) {

View File

@@ -154,6 +154,23 @@ namespace ts {
]
});
verifyTscWithEdits({
scenario: "sample1",
subScenario: "when declarationMap changes",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tests", "--verbose"],
edits: [
{
subScenario: "Disable declarationMap",
modifyFs: fs => replaceText(fs, "/src/core/tsconfig.json", `"declarationMap": true,`, `"declarationMap": false,`),
},
{
subScenario: "Enable declarationMap",
modifyFs: fs => replaceText(fs, "/src/core/tsconfig.json", `"declarationMap": false,`, `"declarationMap": true,`),
},
]
});
verifyTsc({
scenario: "sample1",
subScenario: "indicates that it would skip builds during a dry build",

View File

@@ -425,7 +425,6 @@ declare global {
]
});
verifyTscWithEdits({
scenario: "incremental",
subScenario: "when project has strict true",
@@ -580,5 +579,241 @@ console.log(a);`,
}
verifyModifierChange(/*declaration*/ false);
verifyModifierChange(/*declaration*/ true);
verifyTscWithEdits({
scenario: "incremental",
subScenario: `when declarationMap changes`,
fs: () => loadProjectFromFiles({
"/src/project/tsconfig.json": JSON.stringify({
compilerOptions: {
noEmitOnError: true,
declaration: true,
composite: true,
}
}),
"/src/project/a.ts": "const x = 10;",
"/src/project/b.ts": "const y = 10;"
}),
commandLineArgs: ["--p", "/src/project"],
edits: [
{
subScenario: "error and enable declarationMap",
modifyFs: fs => replaceText(fs, "/src/project/a.ts", "x", "x: 20"),
commandLineArgs: ["--p", "/src/project", "--declarationMap"],
discrepancyExplanation: () => [
`Clean build does not emit any file so will have emitSignatures with all files since they are not emitted`,
`Incremental build has emitSignatures from before, so it will have a.ts with signature since file.version isnt same`,
`Incremental build will also have emitSignatureDtsMapDiffers for both files since the emitSignatures were without declarationMap but currentOptions have declrationMap`,
]
},
{
subScenario: "fix error declarationMap",
modifyFs: fs => replaceText(fs, "/src/project/a.ts", "x: 20", "x"),
commandLineArgs: ["--p", "/src/project", "--declarationMap"],
},
]
});
verifyTscWithEdits({
scenario: "incremental",
subScenario: `when declarationMap changes with outFile`,
fs: () => loadProjectFromFiles({
"/src/project/tsconfig.json": JSON.stringify({
compilerOptions: {
noEmitOnError: true,
declaration: true,
composite: true,
outFile: "../outFile.js",
}
}),
"/src/project/a.ts": "const x = 10;",
"/src/project/b.ts": "const y = 10;"
}),
commandLineArgs: ["--p", "/src/project"],
edits: [
{
subScenario: "error and enable declarationMap",
modifyFs: fs => replaceText(fs, "/src/project/a.ts", "x", "x: 20"),
commandLineArgs: ["--p", "/src/project", "--declarationMap"],
},
{
subScenario: "fix error declarationMap",
modifyFs: fs => replaceText(fs, "/src/project/a.ts", "x: 20", "x"),
commandLineArgs: ["--p", "/src/project", "--declarationMap"],
},
]
});
describe("different options::", () => {
function withOptionChange(subScenario: string, ...options: readonly string[]): TestTscEdit {
return {
subScenario,
modifyFs: noop,
commandLineArgs: ["--p", "/src/project", ...options],
};
}
function noChangeWithSubscenario(subScenario: string): TestTscEdit {
return { ...noChangeRun, subScenario };
}
function withOptionChangeAndDiscrepancyExplanation(subScenario: string, option: string): TestTscEdit {
return {
...withOptionChange(subScenario, option),
discrepancyExplanation: () => [
`Clean build tsbuildinfo will have compilerOptions with composite and ${option.replace(/\-/g, "")}`,
`Incremental build will detect that it doesnt need to rebuild so tsbuild info is from before which has option composite only`,
]
};
}
function withEmitDeclarationOnlyChangeAndDiscrepancyExplanation(subScenario: string): TestTscEdit {
const edit = withOptionChangeAndDiscrepancyExplanation(subScenario, "--emitDeclarationOnly");
const discrepancyExplanation = edit.discrepancyExplanation!;
edit.discrepancyExplanation = () => [
...discrepancyExplanation(),
`Clean build info does not have js section because its fresh build`,
`Incremental build info has js section from old build`
];
return edit;
}
function withOptionChangeAndExportExplanation(subScenario: string, ...options: readonly string[]): TestTscEdit {
return {
...withOptionChange(subScenario, ...options),
discrepancyExplanation: noChangeWithExportsDiscrepancyRun.discrepancyExplanation,
};
}
function nochangeWithIncrementalDeclarationFromBeforeExplaination(): TestTscEdit {
return {
...noChangeRun,
discrepancyExplanation: () => [
`Clean build tsbuildinfo will have compilerOptions {}`,
`Incremental build will detect that it doesnt need to rebuild so tsbuild info is from before which has option declaration and declarationMap`,
],
};
}
function nochangeWithIncrementalOutDeclarationFromBeforeExplaination(): TestTscEdit {
const edit = nochangeWithIncrementalDeclarationFromBeforeExplaination();
const discrepancyExplanation = edit.discrepancyExplanation!;
edit.discrepancyExplanation = () => [
...discrepancyExplanation(),
`Clean build does not have dts bundle section`,
`Incremental build contains the dts build section from before`,
];
return edit;
}
function localChange(): TestTscEdit {
return {
subScenario: "local change",
modifyFs: fs => replaceText(fs, "/src/project/a.ts", "Local = 1", "Local = 10"),
};
}
function fs(options: CompilerOptions) {
return loadProjectFromFiles({
"/src/project/tsconfig.json": JSON.stringify({ compilerOptions: compilerOptionsToConfigJson(options) }),
"/src/project/a.ts": `export const a = 10;const aLocal = 10;`,
"/src/project/b.ts": `export const b = 10;const bLocal = 10;`,
"/src/project/c.ts": `import { a } from "./a";export const c = a;`,
"/src/project/d.ts": `import { b } from "./b";export const d = b;`,
});
}
function enableDeclarationMap(): TestTscEdit {
return {
subScenario: "declarationMap enabling",
modifyFs: fs => {
const config = JSON.parse(fs.readFileSync("/src/project/tsconfig.json", "utf-8"));
config.compilerOptions.declarationMap = true;
fs.writeFileSync("/src/project/tsconfig.json", JSON.stringify(config));
},
};
}
verifyTscWithEdits({
scenario: "incremental",
subScenario: "different options",
fs: () => fs({ composite: true }),
commandLineArgs: ["--p", "/src/project"],
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
withOptionChangeAndDiscrepancyExplanation("with declaration should not emit anything", "--declaration"),
noChangeRun,
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
noChangeWithSubscenario("should re-emit only dts so they dont contain sourcemap"),
withOptionChangeAndDiscrepancyExplanation("with emitDeclarationOnly should not emit anything", "--emitDeclarationOnly"),
noChangeRun,
localChange(),
withOptionChangeAndDiscrepancyExplanation("with declaration should not emit anything", "--declaration"),
withOptionChange("with inlineSourceMap", "--inlineSourceMap"),
withOptionChange("with sourceMap", "--sourceMap"),
enableDeclarationMap(),
withOptionChange("with sourceMap should not emit d.ts", "--sourceMap"),
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "incremental",
subScenario: "different options with outFile",
fs: () => fs({ composite: true, outFile: "../outFile.js", module: ModuleKind.AMD }),
commandLineArgs: ["--p", "/src/project"],
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
withOptionChangeAndDiscrepancyExplanation("with declaration should not emit anything", "--declaration"),
noChangeRun,
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
noChangeWithSubscenario("should re-emit only dts so they dont contain sourcemap"),
withEmitDeclarationOnlyChangeAndDiscrepancyExplanation("with emitDeclarationOnly should not emit anything"),
noChangeRun,
localChange(),
withOptionChangeAndDiscrepancyExplanation("with declaration should not emit anything", "--declaration"),
withOptionChange("with inlineSourceMap", "--inlineSourceMap"),
withOptionChange("with sourceMap", "--sourceMap"),
enableDeclarationMap(),
withOptionChange("with sourceMap should not emit d.ts", "--sourceMap"),
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "incremental",
subScenario: "different options with incremental",
fs: () => fs({ incremental: true }),
commandLineArgs: ["--p", "/src/project"],
edits: [
withOptionChangeAndExportExplanation("with sourceMap", "--sourceMap"),
withOptionChangeAndExportExplanation("should re-emit only js so they dont contain sourcemap"),
withOptionChange("with declaration, emit Dts and should not emit js", "--declaration"),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
nochangeWithIncrementalDeclarationFromBeforeExplaination(),
localChange(),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
nochangeWithIncrementalDeclarationFromBeforeExplaination(),
withOptionChange("with inlineSourceMap", "--inlineSourceMap"),
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("emit js files"),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
withOptionChange("with declaration and declarationMap, should not re-emit", "--declaration", "--declarationMap"),
],
baselinePrograms: true,
});
verifyTscWithEdits({
scenario: "incremental",
subScenario: "different options with incremental with outFile",
fs: () => fs({ incremental: true, outFile: "../outFile.js", module: ModuleKind.AMD }),
commandLineArgs: ["--p", "/src/project"],
edits: [
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("should re-emit only js so they dont contain sourcemap"),
withOptionChange("with declaration, emit Dts and should not emit js", "--declaration"),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
nochangeWithIncrementalOutDeclarationFromBeforeExplaination(),
localChange(),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
nochangeWithIncrementalOutDeclarationFromBeforeExplaination(),
withOptionChange("with inlineSourceMap", "--inlineSourceMap"),
withOptionChange("with sourceMap", "--sourceMap"),
noChangeWithSubscenario("emit js files"),
withOptionChange("with declaration and declarationMap", "--declaration", "--declarationMap"),
withOptionChange("with declaration and declarationMap, should not re-emit", "--declaration", "--declarationMap"),
],
baselinePrograms: true,
});
});
});
}

View File

@@ -410,5 +410,36 @@ X;`,
],
baselineDependencies: true,
});
verifyTscWatch({
scenario: "projectsWithReferences",
subScenario: "when declarationMap changes for dependency",
sys: () => createSystemWithSolutionBuild(
["core"],
[
libFile,
TestFSWithWatch.getTsBuildProjectFile("sample1", "core/tsconfig.json"),
TestFSWithWatch.getTsBuildProjectFile("sample1", "core/index.ts"),
TestFSWithWatch.getTsBuildProjectFile("sample1", "core/anotherModule.ts"),
TestFSWithWatch.getTsBuildProjectFile("sample1", "core/some_decl.d.ts"),
TestFSWithWatch.getTsBuildProjectFile("sample1", "logic/tsconfig.json"),
TestFSWithWatch.getTsBuildProjectFile("sample1", "logic/index.ts"),
],
{ currentDirectory: `${TestFSWithWatch.tsbuildProjectsLocation}/sample1` }
),
commandLineArgs: ["-w", "-p", "logic"],
changes: [
{
caption: "change declration map in core",
change: sys => {
replaceFileText(sys, TestFSWithWatch.getTsBuildProjectFilePath("sample1", "core/tsconfig.json"), `"declarationMap": true,`, `"declarationMap": false,`);
const solutionBuilder = createSolutionBuilder(sys, ["core"]);
solutionBuilder.build();
},
timeouts: sys => sys.runQueuedTimeoutCallbacks(0),
},
],
baselineDependencies: true
});
});
}

View File

@@ -611,4 +611,97 @@ namespace ts.tscWatch {
});
});
});
describe("unittests:: tsc-watch:: watchAPI:: when builder emit occurs with emitOnlyDtsFiles", () => {
function verify(subScenario: string, outFile?: string) {
it(subScenario, () => {
const system = createWatchedSystem({
[`${projectRoot}/tsconfig.json`]: JSON.stringify({
compilerOptions: { composite: true, noEmitOnError: true, module: "amd", outFile },
files: ["a.ts", "b.ts"],
}),
[`${projectRoot}/a.ts`]: "export const x = 10;",
[`${projectRoot}/b.ts`]: "export const y: 10 = 20;",
[libFile.path]: libFile.content,
}, { currentDirectory: projectRoot });
const baseline = createBaseline(system);
const compilerHost = createWatchCompilerHostOfConfigFileForBaseline({
cb: baseline.cb,
system,
configFileName: `${projectRoot}/tsconfig.json`,
optionsToExtend: { extendedDiagnostics: true }
});
const originalEmitProgram = compilerHost.afterProgramCreate;
compilerHost.afterProgramCreate = myAfterProgramCreate;
let callFullEmit = true;
const watch = createWatchProgram(compilerHost);
runWatchBaseline({
scenario: "watchApi",
subScenario,
commandLineArgs: ["--w", "--extendedDiagnostics"],
...baseline,
changes: [
{
caption: "Fix error but run emit with emitOnlyDts",
change: sys => {
sys.writeFile(`${projectRoot}/b.ts`, `export const y = 10;`);
callFullEmit = false;
},
timeouts: checkSingleTimeoutQueueLengthAndRun,
},
{
caption: "Emit with emitOnlyDts shouldnt emit anything",
change: () => {
const program = watch.getCurrentProgram();
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ true);
baseline.cb(program);
},
timeouts: sys => sys.checkTimeoutQueueLength(0),
},
{
caption: "Emit all files",
change: () => {
const program = watch.getCurrentProgram();
program.emit();
baseline.cb(program);
},
timeouts: sys => sys.checkTimeoutQueueLength(0),
},
{
caption: "Emit with emitOnlyDts shouldnt emit anything",
change: () => {
const program = watch.getCurrentProgram();
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ true);
baseline.cb(program);
},
timeouts: sys => sys.checkTimeoutQueueLength(0),
},
{
caption: "Emit full should not emit anything",
change: () => {
const program = watch.getCurrentProgram();
program.emit();
baseline.cb(program);
},
timeouts: sys => sys.checkTimeoutQueueLength(0),
},
],
watchOrSolution: watch
});
function myAfterProgramCreate(program: EmitAndSemanticDiagnosticsBuilderProgram) {
if (callFullEmit) {
originalEmitProgram!.call(compilerHost, program);
}
else {
program.getSemanticDiagnostics(); // Get Diagnostics
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ true);
baseline.cb(program);
}
}
});
}
verify("when emitting with emitOnlyDtsFiles");
verify("when emitting with emitOnlyDtsFiles with outFile", "outFile.js");
});
}