mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
add some errors for diagnosing repos with issues
This commit is contained in:
@@ -664,6 +664,21 @@ export function getCommonSourceDirectory(
|
||||
return commonSourceDirectory;
|
||||
}
|
||||
|
||||
export function getCommonSourceDirectory60(options: CompilerOptions): string | undefined {
|
||||
if (!options.rootDir && !options.composite && !options.outFile && options.configFilePath) {
|
||||
// Project compilations never infer their root from the input source paths
|
||||
let commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath));
|
||||
|
||||
if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
|
||||
// Make sure directory path ends with directory separator so this string can directly
|
||||
// used to replace with "" to get the relative path of the source file and the relative path doesn't
|
||||
// start with / making it rooted path
|
||||
commonSourceDirectory += directorySeparator;
|
||||
}
|
||||
return commonSourceDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getCommonSourceDirectoryOfConfig({ options, fileNames }: ParsedCommandLine, ignoreCase: boolean): string {
|
||||
return getCommonSourceDirectory(
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
getBaseFileName,
|
||||
GetCanonicalFileName,
|
||||
getCommonSourceDirectory,
|
||||
getCommonSourceDirectory60,
|
||||
getCompilerOptionValue,
|
||||
getDirectoryPath,
|
||||
GetEffectiveTypeRootsHost,
|
||||
@@ -80,6 +81,7 @@ import {
|
||||
normalizePath,
|
||||
normalizeSlashes,
|
||||
PackageId,
|
||||
packageIdIsEqual,
|
||||
packageIdToString,
|
||||
ParsedPatterns,
|
||||
Path,
|
||||
@@ -148,6 +150,16 @@ function removeIgnoredPackageId(r: Resolved | undefined): PathAndExtension | und
|
||||
}
|
||||
}
|
||||
|
||||
function resolvedIsEqual(a: Resolved | undefined, b: Resolved | undefined) {
|
||||
return a === b ||
|
||||
!!a && !!b &&
|
||||
a.path === b.path &&
|
||||
a.extension === b.extension &&
|
||||
packageIdIsEqual(a.packageId, b.packageId) &&
|
||||
a.originalPath === b.originalPath &&
|
||||
a.resolvedUsingTsExtension === b.resolvedUsingTsExtension;
|
||||
}
|
||||
|
||||
/** Result of trying to resolve a module. */
|
||||
interface Resolved {
|
||||
path: string;
|
||||
@@ -2932,23 +2944,46 @@ function getLoadModuleFromTargetExportOrImport(extensions: Extensions, state: Mo
|
||||
packagePath,
|
||||
));
|
||||
}
|
||||
for (const commonSourceDirGuess of commonSourceDirGuesses) {
|
||||
const candidateDirectories = getOutputDirectoriesForBaseDirectory(commonSourceDirGuess);
|
||||
for (const candidateDir of candidateDirectories) {
|
||||
if (containsPath(candidateDir, finalPath, !useCaseSensitiveFileNames(state))) {
|
||||
// The matched export is looking up something in either the out declaration or js dir, now map the written path back into the source dir and source extension
|
||||
const pathFragment = finalPath.slice(candidateDir.length + 1); // +1 to also remove directory seperator
|
||||
const possibleInputBase = combinePaths(commonSourceDirGuess, pathFragment);
|
||||
const jsAndDtsExtensions = [Extension.Mjs, Extension.Cjs, Extension.Js, Extension.Json, Extension.Dmts, Extension.Dcts, Extension.Dts];
|
||||
for (const ext of jsAndDtsExtensions) {
|
||||
if (fileExtensionIs(possibleInputBase, ext)) {
|
||||
const inputExts = getPossibleOriginalInputExtensionForExtension(possibleInputBase);
|
||||
for (const possibleExt of inputExts) {
|
||||
if (!extensionIsOk(extensions, possibleExt)) continue;
|
||||
const possibleInputWithInputExtension = changeAnyExtension(possibleInputBase, possibleExt, ext, !useCaseSensitiveFileNames(state));
|
||||
if (state.host.fileExists(possibleInputWithInputExtension)) {
|
||||
return toSearchResult(withPackageId(scope, loadFileNameFromPackageJsonField(extensions, possibleInputWithInputExtension, /*packageJsonValue*/ undefined, /*onlyRecordFailures*/ false, state), state));
|
||||
}
|
||||
const result = guessFromCommonDirs(commonSourceDirGuesses, finalPath);
|
||||
const commonDir60 = toAbsolutePath(getCommonSourceDirectory60(state.compilerOptions));
|
||||
if (commonDir60) {
|
||||
if (!arrayIsEqualTo(commonSourceDirGuesses, [commonDir60])) {
|
||||
const result60 = guessFromCommonDirs([commonDir60], finalPath);
|
||||
// Compare and if not same report -- and add made up diagnostics
|
||||
if (!searchResultIsEqual(result, result60, resolvedIsEqual)) {
|
||||
state.reportDiagnostic(createCompilerDiagnostic(
|
||||
isImports
|
||||
? Diagnostics.The_project_root_is_ambiguous_but_is_required_to_resolve_import_map_entry_0_in_file_1_Supply_the_rootDir_compiler_option_to_disambiguate
|
||||
: Diagnostics.The_project_root_is_ambiguous_but_is_required_to_resolve_export_map_entry_0_in_file_1_Supply_the_rootDir_compiler_option_to_disambiguate,
|
||||
entry === "" ? "." : entry, // replace empty string with `.` - the reverse of the operation done when entries are built - so main entrypoint errors don't look weird
|
||||
packagePath + "\nSheetal:: Change in behaviour: guessed " + commonSourceDirGuesses.join(", ") + " will be in 6.0::" + commonDir60 +
|
||||
"\nResult " + JSON.stringify(result) + "\n Result.6.0: " + JSON.stringify(result60),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function guessFromCommonDirs(commonSourceDirGuesses: string[], finalPath: string) {
|
||||
for (const commonSourceDirGuess of commonSourceDirGuesses) {
|
||||
const candidateDirectories = getOutputDirectoriesForBaseDirectory(commonSourceDirGuess);
|
||||
for (const candidateDir of candidateDirectories) {
|
||||
if (containsPath(candidateDir, finalPath, !useCaseSensitiveFileNames(state))) {
|
||||
// The matched export is looking up something in either the out declaration or js dir, now map the written path back into the source dir and source extension
|
||||
const pathFragment = finalPath.slice(candidateDir.length + 1); // +1 to also remove directory seperator
|
||||
const possibleInputBase = combinePaths(commonSourceDirGuess, pathFragment);
|
||||
const jsAndDtsExtensions = [Extension.Mjs, Extension.Cjs, Extension.Js, Extension.Json, Extension.Dmts, Extension.Dcts, Extension.Dts];
|
||||
for (const ext of jsAndDtsExtensions) {
|
||||
if (fileExtensionIs(possibleInputBase, ext)) {
|
||||
const inputExts = getPossibleOriginalInputExtensionForExtension(possibleInputBase);
|
||||
for (const possibleExt of inputExts) {
|
||||
if (!extensionIsOk(extensions, possibleExt)) continue;
|
||||
const possibleInputWithInputExtension = changeAnyExtension(possibleInputBase, possibleExt, ext, !useCaseSensitiveFileNames(state));
|
||||
if (state.host.fileExists(possibleInputWithInputExtension)) {
|
||||
return toSearchResult(withPackageId(scope, loadFileNameFromPackageJsonField(extensions, possibleInputWithInputExtension, /*packageJsonValue*/ undefined, /*onlyRecordFailures*/ false, state), state));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2956,7 +2991,6 @@ function getLoadModuleFromTargetExportOrImport(extensions: Extensions, state: Mo
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
||||
function getOutputDirectoriesForBaseDirectory(commonSourceDirGuess: string) {
|
||||
// Config file ouput paths are processed to be relative to the host's current directory, while
|
||||
@@ -3416,3 +3450,7 @@ function useCaseSensitiveFileNames(state: ModuleResolutionState) {
|
||||
typeof state.host.useCaseSensitiveFileNames === "boolean" ? state.host.useCaseSensitiveFileNames :
|
||||
state.host.useCaseSensitiveFileNames();
|
||||
}
|
||||
|
||||
function searchResultIsEqual<T>(a: SearchResult<T> | undefined, b: SearchResult<T> | undefined, compareValue: (a: T | undefined, b: T | undefined) => boolean) {
|
||||
return a === b || !!a && !!b && compareValue(a.value, b.value);
|
||||
}
|
||||
|
||||
+71
-7
@@ -63,6 +63,8 @@ import {
|
||||
DiagnosticWithLocation,
|
||||
directorySeparator,
|
||||
DirectoryStructureHost,
|
||||
emitDetachedComments,
|
||||
emitFileNamesIsEqual,
|
||||
emitFiles,
|
||||
EmitHost,
|
||||
emitModuleKindIsNonNodeESM,
|
||||
@@ -110,6 +112,7 @@ import {
|
||||
getBaseFileName,
|
||||
GetCanonicalFileName,
|
||||
getCommonSourceDirectory as ts_getCommonSourceDirectory,
|
||||
getCommonSourceDirectory60,
|
||||
getCommonSourceDirectoryOfConfig,
|
||||
getDeclarationDiagnostics as ts_getDeclarationDiagnostics,
|
||||
getDefaultLibFileName,
|
||||
@@ -131,6 +134,7 @@ import {
|
||||
getNormalizedAbsolutePathWithoutRoot,
|
||||
getNormalizedPathComponents,
|
||||
getOutputDeclarationFileName,
|
||||
getOutputPathsFor,
|
||||
getPackageScopeForPath,
|
||||
getPathFromPathComponents,
|
||||
getPositionOfLineAndCharacter,
|
||||
@@ -233,6 +237,7 @@ import {
|
||||
NodeWithTypeArguments,
|
||||
noop,
|
||||
normalizePath,
|
||||
normalizeSlashes,
|
||||
notImplementedResolver,
|
||||
noTransformers,
|
||||
ObjectLiteralExpression,
|
||||
@@ -293,6 +298,7 @@ import {
|
||||
SourceFile,
|
||||
sourceFileAffectingCompilerOptions,
|
||||
sourceFileMayBeEmitted,
|
||||
sourceFileMayBeEmitted60,
|
||||
startsWith,
|
||||
Statement,
|
||||
StringLiteral,
|
||||
@@ -2149,6 +2155,7 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
|
||||
return commonSourceDirectory;
|
||||
}
|
||||
const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, program));
|
||||
|
||||
commonSourceDirectory = ts_getCommonSourceDirectory(
|
||||
options,
|
||||
() => mapDefined(emittedFiles, file => file.isDeclarationFile ? undefined : file.fileName),
|
||||
@@ -2156,6 +2163,24 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
|
||||
getCanonicalFileName,
|
||||
commonSourceDirectory => checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory),
|
||||
);
|
||||
|
||||
const commonDir60 = getCommonSourceDirectory60(options);
|
||||
if (commonDir60) {
|
||||
const emittedFiles60 = filter(files, file => sourceFileMayBeEmitted60(file, program));
|
||||
const commonDir2 = getDirectoryPath(normalizeSlashes(options.configFilePath!));
|
||||
const result = checkSourceFilesBelongToPathWorker(emittedFiles60, commonDir2);
|
||||
if (!result.allFilesBelongToPath) {
|
||||
result.filesWithError?.forEach(sourceFile => {
|
||||
programDiagnostics.addLazyConfigDiagnostic(
|
||||
sourceFile,
|
||||
Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
|
||||
sourceFile.fileName,
|
||||
"!!! Sheetal CommonDir computed: " + commonSourceDirectory + " commonDir in 6.0 : " + commonDir60,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
programDiagnostics.setCommonSourceDirectory(commonSourceDirectory);
|
||||
return commonSourceDirectory;
|
||||
}
|
||||
@@ -4009,24 +4034,33 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
|
||||
}
|
||||
|
||||
function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
|
||||
const result = checkSourceFilesBelongToPathWorker(sourceFiles, rootDirectory);
|
||||
result.filesWithError?.forEach(sourceFile => {
|
||||
programDiagnostics.addLazyConfigDiagnostic(
|
||||
sourceFile,
|
||||
Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
|
||||
sourceFile.fileName,
|
||||
rootDirectory,
|
||||
);
|
||||
});
|
||||
return result.allFilesBelongToPath;
|
||||
}
|
||||
|
||||
function checkSourceFilesBelongToPathWorker(sourceFiles: readonly SourceFile[], rootDirectory: string) {
|
||||
let allFilesBelongToPath = true;
|
||||
let filesWithError: SourceFile[] | undefined;
|
||||
const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
|
||||
for (const sourceFile of sourceFiles) {
|
||||
if (!sourceFile.isDeclarationFile) {
|
||||
const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
|
||||
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
|
||||
programDiagnostics.addLazyConfigDiagnostic(
|
||||
sourceFile,
|
||||
Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
|
||||
sourceFile.fileName,
|
||||
rootDirectory,
|
||||
);
|
||||
(filesWithError ??= []).push(sourceFile);
|
||||
allFilesBelongToPath = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allFilesBelongToPath;
|
||||
return { allFilesBelongToPath, filesWithError };
|
||||
}
|
||||
|
||||
function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined {
|
||||
@@ -4403,6 +4437,36 @@ export function createProgram(_rootNamesOrOptions: readonly string[] | CreatePro
|
||||
verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
|
||||
});
|
||||
}
|
||||
{
|
||||
const commonDirWithConfig = getCommonSourceDirectory60(options);
|
||||
if (commonDirWithConfig) {
|
||||
const emitHost = getEmitHost();
|
||||
const emitHostWithConfig = {
|
||||
...emitHost,
|
||||
getCommonSourceDirectory: () => commonDirWithConfig,
|
||||
};
|
||||
|
||||
for (const sourceFile of emitHost.getSourceFiles()) {
|
||||
const canBeEmitted = sourceFileMayBeEmitted(sourceFile, emitHost);
|
||||
const canBeEmitted60 = sourceFileMayBeEmitted60(sourceFile, emitHostWithConfig);
|
||||
const outputPaths = canBeEmitted ?
|
||||
getOutputPathsFor(sourceFile, emitHost, /*forceDtsPaths*/ false) :
|
||||
undefined;
|
||||
const outputPaths60 = canBeEmitted60 ?
|
||||
getOutputPathsFor(sourceFile, emitHostWithConfig, /*forceDtsPaths*/ false) :
|
||||
undefined;
|
||||
if (!emitFileNamesIsEqual(outputPaths, outputPaths60)) {
|
||||
// Report error
|
||||
programDiagnostics.addConfigDiagnostic(createCompilerDiagnostic(
|
||||
Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files,
|
||||
"!!! Sheetal: Output layout chaned for file: " + sourceFile.fileName +
|
||||
"\n outputPaths:: " + JSON.stringify(outputPaths) +
|
||||
"\n Output paths in 6.0: " + JSON.stringify(outputPaths60),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that all the emit files are unique and don't overwrite input files
|
||||
function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set<string>) {
|
||||
|
||||
@@ -170,6 +170,7 @@ import {
|
||||
getCombinedModifierFlags,
|
||||
getCombinedNodeFlags,
|
||||
getCommonSourceDirectory,
|
||||
getCommonSourceDirectory60,
|
||||
getContainerFlags,
|
||||
getDirectoryPath,
|
||||
getImpliedNodeFormatForEmitWorker,
|
||||
@@ -897,7 +898,8 @@ export function createModeMismatchDetails(currentSourceFile: SourceFile): Diagno
|
||||
return result;
|
||||
}
|
||||
|
||||
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
|
||||
/** @internal */
|
||||
export function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
|
||||
return a === b || !!a && !!b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version && a.peerDependencies === b.peerDependencies;
|
||||
}
|
||||
|
||||
@@ -6549,6 +6551,16 @@ export interface EmitFileNames {
|
||||
buildInfoPath?: string | undefined;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function emitFileNamesIsEqual(a: EmitFileNames | undefined, b: EmitFileNames | undefined): boolean {
|
||||
return a === b || !!a && !!b &&
|
||||
a.jsFilePath === b.jsFilePath &&
|
||||
a.sourceMapFilePath === b.jsFilePath &&
|
||||
a.declarationFilePath === b.declarationFilePath &&
|
||||
a.declarationMapPath === b.declarationMapPath &&
|
||||
a.buildInfoPath === b.buildInfoPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source files that are expected to have an emit output.
|
||||
*
|
||||
@@ -6616,6 +6628,35 @@ export function sourceFileMayBeEmitted(sourceFile: SourceFile, host: SourceFileM
|
||||
return true;
|
||||
}
|
||||
|
||||
export function sourceFileMayBeEmitted60(sourceFile: SourceFile, host: SourceFileMayBeEmittedHost, forceDtsEmit?: boolean): boolean {
|
||||
const options = host.getCompilerOptions();
|
||||
// Js files are emitted only if option is enabled
|
||||
if (options.noEmitForJsFiles && isSourceFileJS(sourceFile)) return false;
|
||||
// Declaration files are not emitted
|
||||
if (sourceFile.isDeclarationFile) return false;
|
||||
// Source file from node_modules are not emitted
|
||||
if (host.isSourceFileFromExternalLibrary(sourceFile)) return false;
|
||||
// forcing dts emit => file needs to be emitted
|
||||
if (forceDtsEmit) return true;
|
||||
// Check other conditions for file emit
|
||||
// Source files from referenced projects are not emitted
|
||||
if (host.isSourceOfProjectReferenceRedirect(sourceFile.fileName)) return false;
|
||||
// Any non json file should be emitted
|
||||
if (!isJsonSourceFile(sourceFile)) return true;
|
||||
if (host.getRedirectFromSourceFile(sourceFile.fileName)) return false;
|
||||
// Emit json file if outFile is specified
|
||||
if (options.outFile) return true;
|
||||
// Json file is not emitted if outDir is not specified
|
||||
if (!options.outDir) return false;
|
||||
// Otherwise if rootDir or composite config file, we know common sourceDir and can check if file would be emitted in same location
|
||||
if (!options.rootDir && !options.composite && options.configFilePath) {
|
||||
const commonDir = getNormalizedAbsolutePath(getCommonSourceDirectory60(options)!, host.getCurrentDirectory());
|
||||
const outputPath = getSourceFilePathInNewDirWorker(sourceFile.fileName, options.outDir, host.getCurrentDirectory(), commonDir, host.getCanonicalFileName);
|
||||
if (comparePaths(sourceFile.fileName, outputPath, host.getCurrentDirectory(), !host.useCaseSensitiveFileNames()) === Comparison.EqualTo) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getSourceFilePathInNewDir(fileName: string, host: EmitHost, newDirPath: string): string {
|
||||
return getSourceFilePathInNewDirWorker(fileName, newDirPath, host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f));
|
||||
|
||||
Reference in New Issue
Block a user