mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-07 05:41:22 -06:00
Do no path canonicalization during config parsing (#20311)
* Do no canonicalization during config parsing * Add test from issue * Apply code review feedback
This commit is contained in:
parent
b0ea899d13
commit
6219be6144
@ -1425,9 +1425,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
function directoryOfCombinedPath(fileName: string, basePath: string) {
|
||||
// Use the `identity` function to avoid canonicalizing the path, as it must remain noncanonical
|
||||
// Use the `getNormalizedAbsolutePath` function to avoid canonicalizing the path, as it must remain noncanonical
|
||||
// until consistient casing errors are reported
|
||||
return getDirectoryPath(toPath(fileName, basePath, identity));
|
||||
return getDirectoryPath(getNormalizedAbsolutePath(fileName, basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1452,8 +1452,7 @@ namespace ts {
|
||||
Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined));
|
||||
const errors: Diagnostic[] = [];
|
||||
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
|
||||
const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, getCanonicalFileName, resolutionStack, errors);
|
||||
const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors);
|
||||
const { raw } = parsedConfig;
|
||||
const options = extend(existingOptions, parsedConfig.options || {});
|
||||
options.configFilePath = configFileName;
|
||||
@ -1547,7 +1546,10 @@ namespace ts {
|
||||
raw: any;
|
||||
options?: CompilerOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
extendedConfigPath?: Path;
|
||||
/**
|
||||
* Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet
|
||||
*/
|
||||
extendedConfigPath?: string;
|
||||
}
|
||||
|
||||
function isSuccessfulParsedTsconfig(value: ParsedTsconfig) {
|
||||
@ -1564,12 +1566,11 @@ namespace ts {
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
configFileName: string,
|
||||
getCanonicalFileName: GetCanonicalFileName,
|
||||
resolutionStack: Path[],
|
||||
resolutionStack: string[],
|
||||
errors: Push<Diagnostic>,
|
||||
): ParsedTsconfig {
|
||||
basePath = normalizeSlashes(basePath);
|
||||
const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName);
|
||||
const resolvedPath = getNormalizedAbsolutePath(configFileName || "", basePath);
|
||||
|
||||
if (resolutionStack.indexOf(resolvedPath) >= 0) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> ")));
|
||||
@ -1577,14 +1578,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
const ownConfig = json ?
|
||||
parseOwnConfigOfJson(json, host, basePath, getCanonicalFileName, configFileName, errors) :
|
||||
parseOwnConfigOfJsonSourceFile(sourceFile, host, basePath, getCanonicalFileName, configFileName, errors);
|
||||
parseOwnConfigOfJson(json, host, basePath, configFileName, errors) :
|
||||
parseOwnConfigOfJsonSourceFile(sourceFile, host, basePath, configFileName, errors);
|
||||
|
||||
if (ownConfig.extendedConfigPath) {
|
||||
// copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
|
||||
resolutionStack = resolutionStack.concat([resolvedPath]);
|
||||
const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, basePath, getCanonicalFileName,
|
||||
resolutionStack, errors);
|
||||
const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, basePath, resolutionStack, errors);
|
||||
if (extendedConfig && isSuccessfulParsedTsconfig(extendedConfig)) {
|
||||
const baseRaw = extendedConfig.raw;
|
||||
const raw = ownConfig.raw;
|
||||
@ -1612,7 +1612,6 @@ namespace ts {
|
||||
json: any,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: GetCanonicalFileName,
|
||||
configFileName: string | undefined,
|
||||
errors: Push<Diagnostic>
|
||||
): ParsedTsconfig {
|
||||
@ -1625,7 +1624,7 @@ namespace ts {
|
||||
// It should be removed in future releases - use typeAcquisition instead.
|
||||
const typeAcquisition = convertTypeAcquisitionFromJsonWorker(json.typeAcquisition || json.typingOptions, basePath, errors, configFileName);
|
||||
json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
|
||||
let extendedConfigPath: Path;
|
||||
let extendedConfigPath: string;
|
||||
|
||||
if (json.extends) {
|
||||
if (!isString(json.extends)) {
|
||||
@ -1633,7 +1632,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath;
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, getCanonicalFileName, errors, createCompilerDiagnostic);
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, errors, createCompilerDiagnostic);
|
||||
}
|
||||
}
|
||||
return { raw: json, options, typeAcquisition, extendedConfigPath };
|
||||
@ -1643,13 +1642,12 @@ namespace ts {
|
||||
sourceFile: JsonSourceFile,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: GetCanonicalFileName,
|
||||
configFileName: string | undefined,
|
||||
errors: Push<Diagnostic>
|
||||
): ParsedTsconfig {
|
||||
const options = getDefaultCompilerOptions(configFileName);
|
||||
let typeAcquisition: TypeAcquisition, typingOptionstypeAcquisition: TypeAcquisition;
|
||||
let extendedConfigPath: Path;
|
||||
let extendedConfigPath: string;
|
||||
|
||||
const optionsIterator: JsonConversionNotifier = {
|
||||
onSetValidOptionKeyValueInParent(parentOption: string, option: CommandLineOption, value: CompilerOptionsValue) {
|
||||
@ -1670,7 +1668,6 @@ namespace ts {
|
||||
<string>value,
|
||||
host,
|
||||
newBase,
|
||||
getCanonicalFileName,
|
||||
errors,
|
||||
(message, arg0) =>
|
||||
createDiagnosticForNodeInSourceFile(sourceFile, valueNode, message, arg0)
|
||||
@ -1712,7 +1709,6 @@ namespace ts {
|
||||
extendedConfig: string,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: GetCanonicalFileName,
|
||||
errors: Push<Diagnostic>,
|
||||
createDiagnostic: (message: DiagnosticMessage, arg1?: string) => Diagnostic) {
|
||||
extendedConfig = normalizeSlashes(extendedConfig);
|
||||
@ -1721,9 +1717,9 @@ namespace ts {
|
||||
errors.push(createDiagnostic(Diagnostics.A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not, extendedConfig));
|
||||
return undefined;
|
||||
}
|
||||
let extendedConfigPath = toPath(extendedConfig, basePath, getCanonicalFileName);
|
||||
let extendedConfigPath = getNormalizedAbsolutePath(extendedConfig, basePath);
|
||||
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) {
|
||||
extendedConfigPath = `${extendedConfigPath}.json` as Path;
|
||||
extendedConfigPath = `${extendedConfigPath}.json`;
|
||||
if (!host.fileExists(extendedConfigPath)) {
|
||||
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
|
||||
return undefined;
|
||||
@ -1734,11 +1730,10 @@ namespace ts {
|
||||
|
||||
function getExtendedConfig(
|
||||
sourceFile: JsonSourceFile,
|
||||
extendedConfigPath: Path,
|
||||
extendedConfigPath: string,
|
||||
host: ts.ParseConfigHost,
|
||||
basePath: string,
|
||||
getCanonicalFileName: GetCanonicalFileName,
|
||||
resolutionStack: Path[],
|
||||
resolutionStack: string[],
|
||||
errors: Push<Diagnostic>,
|
||||
): ParsedTsconfig | undefined {
|
||||
const extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path));
|
||||
@ -1752,14 +1747,14 @@ namespace ts {
|
||||
|
||||
const extendedDirname = getDirectoryPath(extendedConfigPath);
|
||||
const extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, extendedDirname,
|
||||
getBaseFileName(extendedConfigPath), getCanonicalFileName, resolutionStack, errors);
|
||||
getBaseFileName(extendedConfigPath), resolutionStack, errors);
|
||||
if (sourceFile) {
|
||||
sourceFile.extendedSourceFiles.push(...extendedResult.extendedSourceFiles);
|
||||
}
|
||||
|
||||
if (isSuccessfulParsedTsconfig(extendedConfig)) {
|
||||
// Update the paths to reflect base path
|
||||
const relativeDifference = convertToRelativePath(extendedDirname, basePath, getCanonicalFileName);
|
||||
const relativeDifference = convertToRelativePath(extendedDirname, basePath, identity);
|
||||
const updatePath = (path: string) => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
|
||||
const mapPropertiesInRawIfNotUndefined = (propertyName: string) => {
|
||||
if (raw[propertyName]) {
|
||||
|
||||
@ -6795,4 +6795,48 @@ namespace ts.projectSystem {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem forceConsistentCasingInFileNames", () => {
|
||||
it("works when extends is specified with a case insensitive file system", () => {
|
||||
const rootPath = "/Users/username/dev/project";
|
||||
const file1: FileOrFolder = {
|
||||
path: `${rootPath}/index.ts`,
|
||||
content: 'import {x} from "file2";',
|
||||
};
|
||||
const file2: FileOrFolder = {
|
||||
path: `${rootPath}/file2.js`,
|
||||
content: "",
|
||||
};
|
||||
const file2Dts: FileOrFolder = {
|
||||
path: `${rootPath}/types/file2/index.d.ts`,
|
||||
content: "export declare const x: string;",
|
||||
};
|
||||
const tsconfigAll: FileOrFolder = {
|
||||
path: `${rootPath}/tsconfig.all.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
baseUrl: ".",
|
||||
paths: { file2: ["./file2.js"] },
|
||||
typeRoots: ["./types"],
|
||||
forceConsistentCasingInFileNames: true,
|
||||
},
|
||||
}),
|
||||
};
|
||||
const tsconfig: FileOrFolder = {
|
||||
path: `${rootPath}/tsconfig.json`,
|
||||
content: JSON.stringify({ extends: "./tsconfig.all.json" }),
|
||||
};
|
||||
|
||||
const host = createServerHost([file1, file2, file2Dts, libFile, tsconfig, tsconfigAll], { useCaseSensitiveFileNames: false });
|
||||
const session = createSession(host);
|
||||
|
||||
openFilesForSession([file1], session);
|
||||
const projectService = session.getProjectService();
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
|
||||
const diagnostics = configuredProjectAt(projectService, 0).getLanguageService().getCompilerOptionsDiagnostics();
|
||||
assert.deepEqual(diagnostics, []);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user