· 5 years ago · Jan 17, 2021, 06:46 PM
1export function createProgram(createProgramOptions: CreateProgramOptions): Program;
2export function createProgram(rootNames: readonly string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: readonly Diagnostic[]): Program;
3export function createProgram(rootNamesOrOptions: readonly string[] | CreateProgramOptions, _options?: CompilerOptions, _host?: CompilerHost, _oldProgram?: Program, _configFileParsingDiagnostics?: readonly Diagnostic[]): Program {
4 const createProgramOptions = isArray(rootNamesOrOptions) ? createCreateProgramOptions(rootNamesOrOptions, _options!, _host, _oldProgram, _configFileParsingDiagnostics) : rootNamesOrOptions; // TODO: GH#18217
5 const { rootNames, options, configFileParsingDiagnostics, projectReferences } = createProgramOptions;
6 let { oldProgram } = createProgramOptions;
7
8 let processingDefaultLibFiles: SourceFile[] | undefined;
9 let processingOtherFiles: SourceFile[] | undefined;
10 let files: SourceFile[];
11 let symlinks: SymlinkCache | undefined;
12 let commonSourceDirectory: string;
13 let diagnosticsProducingTypeChecker: TypeChecker;
14 let noDiagnosticsTypeChecker: TypeChecker;
15 let classifiableNames: Set<__String>;
16 const ambientModuleNameToUnmodifiedFileName = new Map<string, string>();
17 // Todo:: Use this to report why file was included in --extendedDiagnostics
18 let refFileMap: MultiMap<Path, ts.RefFile> | undefined;
19
20 const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
21 const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};
22
23 let resolvedTypeReferenceDirectives = new Map<string, ResolvedTypeReferenceDirective | undefined>();
24 let fileProcessingDiagnostics = createDiagnosticCollection();
25
26 // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
27 // This works as imported modules are discovered recursively in a depth first manner, specifically:
28 // - For each root file, findSourceFile is called.
29 // - This calls processImportedModules for each module imported in the source file.
30 // - This calls resolveModuleNames, and then calls findSourceFile for each resolved module.
31 // As all these operations happen - and are nested - within the createProgram call, they close over the below variables.
32 // The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses.
33 const maxNodeModuleJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 0;
34 let currentNodeModulesDepth = 0;
35
36 // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track
37 // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed.
38 const modulesWithElidedImports = new Map<string, boolean>();
39
40 // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled.
41 const sourceFilesFoundSearchingNodeModules = new Map<string, boolean>();
42
43 tracing.push(tracing.Phase.Program, "createProgram", { configFilePath: options.configFilePath, rootDir: options.rootDir }, /*separateBeginAndEnd*/ true);
44 performance.mark("beforeProgram");
45
46 const host = createProgramOptions.host || createCompilerHost(options);
47 const configParsingHost = parseConfigHostFromCompilerHostLike(host);
48
49 let skipDefaultLib = options.noLib;
50 const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options));
51 const defaultLibraryPath = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(getDefaultLibraryFileName());
52 const programDiagnostics = createDiagnosticCollection();
53 const currentDirectory = host.getCurrentDirectory();
54 const supportedExtensions = getSupportedExtensions(options);
55 const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions);
56
57 // Map storing if there is emit blocking diagnostics for given input
58 const hasEmitBlockingDiagnostics = new Map<string, boolean>();
59 let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | null | undefined;
60
61 let moduleResolutionCache: ModuleResolutionCache | undefined;
62 let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[];
63 const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
64 if (host.resolveModuleNames) {
65 actualResolveModuleNamesWorker = (moduleNames, containingFile, reusedNames, redirectedReference) => host.resolveModuleNames!(Debug.checkEachDefined(moduleNames), containingFile, reusedNames, redirectedReference, options).map(resolved => {
66 // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
67 if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
68 return resolved as ResolvedModuleFull;
69 }
70 const withExtension = clone(resolved) as ResolvedModuleFull;
71 withExtension.extension = extensionFromPath(resolved.resolvedFileName);
72 return withExtension;
73 });
74 }
75 else {
76 moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x), options);
77 const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache, redirectedReference).resolvedModule!; // TODO: GH#18217
78 actualResolveModuleNamesWorker = (moduleNames, containingFile, _reusedNames, redirectedReference) => loadWithLocalCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader);
79 }
80
81 let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference) => (ResolvedTypeReferenceDirective | undefined)[];
82 if (host.resolveTypeReferenceDirectives) {
83 actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference) => host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options);
84 }
85 else {
86 const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveTypeReferenceDirective(typesRef, containingFile, options, host, redirectedReference).resolvedTypeReferenceDirective!; // TODO: GH#18217
87 actualResolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile, redirectedReference) => loadWithLocalCache<ResolvedTypeReferenceDirective>(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, loader);
88 }
89
90 // Map from a stringified PackageId to the source file with that id.
91 // Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile).
92 // `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around.
93 const packageIdToSourceFile = new Map<string, SourceFile>();
94 // Maps from a SourceFile's `.path` to the name of the package it was imported with.
95 let sourceFileToPackageName = new Map<string, string>();
96 // Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it.
97 let redirectTargetsMap = createMultiMap<string>();
98
99 /**
100 * map with
101 * - SourceFile if present
102 * - false if sourceFile missing for source of project reference redirect
103 * - undefined otherwise
104 */
105 const filesByName = new Map<string, SourceFile | false | undefined>();
106 let missingFilePaths: readonly Path[] | undefined;
107 // stores 'filename -> file association' ignoring case
108 // used to track cases when two file names differ only in casing
109 const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? new Map<string, SourceFile>() : undefined;
110
111 // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files
112 let resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined;
113 let projectReferenceRedirects: ESMap<Path, ResolvedProjectReference | false> | undefined;
114 let mapFromFileToProjectReferenceRedirects: ESMap<Path, Path> | undefined;
115 let mapFromToProjectReferenceRedirectSource: ESMap<Path, SourceOfProjectReferenceRedirect> | undefined;
116
117 const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() &&
118 !options.disableSourceOfProjectReferenceRedirect;
119 const { onProgramCreateComplete, fileExists, directoryExists } = updateHostForUseSourceOfProjectReferenceRedirect({
120 compilerHost: host,
121 getSymlinkCache,
122 useSourceOfProjectReferenceRedirect,
123 toPath,
124 getResolvedProjectReferences,
125 getSourceOfProjectReferenceRedirect,
126 forEachResolvedProjectReference
127 });
128
129 tracing.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
130 const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
131 tracing.pop();
132 // We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
133 // `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`.
134 let structureIsReused: StructureIsReused;
135 tracing.push(tracing.Phase.Program, "tryReuseStructureFromOldProgram", {});
136 structureIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const
137 tracing.pop();
138 if (structureIsReused !== StructureIsReused.Completely) {
139 processingDefaultLibFiles = [];
140 processingOtherFiles = [];
141
142 if (projectReferences) {
143 if (!resolvedProjectReferences) {
144 resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
145 }
146 if (rootNames.length) {
147 for (const parsedRef of resolvedProjectReferences) {
148 if (!parsedRef) continue;
149 const out = outFile(parsedRef.commandLine.options);
150 if (useSourceOfProjectReferenceRedirect) {
151 if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
152 for (const fileName of parsedRef.commandLine.fileNames) {
153 processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
154 }
155 }
156 }
157 else {
158 if (out) {
159 processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
160 }
161 else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
162 for (const fileName of parsedRef.commandLine.fileNames) {
163 if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
164 processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
165 }
166 }
167 }
168 }
169 }
170 }
171 }
172
173 tracing.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length });
174 forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false));
175 tracing.pop();
176
177 // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
178 const typeReferences: string[] = rootNames.length ? getAutomaticTypeDirectiveNames(options, host) : emptyArray;
179
180 if (typeReferences.length) {
181 tracing.push(tracing.Phase.Program, "processTypeReferences", { count: typeReferences.length });
182 // This containingFilename needs to match with the one used in managed-side
183 const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory();
184 const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
185 const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
186 for (let i = 0; i < typeReferences.length; i++) {
187 processTypeReferenceDirective(typeReferences[i], resolutions[i]);
188 }
189 tracing.pop();
190 }
191
192 // Do not process the default library if:
193 // - The '--noLib' flag is used.
194 // - A 'no-default-lib' reference comment is encountered in
195 // processing the root files.
196 if (rootNames.length && !skipDefaultLib) {
197 // If '--lib' is not specified, include default library file according to '--target'
198 // otherwise, using options specified in '--lib' instead of '--target' default library file
199 const defaultLibraryFileName = getDefaultLibraryFileName();
200 if (!options.lib && defaultLibraryFileName) {
201 processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false);
202 }
203 else {
204 forEach(options.lib, libFileName => {
205 processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false);
206 });
207 }
208 }
209
210 missingFilePaths = arrayFrom(mapDefinedIterator(filesByName.entries(), ([path, file]) => file === undefined ? path as Path : undefined));
211 files = stableSort(processingDefaultLibFiles, compareDefaultLibFiles).concat(processingOtherFiles);
212 processingDefaultLibFiles = undefined;
213 processingOtherFiles = undefined;
214 }
215
216 Debug.assert(!!missingFilePaths);
217
218 // Release any files we have acquired in the old program but are
219 // not part of the new program.
220 if (oldProgram && host.onReleaseOldSourceFile) {
221 const oldSourceFiles = oldProgram.getSourceFiles();
222 for (const oldSourceFile of oldSourceFiles) {
223 const newFile = getSourceFileByPath(oldSourceFile.resolvedPath);
224 if (shouldCreateNewSourceFile || !newFile ||
225 // old file wasnt redirect but new file is
226 (oldSourceFile.resolvedPath === oldSourceFile.path && newFile.resolvedPath !== oldSourceFile.path)) {
227 host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions(), !!getSourceFileByPath(oldSourceFile.path));
228 }
229 }
230 oldProgram.forEachResolvedProjectReference(resolvedProjectReference => {
231 if (!getResolvedProjectReferenceByPath(resolvedProjectReference.sourceFile.path)) {
232 host.onReleaseOldSourceFile!(resolvedProjectReference.sourceFile, oldProgram!.getCompilerOptions(), /*hasSourceFileByPath*/ false);
233 }
234 });
235 }
236
237 // unconditionally set oldProgram to undefined to prevent it from being captured in closure
238 oldProgram = undefined;
239
240 const program: Program = {
241 getRootFileNames: () => rootNames,
242 getSourceFile,
243 getSourceFileByPath,
244 getSourceFiles: () => files,
245 getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217
246 getRefFileMap: () => refFileMap,
247 getFilesByNameMap: () => filesByName,
248 getCompilerOptions: () => options,
249 getSyntacticDiagnostics,
250 getOptionsDiagnostics,
251 getGlobalDiagnostics,
252 getSemanticDiagnostics,
253 getCachedSemanticDiagnostics,
254 getSuggestionDiagnostics,
255 getDeclarationDiagnostics,
256 getBindAndCheckDiagnostics,
257 getProgramDiagnostics,
258 getTypeChecker,
259 getClassifiableNames,
260 getDiagnosticsProducingTypeChecker,
261 getCommonSourceDirectory,
262 emit,
263 getCurrentDirectory: () => currentDirectory,
264 getNodeCount: () => getDiagnosticsProducingTypeChecker().getNodeCount(),
265 getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
266 getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
267 getTypeCatalog: () => getDiagnosticsProducingTypeChecker().getTypeCatalog(),
268 getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
269 getInstantiationCount: () => getDiagnosticsProducingTypeChecker().getInstantiationCount(),
270 getRelationCacheSizes: () => getDiagnosticsProducingTypeChecker().getRelationCacheSizes(),
271 getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
272 getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
273 isSourceFileFromExternalLibrary,
274 isSourceFileDefaultLibrary,
275 dropDiagnosticsProducingTypeChecker,
276 getSourceFileFromReference,
277 getLibFileFromReference,
278 sourceFileToPackageName,
279 redirectTargetsMap,
280 isEmittedFile,
281 getConfigFileParsingDiagnostics,
282 getResolvedModuleWithFailedLookupLocationsFromCache,
283 getProjectReferences,
284 getResolvedProjectReferences,
285 getProjectReferenceRedirect,
286 getResolvedProjectReferenceToRedirect,
287 getResolvedProjectReferenceByPath,
288 forEachResolvedProjectReference,
289 isSourceOfProjectReferenceRedirect,
290 emitBuildInfo,
291 fileExists,
292 directoryExists,
293 getSymlinkCache,
294 realpath: host.realpath?.bind(host),
295 useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
296 structureIsReused,
297 };
298
299 onProgramCreateComplete();
300 verifyCompilerOptions();
301 performance.mark("afterProgram");
302 performance.measure("Program", "beforeProgram", "afterProgram");
303 tracing.pop();
304
305 return program;
306
307 function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, reusedNames: string[] | undefined): readonly ResolvedModuleFull[] {
308 if (!moduleNames.length) return emptyArray;
309 const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory);
310 const redirectedReference = getRedirectReferenceForResolution(containingFile);
311 tracing.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName });
312 performance.mark("beforeResolveModule");
313 const result = actualResolveModuleNamesWorker(moduleNames, containingFileName, reusedNames, redirectedReference);
314 performance.mark("afterResolveModule");
315 performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule");
316 tracing.pop();
317 return result;
318 }
319
320 function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] {
321 if (!typeDirectiveNames.length) return [];
322 const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile;
323 const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined;
324 tracing.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName });
325 performance.mark("beforeResolveTypeReference");
326 const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference);
327 performance.mark("afterResolveTypeReference");
328 performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference");
329 tracing.pop();
330 return result;
331 }
332
333 function getRedirectReferenceForResolution(file: SourceFile) {
334 const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName);
335 if (redirect || !fileExtensionIs(file.originalFileName, Extension.Dts)) return redirect;
336
337 // The originalFileName could not be actual source file name if file found was d.ts from referecned project
338 // So in this case try to look up if this is output from referenced project, if it is use the redirected project in that case
339 const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.originalFileName, file.path);
340 if (resultFromDts) return resultFromDts;
341
342 // If preserveSymlinks is true, module resolution wont jump the symlink
343 // but the resolved real path may be the .d.ts from project reference
344 // Note:: Currently we try the real path only if the
345 // file is from node_modules to avoid having to run real path on all file paths
346 if (!host.realpath || !options.preserveSymlinks || !stringContains(file.originalFileName, nodeModulesPathPart)) return undefined;
347 const realDeclarationFileName = host.realpath(file.originalFileName);
348 const realDeclarationPath = toPath(realDeclarationFileName);
349 return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationFileName, realDeclarationPath);
350 }
351
352 function getRedirectReferenceForResolutionFromSourceOfProject(fileName: string, filePath: Path) {
353 const source = getSourceOfProjectReferenceRedirect(fileName);
354 if (isString(source)) return getResolvedProjectReferenceToRedirect(source);
355 if (!source) return undefined;
356 // Output of .d.ts file so return resolved ref that matches the out file name
357 return forEachResolvedProjectReference(resolvedRef => {
358 const out = outFile(resolvedRef.commandLine.options);
359 if (!out) return undefined;
360 return toPath(out) === filePath ? resolvedRef : undefined;
361 });
362 }
363
364 function compareDefaultLibFiles(a: SourceFile, b: SourceFile) {
365 return compareValues(getDefaultLibFilePriority(a), getDefaultLibFilePriority(b));
366 }
367
368 function getDefaultLibFilePriority(a: SourceFile) {
369 if (containsPath(defaultLibraryPath, a.fileName, /*ignoreCase*/ false)) {
370 const basename = getBaseFileName(a.fileName);
371 if (basename === "lib.d.ts" || basename === "lib.es6.d.ts") return 0;
372 const name = removeSuffix(removePrefix(basename, "lib."), ".d.ts");
373 const index = libs.indexOf(name);
374 if (index !== -1) return index + 1;
375 }
376 return libs.length + 2;
377 }
378
379 function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined {
380 return moduleResolutionCache && resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache);
381 }
382
383 function toPath(fileName: string): Path {
384 return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
385 }
386
387 function getCommonSourceDirectory() {
388 if (commonSourceDirectory === undefined) {
389 const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, program));
390 if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) {
391 // If a rootDir is specified use it as the commonSourceDirectory
392 commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
393 }
394 else if (options.composite && options.configFilePath) {
395 // Project compilations never infer their root from the input source paths
396 commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath));
397 checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory);
398 }
399 else {
400 commonSourceDirectory = computeCommonSourceDirectory(emittedFiles);
401 }
402
403 if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
404 // Make sure directory path ends with directory separator so this string can directly
405 // used to replace with "" to get the relative path of the source file and the relative path doesn't
406 // start with / making it rooted path
407 commonSourceDirectory += directorySeparator;
408 }
409 }
410 return commonSourceDirectory;
411 }
412
413 function getClassifiableNames() {
414 if (!classifiableNames) {
415 // Initialize a checker so that all our files are bound.
416 getTypeChecker();
417 classifiableNames = new Set();
418
419 for (const sourceFile of files) {
420 sourceFile.classifiableNames?.forEach(value => classifiableNames.add(value));
421 }
422 }
423
424 return classifiableNames;
425 }
426
427 function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly ResolvedModuleFull[] {
428 if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
429 // If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
430 // the best we can do is fallback to the default logic.
431 return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined);
432 }
433
434 const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName);
435 if (oldSourceFile !== file && file.resolvedModules) {
436 // `file` was created for the new program.
437 //
438 // We only set `file.resolvedModules` via work from the current function,
439 // so it is defined iff we already called the current function on `file`.
440 // That call happened no later than the creation of the `file` object,
441 // which per above occurred during the current program creation.
442 // Since we assume the filesystem does not change during program creation,
443 // it is safe to reuse resolutions from the earlier call.
444 const result: ResolvedModuleFull[] = [];
445 for (const moduleName of moduleNames) {
446 const resolvedModule = file.resolvedModules.get(moduleName)!;
447 result.push(resolvedModule);
448 }
449 return result;
450 }
451 // At this point, we know at least one of the following hold:
452 // - file has local declarations for ambient modules
453 // - old program state is available
454 // With this information, we can infer some module resolutions without performing resolution.
455
456 /** An ordered list of module names for which we cannot recover the resolution. */
457 let unknownModuleNames: string[] | undefined;
458 /**
459 * The indexing of elements in this list matches that of `moduleNames`.
460 *
461 * Before combining results, result[i] is in one of the following states:
462 * * undefined: needs to be recomputed,
463 * * predictedToResolveToAmbientModuleMarker: known to be an ambient module.
464 * Needs to be reset to undefined before returning,
465 * * ResolvedModuleFull instance: can be reused.
466 */
467 let result: ResolvedModuleFull[] | undefined;
468 let reusedNames: string[] | undefined;
469 /** A transient placeholder used to mark predicted resolution in the result list. */
470 const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = <any>{};
471
472 for (let i = 0; i < moduleNames.length; i++) {
473 const moduleName = moduleNames[i];
474 // If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
475 if (file === oldSourceFile && !hasInvalidatedResolution(oldSourceFile.path)) {
476 const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName);
477 if (oldResolvedModule) {
478 if (isTraceEnabled(options, host)) {
479 trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory));
480 }
481 (result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule;
482 (reusedNames || (reusedNames = [])).push(moduleName);
483 continue;
484 }
485 }
486 // We know moduleName resolves to an ambient module provided that moduleName:
487 // - is in the list of ambient modules locally declared in the current source file.
488 // - resolved to an ambient module in the old program whose declaration is in an unmodified file
489 // (so the same module declaration will land in the new program)
490 let resolvesToAmbientModuleInNonModifiedFile = false;
491 if (contains(file.ambientModuleNames, moduleName)) {
492 resolvesToAmbientModuleInNonModifiedFile = true;
493 if (isTraceEnabled(options, host)) {
494 trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory));
495 }
496 }
497 else {
498 resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName);
499 }
500
501 if (resolvesToAmbientModuleInNonModifiedFile) {
502 (result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker;
503 }
504 else {
505 // Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
506 (unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
507 }
508 }
509
510 const resolutions = unknownModuleNames && unknownModuleNames.length
511 ? resolveModuleNamesWorker(unknownModuleNames, file, reusedNames)
512 : emptyArray;
513
514 // Combine results of resolutions and predicted results
515 if (!result) {
516 // There were no unresolved/ambient resolutions.
517 Debug.assert(resolutions.length === moduleNames.length);
518 return resolutions;
519 }
520
521 let j = 0;
522 for (let i = 0; i < result.length; i++) {
523 if (result[i]) {
524 // `result[i]` is either a `ResolvedModuleFull` or a marker.
525 // If it is the former, we can leave it as is.
526 if (result[i] === predictedToResolveToAmbientModuleMarker) {
527 result[i] = undefined!; // TODO: GH#18217
528 }
529 }
530 else {
531 result[i] = resolutions[j];
532 j++;
533 }
534 }
535 Debug.assert(j === resolutions.length);
536
537 return result;
538
539 // If we change our policy of rechecking failed lookups on each program create,
540 // we should adjust the value returned here.
541 function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string): boolean {
542 const resolutionToFile = getResolvedModule(oldSourceFile, moduleName);
543 const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName);
544 if (resolutionToFile && resolvedFile) {
545 // In the old program, we resolved to an ambient module that was in the same
546 // place as we expected to find an actual module file.
547 // We actually need to return 'false' here even though this seems like a 'true' case
548 // because the normal module resolution algorithm will find this anyway.
549 return false;
550 }
551
552 // at least one of declarations should come from non-modified source file
553 const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName);
554
555 if (!unmodifiedFile) {
556 return false;
557 }
558
559 if (isTraceEnabled(options, host)) {
560 trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile);
561 }
562 return true;
563 }
564 }
565
566 function canReuseProjectReferences(): boolean {
567 return !forEachProjectReference(
568 oldProgram!.getProjectReferences(),
569 oldProgram!.getResolvedProjectReferences(),
570 (oldResolvedRef, parent, index) => {
571 const newRef = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
572 const newResolvedRef = parseProjectReferenceConfigFile(newRef);
573 if (oldResolvedRef) {
574 // Resolved project reference has gone missing or changed
575 return !newResolvedRef || newResolvedRef.sourceFile !== oldResolvedRef.sourceFile;
576 }
577 else {
578 // A previously-unresolved reference may be resolved now
579 return newResolvedRef !== undefined;
580 }
581 },
582 (oldProjectReferences, parent) => {
583 // If array of references is changed, we cant resue old program
584 const newReferences = parent ? getResolvedProjectReferenceByPath(parent.sourceFile.path)!.commandLine.projectReferences : projectReferences;
585 return !arrayIsEqualTo(oldProjectReferences, newReferences, projectReferenceIsEqualTo);
586 }
587 );
588 }
589
590 function tryReuseStructureFromOldProgram(): StructureIsReused {
591 if (!oldProgram) {
592 return StructureIsReused.Not;
593 }
594
595 // check properties that can affect structure of the program or module resolution strategy
596 // if any of these properties has changed - structure cannot be reused
597 const oldOptions = oldProgram.getCompilerOptions();
598 if (changesAffectModuleResolution(oldOptions, options)) {
599 return StructureIsReused.Not;
600 }
601
602 // there is an old program, check if we can reuse its structure
603 const oldRootNames = oldProgram.getRootFileNames();
604 if (!arrayIsEqualTo(oldRootNames, rootNames)) {
605 return StructureIsReused.Not;
606 }
607
608 if (!arrayIsEqualTo(options.types, oldOptions.types)) {
609 return StructureIsReused.Not;
610 }
611
612 // Check if any referenced project tsconfig files are different
613 if (!canReuseProjectReferences()) {
614 return StructureIsReused.Not;
615 }
616 if (projectReferences) {
617 resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
618 }
619
620 // check if program source files has changed in the way that can affect structure of the program
621 const newSourceFiles: SourceFile[] = [];
622 const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
623 structureIsReused = StructureIsReused.Completely;
624
625 // If the missing file paths are now present, it can change the progam structure,
626 // and hence cant reuse the structure.
627 // This is same as how we dont reuse the structure if one of the file from old program is now missing
628 if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) {
629 return StructureIsReused.Not;
630 }
631
632 const oldSourceFiles = oldProgram.getSourceFiles();
633 const enum SeenPackageName { Exists, Modified }
634 const seenPackageNames = new Map<string, SeenPackageName>();
635
636 for (const oldSourceFile of oldSourceFiles) {
637 let newSourceFile = host.getSourceFileByPath
638 ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.resolvedPath, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile)
639 : host.getSourceFile(oldSourceFile.fileName, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217
640
641 if (!newSourceFile) {
642 return StructureIsReused.Not;
643 }
644
645 Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");
646
647 let fileChanged: boolean;
648 if (oldSourceFile.redirectInfo) {
649 // We got `newSourceFile` by path, so it is actually for the unredirected file.
650 // This lets us know if the unredirected file has changed. If it has we should break the redirect.
651 if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) {
652 // Underlying file has changed. Might not redirect anymore. Must rebuild program.
653 return StructureIsReused.Not;
654 }
655 fileChanged = false;
656 newSourceFile = oldSourceFile; // Use the redirect.
657 }
658 else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) {
659 // If a redirected-to source file changes, the redirect may be broken.
660 if (newSourceFile !== oldSourceFile) {
661 return StructureIsReused.Not;
662 }
663 fileChanged = false;
664 }
665 else {
666 fileChanged = newSourceFile !== oldSourceFile;
667 }
668
669 // Since the project references havent changed, its right to set originalFileName and resolvedPath here
670 newSourceFile.path = oldSourceFile.path;
671 newSourceFile.originalFileName = oldSourceFile.originalFileName;
672 newSourceFile.resolvedPath = oldSourceFile.resolvedPath;
673 newSourceFile.fileName = oldSourceFile.fileName;
674
675 const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
676 if (packageName !== undefined) {
677 // If there are 2 different source files for the same package name and at least one of them changes,
678 // they might become redirects. So we must rebuild the program.
679 const prevKind = seenPackageNames.get(packageName);
680 const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists;
681 if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) {
682 return StructureIsReused.Not;
683 }
684 seenPackageNames.set(packageName, newKind);
685 }
686
687 if (fileChanged) {
688 // The `newSourceFile` object was created for the new program.
689
690 if (!arrayIsEqualTo(oldSourceFile.libReferenceDirectives, newSourceFile.libReferenceDirectives, fileReferenceIsEqualTo)) {
691 // 'lib' references has changed. Matches behavior in changesAffectModuleResolution
692 return StructureIsReused.Not;
693 }
694
695 if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
696 // value of no-default-lib has changed
697 // this will affect if default library is injected into the list of files
698 structureIsReused = StructureIsReused.SafeModules;
699 }
700
701 // check tripleslash references
702 if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
703 // tripleslash references has changed
704 structureIsReused = StructureIsReused.SafeModules;
705 }
706
707 // check imports and module augmentations
708 collectExternalModuleReferences(newSourceFile);
709 if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
710 // imports has changed
711 structureIsReused = StructureIsReused.SafeModules;
712 }
713 if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
714 // moduleAugmentations has changed
715 structureIsReused = StructureIsReused.SafeModules;
716 }
717 if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) {
718 // dynamicImport has changed
719 structureIsReused = StructureIsReused.SafeModules;
720 }
721
722 if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
723 // 'types' references has changed
724 structureIsReused = StructureIsReused.SafeModules;
725 }
726
727 // tentatively approve the file
728 modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
729 }
730 else if (hasInvalidatedResolution(oldSourceFile.path)) {
731 // 'module/types' references could have changed
732 structureIsReused = StructureIsReused.SafeModules;
733
734 // add file to the modified list so that we will resolve it later
735 modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
736 }
737
738 // if file has passed all checks it should be safe to reuse it
739 newSourceFiles.push(newSourceFile);
740 }
741
742 if (structureIsReused !== StructureIsReused.Completely) {
743 return structureIsReused;
744 }
745
746 const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile);
747 for (const oldFile of oldSourceFiles) {
748 if (!contains(modifiedFiles, oldFile)) {
749 for (const moduleName of oldFile.ambientModuleNames) {
750 ambientModuleNameToUnmodifiedFileName.set(moduleName, oldFile.fileName);
751 }
752 }
753 }
754 // try to verify results of module resolution
755 for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
756 const moduleNames = getModuleNames(newSourceFile);
757 const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile);
758 // ensure that module resolution results are still correct
759 const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
760 if (resolutionsChanged) {
761 structureIsReused = StructureIsReused.SafeModules;
762 newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
763 }
764 else {
765 newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
766 }
767 // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
768 const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
769 const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile);
770 // ensure that types resolutions are still correct
771 const typeReferenceEesolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
772 if (typeReferenceEesolutionsChanged) {
773 structureIsReused = StructureIsReused.SafeModules;
774 newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, typeReferenceResolutions);
775 }
776 else {
777 newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
778 }
779 }
780
781 if (structureIsReused !== StructureIsReused.Completely) {
782 return structureIsReused;
783 }
784
785 if (host.hasChangedAutomaticTypeDirectiveNames?.()) {
786 return StructureIsReused.SafeModules;
787 }
788
789 missingFilePaths = oldProgram.getMissingFilePaths();
790 refFileMap = oldProgram.getRefFileMap();
791
792 // update fileName -> file mapping
793 Debug.assert(newSourceFiles.length === oldProgram.getSourceFiles().length);
794 for (const newSourceFile of newSourceFiles) {
795 filesByName.set(newSourceFile.path, newSourceFile);
796 }
797 const oldFilesByNameMap = oldProgram.getFilesByNameMap();
798 oldFilesByNameMap.forEach((oldFile, path) => {
799 if (!oldFile) {
800 filesByName.set(path, oldFile);
801 return;
802 }
803 if (oldFile.path === path) {
804 // Set the file as found during node modules search if it was found that way in old progra,
805 if (oldProgram!.isSourceFileFromExternalLibrary(oldFile)) {
806 sourceFilesFoundSearchingNodeModules.set(oldFile.path, true);
807 }
808 return;
809 }
810 filesByName.set(path, filesByName.get(oldFile.path));
811 });
812
813 files = newSourceFiles;
814 fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics();
815
816 for (const modifiedFile of modifiedSourceFiles) {
817 fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
818 }
819 resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
820
821 sourceFileToPackageName = oldProgram.sourceFileToPackageName;
822 redirectTargetsMap = oldProgram.redirectTargetsMap;
823
824 return StructureIsReused.Completely;
825 }
826
827 function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
828 return {
829 getPrependNodes,
830 getCanonicalFileName,
831 getCommonSourceDirectory: program.getCommonSourceDirectory,
832 getCompilerOptions: program.getCompilerOptions,
833 getCurrentDirectory: () => currentDirectory,
834 getNewLine: () => host.getNewLine(),
835 getSourceFile: program.getSourceFile,
836 getSourceFileByPath: program.getSourceFileByPath,
837 getSourceFiles: program.getSourceFiles,
838 getLibFileFromReference: program.getLibFileFromReference,
839 isSourceFileFromExternalLibrary,
840 getResolvedProjectReferenceToRedirect,
841 getProjectReferenceRedirect,
842 isSourceOfProjectReferenceRedirect,
843 getSymlinkCache,
844 writeFile: writeFileCallback || (
845 (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
846 isEmitBlocked,
847 readFile: f => host.readFile(f),
848 fileExists: f => {
849 // Use local caches
850 const path = toPath(f);
851 if (getSourceFileByPath(path)) return true;
852 if (contains(missingFilePaths, path)) return false;
853 // Before falling back to the host
854 return host.fileExists(f);
855 },
856 useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
857 getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo(),
858 getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref),
859 redirectTargetsMap,
860 };
861 }
862
863 function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult {
864 Debug.assert(!outFile(options));
865 tracing.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true);
866 performance.mark("beforeEmit");
867 const emitResult = emitFiles(
868 notImplementedResolver,
869 getEmitHost(writeFileCallback),
870 /*targetSourceFile*/ undefined,
871 /*transformers*/ noTransformers,
872 /*emitOnlyDtsFiles*/ false,
873 /*onlyBuildInfo*/ true
874 );
875
876 performance.mark("afterEmit");
877 performance.measure("Emit", "beforeEmit", "afterEmit");
878 tracing.pop();
879 return emitResult;
880 }
881
882 function getResolvedProjectReferences() {
883 return resolvedProjectReferences;
884 }
885
886 function getProjectReferences() {
887 return projectReferences;
888 }
889
890 function getPrependNodes() {
891 return createPrependNodes(
892 projectReferences,
893 (_ref, index) => resolvedProjectReferences![index]?.commandLine,
894 fileName => {
895 const path = toPath(fileName);
896 const sourceFile = getSourceFileByPath(path);
897 return sourceFile ? sourceFile.text : filesByName.has(path) ? undefined : host.readFile(path);
898 }
899 );
900 }
901
902 function isSourceFileFromExternalLibrary(file: SourceFile): boolean {
903 return !!sourceFilesFoundSearchingNodeModules.get(file.path);
904 }
905
906 function isSourceFileDefaultLibrary(file: SourceFile): boolean {
907 if (file.hasNoDefaultLib) {
908 return true;
909 }
910
911 if (!options.noLib) {
912 return false;
913 }
914
915 // If '--lib' is not specified, include default library file according to '--target'
916 // otherwise, using options specified in '--lib' instead of '--target' default library file
917 const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive;
918 if (!options.lib) {
919 return equalityComparer(file.fileName, getDefaultLibraryFileName());
920 }
921 else {
922 return some(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName)));
923 }
924 }
925
926 function getDiagnosticsProducingTypeChecker() {
927 return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
928 }
929
930 function dropDiagnosticsProducingTypeChecker() {
931 diagnosticsProducingTypeChecker = undefined!;
932 }
933
934 function getTypeChecker() {
935 return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
936 }
937
938 function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
939 tracing.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true);
940 const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit));
941 tracing.pop();
942 return result;
943 }
944
945 function isEmitBlocked(emitFileName: string): boolean {
946 return hasEmitBlockingDiagnostics.has(toPath(emitFileName));
947 }
948
949 function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
950 if (!forceDtsEmit) {
951 const result = handleNoEmitOptions(program, sourceFile, writeFileCallback, cancellationToken);
952 if (result) return result;
953 }
954
955 // Create the emit resolver outside of the "emitTime" tracking code below. That way
956 // any cost associated with it (like type checking) are appropriate associated with
957 // the type-checking counter.
958 //
959 // If the -out option is specified, we should not pass the source file to getEmitResolver.
960 // This is because in the -out scenario all files need to be emitted, and therefore all
961 // files need to be type checked. And the way to specify that all files need to be type
962 // checked is to not pass the file to getEmitResolver.
963 const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver(outFile(options) ? undefined : sourceFile, cancellationToken);
964
965 performance.mark("beforeEmit");
966
967 const emitResult = emitFiles(
968 emitResolver,
969 getEmitHost(writeFileCallback),
970 sourceFile,
971 getTransformers(options, customTransformers, emitOnlyDtsFiles),
972 emitOnlyDtsFiles,
973 /*onlyBuildInfo*/ false,
974 forceDtsEmit
975 );
976
977 performance.mark("afterEmit");
978 performance.measure("Emit", "beforeEmit", "afterEmit");
979 return emitResult;
980 }
981
982 function getSourceFile(fileName: string): SourceFile | undefined {
983 return getSourceFileByPath(toPath(fileName));
984 }
985
986 function getSourceFileByPath(path: Path): SourceFile | undefined {
987 return filesByName.get(path) || undefined;
988 }
989
990 function getDiagnosticsHelper<T extends Diagnostic>(
991 sourceFile: SourceFile | undefined,
992 getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken | undefined) => readonly T[],
993 cancellationToken: CancellationToken | undefined): readonly T[] {
994 if (sourceFile) {
995 return getDiagnostics(sourceFile, cancellationToken);
996 }
997 return sortAndDeduplicateDiagnostics(flatMap(program.getSourceFiles(), sourceFile => {
998 if (cancellationToken) {
999 cancellationToken.throwIfCancellationRequested();
1000 }
1001 return getDiagnostics(sourceFile, cancellationToken);
1002 }));
1003 }
1004
1005 function getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
1006 return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken);
1007 }
1008
1009 function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
1010 return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken);
1011 }
1012
1013 function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined {
1014 return sourceFile
1015 ? cachedBindAndCheckDiagnosticsForFile.perFile?.get(sourceFile.path)
1016 : cachedBindAndCheckDiagnosticsForFile.allDiagnostics;
1017 }
1018
1019 function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
1020 return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken);
1021 }
1022
1023 function getProgramDiagnostics(sourceFile: SourceFile): readonly Diagnostic[] {
1024 if (skipTypeChecking(sourceFile, options, program)) {
1025 return emptyArray;
1026 }
1027
1028 const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName);
1029 const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
1030
1031 return getMergedProgramDiagnostics(sourceFile, fileProcessingDiagnosticsInFile, programDiagnosticsInFile);
1032 }
1033
1034 function getMergedProgramDiagnostics(sourceFile: SourceFile, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) {
1035 const flatDiagnostics = flatten(allDiagnostics);
1036 if (!sourceFile.commentDirectives?.length) {
1037 return flatDiagnostics;
1038 }
1039
1040 return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics).diagnostics;
1041 }
1042
1043 function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
1044 const options = program.getCompilerOptions();
1045 // collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
1046 if (!sourceFile || outFile(options)) {
1047 return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
1048 }
1049 else {
1050 return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken);
1051 }
1052 }
1053
1054 function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): readonly DiagnosticWithLocation[] {
1055 // For JavaScript files, we report semantic errors for using TypeScript-only
1056 // constructs from within a JavaScript file as syntactic errors.
1057 if (isSourceFileJS(sourceFile)) {
1058 if (!sourceFile.additionalSyntacticDiagnostics) {
1059 sourceFile.additionalSyntacticDiagnostics = getJSSyntacticDiagnosticsForFile(sourceFile);
1060 }
1061 return concatenate(sourceFile.additionalSyntacticDiagnostics, sourceFile.parseDiagnostics);
1062 }
1063 return sourceFile.parseDiagnostics;
1064 }
1065
1066 function runWithCancellationToken<T>(func: () => T): T {
1067 try {
1068 return func();
1069 }
1070 catch (e) {
1071 if (e instanceof OperationCanceledException) {
1072 // We were canceled while performing the operation. Because our type checker
1073 // might be a bad state, we need to throw it away.
1074 //
1075 // Note: we are overly aggressive here. We do not actually *have* to throw away
1076 // the "noDiagnosticsTypeChecker". However, for simplicity, i'd like to keep
1077 // the lifetimes of these two TypeCheckers the same. Also, we generally only
1078 // cancel when the user has made a change anyways. And, in that case, we (the
1079 // program instance) will get thrown away anyways. So trying to keep one of
1080 // these type checkers alive doesn't serve much purpose.
1081 noDiagnosticsTypeChecker = undefined!;
1082 diagnosticsProducingTypeChecker = undefined!;
1083 }
1084
1085 throw e;
1086 }
1087 }
1088
1089 function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1090 return concatenate(
1091 filterSemanticDiagnotics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken), options),
1092 getProgramDiagnostics(sourceFile)
1093 );
1094 }
1095
1096 function getBindAndCheckDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1097 return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache);
1098 }
1099
1100 function getBindAndCheckDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1101 return runWithCancellationToken(() => {
1102 if (skipTypeChecking(sourceFile, options, program)) {
1103 return emptyArray;
1104 }
1105
1106 const typeChecker = getDiagnosticsProducingTypeChecker();
1107
1108 Debug.assert(!!sourceFile.bindDiagnostics);
1109
1110 const isCheckJs = isCheckJsEnabledForFile(sourceFile, options);
1111 const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false;
1112 // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins)
1113 const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX ||
1114 sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred);
1115 const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
1116 const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
1117
1118 return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined);
1119 });
1120 }
1121
1122 function getMergedBindAndCheckDiagnostics(sourceFile: SourceFile, includeBindAndCheckDiagnostics: boolean, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) {
1123 const flatDiagnostics = flatten(allDiagnostics);
1124 if (!includeBindAndCheckDiagnostics || !sourceFile.commentDirectives?.length) {
1125 return flatDiagnostics;
1126 }
1127
1128 const { diagnostics, directives } = getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics);
1129
1130 for (const errorExpectation of directives.getUnusedExpectations()) {
1131 diagnostics.push(createDiagnosticForRange(sourceFile, errorExpectation.range, Diagnostics.Unused_ts_expect_error_directive));
1132 }
1133
1134 return diagnostics;
1135 }
1136
1137 /**
1138 * Creates a map of comment directives along with the diagnostics immediately preceded by one of them.
1139 * Comments that match to any of those diagnostics are marked as used.
1140 */
1141 function getDiagnosticsWithPrecedingDirectives(sourceFile: SourceFile, commentDirectives: CommentDirective[], flatDiagnostics: Diagnostic[]) {
1142 // Diagnostics are only reported if there is no comment directive preceding them
1143 // This will modify the directives map by marking "used" ones with a corresponding diagnostic
1144 const directives = createCommentDirectivesMap(sourceFile, commentDirectives);
1145 const diagnostics = flatDiagnostics.filter(diagnostic => markPrecedingCommentDirectiveLine(diagnostic, directives) === -1);
1146
1147 return { diagnostics, directives };
1148 }
1149
1150 function getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
1151 return runWithCancellationToken(() => {
1152 return getDiagnosticsProducingTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken);
1153 });
1154 }
1155
1156 /**
1157 * @returns The line index marked as preceding the diagnostic, or -1 if none was.
1158 */
1159 function markPrecedingCommentDirectiveLine(diagnostic: Diagnostic, directives: CommentDirectivesMap) {
1160 const { file, start } = diagnostic;
1161 if (!file) {
1162 return -1;
1163 }
1164
1165 // Start out with the line just before the text
1166 const lineStarts = getLineStarts(file);
1167 let line = computeLineAndCharacterOfPosition(lineStarts, start!).line - 1; // TODO: GH#18217
1168 while (line >= 0) {
1169 // As soon as that line is known to have a comment directive, use that
1170 if (directives.markUsed(line)) {
1171 return line;
1172 }
1173
1174 // Stop searching if the line is not empty and not a comment
1175 const lineText = file.text.slice(lineStarts[line], lineStarts[line + 1]).trim();
1176 if (lineText !== "" && !/^(\s*)\/\/(.*)$/.test(lineText)) {
1177 return -1;
1178 }
1179
1180 line--;
1181 }
1182
1183 return -1;
1184 }
1185
1186 function getJSSyntacticDiagnosticsForFile(sourceFile: SourceFile): DiagnosticWithLocation[] {
1187 return runWithCancellationToken(() => {
1188 const diagnostics: DiagnosticWithLocation[] = [];
1189 walk(sourceFile, sourceFile);
1190 forEachChildRecursively(sourceFile, walk, walkArray);
1191
1192 return diagnostics;
1193
1194 function walk(node: Node, parent: Node) {
1195 // Return directly from the case if the given node doesnt want to visit each child
1196 // Otherwise break to visit each child
1197
1198 switch (parent.kind) {
1199 case SyntaxKind.Parameter:
1200 case SyntaxKind.PropertyDeclaration:
1201 case SyntaxKind.MethodDeclaration:
1202 if ((<ParameterDeclaration | PropertyDeclaration | MethodDeclaration>parent).questionToken === node) {
1203 diagnostics.push(createDiagnosticForNode(node, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?"));
1204 return "skip";
1205 }
1206 // falls through
1207 case SyntaxKind.MethodSignature:
1208 case SyntaxKind.Constructor:
1209 case SyntaxKind.GetAccessor:
1210 case SyntaxKind.SetAccessor:
1211 case SyntaxKind.FunctionExpression:
1212 case SyntaxKind.FunctionDeclaration:
1213 case SyntaxKind.ArrowFunction:
1214 case SyntaxKind.VariableDeclaration:
1215 // type annotation
1216 if ((<FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration>parent).type === node) {
1217 diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_annotations_can_only_be_used_in_TypeScript_files));
1218 return "skip";
1219 }
1220 }
1221
1222 switch (node.kind) {
1223 case SyntaxKind.ImportClause:
1224 if ((node as ImportClause).isTypeOnly) {
1225 diagnostics.push(createDiagnosticForNode(parent, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "import type"));
1226 return "skip";
1227 }
1228 break;
1229 case SyntaxKind.ExportDeclaration:
1230 if ((node as ExportDeclaration).isTypeOnly) {
1231 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "export type"));
1232 return "skip";
1233 }
1234 break;
1235 case SyntaxKind.ImportEqualsDeclaration:
1236 diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_TypeScript_files));
1237 return "skip";
1238 case SyntaxKind.ExportAssignment:
1239 if ((<ExportAssignment>node).isExportEquals) {
1240 diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_TypeScript_files));
1241 return "skip";
1242 }
1243 break;
1244 case SyntaxKind.HeritageClause:
1245 const heritageClause = <HeritageClause>node;
1246 if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
1247 diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_TypeScript_files));
1248 return "skip";
1249 }
1250 break;
1251 case SyntaxKind.InterfaceDeclaration:
1252 const interfaceKeyword = tokenToString(SyntaxKind.InterfaceKeyword);
1253 Debug.assertIsDefined(interfaceKeyword);
1254 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, interfaceKeyword));
1255 return "skip";
1256 case SyntaxKind.ModuleDeclaration:
1257 const moduleKeyword = node.flags & NodeFlags.Namespace ? tokenToString(SyntaxKind.NamespaceKeyword) : tokenToString(SyntaxKind.ModuleKeyword);
1258 Debug.assertIsDefined(moduleKeyword);
1259 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, moduleKeyword));
1260 return "skip";
1261 case SyntaxKind.TypeAliasDeclaration:
1262 diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_aliases_can_only_be_used_in_TypeScript_files));
1263 return "skip";
1264 case SyntaxKind.EnumDeclaration:
1265 const enumKeyword = Debug.checkDefined(tokenToString(SyntaxKind.EnumKeyword));
1266 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, enumKeyword));
1267 return "skip";
1268 case SyntaxKind.NonNullExpression:
1269 diagnostics.push(createDiagnosticForNode(node, Diagnostics.Non_null_assertions_can_only_be_used_in_TypeScript_files));
1270 return "skip";
1271 case SyntaxKind.AsExpression:
1272 diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.Type_assertion_expressions_can_only_be_used_in_TypeScript_files));
1273 return "skip";
1274 case SyntaxKind.TypeAssertionExpression:
1275 Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX.
1276 }
1277 }
1278
1279 function walkArray(nodes: NodeArray<Node>, parent: Node) {
1280 if (parent.decorators === nodes && !options.experimentalDecorators) {
1281 diagnostics.push(createDiagnosticForNode(parent, Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_in_your_tsconfig_or_jsconfig_to_remove_this_warning));
1282 }
1283
1284 switch (parent.kind) {
1285 case SyntaxKind.ClassDeclaration:
1286 case SyntaxKind.ClassExpression:
1287 case SyntaxKind.MethodDeclaration:
1288 case SyntaxKind.Constructor:
1289 case SyntaxKind.GetAccessor:
1290 case SyntaxKind.SetAccessor:
1291 case SyntaxKind.FunctionExpression:
1292 case SyntaxKind.FunctionDeclaration:
1293 case SyntaxKind.ArrowFunction:
1294 // Check type parameters
1295 if (nodes === (<DeclarationWithTypeParameterChildren>parent).typeParameters) {
1296 diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_parameter_declarations_can_only_be_used_in_TypeScript_files));
1297 return "skip";
1298 }
1299 // falls through
1300
1301 case SyntaxKind.VariableStatement:
1302 // Check modifiers
1303 if (nodes === parent.modifiers) {
1304 checkModifiers(parent.modifiers, parent.kind === SyntaxKind.VariableStatement);
1305 return "skip";
1306 }
1307 break;
1308 case SyntaxKind.PropertyDeclaration:
1309 // Check modifiers of property declaration
1310 if (nodes === (<PropertyDeclaration>parent).modifiers) {
1311 for (const modifier of <NodeArray<Modifier>>nodes) {
1312 if (modifier.kind !== SyntaxKind.StaticKeyword) {
1313 diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
1314 }
1315 }
1316 return "skip";
1317 }
1318 break;
1319 case SyntaxKind.Parameter:
1320 // Check modifiers of parameter declaration
1321 if (nodes === (<ParameterDeclaration>parent).modifiers) {
1322 diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Parameter_modifiers_can_only_be_used_in_TypeScript_files));
1323 return "skip";
1324 }
1325 break;
1326 case SyntaxKind.CallExpression:
1327 case SyntaxKind.NewExpression:
1328 case SyntaxKind.ExpressionWithTypeArguments:
1329 case SyntaxKind.JsxSelfClosingElement:
1330 case SyntaxKind.JsxOpeningElement:
1331 case SyntaxKind.TaggedTemplateExpression:
1332 // Check type arguments
1333 if (nodes === (<NodeWithTypeArguments>parent).typeArguments) {
1334 diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_arguments_can_only_be_used_in_TypeScript_files));
1335 return "skip";
1336 }
1337 break;
1338 }
1339 }
1340
1341 function checkModifiers(modifiers: NodeArray<Modifier>, isConstValid: boolean) {
1342 for (const modifier of modifiers) {
1343 switch (modifier.kind) {
1344 case SyntaxKind.ConstKeyword:
1345 if (isConstValid) {
1346 continue;
1347 }
1348 // to report error,
1349 // falls through
1350 case SyntaxKind.PublicKeyword:
1351 case SyntaxKind.PrivateKeyword:
1352 case SyntaxKind.ProtectedKeyword:
1353 case SyntaxKind.ReadonlyKeyword:
1354 case SyntaxKind.DeclareKeyword:
1355 case SyntaxKind.AbstractKeyword:
1356 diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
1357 break;
1358
1359 // These are all legal modifiers.
1360 case SyntaxKind.StaticKeyword:
1361 case SyntaxKind.ExportKeyword:
1362 case SyntaxKind.DefaultKeyword:
1363 }
1364 }
1365 }
1366
1367 function createDiagnosticForNodeArray(nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
1368 const start = nodes.pos;
1369 return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2);
1370 }
1371
1372 // Since these are syntactic diagnostics, parent might not have been set
1373 // this means the sourceFile cannot be infered from the node
1374 function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
1375 return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2);
1376 }
1377 });
1378 }
1379
1380 function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
1381 return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache);
1382 }
1383
1384 function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
1385 return runWithCancellationToken(() => {
1386 const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);
1387 // Don't actually write any files since we're just getting diagnostics.
1388 return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray;
1389 });
1390 }
1391
1392 function getAndCacheDiagnostics<T extends SourceFile | undefined, U extends Diagnostic>(
1393 sourceFile: T,
1394 cancellationToken: CancellationToken | undefined,
1395 cache: DiagnosticCache<U>,
1396 getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined) => readonly U[],
1397 ): readonly U[] {
1398
1399 const cachedResult = sourceFile
1400 ? cache.perFile?.get(sourceFile.path)
1401 : cache.allDiagnostics;
1402
1403 if (cachedResult) {
1404 return cachedResult;
1405 }
1406 const result = getDiagnostics(sourceFile, cancellationToken);
1407 if (sourceFile) {
1408 (cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result);
1409 }
1410 else {
1411 cache.allDiagnostics = result;
1412 }
1413 return result;
1414 }
1415
1416 function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
1417 return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
1418 }
1419
1420 function getOptionsDiagnostics(): SortedReadonlyArray<Diagnostic> {
1421 return sortAndDeduplicateDiagnostics(concatenate(
1422 fileProcessingDiagnostics.getGlobalDiagnostics(),
1423 concatenate(
1424 programDiagnostics.getGlobalDiagnostics(),
1425 getOptionsDiagnosticsOfConfigFile()
1426 )
1427 ));
1428 }
1429
1430 function getOptionsDiagnosticsOfConfigFile() {
1431 if (!options.configFile) { return emptyArray; }
1432 let diagnostics = programDiagnostics.getDiagnostics(options.configFile.fileName);
1433 forEachResolvedProjectReference(resolvedRef => {
1434 diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName));
1435 });
1436 return diagnostics;
1437 }
1438
1439 function getGlobalDiagnostics(): SortedReadonlyArray<Diagnostic> {
1440 return rootNames.length ? sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice()) : emptyArray as any as SortedReadonlyArray<Diagnostic>;
1441 }
1442
1443 function getConfigFileParsingDiagnostics(): readonly Diagnostic[] {
1444 return configFileParsingDiagnostics || emptyArray;
1445 }
1446
1447 function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean) {
1448 processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined);
1449 }
1450
1451 function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
1452 return a.fileName === b.fileName;
1453 }
1454
1455 function moduleNameIsEqualTo(a: StringLiteralLike | Identifier, b: StringLiteralLike | Identifier): boolean {
1456 return a.kind === SyntaxKind.Identifier
1457 ? b.kind === SyntaxKind.Identifier && a.escapedText === b.escapedText
1458 : b.kind === SyntaxKind.StringLiteral && a.text === b.text;
1459 }
1460
1461 function createSyntheticImport(text: string, file: SourceFile) {
1462 const externalHelpersModuleReference = factory.createStringLiteral(text);
1463 const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference);
1464 addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper);
1465 setParent(externalHelpersModuleReference, importDecl);
1466 setParent(importDecl, file);
1467 // explicitly unset the synthesized flag on these declarations so the checker API will answer questions about them
1468 // (which is required to build the dependency graph for incremental emit)
1469 (externalHelpersModuleReference as Mutable<Node>).flags &= ~NodeFlags.Synthesized;
1470 (importDecl as Mutable<Node>).flags &= ~NodeFlags.Synthesized;
1471 return externalHelpersModuleReference;
1472 }
1473
1474 function collectExternalModuleReferences(file: SourceFile): void {
1475 if (file.imports) {
1476 return;
1477 }
1478
1479 const isJavaScriptFile = isSourceFileJS(file);
1480 const isExternalModuleFile = isExternalModule(file);
1481
1482 // file.imports may not be undefined if there exists dynamic import
1483 let imports: StringLiteralLike[] | undefined;
1484 let moduleAugmentations: (StringLiteral | Identifier)[] | undefined;
1485 let ambientModules: string[] | undefined;
1486
1487 // If we are importing helpers, we need to add a synthetic reference to resolve the
1488 // helpers library.
1489 if ((options.isolatedModules || isExternalModuleFile)
1490 && !file.isDeclarationFile) {
1491 if (options.importHelpers) {
1492 // synthesize 'import "tslib"' declaration
1493 imports = [createSyntheticImport(externalHelpersModuleNameText, file)];
1494 }
1495 const jsxImport = getJSXRuntimeImport(getJSXImplicitImportBase(options, file), options);
1496 if (jsxImport) {
1497 // synthesize `import "base/jsx-runtime"` declaration
1498 (imports ||= []).push(createSyntheticImport(jsxImport, file));
1499 }
1500 }
1501
1502 for (const node of file.statements) {
1503 collectModuleReferences(node, /*inAmbientModule*/ false);
1504 }
1505 if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) {
1506 collectDynamicImportOrRequireCalls(file);
1507 }
1508
1509 file.imports = imports || emptyArray;
1510 file.moduleAugmentations = moduleAugmentations || emptyArray;
1511 file.ambientModuleNames = ambientModules || emptyArray;
1512
1513 return;
1514
1515 function collectModuleReferences(node: Statement, inAmbientModule: boolean): void {
1516 if (isAnyImportOrReExport(node)) {
1517 const moduleNameExpr = getExternalModuleName(node);
1518 // TypeScript 1.0 spec (April 2014): 12.1.6
1519 // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
1520 // only through top - level external module names. Relative external module names are not permitted.
1521 if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text && (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text))) {
1522 imports = append(imports, moduleNameExpr);
1523 }
1524 }
1525 else if (isModuleDeclaration(node)) {
1526 if (isAmbientModule(node) && (inAmbientModule || hasSyntacticModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
1527 const nameText = getTextOfIdentifierOrLiteral(node.name);
1528 // Ambient module declarations can be interpreted as augmentations for some existing external modules.
1529 // This will happen in two cases:
1530 // - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
1531 // - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
1532 // immediately nested in top level ambient module declaration .
1533 if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(nameText))) {
1534 (moduleAugmentations || (moduleAugmentations = [])).push(node.name);
1535 }
1536 else if (!inAmbientModule) {
1537 if (file.isDeclarationFile) {
1538 // for global .d.ts files record name of ambient module
1539 (ambientModules || (ambientModules = [])).push(nameText);
1540 }
1541 // An AmbientExternalModuleDeclaration declares an external module.
1542 // This type of declaration is permitted only in the global module.
1543 // The StringLiteral must specify a top - level external module name.
1544 // Relative external module names are not permitted
1545
1546 // NOTE: body of ambient module is always a module block, if it exists
1547 const body = <ModuleBlock>(<ModuleDeclaration>node).body;
1548 if (body) {
1549 for (const statement of body.statements) {
1550 collectModuleReferences(statement, /*inAmbientModule*/ true);
1551 }
1552 }
1553 }
1554 }
1555 }
1556 }
1557
1558 function collectDynamicImportOrRequireCalls(file: SourceFile) {
1559 const r = /import|require/g;
1560 while (r.exec(file.text) !== null) { // eslint-disable-line no-null/no-null
1561 const node = getNodeAtPosition(file, r.lastIndex);
1562 if (isJavaScriptFile && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ true)) {
1563 imports = append(imports, node.arguments[0]);
1564 }
1565 // we have to check the argument list has length of 1. We will still have to process these even though we have parsing error.
1566 else if (isImportCall(node) && node.arguments.length === 1 && isStringLiteralLike(node.arguments[0])) {
1567 imports = append(imports, node.arguments[0] as StringLiteralLike);
1568 }
1569 else if (isLiteralImportTypeNode(node)) {
1570 imports = append(imports, node.argument.literal);
1571 }
1572 }
1573 }
1574
1575 /** Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only in JS files */
1576 function getNodeAtPosition(sourceFile: SourceFile, position: number): Node {
1577 let current: Node = sourceFile;
1578 const getContainingChild = (child: Node) => {
1579 if (child.pos <= position && (position < child.end || (position === child.end && (child.kind === SyntaxKind.EndOfFileToken)))) {
1580 return child;
1581 }
1582 };
1583 while (true) {
1584 const child = isJavaScriptFile && hasJSDocNodes(current) && forEach(current.jsDoc, getContainingChild) || forEachChild(current, getContainingChild);
1585 if (!child) {
1586 return current;
1587 }
1588 current = child;
1589 }
1590 }
1591 }
1592
1593 function getLibFileFromReference(ref: FileReference) {
1594 const libName = toFileNameLowerCase(ref.fileName);
1595 const libFileName = libMap.get(libName);
1596 if (libFileName) {
1597 return getSourceFile(combinePaths(defaultLibraryPath, libFileName));
1598 }
1599 }
1600
1601 /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
1602 function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined {
1603 return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName)) || undefined);
1604 }
1605
1606 function getSourceFileFromReferenceWorker(
1607 fileName: string,
1608 getSourceFile: (fileName: string) => SourceFile | undefined,
1609 fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
1610 refFile?: SourceFile): SourceFile | undefined {
1611
1612 if (hasExtension(fileName)) {
1613 const canonicalFileName = host.getCanonicalFileName(fileName);
1614 if (!options.allowNonTsExtensions && !forEach(supportedExtensionsWithJsonIfResolveJsonModule, extension => fileExtensionIs(canonicalFileName, extension))) {
1615 if (fail) {
1616 if (hasJSFileExtension(canonicalFileName)) {
1617 fail(Diagnostics.File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option, fileName);
1618 }
1619 else {
1620 fail(Diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + supportedExtensions.join("', '") + "'");
1621 }
1622 }
1623 return undefined;
1624 }
1625
1626 const sourceFile = getSourceFile(fileName);
1627 if (fail) {
1628 if (!sourceFile) {
1629 const redirect = getProjectReferenceRedirect(fileName);
1630 if (redirect) {
1631 fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName);
1632 }
1633 else {
1634 fail(Diagnostics.File_0_not_found, fileName);
1635 }
1636 }
1637 else if (refFile && canonicalFileName === host.getCanonicalFileName(refFile.fileName)) {
1638 fail(Diagnostics.A_file_cannot_have_a_reference_to_itself);
1639 }
1640 }
1641 return sourceFile;
1642 }
1643 else {
1644 const sourceFileNoExtension = options.allowNonTsExtensions && getSourceFile(fileName);
1645 if (sourceFileNoExtension) return sourceFileNoExtension;
1646
1647 if (fail && options.allowNonTsExtensions) {
1648 fail(Diagnostics.File_0_not_found, fileName);
1649 return undefined;
1650 }
1651
1652 const sourceFileWithAddedExtension = forEach(supportedExtensions, extension => getSourceFile(fileName + extension));
1653 if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + supportedExtensions.join("', '") + "'");
1654 return sourceFileWithAddedExtension;
1655 }
1656 }
1657
1658 /** This has side effects through `findSourceFile`. */
1659 function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, refFile?: RefFile): void {
1660 getSourceFileFromReferenceWorker(
1661 fileName,
1662 fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, refFile, packageId), // TODO: GH#18217
1663 (diagnostic, ...args) => fileProcessingDiagnostics.add(
1664 createRefFileDiagnostic(refFile, diagnostic, ...args)
1665 ),
1666 refFile && refFile.file
1667 );
1668 }
1669
1670 function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, refFile: RefFile | undefined): void {
1671 const refs = !refFile ? refFileMap && refFileMap.get(existingFile.path) : undefined;
1672 const refToReportErrorOn = refs && find(refs, ref => ref.referencedFileName === existingFile.fileName);
1673 fileProcessingDiagnostics.add(refToReportErrorOn ?
1674 createFileDiagnosticAtReference(
1675 refToReportErrorOn,
1676 Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
1677 existingFile.fileName,
1678 fileName,
1679 ) :
1680 createRefFileDiagnostic(
1681 refFile,
1682 Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
1683 fileName,
1684 existingFile.fileName
1685 )
1686 );
1687 }
1688
1689 function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string): SourceFile {
1690 const redirect: SourceFile = Object.create(redirectTarget);
1691 redirect.fileName = fileName;
1692 redirect.path = path;
1693 redirect.resolvedPath = resolvedPath;
1694 redirect.originalFileName = originalFileName;
1695 redirect.redirectInfo = { redirectTarget, unredirected };
1696 sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
1697 Object.defineProperties(redirect, {
1698 id: {
1699 get(this: SourceFile) { return this.redirectInfo!.redirectTarget.id; },
1700 set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo!.redirectTarget.id = value; },
1701 },
1702 symbol: {
1703 get(this: SourceFile) { return this.redirectInfo!.redirectTarget.symbol; },
1704 set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo!.redirectTarget.symbol = value; },
1705 },
1706 });
1707 return redirect;
1708 }
1709
1710 // Get source file from normalized fileName
1711 function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: RefFile | undefined, packageId: PackageId | undefined): SourceFile | undefined {
1712 tracing.push(tracing.Phase.Program, "findSourceFile", {
1713 fileName,
1714 isDefaultLib: isDefaultLib || undefined,
1715 refKind: refFile ? (RefFileKind as any)[refFile.kind] : undefined,
1716 });
1717 const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, refFile, packageId);
1718 tracing.pop();
1719 return result;
1720 }
1721
1722 function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: RefFile | undefined, packageId: PackageId | undefined): SourceFile | undefined {
1723 if (useSourceOfProjectReferenceRedirect) {
1724 let source = getSourceOfProjectReferenceRedirect(fileName);
1725 // If preserveSymlinks is true, module resolution wont jump the symlink
1726 // but the resolved real path may be the .d.ts from project reference
1727 // Note:: Currently we try the real path only if the
1728 // file is from node_modules to avoid having to run real path on all file paths
1729 if (!source &&
1730 host.realpath &&
1731 options.preserveSymlinks &&
1732 isDeclarationFileName(fileName) &&
1733 stringContains(fileName, nodeModulesPathPart)) {
1734 const realPath = host.realpath(fileName);
1735 if (realPath !== fileName) source = getSourceOfProjectReferenceRedirect(realPath);
1736 }
1737 if (source) {
1738 const file = isString(source) ?
1739 findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, refFile, packageId) :
1740 undefined;
1741 if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
1742 return file;
1743 }
1744 }
1745 const originalFileName = fileName;
1746 if (filesByName.has(path)) {
1747 const file = filesByName.get(path);
1748 addFileToRefFileMap(fileName, file || undefined, refFile);
1749 // try to check if we've already seen this file but with a different casing in path
1750 // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected
1751 if (file && options.forceConsistentCasingInFileNames) {
1752 const checkedName = file.fileName;
1753 const isRedirect = toPath(checkedName) !== toPath(fileName);
1754 if (isRedirect) {
1755 fileName = getProjectReferenceRedirect(fileName) || fileName;
1756 }
1757 // Check if it differs only in drive letters its ok to ignore that error:
1758 const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
1759 const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory);
1760 if (checkedAbsolutePath !== inputAbsolutePath) {
1761 reportFileNamesDifferOnlyInCasingError(fileName, file, refFile);
1762 }
1763 }
1764
1765 // If the file was previously found via a node_modules search, but is now being processed as a root file,
1766 // then everything it sucks in may also be marked incorrectly, and needs to be checked again.
1767 if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) {
1768 sourceFilesFoundSearchingNodeModules.set(file.path, false);
1769 if (!options.noResolve) {
1770 processReferencedFiles(file, isDefaultLib);
1771 processTypeReferenceDirectives(file);
1772 }
1773 if (!options.noLib) {
1774 processLibReferenceDirectives(file);
1775 }
1776
1777 modulesWithElidedImports.set(file.path, false);
1778 processImportedModules(file);
1779 }
1780 // See if we need to reprocess the imports due to prior skipped imports
1781 else if (file && modulesWithElidedImports.get(file.path)) {
1782 if (currentNodeModulesDepth < maxNodeModuleJsDepth) {
1783 modulesWithElidedImports.set(file.path, false);
1784 processImportedModules(file);
1785 }
1786 }
1787
1788 return file || undefined;
1789 }
1790
1791 let redirectedPath: Path | undefined;
1792 if (refFile && !useSourceOfProjectReferenceRedirect) {
1793 const redirectProject = getProjectReferenceRedirectProject(fileName);
1794 if (redirectProject) {
1795 if (outFile(redirectProject.commandLine.options)) {
1796 // Shouldnt create many to 1 mapping file in --out scenario
1797 return undefined;
1798 }
1799 const redirect = getProjectReferenceOutputName(redirectProject, fileName);
1800 fileName = redirect;
1801 // Once we start redirecting to a file, we can potentially come back to it
1802 // via a back-reference from another file in the .d.ts folder. If that happens we'll
1803 // end up trying to add it to the program *again* because we were tracking it via its
1804 // original (un-redirected) name. So we have to map both the original path and the redirected path
1805 // to the source file we're about to find/create
1806 redirectedPath = toPath(redirect);
1807 }
1808 }
1809
1810 // We haven't looked for this file, do so now and cache result
1811 const file = host.getSourceFile(
1812 fileName,
1813 options.target!,
1814 hostErrorMessage => fileProcessingDiagnostics.add(createRefFileDiagnostic(
1815 refFile,
1816 Diagnostics.Cannot_read_file_0_Colon_1,
1817 fileName,
1818 hostErrorMessage
1819 )),
1820 shouldCreateNewSourceFile
1821 );
1822
1823 if (packageId) {
1824 const packageIdKey = packageIdToString(packageId);
1825 const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
1826 if (fileFromPackageId) {
1827 // Some other SourceFile already exists with this package name and version.
1828 // Instead of creating a duplicate, just redirect to the existing one.
1829 const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName); // TODO: GH#18217
1830 redirectTargetsMap.add(fileFromPackageId.path, fileName);
1831 addFileToFilesByName(dupFile, path, redirectedPath);
1832 sourceFileToPackageName.set(path, packageId.name);
1833 processingOtherFiles!.push(dupFile);
1834 return dupFile;
1835 }
1836 else if (file) {
1837 // This is the first source file to have this packageId.
1838 packageIdToSourceFile.set(packageIdKey, file);
1839 sourceFileToPackageName.set(path, packageId.name);
1840 }
1841 }
1842 addFileToFilesByName(file, path, redirectedPath);
1843
1844 if (file) {
1845 sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
1846 file.fileName = fileName; // Ensure that source file has same name as what we were looking for
1847 file.path = path;
1848 file.resolvedPath = toPath(fileName);
1849 file.originalFileName = originalFileName;
1850 addFileToRefFileMap(fileName, file, refFile);
1851
1852 if (host.useCaseSensitiveFileNames()) {
1853 const pathLowerCase = toFileNameLowerCase(path);
1854 // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
1855 const existingFile = filesByNameIgnoreCase!.get(pathLowerCase);
1856 if (existingFile) {
1857 reportFileNamesDifferOnlyInCasingError(fileName, existingFile, refFile);
1858 }
1859 else {
1860 filesByNameIgnoreCase!.set(pathLowerCase, file);
1861 }
1862 }
1863
1864 skipDefaultLib = skipDefaultLib || (file.hasNoDefaultLib && !ignoreNoDefaultLib);
1865
1866 if (!options.noResolve) {
1867 processReferencedFiles(file, isDefaultLib);
1868 processTypeReferenceDirectives(file);
1869 }
1870 if (!options.noLib) {
1871 processLibReferenceDirectives(file);
1872 }
1873
1874
1875 // always process imported modules to record module name resolutions
1876 processImportedModules(file);
1877
1878 if (isDefaultLib) {
1879 processingDefaultLibFiles!.push(file);
1880 }
1881 else {
1882 processingOtherFiles!.push(file);
1883 }
1884 }
1885 return file;
1886 }
1887
1888 function addFileToRefFileMap(referencedFileName: string, file: SourceFile | undefined, refFile: RefFile | undefined) {
1889 if (refFile && file) {
1890 (refFileMap || (refFileMap = createMultiMap())).add(file.path, {
1891 referencedFileName,
1892 kind: refFile.kind,
1893 index: refFile.index,
1894 file: refFile.file.path
1895 });
1896 }
1897 }
1898
1899 function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
1900 if (redirectedPath) {
1901 filesByName.set(redirectedPath, file);
1902 filesByName.set(path, file || false);
1903 }
1904 else {
1905 filesByName.set(path, file);
1906 }
1907 }
1908
1909 function getProjectReferenceRedirect(fileName: string): string | undefined {
1910 const referencedProject = getProjectReferenceRedirectProject(fileName);
1911 return referencedProject && getProjectReferenceOutputName(referencedProject, fileName);
1912 }
1913
1914 function getProjectReferenceRedirectProject(fileName: string) {
1915 // Ignore dts or any json files
1916 if (!resolvedProjectReferences || !resolvedProjectReferences.length || fileExtensionIs(fileName, Extension.Dts) || fileExtensionIs(fileName, Extension.Json)) {
1917 return undefined;
1918 }
1919
1920 // If this file is produced by a referenced project, we need to rewrite it to
1921 // look in the output folder of the referenced project rather than the input
1922 return getResolvedProjectReferenceToRedirect(fileName);
1923 }
1924
1925
1926 function getProjectReferenceOutputName(referencedProject: ResolvedProjectReference, fileName: string) {
1927 const out = outFile(referencedProject.commandLine.options);
1928 return out ?
1929 changeExtension(out, Extension.Dts) :
1930 getOutputDeclarationFileName(fileName, referencedProject.commandLine, !host.useCaseSensitiveFileNames());
1931 }
1932
1933 /**
1934 * Get the referenced project if the file is input file from that reference project
1935 */
1936 function getResolvedProjectReferenceToRedirect(fileName: string) {
1937 if (mapFromFileToProjectReferenceRedirects === undefined) {
1938 mapFromFileToProjectReferenceRedirects = new Map();
1939 forEachResolvedProjectReference(referencedProject => {
1940 // not input file from the referenced project, ignore
1941 if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) {
1942 referencedProject.commandLine.fileNames.forEach(f =>
1943 mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path));
1944 }
1945 });
1946 }
1947
1948 const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPath(fileName));
1949 return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath);
1950 }
1951
1952 function forEachResolvedProjectReference<T>(
1953 cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined
1954 ): T | undefined {
1955 return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb);
1956 }
1957
1958 function getSourceOfProjectReferenceRedirect(file: string) {
1959 if (!isDeclarationFileName(file)) return undefined;
1960 if (mapFromToProjectReferenceRedirectSource === undefined) {
1961 mapFromToProjectReferenceRedirectSource = new Map();
1962 forEachResolvedProjectReference(resolvedRef => {
1963 const out = outFile(resolvedRef.commandLine.options);
1964 if (out) {
1965 // Dont know which source file it means so return true?
1966 const outputDts = changeExtension(out, Extension.Dts);
1967 mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
1968 }
1969 else {
1970 forEach(resolvedRef.commandLine.fileNames, fileName => {
1971 if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
1972 const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, host.useCaseSensitiveFileNames());
1973 mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
1974 }
1975 });
1976 }
1977 });
1978 }
1979 return mapFromToProjectReferenceRedirectSource.get(toPath(file));
1980 }
1981
1982 function isSourceOfProjectReferenceRedirect(fileName: string) {
1983 return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName);
1984 }
1985
1986 function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined {
1987 if (!projectReferenceRedirects) {
1988 return undefined;
1989 }
1990
1991 return projectReferenceRedirects.get(projectReferencePath) || undefined;
1992 }
1993
1994 function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
1995 forEach(file.referencedFiles, (ref, index) => {
1996 const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
1997 processSourceFile(
1998 referencedFileName,
1999 isDefaultLib,
2000 /*ignoreNoDefaultLib*/ false,
2001 /*packageId*/ undefined,
2002 {
2003 kind: RefFileKind.ReferenceFile,
2004 index,
2005 file,
2006 pos: ref.pos,
2007 end: ref.end
2008 }
2009 );
2010 });
2011 }
2012
2013 function processTypeReferenceDirectives(file: SourceFile) {
2014 // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
2015 const typeDirectives = map(file.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
2016 if (!typeDirectives) {
2017 return;
2018 }
2019
2020 const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file);
2021
2022 for (let i = 0; i < typeDirectives.length; i++) {
2023 const ref = file.typeReferenceDirectives[i];
2024 const resolvedTypeReferenceDirective = resolutions[i];
2025 // store resolved type directive on the file
2026 const fileName = toFileNameLowerCase(ref.fileName);
2027 setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective);
2028 processTypeReferenceDirective(
2029 fileName,
2030 resolvedTypeReferenceDirective,
2031 {
2032 kind: RefFileKind.TypeReferenceDirective,
2033 index: i,
2034 file,
2035 pos: ref.pos,
2036 end: ref.end
2037 }
2038 );
2039 }
2040 }
2041
2042 function processTypeReferenceDirective(
2043 typeReferenceDirective: string,
2044 resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective,
2045 refFile?: RefFile
2046 ): void {
2047 tracing.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: refFile?.kind, refPath: refFile?.file.path });
2048 processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, refFile);
2049 tracing.pop();
2050 }
2051
2052 function processTypeReferenceDirectiveWorker(
2053 typeReferenceDirective: string,
2054 resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective,
2055 refFile?: RefFile
2056 ): void {
2057
2058 // If we already found this library as a primary reference - nothing to do
2059 const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective);
2060 if (previousResolution && previousResolution.primary) {
2061 return;
2062 }
2063 let saveResolution = true;
2064 if (resolvedTypeReferenceDirective) {
2065 if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++;
2066
2067 if (resolvedTypeReferenceDirective.primary) {
2068 // resolved from the primary path
2069 processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile); // TODO: GH#18217
2070 }
2071 else {
2072 // If we already resolved to this file, it must have been a secondary reference. Check file contents
2073 // for sameness and possibly issue an error
2074 if (previousResolution) {
2075 // Don't bother reading the file again if it's the same file.
2076 if (resolvedTypeReferenceDirective.resolvedFileName !== previousResolution.resolvedFileName) {
2077 const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!);
2078 const existingFile = getSourceFile(previousResolution.resolvedFileName!)!;
2079 if (otherFileText !== existingFile.text) {
2080 // Try looking up ref for original file
2081 const refs = !refFile ? refFileMap && refFileMap.get(existingFile.path) : undefined;
2082 const refToReportErrorOn = refs && find(refs, ref => ref.referencedFileName === existingFile.fileName);
2083 fileProcessingDiagnostics.add(
2084 refToReportErrorOn ?
2085 createFileDiagnosticAtReference(
2086 refToReportErrorOn,
2087 Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
2088 typeReferenceDirective,
2089 resolvedTypeReferenceDirective.resolvedFileName,
2090 previousResolution.resolvedFileName
2091 ) :
2092 createRefFileDiagnostic(
2093 refFile,
2094 Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
2095 typeReferenceDirective,
2096 resolvedTypeReferenceDirective.resolvedFileName,
2097 previousResolution.resolvedFileName
2098 )
2099 );
2100 }
2101 }
2102 // don't overwrite previous resolution result
2103 saveResolution = false;
2104 }
2105 else {
2106 // First resolution of this library
2107 processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile);
2108 }
2109 }
2110
2111 if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--;
2112 }
2113 else {
2114 fileProcessingDiagnostics.add(createRefFileDiagnostic(
2115 refFile,
2116 Diagnostics.Cannot_find_type_definition_file_for_0,
2117 typeReferenceDirective
2118 ));
2119 }
2120
2121 if (saveResolution) {
2122 resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective);
2123 }
2124 }
2125
2126 function processLibReferenceDirectives(file: SourceFile) {
2127 forEach(file.libReferenceDirectives, libReference => {
2128 const libName = toFileNameLowerCase(libReference.fileName);
2129 const libFileName = libMap.get(libName);
2130 if (libFileName) {
2131 // we ignore any 'no-default-lib' reference set on this file.
2132 processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true);
2133 }
2134 else {
2135 const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts");
2136 const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity);
2137 const message = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0;
2138 fileProcessingDiagnostics.add(createFileDiagnostic(
2139 file,
2140 libReference.pos,
2141 libReference.end - libReference.pos,
2142 message,
2143 libName,
2144 suggestion
2145 ));
2146 }
2147 });
2148 }
2149
2150 function createRefFileDiagnostic(refFile: RefFile | undefined, message: DiagnosticMessage, ...args: any[]): Diagnostic {
2151 if (!refFile) {
2152 return createCompilerDiagnostic(message, ...args);
2153 }
2154 else {
2155 return createFileDiagnostic(refFile.file, refFile.pos, refFile.end - refFile.pos, message, ...args);
2156 }
2157 }
2158
2159 function getCanonicalFileName(fileName: string): string {
2160 return host.getCanonicalFileName(fileName);
2161 }
2162
2163 function processImportedModules(file: SourceFile) {
2164 collectExternalModuleReferences(file);
2165 if (file.imports.length || file.moduleAugmentations.length) {
2166 // Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
2167 const moduleNames = getModuleNames(file);
2168 const resolutions = resolveModuleNamesReusingOldState(moduleNames, file);
2169 Debug.assert(resolutions.length === moduleNames.length);
2170 for (let i = 0; i < moduleNames.length; i++) {
2171 const resolution = resolutions[i];
2172 setResolvedModule(file, moduleNames[i], resolution);
2173
2174 if (!resolution) {
2175 continue;
2176 }
2177
2178 const isFromNodeModulesSearch = resolution.isExternalLibraryImport;
2179 const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension);
2180 const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile;
2181 const resolvedFileName = resolution.resolvedFileName;
2182
2183 if (isFromNodeModulesSearch) {
2184 currentNodeModulesDepth++;
2185 }
2186
2187 // add file to program only if:
2188 // - resolution was successful
2189 // - noResolve is falsy
2190 // - module name comes from the list of imports
2191 // - it's not a top level JavaScript module that exceeded the search max
2192 const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth;
2193 // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
2194 // This may still end up being an untyped module -- the file won't be included but imports will be allowed.
2195 const shouldAddFile = resolvedFileName
2196 && !getResolutionDiagnostic(options, resolution)
2197 && !options.noResolve
2198 && i < file.imports.length
2199 && !elideImport
2200 && !(isJsFile && !getAllowJSCompilerOption(options))
2201 && (isInJSFile(file.imports[i]) || !(file.imports[i].flags & NodeFlags.JSDoc));
2202
2203 if (elideImport) {
2204 modulesWithElidedImports.set(file.path, true);
2205 }
2206 else if (shouldAddFile) {
2207 const path = toPath(resolvedFileName);
2208 const pos = skipTrivia(file.text, file.imports[i].pos);
2209 findSourceFile(
2210 resolvedFileName,
2211 path,
2212 /*isDefaultLib*/ false,
2213 /*ignoreNoDefaultLib*/ false,
2214 {
2215 kind: RefFileKind.Import,
2216 index: i,
2217 file,
2218 pos,
2219 end: file.imports[i].end
2220 },
2221 resolution.packageId
2222 );
2223 }
2224
2225 if (isFromNodeModulesSearch) {
2226 currentNodeModulesDepth--;
2227 }
2228 }
2229 }
2230 else {
2231 // no imports - drop cached module resolutions
2232 file.resolvedModules = undefined;
2233 }
2234 }
2235
2236 function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
2237 const fileNames = mapDefined(sourceFiles, file => file.isDeclarationFile ? undefined : file.fileName);
2238 return computeCommonSourceDirectoryOfFilenames(fileNames, currentDirectory, getCanonicalFileName);
2239 }
2240
2241 function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
2242 let allFilesBelongToPath = true;
2243 const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
2244 let rootPaths: Set<Path> | undefined;
2245
2246 for (const sourceFile of sourceFiles) {
2247 if (!sourceFile.isDeclarationFile) {
2248 const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
2249 if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
2250 if (!rootPaths) rootPaths = new Set(rootNames.map(toPath));
2251 addProgramDiagnosticAtRefPath(
2252 sourceFile,
2253 rootPaths,
2254 Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
2255 sourceFile.fileName,
2256 rootDirectory
2257 );
2258 allFilesBelongToPath = false;
2259 }
2260 }
2261 }
2262
2263 return allFilesBelongToPath;
2264 }
2265
2266 function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined {
2267 if (!projectReferenceRedirects) {
2268 projectReferenceRedirects = new Map();
2269 }
2270
2271 // The actual filename (i.e. add "/tsconfig.json" if necessary)
2272 const refPath = resolveProjectReferencePath(ref);
2273 const sourceFilePath = toPath(refPath);
2274 const fromCache = projectReferenceRedirects.get(sourceFilePath);
2275 if (fromCache !== undefined) {
2276 return fromCache || undefined;
2277 }
2278
2279 let commandLine: ParsedCommandLine | undefined;
2280 let sourceFile: JsonSourceFile | undefined;
2281 if (host.getParsedCommandLine) {
2282 commandLine = host.getParsedCommandLine(refPath);
2283 if (!commandLine) {
2284 addFileToFilesByName(/*sourceFile*/ undefined, sourceFilePath, /*redirectedPath*/ undefined);
2285 projectReferenceRedirects.set(sourceFilePath, false);
2286 return undefined;
2287 }
2288 sourceFile = Debug.checkDefined(commandLine.options.configFile);
2289 Debug.assert(!sourceFile.path || sourceFile.path === sourceFilePath);
2290 addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
2291 }
2292 else {
2293 // An absolute path pointing to the containing directory of the config file
2294 const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory());
2295 sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined;
2296 addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
2297 if (sourceFile === undefined) {
2298 projectReferenceRedirects.set(sourceFilePath, false);
2299 return undefined;
2300 }
2301 commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
2302 }
2303 sourceFile.fileName = refPath;
2304 sourceFile.path = sourceFilePath;
2305 sourceFile.resolvedPath = sourceFilePath;
2306 sourceFile.originalFileName = refPath;
2307
2308 const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile };
2309 projectReferenceRedirects.set(sourceFilePath, resolvedRef);
2310 if (commandLine.projectReferences) {
2311 resolvedRef.references = commandLine.projectReferences.map(parseProjectReferenceConfigFile);
2312 }
2313 return resolvedRef;
2314 }
2315
2316 function verifyCompilerOptions() {
2317 if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) {
2318 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks");
2319 }
2320
2321 if (options.isolatedModules) {
2322 if (options.out) {
2323 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules");
2324 }
2325
2326 if (options.outFile) {
2327 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules");
2328 }
2329 }
2330
2331 if (options.inlineSourceMap) {
2332 if (options.sourceMap) {
2333 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap");
2334 }
2335 if (options.mapRoot) {
2336 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap");
2337 }
2338 }
2339
2340 if (options.composite) {
2341 if (options.declaration === false) {
2342 createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration");
2343 }
2344 if (options.incremental === false) {
2345 createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_incremental_compilation, "declaration");
2346 }
2347 }
2348
2349 const outputFile = outFile(options);
2350 if (options.tsBuildInfoFile) {
2351 if (!isIncrementalCompilation(options)) {
2352 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "tsBuildInfoFile", "incremental", "composite");
2353 }
2354 }
2355 else if (options.incremental && !outputFile && !options.configFilePath) {
2356 programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_incremental_can_only_be_specified_using_tsconfig_emitting_to_single_file_or_when_option_tsBuildInfoFile_is_specified));
2357 }
2358
2359 verifyProjectReferences();
2360
2361 // List of collected files is complete; validate exhautiveness if this is a project with a file list
2362 if (options.composite) {
2363 const rootPaths = new Set(rootNames.map(toPath));
2364 for (const file of files) {
2365 // Ignore file that is not emitted
2366 if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) {
2367 addProgramDiagnosticAtRefPath(
2368 file,
2369 rootPaths,
2370 Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
2371 file.fileName,
2372 options.configFilePath || ""
2373 );
2374 }
2375 }
2376 }
2377
2378 if (options.paths) {
2379 for (const key in options.paths) {
2380 if (!hasProperty(options.paths, key)) {
2381 continue;
2382 }
2383 if (!hasZeroOrOneAsteriskCharacter(key)) {
2384 createDiagnosticForOptionPaths(/*onKey*/ true, key, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key);
2385 }
2386 if (isArray(options.paths[key])) {
2387 const len = options.paths[key].length;
2388 if (len === 0) {
2389 createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key);
2390 }
2391 for (let i = 0; i < len; i++) {
2392 const subst = options.paths[key][i];
2393 const typeOfSubst = typeof subst;
2394 if (typeOfSubst === "string") {
2395 if (!hasZeroOrOneAsteriskCharacter(subst)) {
2396 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character, subst, key);
2397 }
2398 if (!options.baseUrl && !pathIsRelative(subst) && !pathIsAbsolute(subst)) {
2399 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Non_relative_paths_are_not_allowed_when_baseUrl_is_not_set_Did_you_forget_a_leading_Slash);
2400 }
2401 }
2402 else {
2403 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst);
2404 }
2405 }
2406 }
2407 else {
2408 createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key);
2409 }
2410 }
2411 }
2412
2413 if (!options.sourceMap && !options.inlineSourceMap) {
2414 if (options.inlineSources) {
2415 createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources");
2416 }
2417 if (options.sourceRoot) {
2418 createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot");
2419 }
2420 }
2421
2422 if (options.out && options.outFile) {
2423 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile");
2424 }
2425
2426 if (options.mapRoot && !(options.sourceMap || options.declarationMap)) {
2427 // Error to specify --mapRoot without --sourcemap
2428 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap");
2429 }
2430
2431 if (options.declarationDir) {
2432 if (!getEmitDeclarations(options)) {
2433 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationDir", "declaration", "composite");
2434 }
2435 if (outputFile) {
2436 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile");
2437 }
2438 }
2439
2440 if (options.declarationMap && !getEmitDeclarations(options)) {
2441 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationMap", "declaration", "composite");
2442 }
2443
2444 if (options.lib && options.noLib) {
2445 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib");
2446 }
2447
2448 if (options.noImplicitUseStrict && getStrictOptionValue(options, "alwaysStrict")) {
2449 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict");
2450 }
2451
2452 const languageVersion = options.target || ScriptTarget.ES3;
2453
2454 const firstNonAmbientExternalModuleSourceFile = find(files, f => isExternalModule(f) && !f.isDeclarationFile);
2455 if (options.isolatedModules) {
2456 if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) {
2457 createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
2458 }
2459
2460 const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !isSourceFileJS(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
2461 if (firstNonExternalModuleSourceFile) {
2462 const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
2463 programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length,
2464 Diagnostics._0_cannot_be_compiled_under_isolatedModules_because_it_is_considered_a_global_script_file_Add_an_import_export_or_an_empty_export_statement_to_make_it_a_module, getBaseFileName(firstNonExternalModuleSourceFile.fileName)));
2465 }
2466 }
2467 else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) {
2468 // We cannot use createDiagnosticFromNode because nodes do not have parents yet
2469 const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!);
2470 programDiagnostics.add(createFileDiagnostic(firstNonAmbientExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none));
2471 }
2472
2473 // Cannot specify module gen that isn't amd or system with --out
2474 if (outputFile && !options.emitDeclarationOnly) {
2475 if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
2476 createDiagnosticForOptionName(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile", "module");
2477 }
2478 else if (options.module === undefined && firstNonAmbientExternalModuleSourceFile) {
2479 const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!);
2480 programDiagnostics.add(createFileDiagnostic(firstNonAmbientExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_modules_using_option_0_unless_the_module_flag_is_amd_or_system, options.out ? "out" : "outFile"));
2481 }
2482 }
2483
2484 // Without a package name, for multiple files being bundled into a .d.ts file you basically get a file which doesn't wrk
2485 if (outputFile && getEmitDeclarations(options) && getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeJs && !options.bundledPackageName) {
2486 createDiagnosticForOptionName(Diagnostics.The_bundledPackageName_option_must_be_provided_when_using_outFile_and_node_module_resolution_with_declaration_emit, options.out ? "out" : "outFile");
2487 }
2488
2489 if (options.resolveJsonModule) {
2490 if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
2491 createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy, "resolveJsonModule");
2492 }
2493 // Any emit other than common js, amd, es2015 or esnext is error
2494 else if (!hasJsonModuleEmitEnabled(options)) {
2495 createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_can_only_be_specified_when_module_code_generation_is_commonjs_amd_es2015_or_esNext, "resolveJsonModule", "module");
2496 }
2497 }
2498
2499 // there has to be common source directory if user specified --outdir || --sourceRoot
2500 // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted
2501 if (options.outDir || // there is --outDir specified
2502 options.sourceRoot || // there is --sourceRoot specified
2503 options.mapRoot) { // there is --mapRoot specified
2504
2505 // Precalculate and cache the common source directory
2506 const dir = getCommonSourceDirectory();
2507
2508 // If we failed to find a good common directory, but outDir is specified and at least one of our files is on a windows drive/URL/other resource, add a failure
2509 if (options.outDir && dir === "" && files.some(file => getRootLength(file.fileName) > 1)) {
2510 createDiagnosticForOptionName(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir");
2511 }
2512 }
2513
2514 if (options.useDefineForClassFields && languageVersion === ScriptTarget.ES3) {
2515 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_target_is_ES3, "useDefineForClassFields");
2516 }
2517
2518 if (options.checkJs && !getAllowJSCompilerOption(options)) {
2519 programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs"));
2520 }
2521
2522 if (options.emitDeclarationOnly) {
2523 if (!getEmitDeclarations(options)) {
2524 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "emitDeclarationOnly", "declaration", "composite");
2525 }
2526
2527 if (options.noEmit) {
2528 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "emitDeclarationOnly", "noEmit");
2529 }
2530 }
2531
2532 if (options.emitDecoratorMetadata &&
2533 !options.experimentalDecorators) {
2534 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators");
2535 }
2536
2537 if (options.jsxFactory) {
2538 if (options.reactNamespace) {
2539 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory");
2540 }
2541 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
2542 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", inverseJsxOptionMap.get("" + options.jsx));
2543 }
2544 if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
2545 createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory);
2546 }
2547 }
2548 else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
2549 createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace);
2550 }
2551
2552 if (options.jsxFragmentFactory) {
2553 if (!options.jsxFactory) {
2554 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory");
2555 }
2556 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
2557 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", inverseJsxOptionMap.get("" + options.jsx));
2558 }
2559 if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) {
2560 createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory);
2561 }
2562 }
2563
2564 if (options.reactNamespace) {
2565 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
2566 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "reactNamespace", inverseJsxOptionMap.get("" + options.jsx));
2567 }
2568 }
2569
2570 if (options.jsxImportSource) {
2571 if (options.jsx === JsxEmit.React) {
2572 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxImportSource", inverseJsxOptionMap.get("" + options.jsx));
2573 }
2574 }
2575
2576 // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
2577 if (!options.noEmit && !options.suppressOutputPathCheck) {
2578 const emitHost = getEmitHost();
2579 const emitFilesSeen = new Set<string>();
2580 forEachEmittedFile(emitHost, (emitFileNames) => {
2581 if (!options.emitDeclarationOnly) {
2582 verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen);
2583 }
2584 verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
2585 });
2586 }
2587
2588 // Verify that all the emit files are unique and don't overwrite input files
2589 function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set<string>) {
2590 if (emitFileName) {
2591 const emitFilePath = toPath(emitFileName);
2592 // Report error if the output overwrites input file
2593 if (filesByName.has(emitFilePath)) {
2594 let chain: DiagnosticMessageChain | undefined;
2595 if (!options.configFilePath) {
2596 // The program is from either an inferred project or an external project
2597 chain = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Adding_a_tsconfig_json_file_will_help_organize_projects_that_contain_both_TypeScript_and_JavaScript_files_Learn_more_at_https_Colon_Slash_Slashaka_ms_Slashtsconfig);
2598 }
2599 chain = chainDiagnosticMessages(chain, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName);
2600 blockEmittingOfFile(emitFileName, createCompilerDiagnosticFromMessageChain(chain));
2601 }
2602
2603 const emitFileKey = !host.useCaseSensitiveFileNames() ? toFileNameLowerCase(emitFilePath) : emitFilePath;
2604 // Report error if multiple files write into same file
2605 if (emitFilesSeen.has(emitFileKey)) {
2606 // Already seen the same emit file - report error
2607 blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName));
2608 }
2609 else {
2610 emitFilesSeen.add(emitFileKey);
2611 }
2612 }
2613 }
2614 }
2615
2616 function createFileDiagnosticAtReference(refPathToReportErrorOn: ts.RefFile, message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
2617 const refFile = Debug.checkDefined(getSourceFileByPath(refPathToReportErrorOn.file));
2618 const { kind, index } = refPathToReportErrorOn;
2619 let pos: number, end: number;
2620 switch (kind) {
2621 case RefFileKind.Import:
2622 pos = skipTrivia(refFile.text, refFile.imports[index].pos);
2623 end = refFile.imports[index].end;
2624 break;
2625 case RefFileKind.ReferenceFile:
2626 ({ pos, end } = refFile.referencedFiles[index]);
2627 break;
2628 case RefFileKind.TypeReferenceDirective:
2629 ({ pos, end } = refFile.typeReferenceDirectives[index]);
2630 break;
2631 default:
2632 return Debug.assertNever(kind);
2633 }
2634 return createFileDiagnostic(refFile, pos, end - pos, message, ...args);
2635 }
2636
2637 function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Set<Path>, message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
2638 const refPaths = refFileMap?.get(file.path);
2639 const refPathToReportErrorOn = forEach(refPaths, refPath => rootPaths.has(refPath.file) ? refPath : undefined) ||
2640 elementAt(refPaths, 0);
2641 programDiagnostics.add(
2642 refPathToReportErrorOn ?
2643 createFileDiagnosticAtReference(refPathToReportErrorOn, message, ...args) :
2644 createCompilerDiagnostic(message, ...args)
2645 );
2646 }
2647
2648 function verifyProjectReferences() {
2649 const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined;
2650 forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => {
2651 const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
2652 const parentFile = parent && parent.sourceFile as JsonSourceFile;
2653 if (!resolvedRef) {
2654 createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path);
2655 return;
2656 }
2657 const options = resolvedRef.commandLine.options;
2658 if (!options.composite || options.noEmit) {
2659 // ok to not have composite if the current program is container only
2660 const inputs = parent ? parent.commandLine.fileNames : rootNames;
2661 if (inputs.length) {
2662 if (!options.composite) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
2663 if (options.noEmit) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path);
2664 }
2665 }
2666 if (ref.prepend) {
2667 const out = outFile(options);
2668 if (out) {
2669 if (!host.fileExists(out)) {
2670 createDiagnosticForReference(parentFile, index, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path);
2671 }
2672 }
2673 else {
2674 createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path);
2675 }
2676 }
2677 if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) {
2678 createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path);
2679 hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true);
2680 }
2681 });
2682 }
2683
2684 function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) {
2685 let needCompilerDiagnostic = true;
2686 const pathsSyntax = getOptionPathsSyntax();
2687 for (const pathProp of pathsSyntax) {
2688 if (isObjectLiteralExpression(pathProp.initializer)) {
2689 for (const keyProps of getPropertyAssignment(pathProp.initializer, key)) {
2690 const initializer = keyProps.initializer;
2691 if (isArrayLiteralExpression(initializer) && initializer.elements.length > valueIndex) {
2692 programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, initializer.elements[valueIndex], message, arg0, arg1, arg2));
2693 needCompilerDiagnostic = false;
2694 }
2695 }
2696 }
2697 }
2698
2699 if (needCompilerDiagnostic) {
2700 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
2701 }
2702 }
2703
2704 function createDiagnosticForOptionPaths(onKey: boolean, key: string, message: DiagnosticMessage, arg0: string | number) {
2705 let needCompilerDiagnostic = true;
2706 const pathsSyntax = getOptionPathsSyntax();
2707 for (const pathProp of pathsSyntax) {
2708 if (isObjectLiteralExpression(pathProp.initializer) &&
2709 createOptionDiagnosticInObjectLiteralSyntax(
2710 pathProp.initializer, onKey, key, /*key2*/ undefined,
2711 message, arg0)) {
2712 needCompilerDiagnostic = false;
2713 }
2714 }
2715 if (needCompilerDiagnostic) {
2716 programDiagnostics.add(createCompilerDiagnostic(message, arg0));
2717 }
2718 }
2719
2720 function getOptionsSyntaxByName(name: string): object | undefined {
2721 const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
2722 if (compilerOptionsObjectLiteralSyntax) {
2723 return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name);
2724 }
2725 return undefined;
2726 }
2727
2728 function getOptionPathsSyntax(): PropertyAssignment[] {
2729 return getOptionsSyntaxByName("paths") as PropertyAssignment[] || emptyArray;
2730 }
2731
2732 function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) {
2733 createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3);
2734 }
2735
2736 function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string) {
2737 createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
2738 }
2739
2740 function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
2741 const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile || options.configFile, "references"),
2742 property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
2743 if (referencesSyntax && referencesSyntax.elements.length > index) {
2744 programDiagnostics.add(createDiagnosticForNodeInSourceFile(sourceFile || options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
2745 }
2746 else {
2747 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
2748 }
2749 }
2750
2751 function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) {
2752 const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
2753 const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax ||
2754 !createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2);
2755
2756 if (needCompilerDiagnostic) {
2757 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
2758 }
2759 }
2760
2761 function getCompilerOptionsObjectLiteralSyntax() {
2762 if (_compilerOptionsObjectLiteralSyntax === undefined) {
2763 _compilerOptionsObjectLiteralSyntax = null; // eslint-disable-line no-null/no-null
2764 const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile);
2765 if (jsonObjectLiteral) {
2766 for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) {
2767 if (isObjectLiteralExpression(prop.initializer)) {
2768 _compilerOptionsObjectLiteralSyntax = prop.initializer;
2769 break;
2770 }
2771 }
2772 }
2773 }
2774 return _compilerOptionsObjectLiteralSyntax;
2775 }
2776
2777 function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number): boolean {
2778 const props = getPropertyAssignment(objectLiteral, key1, key2);
2779 for (const prop of props) {
2780 programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2));
2781 }
2782 return !!props.length;
2783 }
2784
2785 function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
2786 hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
2787 programDiagnostics.add(diag);
2788 }
2789
2790 function isEmittedFile(file: string): boolean {
2791 if (options.noEmit) {
2792 return false;
2793 }
2794
2795 // If this is source file, its not emitted file
2796 const filePath = toPath(file);
2797 if (getSourceFileByPath(filePath)) {
2798 return false;
2799 }
2800
2801 // If options have --outFile or --out just check that
2802 const out = outFile(options);
2803 if (out) {
2804 return isSameFile(filePath, out) || isSameFile(filePath, removeFileExtension(out) + Extension.Dts);
2805 }
2806
2807 // If declarationDir is specified, return if its a file in that directory
2808 if (options.declarationDir && containsPath(options.declarationDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames())) {
2809 return true;
2810 }
2811
2812 // If --outDir, check if file is in that directory
2813 if (options.outDir) {
2814 return containsPath(options.outDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames());
2815 }
2816
2817 if (fileExtensionIsOneOf(filePath, supportedJSExtensions) || fileExtensionIs(filePath, Extension.Dts)) {
2818 // Otherwise just check if sourceFile with the name exists
2819 const filePathWithoutExtension = removeFileExtension(filePath);
2820 return !!getSourceFileByPath((filePathWithoutExtension + Extension.Ts) as Path) ||
2821 !!getSourceFileByPath((filePathWithoutExtension + Extension.Tsx) as Path);
2822 }
2823 return false;
2824 }
2825
2826 function isSameFile(file1: string, file2: string) {
2827 return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
2828 }
2829
2830 function getSymlinkCache(): SymlinkCache {
2831 if (host.getSymlinkCache) {
2832 return host.getSymlinkCache();
2833 }
2834 return symlinks || (symlinks = discoverProbableSymlinks(
2835 files,
2836 getCanonicalFileName,
2837 host.getCurrentDirectory()));
2838 }
2839}
2840