Skip to content

Commit d47dbb7

Browse files
committed
Merge remote-tracking branch 'upstream/master' into issue-26981
2 parents 6f0fcef + 3426167 commit d47dbb7

33 files changed

+847
-145
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ namespace ts {
388388
const intersectionTypes = createMap<IntersectionType>();
389389
const literalTypes = createMap<LiteralType>();
390390
const indexedAccessTypes = createMap<IndexedAccessType>();
391-
const conditionalTypes = createMap<Type>();
391+
const conditionalTypes = createMap<Type | undefined>();
392392
const evolvingArrayTypes: EvolvingArrayType[] = [];
393393
const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
394394

@@ -10138,11 +10138,24 @@ namespace ts {
1013810138
const trueType = instantiateType(root.trueType, mapper);
1013910139
const falseType = instantiateType(root.falseType, mapper);
1014010140
const instantiationId = `${root.isDistributive ? "d" : ""}${getTypeId(checkType)}>${getTypeId(extendsType)}?${getTypeId(trueType)}:${getTypeId(falseType)}`;
10141-
const result = conditionalTypes.get(instantiationId);
10142-
if (result) {
10143-
return result;
10141+
if (conditionalTypes.has(instantiationId)) {
10142+
const result = conditionalTypes.get(instantiationId);
10143+
if (result !== undefined) {
10144+
return result;
10145+
}
10146+
// Somehow the conditional type depends on itself - usually via `infer` types in the `extends` clause
10147+
// paired with a (potentially deferred) circularly constrained type.
10148+
// The conditional _must_ be deferred.
10149+
const deferred = getDeferredConditionalType(root, mapper, /*combinedMapper*/ undefined, checkType, extendsType, trueType, falseType);
10150+
conditionalTypes.set(instantiationId, deferred);
10151+
return deferred;
1014410152
}
10153+
conditionalTypes.set(instantiationId, undefined);
1014510154
const newResult = getConditionalTypeWorker(root, mapper, checkType, extendsType, trueType, falseType);
10155+
const cachedRecursiveResult = conditionalTypes.get(instantiationId);
10156+
if (cachedRecursiveResult) {
10157+
return cachedRecursiveResult;
10158+
}
1014610159
conditionalTypes.set(instantiationId, newResult);
1014710160
return newResult;
1014810161
}
@@ -10206,6 +10219,10 @@ namespace ts {
1020610219
}
1020710220
}
1020810221
// Return a deferred type for a check that is neither definitely true nor definitely false
10222+
return getDeferredConditionalType(root, mapper, combinedMapper, checkType, extendsType, trueType, falseType);
10223+
}
10224+
10225+
function getDeferredConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined, combinedMapper: TypeMapper | undefined, checkType: Type, extendsType: Type, trueType: Type, falseType: Type) {
1020910226
const erasedCheckType = getActualTypeVariable(checkType);
1021010227
const result = <ConditionalType>createType(TypeFlags.Conditional);
1021110228
result.root = root;

src/compiler/emitter.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,81 @@ namespace ts {
131131
return Extension.Js;
132132
}
133133

134+
function rootDirOfOptions(configFile: ParsedCommandLine) {
135+
return configFile.options.rootDir || getDirectoryPath(Debug.assertDefined(configFile.options.configFilePath));
136+
}
137+
138+
/* @internal */
139+
export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
140+
Debug.assert(!fileExtensionIs(inputFileName, Extension.Dts) && hasTSFileExtension(inputFileName));
141+
const relativePath = getRelativePathFromDirectory(rootDirOfOptions(configFile), inputFileName, ignoreCase);
142+
const outputPath = resolvePath(configFile.options.declarationDir || configFile.options.outDir || getDirectoryPath(Debug.assertDefined(configFile.options.configFilePath)), relativePath);
143+
return changeExtension(outputPath, Extension.Dts);
144+
}
145+
146+
function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
147+
const relativePath = getRelativePathFromDirectory(rootDirOfOptions(configFile), inputFileName, ignoreCase);
148+
const outputPath = resolvePath(configFile.options.outDir || getDirectoryPath(Debug.assertDefined(configFile.options.configFilePath)), relativePath);
149+
const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
150+
const outputFileName = changeExtension(outputPath, isJsonFile ?
151+
Extension.Json :
152+
fileExtensionIs(inputFileName, Extension.Tsx) && configFile.options.jsx === JsxEmit.Preserve ?
153+
Extension.Jsx :
154+
Extension.Js);
155+
return !isJsonFile || comparePaths(inputFileName, outputFileName, Debug.assertDefined(configFile.options.configFilePath), ignoreCase) !== Comparison.EqualTo ?
156+
outputFileName :
157+
undefined;
158+
}
159+
160+
/*@internal*/
161+
export function getAllProjectOutputs(configFile: ParsedCommandLine, ignoreCase: boolean): ReadonlyArray<string> {
162+
let outputs: string[] | undefined;
163+
const addOutput = (path: string | undefined) => path && (outputs || (outputs = [])).push(path);
164+
if (configFile.options.outFile || configFile.options.out) {
165+
const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
166+
addOutput(jsFilePath);
167+
addOutput(sourceMapFilePath);
168+
addOutput(declarationFilePath);
169+
addOutput(declarationMapPath);
170+
addOutput(buildInfoPath);
171+
}
172+
else {
173+
for (const inputFileName of configFile.fileNames) {
174+
if (fileExtensionIs(inputFileName, Extension.Dts)) continue;
175+
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase);
176+
addOutput(js);
177+
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
178+
if (configFile.options.sourceMap) {
179+
addOutput(`${js}.map`);
180+
}
181+
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
182+
const dts = getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
183+
addOutput(dts);
184+
if (configFile.options.declarationMap) {
185+
addOutput(`${dts}.map`);
186+
}
187+
}
188+
}
189+
addOutput(getOutputPathForBuildInfo(configFile.options));
190+
}
191+
return outputs || emptyArray;
192+
}
193+
194+
/*@internal*/
195+
export function getFirstProjectOutput(configFile: ParsedCommandLine, ignoreCase: boolean): string {
196+
if (configFile.options.outFile || configFile.options.out) {
197+
const { jsFilePath } = getOutputPathsForBundle(configFile.options, /*forceDtsPaths*/ false);
198+
return Debug.assertDefined(jsFilePath, `project ${configFile.options.configFilePath} expected to have at least one output`);
199+
}
200+
201+
for (const inputFileName of configFile.fileNames) {
202+
if (fileExtensionIs(inputFileName, Extension.Dts)) continue;
203+
const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase);
204+
if (jsFilePath) return jsFilePath;
205+
}
206+
return Debug.fail(`project ${configFile.options.configFilePath} expected to have at least one output`);
207+
}
208+
134209
/*@internal*/
135210
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
136211
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory<Bundle | SourceFile>[], declarationTransformers?: TransformerFactory<Bundle | SourceFile>[], onlyBuildInfo?: boolean): EmitResult {

src/compiler/program.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -825,11 +825,16 @@ namespace ts {
825825
}
826826
if (rootNames.length) {
827827
for (const parsedRef of resolvedProjectReferences) {
828-
if (parsedRef) {
829-
const out = parsedRef.commandLine.options.outFile || parsedRef.commandLine.options.out;
830-
if (out) {
831-
const dtsOutfile = changeExtension(out, ".d.ts");
832-
processSourceFile(dtsOutfile, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
828+
if (!parsedRef) continue;
829+
const out = parsedRef.commandLine.options.outFile || parsedRef.commandLine.options.out;
830+
if (out) {
831+
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
832+
}
833+
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
834+
for (const fileName of parsedRef.commandLine.fileNames) {
835+
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
836+
processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
837+
}
833838
}
834839
}
835840
}
@@ -2373,7 +2378,7 @@ namespace ts {
23732378
const out = referencedProject.commandLine.options.outFile || referencedProject.commandLine.options.out;
23742379
return out ?
23752380
changeExtension(out, Extension.Dts) :
2376-
getOutputDeclarationFileName(fileName, referencedProject.commandLine);
2381+
getOutputDeclarationFileName(fileName, referencedProject.commandLine, !host.useCaseSensitiveFileNames());
23772382
}
23782383

23792384
/**

src/compiler/sys.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ namespace ts {
11481148
}
11491149

11501150
function readDirectory(path: string, extensions?: ReadonlyArray<string>, excludes?: ReadonlyArray<string>, includes?: ReadonlyArray<string>, depth?: number): string[] {
1151-
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries);
1151+
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries, realpath);
11521152
}
11531153

11541154
function fileSystemEntryExists(path: string, entryKind: FileSystemEntryKind): boolean {

src/compiler/tsbuild.ts

Lines changed: 12 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -276,60 +276,6 @@ namespace ts {
276276
return getOrCreateValueFromConfigFileMap<Map<T>>(configFileMap, resolved, createMap);
277277
}
278278

279-
export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine) {
280-
const relativePath = getRelativePathFromDirectory(rootDirOfOptions(configFile.options, configFile.options.configFilePath!), inputFileName, /*ignoreCase*/ true);
281-
const outputPath = resolvePath(configFile.options.declarationDir || configFile.options.outDir || getDirectoryPath(configFile.options.configFilePath!), relativePath);
282-
return changeExtension(outputPath, Extension.Dts);
283-
}
284-
285-
function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine) {
286-
const relativePath = getRelativePathFromDirectory(rootDirOfOptions(configFile.options, configFile.options.configFilePath!), inputFileName, /*ignoreCase*/ true);
287-
const outputPath = resolvePath(configFile.options.outDir || getDirectoryPath(configFile.options.configFilePath!), relativePath);
288-
const newExtension = fileExtensionIs(inputFileName, Extension.Json) ? Extension.Json :
289-
fileExtensionIs(inputFileName, Extension.Tsx) && configFile.options.jsx === JsxEmit.Preserve ? Extension.Jsx : Extension.Js;
290-
return changeExtension(outputPath, newExtension);
291-
}
292-
293-
function getOutputFileNames(inputFileName: string, configFile: ParsedCommandLine): ReadonlyArray<string> {
294-
// outFile is handled elsewhere; .d.ts files don't generate outputs
295-
if (configFile.options.outFile || configFile.options.out || fileExtensionIs(inputFileName, Extension.Dts)) {
296-
return emptyArray;
297-
}
298-
299-
const outputs: string[] = [];
300-
const js = getOutputJSFileName(inputFileName, configFile);
301-
outputs.push(js);
302-
if (configFile.options.sourceMap) {
303-
outputs.push(`${js}.map`);
304-
}
305-
if (getEmitDeclarations(configFile.options) && !fileExtensionIs(inputFileName, Extension.Json)) {
306-
const dts = getOutputDeclarationFileName(inputFileName, configFile);
307-
outputs.push(dts);
308-
if (configFile.options.declarationMap) {
309-
outputs.push(`${dts}.map`);
310-
}
311-
}
312-
return outputs;
313-
}
314-
315-
function getOutFileOutputs(project: ParsedCommandLine, ignoreBuildInfo?: boolean): ReadonlyArray<string> {
316-
Debug.assert(!!project.options.outFile || !!project.options.out, "outFile must be set");
317-
const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(project.options, /*forceDtsPaths*/ false);
318-
319-
let outputs: string[] | undefined = [];
320-
const addOutput = (path: string | undefined) => path && (outputs || (outputs = [])).push(path);
321-
addOutput(jsFilePath);
322-
addOutput(sourceMapFilePath);
323-
addOutput(declarationFilePath);
324-
addOutput(declarationMapPath);
325-
if (!ignoreBuildInfo) addOutput(buildInfoPath);
326-
return outputs || emptyArray;
327-
}
328-
329-
function rootDirOfOptions(opts: CompilerOptions, configFileName: string) {
330-
return opts.rootDir || getDirectoryPath(configFileName);
331-
}
332-
333279
function newer(date1: Date, date2: Date): Date {
334280
return date2 > date1 ? date2 : date1;
335281
}
@@ -715,7 +661,7 @@ namespace ts {
715661
}
716662

717663
// Collect the expected outputs of this project
718-
const outputs = getAllProjectOutputs(project);
664+
const outputs = getAllProjectOutputs(project, !host.useCaseSensitiveFileNames());
719665

720666
if (outputs.length === 0) {
721667
return {
@@ -1199,10 +1145,10 @@ namespace ts {
11991145
// Update time stamps for rest of the outputs
12001146
newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker(configFile, newestDeclarationFileContentChangedTime, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs);
12011147

1202-
const status: UpToDateStatus = {
1148+
const status: Status.UpToDate = {
12031149
type: UpToDateStatusType.UpToDate,
12041150
newestDeclarationFileContentChangedTime: anyDtsChanged ? maximumDate : newestDeclarationFileContentChangedTime,
1205-
oldestOutputFileName: outputFiles.length ? outputFiles[0].name : getFirstProjectOutput(configFile)
1151+
oldestOutputFileName: outputFiles.length ? outputFiles[0].name : getFirstProjectOutput(configFile, !host.useCaseSensitiveFileNames())
12061152
};
12071153
diagnostics.removeKey(proj);
12081154
projectStatus.setValue(proj, status);
@@ -1275,7 +1221,7 @@ namespace ts {
12751221
// Update timestamps for dts
12761222
const newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker(config, minimumDate, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs);
12771223

1278-
const status: UpToDateStatus = {
1224+
const status: Status.UpToDate = {
12791225
type: UpToDateStatusType.UpToDate,
12801226
newestDeclarationFileContentChangedTime,
12811227
oldestOutputFileName: outputFiles[0].name
@@ -1292,11 +1238,16 @@ namespace ts {
12921238
return reportStatus(Diagnostics.A_non_dry_build_would_update_timestamps_for_output_of_project_0, proj.options.configFilePath!);
12931239
}
12941240
const priorNewestUpdateTime = updateOutputTimestampsWorker(proj, minimumDate, Diagnostics.Updating_output_timestamps_of_project_0);
1295-
projectStatus.setValue(proj.options.configFilePath as ResolvedConfigFilePath, { type: UpToDateStatusType.UpToDate, newestDeclarationFileContentChangedTime: priorNewestUpdateTime } as UpToDateStatus);
1241+
const status: Status.UpToDate = {
1242+
type: UpToDateStatusType.UpToDate,
1243+
newestDeclarationFileContentChangedTime: priorNewestUpdateTime,
1244+
oldestOutputFileName: getFirstProjectOutput(proj, !host.useCaseSensitiveFileNames())
1245+
};
1246+
projectStatus.setValue(proj.options.configFilePath as ResolvedConfigFilePath, status);
12961247
}
12971248

12981249
function updateOutputTimestampsWorker(proj: ParsedCommandLine, priorNewestUpdateTime: Date, verboseMessage: DiagnosticMessage, skipOutputs?: FileMap<true>) {
1299-
const outputs = getAllProjectOutputs(proj);
1250+
const outputs = getAllProjectOutputs(proj, !host.useCaseSensitiveFileNames());
13001251
if (!skipOutputs || outputs.length !== skipOutputs.getSize()) {
13011252
if (options.verbose) {
13021253
reportStatus(verboseMessage, proj.options.configFilePath!);
@@ -1332,7 +1283,7 @@ namespace ts {
13321283
reportParseConfigFileDiagnostic(proj);
13331284
continue;
13341285
}
1335-
const outputs = getAllProjectOutputs(parsed);
1286+
const outputs = getAllProjectOutputs(parsed, !host.useCaseSensitiveFileNames());
13361287
for (const output of outputs) {
13371288
if (host.fileExists(output)) {
13381289
filesToDelete.push(output);
@@ -1494,35 +1445,6 @@ namespace ts {
14941445
return combinePaths(project, "tsconfig.json") as ResolvedConfigFileName;
14951446
}
14961447

1497-
export function getAllProjectOutputs(project: ParsedCommandLine): ReadonlyArray<string> {
1498-
if (project.options.outFile || project.options.out) {
1499-
return getOutFileOutputs(project);
1500-
}
1501-
else {
1502-
const outputs: string[] = [];
1503-
for (const inputFile of project.fileNames) {
1504-
outputs.push(...getOutputFileNames(inputFile, project));
1505-
}
1506-
const buildInfoPath = getOutputPathForBuildInfo(project.options);
1507-
if (buildInfoPath) outputs.push(buildInfoPath);
1508-
return outputs;
1509-
}
1510-
}
1511-
1512-
function getFirstProjectOutput(project: ParsedCommandLine): string {
1513-
if (project.options.outFile || project.options.out) {
1514-
return first(getOutFileOutputs(project));
1515-
}
1516-
1517-
for (const inputFile of project.fileNames) {
1518-
const outputs = getOutputFileNames(inputFile, project);
1519-
if (outputs.length) {
1520-
return first(outputs);
1521-
}
1522-
}
1523-
return Debug.fail(`project ${project.options.configFilePath} expected to have at least one output`);
1524-
}
1525-
15261448
export function formatUpToDateStatus<T>(configFileName: string, status: UpToDateStatus, relName: (fileName: string) => string, formatMessage: (message: DiagnosticMessage, ...args: string[]) => T) {
15271449
switch (status.type) {
15281450
case UpToDateStatusType.OutOfDateWithSelf:

0 commit comments

Comments
 (0)