mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
expose type reference resolution for external consumption
This commit is contained in:
@@ -312,19 +312,19 @@ namespace ts {
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "librarySearchPaths",
|
||||
name: "typesSearchPaths",
|
||||
type: "list",
|
||||
isTSConfigOnly: true,
|
||||
element: {
|
||||
name: "librarySearchPaths",
|
||||
name: "typesSearchPaths",
|
||||
type: "string",
|
||||
isFilePath: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "traceModuleResolution",
|
||||
name: "traceResolution",
|
||||
type: "boolean",
|
||||
description: Diagnostics.Enable_tracing_of_the_module_resolution_process
|
||||
description: Diagnostics.Enable_tracing_of_the_name_resolution_process
|
||||
},
|
||||
{
|
||||
name: "allowJs",
|
||||
|
||||
@@ -2517,7 +2517,7 @@
|
||||
"category": "Message",
|
||||
"code": 6084
|
||||
},
|
||||
"Enable tracing of the module resolution process.": {
|
||||
"Enable tracing of the name resolution process.": {
|
||||
"category": "Message",
|
||||
"code": 6085
|
||||
},
|
||||
@@ -2565,7 +2565,7 @@
|
||||
"category": "Message",
|
||||
"code": 6096
|
||||
},
|
||||
"File '{0}' exist - use it as a module resolution result.": {
|
||||
"File '{0}' exist - use it as a name resolution result.": {
|
||||
"category": "Message",
|
||||
"code": 6097
|
||||
},
|
||||
@@ -2577,11 +2577,11 @@
|
||||
"category": "Message",
|
||||
"code": 6099
|
||||
},
|
||||
"'package.json' does not have 'typings' field.": {
|
||||
"'package.json' does not have 'types' field.": {
|
||||
"category": "Message",
|
||||
"code": 6100
|
||||
},
|
||||
"'package.json' has 'typings' field '{0}' that references '{1}'.": {
|
||||
"'package.json' has '{0}' field '{1}' that references '{2}'.": {
|
||||
"category": "Message",
|
||||
"code": 6101
|
||||
},
|
||||
@@ -2597,7 +2597,7 @@
|
||||
"category": "Message",
|
||||
"code": 6104
|
||||
},
|
||||
"Expected type of 'typings' field in 'package.json' to be 'string', got '{0}'.": {
|
||||
"Expected type of '{0}' field in 'package.json' to be 'string', got '{1}'.": {
|
||||
"category": "Message",
|
||||
"code": 6105
|
||||
},
|
||||
@@ -2637,6 +2637,30 @@
|
||||
"category": "Error",
|
||||
"code": 6114
|
||||
},
|
||||
"======== Resolving type reference directive '{0}' from '{1}' with compilation root dir '{2}'. ========": {
|
||||
"category": "Message",
|
||||
"code": 6115
|
||||
},
|
||||
"Resolving using primary search paths...": {
|
||||
"category": "Message",
|
||||
"code": 6116
|
||||
},
|
||||
"Resolving from node_modules folder...": {
|
||||
"category": "Message",
|
||||
"code": 6117
|
||||
},
|
||||
"======== Type reference directive '{0}' was successfully resolved to '{1}', primary: {2}. ========": {
|
||||
"category": "Message",
|
||||
"code": 6118
|
||||
},
|
||||
"======== Type reference directive '{0}' was not resolved. ========": {
|
||||
"category": "Message",
|
||||
"code": 6119
|
||||
},
|
||||
"Resolving with primary search path '{0}'": {
|
||||
"category": "Message",
|
||||
"code": 6120
|
||||
},
|
||||
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
|
||||
@@ -5505,7 +5505,7 @@ namespace ts {
|
||||
function processReferenceComments(sourceFile: SourceFile): void {
|
||||
const triviaScanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/false, LanguageVariant.Standard, sourceText);
|
||||
const referencedFiles: FileReference[] = [];
|
||||
const referencedLibraries: FileReference[] = [];
|
||||
const typeReferenceDirectives: FileReference[] = [];
|
||||
const amdDependencies: { path: string; name: string }[] = [];
|
||||
let amdModuleName: string;
|
||||
|
||||
@@ -5532,8 +5532,8 @@ namespace ts {
|
||||
sourceFile.hasNoDefaultLib = referencePathMatchResult.isNoDefaultLib;
|
||||
const diagnosticMessage = referencePathMatchResult.diagnosticMessage;
|
||||
if (fileReference) {
|
||||
if (referencePathMatchResult.isLibraryReference) {
|
||||
referencedLibraries.push(fileReference);
|
||||
if (referencePathMatchResult.isTypeReferenceDirective) {
|
||||
typeReferenceDirectives.push(fileReference);
|
||||
}
|
||||
else {
|
||||
referencedFiles.push(fileReference);
|
||||
@@ -5569,7 +5569,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
sourceFile.referencedFiles = referencedFiles;
|
||||
sourceFile.referencedLibraries = referencedLibraries;
|
||||
sourceFile.typeReferenceDirectives = typeReferenceDirectives;
|
||||
sourceFile.amdDependencies = amdDependencies;
|
||||
sourceFile.moduleName = amdModuleName;
|
||||
}
|
||||
|
||||
+337
-198
@@ -13,9 +13,9 @@ namespace ts {
|
||||
const emptyArray: any[] = [];
|
||||
|
||||
const defaultLibrarySearchPaths = <Path[]>[
|
||||
"typings/",
|
||||
"types/",
|
||||
"node_modules/",
|
||||
"node_modules/@typings/",
|
||||
"node_modules/@types/",
|
||||
];
|
||||
|
||||
export const version = "1.9.0";
|
||||
@@ -41,13 +41,64 @@ namespace ts {
|
||||
return normalizePath(referencedFileName);
|
||||
}
|
||||
|
||||
export function computeCompilationRoot(rootFileNames: string[], currentDirectory: string, options: CompilerOptions, getCanonicalFileName: (fileName: string) => string): string {
|
||||
const rootDirOrConfigFilePath = options.rootDir || (options.configFilePath && getDirectoryPath(options.configFilePath));
|
||||
return rootDirOrConfigFilePath
|
||||
? getNormalizedAbsolutePath(rootDirOrConfigFilePath, currentDirectory)
|
||||
: computeCommonSourceDirectoryOfFilenames(rootFileNames, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
function computeCommonSourceDirectoryOfFilenames(fileNames: string[], currentDirectory: string, getCanonicalFileName: (fileName: string) => string): string {
|
||||
let commonPathComponents: string[];
|
||||
const failed = forEach(fileNames, sourceFile => {
|
||||
// Each file contributes into common source file path
|
||||
const sourcePathComponents = getNormalizedPathComponents(sourceFile, currentDirectory);
|
||||
sourcePathComponents.pop(); // The base file name is not part of the common directory path
|
||||
|
||||
if (!commonPathComponents) {
|
||||
// first file
|
||||
commonPathComponents = sourcePathComponents;
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0, n = Math.min(commonPathComponents.length, sourcePathComponents.length); i < n; i++) {
|
||||
if (getCanonicalFileName(commonPathComponents[i]) !== getCanonicalFileName(sourcePathComponents[i])) {
|
||||
if (i === 0) {
|
||||
// Failed to find any common path component
|
||||
return true;
|
||||
}
|
||||
|
||||
// New common path found that is 0 -> i-1
|
||||
commonPathComponents.length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
|
||||
if (sourcePathComponents.length < commonPathComponents.length) {
|
||||
commonPathComponents.length = sourcePathComponents.length;
|
||||
}
|
||||
});
|
||||
|
||||
// A common path can not be found when paths span multiple drives on windows, for example
|
||||
if (failed) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!commonPathComponents) { // Can happen when all input files are .d.ts files
|
||||
return currentDirectory;
|
||||
}
|
||||
|
||||
return getNormalizedPathFromPathComponents(commonPathComponents);
|
||||
}
|
||||
|
||||
function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void;
|
||||
function trace(host: ModuleResolutionHost, message: DiagnosticMessage): void {
|
||||
host.trace(formatMessage.apply(undefined, arguments));
|
||||
}
|
||||
|
||||
function isTraceEnabled(compilerOptions: CompilerOptions, host: ModuleResolutionHost): boolean {
|
||||
return compilerOptions.traceModuleResolution && host.trace !== undefined;
|
||||
return compilerOptions.traceResolution && host.trace !== undefined;
|
||||
}
|
||||
|
||||
function startsWith(str: string, prefix: string): boolean {
|
||||
@@ -97,6 +148,155 @@ namespace ts {
|
||||
skipTsx: boolean;
|
||||
}
|
||||
|
||||
function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
|
||||
let jsonContent: { typings?: string, types?: string };
|
||||
try {
|
||||
const jsonText = state.host.readFile(packageJsonPath);
|
||||
jsonContent = jsonText ? <{ typings?: string, types?: string }>JSON.parse(jsonText) : {};
|
||||
}
|
||||
catch (e) {
|
||||
// gracefully handle if readFile fails or returns not JSON
|
||||
jsonContent = {};
|
||||
}
|
||||
|
||||
let typesFile: string;
|
||||
let fieldName: string;
|
||||
// first try to read content of 'typings' section (backward compatibility)
|
||||
if (jsonContent.typings) {
|
||||
if (typeof jsonContent.typings === "string") {
|
||||
fieldName = "typings";
|
||||
typesFile = jsonContent.typings;
|
||||
}
|
||||
else {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "typings", typeof jsonContent.typings);
|
||||
}
|
||||
}
|
||||
}
|
||||
// then read 'types'
|
||||
if (!typesFile && jsonContent.types) {
|
||||
if (typeof jsonContent.types === "string") {
|
||||
fieldName = "types";
|
||||
typesFile = jsonContent.types;
|
||||
}
|
||||
else {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "types", typeof jsonContent.types);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typesFile) {
|
||||
const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile));
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath);
|
||||
}
|
||||
return typesFilePath;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function tryLoadTypeDeclarationFile(searchPath: string, failedLookupLocations: string[], state: ModuleResolutionState) {
|
||||
let typesFile: string;
|
||||
const packageJsonPath = combinePaths(searchPath, "package.json");
|
||||
if (state.host.fileExists(packageJsonPath)) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
|
||||
}
|
||||
typesFile = tryReadTypesSection(packageJsonPath, searchPath, state);
|
||||
if (!typesFile) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.package_json_does_not_have_types_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
|
||||
}
|
||||
failedLookupLocations.push(packageJsonPath);
|
||||
}
|
||||
|
||||
if (!typesFile) {
|
||||
typesFile = "index.d.ts";
|
||||
}
|
||||
|
||||
const combinedPath = normalizePath(combinePaths(searchPath, typesFile));
|
||||
if (state.host.fileExists(combinedPath)) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, combinedPath);
|
||||
}
|
||||
return combinedPath;
|
||||
}
|
||||
else {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_does_not_exist, combinedPath);
|
||||
}
|
||||
failedLookupLocations.push(combinedPath);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getEffectiveTypesPrimarySearchPaths(options: CompilerOptions): string[] {
|
||||
if (options.typesSearchPaths) {
|
||||
return options.typesSearchPaths;
|
||||
}
|
||||
return options.configFilePath
|
||||
? [getDirectoryPath(options.configFilePath)].concat(defaultLibrarySearchPaths)
|
||||
: defaultLibrarySearchPaths;
|
||||
}
|
||||
|
||||
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string, compilationRoot: string, options: CompilerOptions, host: ModuleResolutionHost): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
|
||||
const traceEnabled = isTraceEnabled(options, host);
|
||||
const moduleResolutionState: ModuleResolutionState = {
|
||||
compilerOptions: options,
|
||||
host: host,
|
||||
skipTsx: true,
|
||||
traceEnabled
|
||||
};
|
||||
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Resolving_type_reference_directive_0_from_1_with_compilation_root_dir_2, typeReferenceDirectiveName, containingFile, compilationRoot);
|
||||
}
|
||||
const failedLookupLocations: string[] = [];
|
||||
// Check primary library paths
|
||||
for (const searchPath of getEffectiveTypesPrimarySearchPaths(options)) {
|
||||
const primaryPath = combinePaths(compilationRoot, searchPath);
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Resolving_with_primary_search_path_0, primaryPath);
|
||||
}
|
||||
const resolvedFile = tryLoadTypeDeclarationFile(combinePaths(primaryPath, typeReferenceDirectiveName), failedLookupLocations, moduleResolutionState);
|
||||
if (resolvedFile) {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, true);
|
||||
}
|
||||
return {
|
||||
resolvedTypeReferenceDirective: { primary: true, resolvedFileName: resolvedFile },
|
||||
failedLookupLocations
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Resolving_from_node_modules_folder);
|
||||
}
|
||||
// check secondary locations
|
||||
const resolvedFile = loadModuleFromNodeModules(typeReferenceDirectiveName, getDirectoryPath(containingFile), failedLookupLocations, moduleResolutionState);
|
||||
if (traceEnabled) {
|
||||
if (resolvedFile) {
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, false);
|
||||
}
|
||||
else {
|
||||
trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName);
|
||||
}
|
||||
}
|
||||
return {
|
||||
resolvedTypeReferenceDirective: resolvedFile
|
||||
? { primary: false, resolvedFileName: resolvedFile }
|
||||
: undefined,
|
||||
failedLookupLocations
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
|
||||
const traceEnabled = isTraceEnabled(compilerOptions, host);
|
||||
if (traceEnabled) {
|
||||
@@ -422,6 +622,13 @@ namespace ts {
|
||||
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
|
||||
*/
|
||||
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
|
||||
if (!onlyRecordFailures) {
|
||||
// check if containig folder exists - if it doesn't then just record failures for all supported extensions without disk probing
|
||||
const directory = getDirectoryPath(candidate);
|
||||
if (directory) {
|
||||
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
|
||||
}
|
||||
}
|
||||
return forEach(extensions, tryLoad);
|
||||
|
||||
function tryLoad(ext: string): string {
|
||||
@@ -431,7 +638,7 @@ namespace ts {
|
||||
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
|
||||
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_module_resolution_result, fileName);
|
||||
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
@@ -452,36 +659,16 @@ namespace ts {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
|
||||
}
|
||||
|
||||
let jsonContent: { typings?: string };
|
||||
|
||||
try {
|
||||
const jsonText = state.host.readFile(packageJsonPath);
|
||||
jsonContent = jsonText ? <{ typings?: string }>JSON.parse(jsonText) : { typings: undefined };
|
||||
}
|
||||
catch (e) {
|
||||
// gracefully handle if readFile fails or returns not JSON
|
||||
jsonContent = { typings: undefined };
|
||||
}
|
||||
|
||||
if (jsonContent.typings) {
|
||||
if (typeof jsonContent.typings === "string") {
|
||||
const typingsFile = normalizePath(combinePaths(candidate, jsonContent.typings));
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.package_json_has_typings_field_0_that_references_1, jsonContent.typings, typingsFile);
|
||||
}
|
||||
const result = loadModuleFromFile(typingsFile, extensions, failedLookupLocation, !directoryProbablyExists(getDirectoryPath(typingsFile), state.host), state);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.Expected_type_of_typings_field_in_package_json_to_be_string_got_0, typeof jsonContent.typings);
|
||||
const typesFile = tryReadTypesSection(packageJsonPath, candidate, state);
|
||||
if (typesFile) {
|
||||
const result = loadModuleFromFile(typesFile, extensions, failedLookupLocation, !directoryProbablyExists(getDirectoryPath(typesFile), state.host), state);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state.traceEnabled) {
|
||||
trace(state.host, Diagnostics.package_json_does_not_have_typings_field);
|
||||
trace(state.host, Diagnostics.package_json_does_not_have_types_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,20 +683,31 @@ namespace ts {
|
||||
return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state);
|
||||
}
|
||||
|
||||
function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
|
||||
const nodeModulesFolder = combinePaths(directory, "node_modules");
|
||||
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
|
||||
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
|
||||
// Load only typescript files irrespective of allowJs option if loading from node modules
|
||||
let result = loadModuleFromFile(candidate, supportedTypeScriptExtensions, failedLookupLocations, !nodeModulesFolderExists, state);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = loadNodeModuleFromDirectory(supportedTypeScriptExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
|
||||
directory = normalizeSlashes(directory);
|
||||
while (true) {
|
||||
const baseName = getBaseFileName(directory);
|
||||
if (baseName !== "node_modules") {
|
||||
const nodeModulesFolder = combinePaths(directory, "node_modules");
|
||||
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
|
||||
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
|
||||
// Load only typescript files irrespective of allowJs option if loading from node modules
|
||||
let result = loadModuleFromFile(candidate, supportedTypeScriptExtensions, failedLookupLocations, !nodeModulesFolderExists, state);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = loadNodeModuleFromDirectory(supportedTypeScriptExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
|
||||
const result =
|
||||
// first: try to load module as-is
|
||||
loadModuleFromNodeModulesFolder(moduleName, directory, failedLookupLocations, state) ||
|
||||
// second: try to load module from the scope '@types'
|
||||
loadModuleFromNodeModulesFolder(combinePaths("@types", moduleName), directory, failedLookupLocations, state);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
@@ -696,54 +894,67 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function loadWithLocalCache<T>(names: string[], containingFile: string, loader: (name: string, containingFile: string) => T): T[] {
|
||||
if (names.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const resolutions: T[] = [];
|
||||
const cache: Map<T> = {};
|
||||
for (const name of names) {
|
||||
let result: T;
|
||||
if (hasProperty(cache, name)) {
|
||||
result = cache[name];
|
||||
}
|
||||
else {
|
||||
result = loader(name, containingFile);
|
||||
cache[name] = result;
|
||||
}
|
||||
resolutions.push(result);
|
||||
}
|
||||
return resolutions;
|
||||
}
|
||||
|
||||
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
|
||||
let program: Program;
|
||||
let files: SourceFile[] = [];
|
||||
let fileProcessingDiagnostics = createDiagnosticCollection();
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const resolvedLibraries: Map<ResolvedLibrary> = {};
|
||||
let libraryRoot =
|
||||
(options.rootDir && ts.toPath(options.rootDir, currentDirectory, host.getCanonicalFileName)) ||
|
||||
(options.configFilePath && getDirectoryPath(getNormalizedAbsolutePath(options.configFilePath, currentDirectory)));
|
||||
if (libraryRoot === undefined) {
|
||||
libraryRoot = computeCommonSourceDirectoryOfFilenames(rootNames);
|
||||
}
|
||||
const programDiagnostics = createDiagnosticCollection();
|
||||
|
||||
let commonSourceDirectory: string;
|
||||
let diagnosticsProducingTypeChecker: TypeChecker;
|
||||
let noDiagnosticsTypeChecker: TypeChecker;
|
||||
let classifiableNames: Map<string>;
|
||||
|
||||
let resolvedTypeReferenceDirectives: Map<ResolvedTypeReferenceDirective> = {};
|
||||
let fileProcessingDiagnostics = createDiagnosticCollection();
|
||||
let skipDefaultLib = options.noLib;
|
||||
const programDiagnostics = createDiagnosticCollection();
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const supportedExtensions = getSupportedExtensions(options);
|
||||
|
||||
const start = new Date().getTime();
|
||||
|
||||
host = host || createCompilerHost(options);
|
||||
|
||||
const compilationRoot = computeCompilationRoot(rootNames, currentDirectory, options, getCanonicalFileName);
|
||||
|
||||
// Map storing if there is emit blocking diagnostics for given input
|
||||
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
|
||||
|
||||
const resolveModuleNamesWorker = host.resolveModuleNames
|
||||
? ((moduleNames: string[], containingFile: string) => host.resolveModuleNames(moduleNames, containingFile))
|
||||
: ((moduleNames: string[], containingFile: string) => {
|
||||
const resolvedModuleNames: ResolvedModule[] = [];
|
||||
// resolveModuleName does not store any results between calls.
|
||||
// lookup is a local cache to avoid resolving the same module name several times
|
||||
const lookup: Map<ResolvedModule> = {};
|
||||
for (const moduleName of moduleNames) {
|
||||
let resolvedName: ResolvedModule;
|
||||
if (hasProperty(lookup, moduleName)) {
|
||||
resolvedName = lookup[moduleName];
|
||||
}
|
||||
else {
|
||||
resolvedName = resolveModuleName(moduleName, containingFile, options, host).resolvedModule;
|
||||
lookup[moduleName] = resolvedName;
|
||||
}
|
||||
resolvedModuleNames.push(resolvedName);
|
||||
}
|
||||
return resolvedModuleNames;
|
||||
});
|
||||
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModule[];
|
||||
if (host.resolveModuleNames) {
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile);
|
||||
}
|
||||
else {
|
||||
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host).resolvedModule;
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(moduleNames, containingFile, loader);
|
||||
}
|
||||
|
||||
let resolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[];
|
||||
if (host.resolveTypeReferenceDirectives) {
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
|
||||
}
|
||||
else {
|
||||
const loader = (typesRef: string, containingFile: string) => resolveTypeReferenceDirective(typesRef, containingFile, compilationRoot, options, host).resolvedTypeReferenceDirective;
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(typeReferenceDirectiveNames, containingFile, loader);
|
||||
}
|
||||
|
||||
const filesByName = createFileMap<SourceFile>();
|
||||
// stores 'filename -> file association' ignoring case
|
||||
@@ -759,7 +970,10 @@ namespace ts {
|
||||
(oldOptions.target !== options.target) ||
|
||||
(oldOptions.noLib !== options.noLib) ||
|
||||
(oldOptions.jsx !== options.jsx) ||
|
||||
(oldOptions.allowJs !== options.allowJs)) {
|
||||
(oldOptions.allowJs !== options.allowJs) ||
|
||||
(oldOptions.rootDir !== options.rootDir) ||
|
||||
(oldOptions.typesSearchPaths !== options.typesSearchPaths) ||
|
||||
(oldOptions.configFilePath !== options.configFilePath)) {
|
||||
oldProgram = undefined;
|
||||
}
|
||||
}
|
||||
@@ -808,7 +1022,8 @@ namespace ts {
|
||||
getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
|
||||
getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
|
||||
getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
|
||||
getFileProcessingDiagnostics: () => fileProcessingDiagnostics
|
||||
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
|
||||
resolvedTypeReferenceDirectives
|
||||
};
|
||||
|
||||
verifyCompilerOptions();
|
||||
@@ -901,26 +1116,33 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
|
||||
// 'types' references has changed
|
||||
return false;
|
||||
}
|
||||
|
||||
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory);
|
||||
if (resolveModuleNamesWorker) {
|
||||
const moduleNames = map(concatenate(newSourceFile.imports, newSourceFile.moduleAugmentations), getTextOfLiteral);
|
||||
const resolutions = resolveModuleNamesWorker(moduleNames, getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory));
|
||||
const resolutions = resolveModuleNamesWorker(moduleNames, newSourceFilePath);
|
||||
// ensure that module resolution results are still correct
|
||||
for (let i = 0; i < moduleNames.length; i++) {
|
||||
const newResolution = resolutions[i];
|
||||
const oldResolution = getResolvedModule(oldSourceFile, moduleNames[i]);
|
||||
const resolutionChanged = oldResolution
|
||||
? !newResolution ||
|
||||
oldResolution.resolvedFileName !== newResolution.resolvedFileName ||
|
||||
!!oldResolution.isExternalLibraryImport !== !!newResolution.isExternalLibraryImport
|
||||
: newResolution;
|
||||
|
||||
if (resolutionChanged) {
|
||||
return false;
|
||||
}
|
||||
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
|
||||
if (resolutionsChanged) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// pass the cache of module resolutions from the old source file
|
||||
if (resolveTypeReferenceDirectiveNamesWorker) {
|
||||
const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, x => x.fileName);
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFilePath);
|
||||
// ensure that types resolutions are still correct
|
||||
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
|
||||
if (resolutionsChanged) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// pass the cache of module/types resolutions from the old source file
|
||||
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
|
||||
modifiedSourceFiles.push(newSourceFile);
|
||||
}
|
||||
else {
|
||||
@@ -943,6 +1165,7 @@ namespace ts {
|
||||
for (const modifiedFile of modifiedSourceFiles) {
|
||||
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile);
|
||||
}
|
||||
resolvedTypeReferenceDirectives = oldProgram.resolvedTypeReferenceDirectives;
|
||||
oldProgram.structureIsReused = true;
|
||||
|
||||
return true;
|
||||
@@ -1510,8 +1733,8 @@ namespace ts {
|
||||
|
||||
const basePath = getDirectoryPath(fileName);
|
||||
if (!options.noResolve) {
|
||||
processReferencedFiles(file, basePath, /*isDefaultLib*/ isDefaultLib);
|
||||
processReferencedLibraries(file, libraryRoot);
|
||||
processReferencedFiles(file, basePath, isDefaultLib);
|
||||
processTypeReferenceDirectives(file, compilationRoot);
|
||||
}
|
||||
|
||||
// always process imported modules to record module name resolutions
|
||||
@@ -1535,97 +1758,57 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function findLibraryDefinition(searchPath: string) {
|
||||
let typingFilename = "index.d.ts";
|
||||
const packageJsonPath = combinePaths(searchPath, "package.json");
|
||||
if (host.fileExists(packageJsonPath)) {
|
||||
let package: { typings?: string } = {};
|
||||
try {
|
||||
package = JSON.parse(host.readFile(packageJsonPath));
|
||||
}
|
||||
catch (e) { }
|
||||
function processTypeReferenceDirectives(file: SourceFile, compilationRoot: string) {
|
||||
const typeDirectives = map(file.typeReferenceDirectives, l => l.fileName);
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.fileName);
|
||||
|
||||
if (package.typings) {
|
||||
typingFilename = package.typings;
|
||||
}
|
||||
}
|
||||
|
||||
const combinedPath = normalizePath(combinePaths(searchPath, typingFilename));
|
||||
return host.fileExists(combinedPath) ? combinedPath : undefined;
|
||||
}
|
||||
|
||||
function processReferencedLibraries(file: SourceFile, compilationRoot: string) {
|
||||
const primarySearchPaths = map(getEffectiveLibraryPrimarySearchPaths(), path => combinePaths(compilationRoot, path));
|
||||
|
||||
const failedSearchPaths: string[] = [];
|
||||
const moduleResolutionState: ModuleResolutionState = {
|
||||
compilerOptions: options,
|
||||
host: host,
|
||||
skipTsx: true,
|
||||
traceEnabled: false
|
||||
};
|
||||
|
||||
for (const ref of file.referencedLibraries) {
|
||||
for (let i = 0; i < typeDirectives.length; i++) {
|
||||
const ref = file.typeReferenceDirectives[i];
|
||||
const resolvedTypeReferenceDirective = resolutions[i];
|
||||
// store resolved type directive on the file
|
||||
setResolvedTypeReferenceDirective(file, ref.fileName, resolvedTypeReferenceDirective);
|
||||
// If we already found this library as a primary reference, or failed to find it, nothing to do
|
||||
const previousResolution = resolvedLibraries[ref.fileName];
|
||||
const previousResolution = resolvedTypeReferenceDirectives[ref.fileName];
|
||||
if (previousResolution && (previousResolution.primary || (previousResolution.resolvedFileName === undefined))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let foundIt = false;
|
||||
|
||||
// Check primary library paths
|
||||
for (const primaryPath of primarySearchPaths) {
|
||||
const searchPath = combinePaths(primaryPath, ref.fileName);
|
||||
const resolvedFile = findLibraryDefinition(searchPath);
|
||||
if (resolvedFile) {
|
||||
resolvedLibraries[ref.fileName] = { primary: true, resolvedFileName: resolvedFile };
|
||||
processSourceFile(resolvedFile, /*isDefaultLib*/ false, /*isReference*/ true, file, ref.pos, ref.end);
|
||||
foundIt = true;
|
||||
break;
|
||||
let saveResolution = true;
|
||||
if (resolvedTypeReferenceDirective) {
|
||||
if (resolvedTypeReferenceDirective.primary) {
|
||||
// resolved from the primary path
|
||||
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, /*isReference*/ true, file, ref.pos, ref.end);
|
||||
}
|
||||
}
|
||||
|
||||
// Check secondary library paths
|
||||
if (!foundIt) {
|
||||
const secondaryResult = loadModuleFromNodeModules(ref.fileName, file.fileName, failedSearchPaths, moduleResolutionState);
|
||||
if (secondaryResult) {
|
||||
foundIt = true;
|
||||
else {
|
||||
// If we already resolved to this file, it must have been a secondary reference. Check file contents
|
||||
// for sameness and possibly issue an error
|
||||
if (previousResolution) {
|
||||
const otherFileText = host.readFile(secondaryResult);
|
||||
const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName);
|
||||
if (otherFileText !== getSourceFile(previousResolution.resolvedFileName).text) {
|
||||
fileProcessingDiagnostics.add(createFileDiagnostic(file, ref.pos, ref.end - ref.pos,
|
||||
Diagnostics.Conflicting_library_definitions_for_0_found_at_1_and_2_Copy_the_correct_file_to_the_typings_folder_to_resolve_this_conflict,
|
||||
ref.fileName,
|
||||
secondaryResult,
|
||||
resolvedTypeReferenceDirective.resolvedFileName,
|
||||
previousResolution.resolvedFileName));
|
||||
}
|
||||
// don't overwrite previous resolution result
|
||||
saveResolution = false;
|
||||
}
|
||||
else {
|
||||
// First resolution of this library
|
||||
resolvedLibraries[ref.fileName] = { primary: false, resolvedFileName: secondaryResult };
|
||||
processSourceFile(secondaryResult, /*isDefaultLib*/ false, /*isReference*/ true, file, ref.pos, ref.end);
|
||||
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, /*isReference*/ true, file, ref.pos, ref.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundIt) {
|
||||
else {
|
||||
fileProcessingDiagnostics.add(createFileDiagnostic(file, ref.pos, ref.end - ref.pos, Diagnostics.Cannot_find_name_0, ref.fileName));
|
||||
// Create an entry as a primary lookup result so we don't keep doing this
|
||||
resolvedLibraries[ref.fileName] = { primary: true, resolvedFileName: undefined };
|
||||
}
|
||||
|
||||
if (saveResolution) {
|
||||
resolvedTypeReferenceDirectives[ref.fileName] = resolvedTypeReferenceDirective;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getEffectiveLibraryPrimarySearchPaths(): Path[] {
|
||||
return <Path[]>(options.librarySearchPaths ||
|
||||
(options.configFilePath ?
|
||||
[options.configFilePath].concat(defaultLibrarySearchPaths) :
|
||||
defaultLibrarySearchPaths));
|
||||
}
|
||||
|
||||
function getCanonicalFileName(fileName: string): string {
|
||||
return host.getCanonicalFileName(fileName);
|
||||
}
|
||||
@@ -1672,50 +1855,6 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
function computeCommonSourceDirectoryOfFilenames(fileNames: string[]): string {
|
||||
let commonPathComponents: string[];
|
||||
const failed = forEach(fileNames, sourceFile => {
|
||||
// Each file contributes into common source file path
|
||||
const sourcePathComponents = getNormalizedPathComponents(sourceFile, currentDirectory);
|
||||
sourcePathComponents.pop(); // The base file name is not part of the common directory path
|
||||
|
||||
if (!commonPathComponents) {
|
||||
// first file
|
||||
commonPathComponents = sourcePathComponents;
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0, n = Math.min(commonPathComponents.length, sourcePathComponents.length); i < n; i++) {
|
||||
if (getCanonicalFileName(commonPathComponents[i]) !== getCanonicalFileName(sourcePathComponents[i])) {
|
||||
if (i === 0) {
|
||||
// Failed to find any common path component
|
||||
return true;
|
||||
}
|
||||
|
||||
// New common path found that is 0 -> i-1
|
||||
commonPathComponents.length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
|
||||
if (sourcePathComponents.length < commonPathComponents.length) {
|
||||
commonPathComponents.length = sourcePathComponents.length;
|
||||
}
|
||||
});
|
||||
|
||||
// A common path can not be found when paths span multiple drives on windows, for example
|
||||
if (failed) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!commonPathComponents) { // Can happen when all input files are .d.ts files
|
||||
return currentDirectory;
|
||||
}
|
||||
|
||||
return getNormalizedPathFromPathComponents(commonPathComponents);
|
||||
}
|
||||
|
||||
function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
|
||||
const fileNames: string[] = [];
|
||||
for (const file of sourceFiles) {
|
||||
@@ -1723,7 +1862,7 @@ namespace ts {
|
||||
fileNames.push(file.fileName);
|
||||
}
|
||||
}
|
||||
return computeCommonSourceDirectoryOfFilenames(fileNames);
|
||||
return computeCommonSourceDirectoryOfFilenames(fileNames, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
function checkSourceFilesBelongToPath(sourceFiles: SourceFile[], rootDirectory: string): boolean {
|
||||
|
||||
+21
-3
@@ -1536,7 +1536,7 @@ namespace ts {
|
||||
amdDependencies: AmdDependency[];
|
||||
moduleName: string;
|
||||
referencedFiles: FileReference[];
|
||||
referencedLibraries: FileReference[];
|
||||
typeReferenceDirectives: FileReference[];
|
||||
languageVariant: LanguageVariant;
|
||||
isDeclarationFile: boolean;
|
||||
|
||||
@@ -1584,6 +1584,7 @@ namespace ts {
|
||||
// It is used to resolve module names in the checker.
|
||||
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
|
||||
/* @internal */ resolvedModules: Map<ResolvedModule>;
|
||||
/* @internal */ resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
|
||||
/* @internal */ imports: LiteralExpression[];
|
||||
/* @internal */ moduleAugmentations: LiteralExpression[];
|
||||
}
|
||||
@@ -1660,6 +1661,7 @@ namespace ts {
|
||||
/* @internal */ getTypeCount(): number;
|
||||
|
||||
/* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection;
|
||||
/* @internal */ resolvedTypeReferenceDirectives: Map<ResolvedTypeReferenceDirective>;
|
||||
// For testing purposes only.
|
||||
/* @internal */ structureIsReused?: boolean;
|
||||
}
|
||||
@@ -2416,7 +2418,7 @@ namespace ts {
|
||||
jsx?: JsxEmit;
|
||||
reactNamespace?: string;
|
||||
listFiles?: boolean;
|
||||
librarySearchPaths?: string[];
|
||||
typesSearchPaths?: string[];
|
||||
locale?: string;
|
||||
mapRoot?: string;
|
||||
module?: ModuleKind;
|
||||
@@ -2455,7 +2457,7 @@ namespace ts {
|
||||
baseUrl?: string;
|
||||
paths?: PathSubstitutions;
|
||||
rootDirs?: RootPaths;
|
||||
traceModuleResolution?: boolean;
|
||||
traceResolution?: boolean;
|
||||
allowSyntheticDefaultImports?: boolean;
|
||||
allowJs?: boolean;
|
||||
noImplicitUseStrict?: boolean;
|
||||
@@ -2760,6 +2762,18 @@ namespace ts {
|
||||
failedLookupLocations: string[];
|
||||
}
|
||||
|
||||
export interface ResolvedTypeReferenceDirective {
|
||||
// True if the type declaration file was found in a primary lookup location
|
||||
primary: boolean;
|
||||
// The location of the .d.ts file we located, or undefined if resolution failed
|
||||
resolvedFileName?: string;
|
||||
}
|
||||
|
||||
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
|
||||
resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective;
|
||||
failedLookupLocations: string[];
|
||||
}
|
||||
|
||||
export interface CompilerHost extends ModuleResolutionHost {
|
||||
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
|
||||
getCancellationToken?(): CancellationToken;
|
||||
@@ -2779,6 +2793,10 @@ namespace ts {
|
||||
* 'throw new Error("NotImplemented")'
|
||||
*/
|
||||
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
|
||||
/**
|
||||
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
|
||||
*/
|
||||
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
}
|
||||
|
||||
export interface TextSpan {
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ts {
|
||||
fileReference?: FileReference;
|
||||
diagnosticMessage?: DiagnosticMessage;
|
||||
isNoDefaultLib?: boolean;
|
||||
isLibraryReference?: boolean;
|
||||
isTypeReferenceDirective?: boolean;
|
||||
}
|
||||
|
||||
export interface SynthesizedNode extends Node {
|
||||
@@ -119,6 +119,43 @@ namespace ts {
|
||||
sourceFile.resolvedModules[moduleNameText] = resolvedModule;
|
||||
}
|
||||
|
||||
export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective): void {
|
||||
if (!sourceFile.resolvedTypeReferenceDirectiveNames) {
|
||||
sourceFile.resolvedTypeReferenceDirectiveNames = {};
|
||||
}
|
||||
|
||||
sourceFile.resolvedTypeReferenceDirectiveNames[typeReferenceDirectiveName] = resolvedTypeReferenceDirective;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function moduleResolutionIsEqualTo(oldResolution: ResolvedModule, newResolution: ResolvedModule): boolean {
|
||||
return oldResolution.resolvedFileName === newResolution.resolvedFileName && oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
|
||||
return oldResolution.resolvedFileName === newResolution.resolvedFileName && oldResolution.primary === newResolution.primary;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function hasChangesInResolutions<T>(names: string[], newResolutions: T[], oldResolutions: Map<T>, comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
|
||||
if (names.length !== newResolutions.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const newResolution = newResolutions[i];
|
||||
const oldResolution = oldResolutions && hasProperty(oldResolutions, names[i]) ? oldResolutions[names[i]] : undefined;
|
||||
const changed =
|
||||
oldResolution
|
||||
? !newResolution || !comparer(oldResolution, newResolution)
|
||||
: newResolution;
|
||||
if (changed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if this node contains a parse error anywhere underneath it.
|
||||
export function containsParseError(node: Node): boolean {
|
||||
aggregateChildData(node);
|
||||
@@ -499,7 +536,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
|
||||
export let fullTripleSlashReferenceLibraryRegEx = /^(\/\/\/\s*<reference\s+library\s*=\s*)('|")(.+?)\2.*?\/>/;
|
||||
export let fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)('|")(.+?)\2.*?\/>/;
|
||||
export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
|
||||
|
||||
export function isTypeNode(node: Node): boolean {
|
||||
@@ -1551,7 +1588,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
const refMatchResult = fullTripleSlashReferencePathRegEx.exec(comment);
|
||||
const refLibResult = !refMatchResult && fullTripleSlashReferenceLibraryRegEx.exec(comment);
|
||||
const refLibResult = !refMatchResult && fullTripleSlashReferenceTypeReferenceDirectiveRegEx.exec(comment);
|
||||
if (refMatchResult || refLibResult) {
|
||||
const start = commentRange.pos;
|
||||
const end = commentRange.end;
|
||||
@@ -1562,7 +1599,7 @@ namespace ts {
|
||||
fileName: (refMatchResult || refLibResult)[3]
|
||||
},
|
||||
isNoDefaultLib: false,
|
||||
isLibraryReference: !!refLibResult
|
||||
isTypeReferenceDirective: !!refLibResult
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
});
|
||||
|
||||
it (`Correct module resolution tracing for ${fileName}`, () => {
|
||||
if (options.traceModuleResolution) {
|
||||
if (options.traceResolution) {
|
||||
Harness.Baseline.runBaseline("Correct sourcemap content for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => {
|
||||
return JSON.stringify(result.traceResults || [], undefined, 4);
|
||||
});
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace FourSlash {
|
||||
function tryAdd(path: string) {
|
||||
const inputFile = inputFiles[path];
|
||||
if (inputFile && !Harness.isLibraryFile(path)) {
|
||||
languageServiceAdapterHost.addScript(path, inputFile);
|
||||
languageServiceAdapterHost.addScript(path, inputFile, /*isRootFile*/ true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -268,7 +268,7 @@ namespace FourSlash {
|
||||
|
||||
if (startResolveFileRef) {
|
||||
// Add the entry-point file itself into the languageServiceShimHost
|
||||
this.languageServiceAdapterHost.addScript(startResolveFileRef.fileName, startResolveFileRef.content);
|
||||
this.languageServiceAdapterHost.addScript(startResolveFileRef.fileName, startResolveFileRef.content, /*isRootFile*/ true);
|
||||
|
||||
const resolvedResult = languageServiceAdapter.getPreProcessedFileInfo(startResolveFileRef.fileName, startResolveFileRef.content);
|
||||
const referencedFiles: ts.FileReference[] = resolvedResult.referencedFiles;
|
||||
@@ -292,18 +292,18 @@ namespace FourSlash {
|
||||
// Check if no-default-lib flag is false and if so add default library
|
||||
if (!resolvedResult.isLibFile) {
|
||||
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName,
|
||||
Harness.Compiler.getDefaultLibrarySourceFile().text);
|
||||
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// resolveReference file-option is not specified then do not resolve any files and include all inputFiles
|
||||
ts.forEachKey(this.inputFiles, fileName => {
|
||||
if (!Harness.isLibraryFile(fileName)) {
|
||||
this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName]);
|
||||
this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName], /*isRootFile*/ true);
|
||||
}
|
||||
});
|
||||
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName,
|
||||
Harness.Compiler.getDefaultLibrarySourceFile().text);
|
||||
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
|
||||
}
|
||||
|
||||
this.formatCodeOptions = {
|
||||
|
||||
@@ -65,6 +65,11 @@ namespace Utils {
|
||||
return Buffer ? (new Buffer(s)).toString("utf8") : s;
|
||||
}
|
||||
|
||||
export function byteLength(s: string, encoding?: string): number {
|
||||
// stub implementation if Buffer is not available (in-browser case)
|
||||
return Buffer ? Buffer.byteLength(s, encoding) : s.length;
|
||||
}
|
||||
|
||||
export function evalFile(fileContents: string, fileName: string, nodeContext?: any) {
|
||||
const environment = getExecutionEnvironment();
|
||||
switch (environment) {
|
||||
@@ -1042,7 +1047,7 @@ namespace Harness {
|
||||
options.newLine);
|
||||
|
||||
let traceResults: string[];
|
||||
if (options.traceModuleResolution) {
|
||||
if (options.traceResolution) {
|
||||
traceResults = [];
|
||||
compilerHost.trace = text => traceResults.push(text);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Harness.LanguageService {
|
||||
public editRanges: { length: number; textChangeRange: ts.TextChangeRange; }[] = [];
|
||||
private lineMap: number[] = undefined;
|
||||
|
||||
constructor(public fileName: string, public content: string) {
|
||||
constructor(public fileName: string, public content: string, public isRootFile: boolean) {
|
||||
this.setContent(content);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,13 @@ namespace Harness.LanguageService {
|
||||
|
||||
public getFilenames(): string[] {
|
||||
const fileNames: string[] = [];
|
||||
ts.forEachKey(this.fileNameToScript, (fileName) => { fileNames.push(fileName); });
|
||||
ts.forEachValue(this.fileNameToScript, (scriptInfo) => {
|
||||
if (scriptInfo.isRootFile) {
|
||||
// only include root files here
|
||||
// usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir.
|
||||
fileNames.push(scriptInfo.fileName);
|
||||
}
|
||||
});
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
@@ -143,8 +149,8 @@ namespace Harness.LanguageService {
|
||||
return ts.lookUp(this.fileNameToScript, fileName);
|
||||
}
|
||||
|
||||
public addScript(fileName: string, content: string): void {
|
||||
this.fileNameToScript[fileName] = new ScriptInfo(fileName, content);
|
||||
public addScript(fileName: string, content: string, isRootFile: boolean): void {
|
||||
this.fileNameToScript[fileName] = new ScriptInfo(fileName, content, isRootFile);
|
||||
}
|
||||
|
||||
public editScript(fileName: string, start: number, end: number, newText: string) {
|
||||
@@ -177,7 +183,7 @@ namespace Harness.LanguageService {
|
||||
getCompilationSettings() { return this.settings; }
|
||||
getCancellationToken() { return this.cancellationToken; }
|
||||
getCurrentDirectory(): string { return ""; }
|
||||
getDefaultLibFileName(): string { return ""; }
|
||||
getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; }
|
||||
getScriptFileNames(): string[] { return this.getFilenames(); }
|
||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
|
||||
const script = this.getScriptInfo(fileName);
|
||||
@@ -210,6 +216,7 @@ namespace Harness.LanguageService {
|
||||
private nativeHost: NativeLanguageServiceHost;
|
||||
|
||||
public getModuleResolutionsForFile: (fileName: string) => string;
|
||||
public getTypeReferenceDirectiveResolutionsForFile: (fileName: string) => string;
|
||||
|
||||
constructor(preprocessToResolve: boolean, cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
|
||||
super(cancellationToken, options);
|
||||
@@ -236,12 +243,28 @@ namespace Harness.LanguageService {
|
||||
}
|
||||
return JSON.stringify(imports);
|
||||
};
|
||||
this.getTypeReferenceDirectiveResolutionsForFile = (fileName) => {
|
||||
const scriptInfo = this.getScriptInfo(fileName);
|
||||
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
|
||||
const resolutions: ts.Map<ts.ResolvedTypeReferenceDirective> = {};
|
||||
const rootFiles = this.getFilenames();
|
||||
const currentDirectory = this.getCurrentDirectory();
|
||||
const settings = this.nativeHost.getCompilationSettings();
|
||||
const compilationRoot = ts.computeCompilationRoot(rootFiles, currentDirectory, settings, ts.createGetCanonicalFileName(false));
|
||||
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
|
||||
const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, compilationRoot, settings, moduleResolutionHost);
|
||||
if (resolutionInfo.resolvedTypeReferenceDirective.resolvedFileName) {
|
||||
resolutions[typeReferenceDirective.fileName] = resolutionInfo.resolvedTypeReferenceDirective;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(resolutions);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getFilenames(): string[] { return this.nativeHost.getFilenames(); }
|
||||
getScriptInfo(fileName: string): ScriptInfo { return this.nativeHost.getScriptInfo(fileName); }
|
||||
addScript(fileName: string, content: string): void { this.nativeHost.addScript(fileName, content); }
|
||||
addScript(fileName: string, content: string, isRootFile: boolean): void { this.nativeHost.addScript(fileName, content, isRootFile); }
|
||||
editScript(fileName: string, start: number, end: number, newText: string): void { this.nativeHost.editScript(fileName, start, end, newText); }
|
||||
positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter { return this.nativeHost.positionToLineAndCharacter(fileName, position); }
|
||||
|
||||
@@ -442,6 +465,7 @@ namespace Harness.LanguageService {
|
||||
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo {
|
||||
let shimResult: {
|
||||
referencedFiles: ts.IFileReference[];
|
||||
typeReferenceDirectives: ts.IFileReference[];
|
||||
importedFiles: ts.IFileReference[];
|
||||
isLibFile: boolean;
|
||||
};
|
||||
@@ -453,7 +477,8 @@ namespace Harness.LanguageService {
|
||||
referencedFiles: [],
|
||||
importedFiles: [],
|
||||
ambientExternalModules: [],
|
||||
isLibFile: shimResult.isLibFile
|
||||
isLibFile: shimResult.isLibFile,
|
||||
typeReferenceDirectives: []
|
||||
};
|
||||
|
||||
ts.forEach(shimResult.referencedFiles, refFile => {
|
||||
@@ -472,6 +497,13 @@ namespace Harness.LanguageService {
|
||||
});
|
||||
});
|
||||
|
||||
ts.forEach(shimResult.typeReferenceDirectives, typeRefDirective => {
|
||||
convertResult.importedFiles.push({
|
||||
fileName: typeRefDirective.path,
|
||||
pos: typeRefDirective.position,
|
||||
end: typeRefDirective.position + typeRefDirective.length
|
||||
});
|
||||
});
|
||||
return convertResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,14 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
interface TimestampedResolvedModule extends ResolvedModuleWithFailedLookupLocations {
|
||||
lastCheckTime: number;
|
||||
interface Timestamped {
|
||||
lastCheckTime?: number;
|
||||
}
|
||||
|
||||
interface TimestampedResolvedModule extends ResolvedModuleWithFailedLookupLocations, Timestamped {
|
||||
}
|
||||
|
||||
interface TimestampedResolvedTypeReferenceDirective extends ResolvedTypeReferenceDirectiveWithFailedLookupLocations, Timestamped {
|
||||
}
|
||||
|
||||
export class LSHost implements ts.LanguageServiceHost {
|
||||
@@ -90,13 +96,17 @@ namespace ts.server {
|
||||
compilationSettings: ts.CompilerOptions;
|
||||
filenameToScript: ts.FileMap<ScriptInfo>;
|
||||
roots: ScriptInfo[] = [];
|
||||
compilationRoot: string;
|
||||
|
||||
private resolvedModuleNames: ts.FileMap<Map<TimestampedResolvedModule>>;
|
||||
private resolvedTypeReferenceDirectives: ts.FileMap<Map<TimestampedResolvedTypeReferenceDirective>>;
|
||||
private moduleResolutionHost: ts.ModuleResolutionHost;
|
||||
private getCanonicalFileName: (fileName: string) => string;
|
||||
|
||||
constructor(public host: ServerHost, public project: Project) {
|
||||
this.getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
|
||||
this.resolvedModuleNames = createFileMap<Map<TimestampedResolvedModule>>();
|
||||
this.resolvedTypeReferenceDirectives = createFileMap<Map<TimestampedResolvedTypeReferenceDirective>>();
|
||||
this.filenameToScript = createFileMap<ScriptInfo>();
|
||||
this.moduleResolutionHost = {
|
||||
fileExists: fileName => this.fileExists(fileName),
|
||||
@@ -105,46 +115,51 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] {
|
||||
private resolveNamesWithLocalCache<T extends Timestamped & { failedLookupLocations: string[] }, R>(
|
||||
names: string[],
|
||||
containingFile: string,
|
||||
cache: ts.FileMap<Map<T>>,
|
||||
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
|
||||
getResult: (s: T) => R): R[] {
|
||||
|
||||
const path = toPath(containingFile, this.host.getCurrentDirectory(), this.getCanonicalFileName);
|
||||
const currentResolutionsInFile = this.resolvedModuleNames.get(path);
|
||||
|
||||
const newResolutions: Map<TimestampedResolvedModule> = {};
|
||||
const resolvedModules: ResolvedModule[] = [];
|
||||
const currentResolutionsInFile = cache.get(path);
|
||||
|
||||
const newResolutions: Map<T> = {};
|
||||
const resolvedModules: R[] = [];
|
||||
const compilerOptions = this.getCompilationSettings();
|
||||
|
||||
for (const moduleName of moduleNames) {
|
||||
for (const name of names) {
|
||||
// check if this is a duplicate entry in the list
|
||||
let resolution = lookUp(newResolutions, moduleName);
|
||||
let resolution = lookUp(newResolutions, name);
|
||||
if (!resolution) {
|
||||
const existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, moduleName);
|
||||
const existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, name);
|
||||
if (moduleResolutionIsValid(existingResolution)) {
|
||||
// ok, it is safe to use existing module resolution results
|
||||
// ok, it is safe to use existing name resolution results
|
||||
resolution = existingResolution;
|
||||
}
|
||||
else {
|
||||
resolution = <TimestampedResolvedModule>resolveModuleName(moduleName, containingFile, compilerOptions, this.moduleResolutionHost);
|
||||
resolution = loader(name, containingFile, compilerOptions, this.moduleResolutionHost);
|
||||
resolution.lastCheckTime = Date.now();
|
||||
newResolutions[moduleName] = resolution;
|
||||
newResolutions[name] = resolution;
|
||||
}
|
||||
}
|
||||
|
||||
ts.Debug.assert(resolution !== undefined);
|
||||
|
||||
resolvedModules.push(resolution.resolvedModule);
|
||||
resolvedModules.push(getResult(resolution));
|
||||
}
|
||||
|
||||
// replace old results with a new one
|
||||
this.resolvedModuleNames.set(path, newResolutions);
|
||||
cache.set(path, newResolutions);
|
||||
return resolvedModules;
|
||||
|
||||
function moduleResolutionIsValid(resolution: TimestampedResolvedModule): boolean {
|
||||
function moduleResolutionIsValid(resolution: T): boolean {
|
||||
if (!resolution) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resolution.resolvedModule) {
|
||||
if (getResult(resolution)) {
|
||||
// TODO: consider checking failedLookupLocations
|
||||
// TODO: use lastCheckTime to track expiration for module name resolution
|
||||
return true;
|
||||
@@ -156,6 +171,20 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
|
||||
if (this.compilationRoot === undefined) {
|
||||
this.compilationRoot = computeCompilationRoot(this.getScriptFileNames(), this.getCurrentDirectory(), this.getCompilationSettings(), this.getCanonicalFileName);
|
||||
}
|
||||
const loader = (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => {
|
||||
return resolveTypeReferenceDirective(name, containingFile, this.compilationRoot, options, this.moduleResolutionHost);
|
||||
};
|
||||
return this.resolveNamesWithLocalCache(typeDirectiveNames, containingFile, this.resolvedTypeReferenceDirectives, loader, m => m.resolvedTypeReferenceDirective);
|
||||
}
|
||||
|
||||
resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] {
|
||||
return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, resolveModuleName, m => m.resolvedModule);
|
||||
}
|
||||
|
||||
getDefaultLibFileName() {
|
||||
const nodeModuleBinDir = ts.getDirectoryPath(ts.normalizePath(this.host.getExecutingFilePath()));
|
||||
return ts.combinePaths(nodeModuleBinDir, ts.getDefaultLibFileName(this.compilationSettings));
|
||||
@@ -172,6 +201,7 @@ namespace ts.server {
|
||||
this.compilationSettings = opt;
|
||||
// conservatively assume that changing compiler options might affect module resolution strategy
|
||||
this.resolvedModuleNames.clear();
|
||||
this.resolvedTypeReferenceDirectives.clear();
|
||||
}
|
||||
|
||||
lineAffectsRefs(filename: string, line: number) {
|
||||
@@ -212,6 +242,7 @@ namespace ts.server {
|
||||
if (!info.isOpen) {
|
||||
this.filenameToScript.remove(info.path);
|
||||
this.resolvedModuleNames.remove(info.path);
|
||||
this.resolvedTypeReferenceDirectives.remove(info.path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +262,8 @@ namespace ts.server {
|
||||
if (!this.filenameToScript.contains(info.path)) {
|
||||
this.filenameToScript.set(info.path, info);
|
||||
this.roots.push(info);
|
||||
// reset compilation root
|
||||
this.compilationRoot = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +272,9 @@ namespace ts.server {
|
||||
this.filenameToScript.remove(info.path);
|
||||
this.roots = copyListRemovingItem(info, this.roots);
|
||||
this.resolvedModuleNames.remove(info.path);
|
||||
this.resolvedTypeReferenceDirectives.remove(info.path);
|
||||
// reset compilation root
|
||||
this.compilationRoot = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-15
@@ -125,6 +125,7 @@ namespace ts {
|
||||
}
|
||||
export interface PreProcessedFileInfo {
|
||||
referencedFiles: FileReference[];
|
||||
typeReferenceDirectives: FileReference[];
|
||||
importedFiles: FileReference[];
|
||||
ambientExternalModules: string[];
|
||||
isLibFile: boolean;
|
||||
@@ -792,7 +793,7 @@ namespace ts {
|
||||
public amdDependencies: { name: string; path: string }[];
|
||||
public moduleName: string;
|
||||
public referencedFiles: FileReference[];
|
||||
public referencedLibraries: FileReference[];
|
||||
public typeReferenceDirectives: FileReference[];
|
||||
|
||||
public syntacticDiagnostics: Diagnostic[];
|
||||
public referenceDiagnostics: Diagnostic[];
|
||||
@@ -814,6 +815,7 @@ namespace ts {
|
||||
public identifiers: Map<string>;
|
||||
public nameTable: Map<number>;
|
||||
public resolvedModules: Map<ResolvedModule>;
|
||||
public resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
|
||||
public imports: LiteralExpression[];
|
||||
public moduleAugmentations: LiteralExpression[];
|
||||
private namedDeclarations: Map<Declaration[]>;
|
||||
@@ -1040,6 +1042,7 @@ namespace ts {
|
||||
* host specific questions using 'getScriptSnapshot'.
|
||||
*/
|
||||
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
|
||||
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
directoryExists?(directoryName: string): boolean;
|
||||
}
|
||||
|
||||
@@ -2147,6 +2150,7 @@ namespace ts {
|
||||
|
||||
export function preProcessFile(sourceText: string, readImportFiles = true, detectJavaScriptImports = false): PreProcessedFileInfo {
|
||||
const referencedFiles: FileReference[] = [];
|
||||
const typeReferenceDirectives: FileReference[] = [];
|
||||
const importedFiles: FileReference[] = [];
|
||||
let ambientExternalModules: { ref: FileReference, depth: number }[];
|
||||
let isNoDefaultLib = false;
|
||||
@@ -2175,7 +2179,11 @@ namespace ts {
|
||||
isNoDefaultLib = referencePathMatchResult.isNoDefaultLib;
|
||||
const fileReference = referencePathMatchResult.fileReference;
|
||||
if (fileReference) {
|
||||
referencedFiles.push(fileReference);
|
||||
const collection = referencePathMatchResult.isTypeReferenceDirective
|
||||
? typeReferenceDirectives
|
||||
: referencedFiles;
|
||||
|
||||
collection.push(fileReference);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2476,7 +2484,7 @@ namespace ts {
|
||||
importedFiles.push(decl.ref);
|
||||
}
|
||||
}
|
||||
return { referencedFiles, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules: undefined };
|
||||
return { referencedFiles, typeReferenceDirectives, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules: undefined };
|
||||
}
|
||||
else {
|
||||
// for global scripts ambient modules still can have augmentations - look for ambient modules with depth > 0
|
||||
@@ -2494,7 +2502,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
return { referencedFiles, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules: ambientModuleNames };
|
||||
return { referencedFiles, typeReferenceDirectives, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules: ambientModuleNames };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2831,7 +2839,7 @@ namespace ts {
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
fileExists: (fileName): boolean => {
|
||||
// stub missing host functionality
|
||||
Debug.assert(!host.resolveModuleNames);
|
||||
Debug.assert(!host.resolveModuleNames || !host.resolveTypeReferenceDirectives);
|
||||
return hostCache.getOrCreateEntry(fileName) !== undefined;
|
||||
},
|
||||
readFile: (fileName): string => {
|
||||
@@ -2840,7 +2848,7 @@ namespace ts {
|
||||
return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength());
|
||||
},
|
||||
directoryExists: directoryName => {
|
||||
Debug.assert(!host.resolveModuleNames);
|
||||
Debug.assert(!host.resolveModuleNames || !host.resolveTypeReferenceDirectives);
|
||||
return directoryProbablyExists(directoryName, host);
|
||||
}
|
||||
};
|
||||
@@ -2851,6 +2859,11 @@ namespace ts {
|
||||
if (host.resolveModuleNames) {
|
||||
compilerHost.resolveModuleNames = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile);
|
||||
}
|
||||
if (host.resolveTypeReferenceDirectives) {
|
||||
compilerHost.resolveTypeReferenceDirectives = (typeReferenceDirectiveNames, containingFile) => {
|
||||
return host.resolveTypeReferenceDirectives(typeReferenceDirectiveNames, containingFile);
|
||||
};
|
||||
}
|
||||
|
||||
const newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program);
|
||||
|
||||
@@ -4671,6 +4684,26 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function findReferenceInPosition(refs: FileReference[], pos: number): FileReference {
|
||||
for (const ref of refs) {
|
||||
if (ref.pos <= pos && pos < ref.end) {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getDefinitionInfoForFileReference(name: string, targetFileName: string): DefinitionInfo {
|
||||
return {
|
||||
fileName: targetFileName,
|
||||
textSpan: createTextSpanFromBounds(0, 0),
|
||||
kind: ScriptElementKind.scriptElement,
|
||||
name: name,
|
||||
containerName: undefined,
|
||||
containerKind: undefined
|
||||
};
|
||||
}
|
||||
|
||||
/// Goto definition
|
||||
function getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
|
||||
synchronizeHostData();
|
||||
@@ -4690,18 +4723,20 @@ namespace ts {
|
||||
}
|
||||
|
||||
/// Triple slash reference comments
|
||||
const comment = forEach(sourceFile.referencedFiles, r => (r.pos <= position && position < r.end) ? r : undefined);
|
||||
const comment = findReferenceInPosition(sourceFile.referencedFiles, position);
|
||||
if (comment) {
|
||||
const referenceFile = tryResolveScriptReference(program, sourceFile, comment);
|
||||
if (referenceFile) {
|
||||
return [{
|
||||
fileName: referenceFile.fileName,
|
||||
textSpan: createTextSpanFromBounds(0, 0),
|
||||
kind: ScriptElementKind.scriptElement,
|
||||
name: comment.fileName,
|
||||
containerName: undefined,
|
||||
containerKind: undefined
|
||||
}];
|
||||
return [getDefinitionInfoForFileReference(comment.fileName, referenceFile.fileName)];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// Type reference directives
|
||||
const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position);
|
||||
if (typeReferenceDirective) {
|
||||
const referenceFile = lookUp(program.resolvedTypeReferenceDirectives, typeReferenceDirective.fileName);
|
||||
if (referenceFile && referenceFile.resolvedFileName) {
|
||||
return [getDefinitionInfoForFileReference(typeReferenceDirective.fileName, referenceFile.resolvedFileName)];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
+49
-21
@@ -67,6 +67,7 @@ namespace ts {
|
||||
useCaseSensitiveFileNames?(): boolean;
|
||||
|
||||
getModuleResolutionsForFile?(fileName: string): string;
|
||||
getTypeReferenceDirectiveResolutionsForFile?(fileName: string): string;
|
||||
directoryExists(directoryName: string): boolean;
|
||||
}
|
||||
|
||||
@@ -281,6 +282,7 @@ namespace ts {
|
||||
private tracingEnabled = false;
|
||||
|
||||
public resolveModuleNames: (moduleName: string[], containingFile: string) => ResolvedModule[];
|
||||
public resolveTypeReferenceDirectives: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[];
|
||||
public directoryExists: (directoryName: string) => boolean;
|
||||
|
||||
constructor(private shimHost: LanguageServiceShimHost) {
|
||||
@@ -298,6 +300,12 @@ namespace ts {
|
||||
if ("directoryExists" in this.shimHost) {
|
||||
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
|
||||
}
|
||||
if ("getTypeReferenceDirectiveResolutionsForFile" in this.shimHost) {
|
||||
this.resolveTypeReferenceDirectives = (typeDirectiveNames: string[], containingFile: string) => {
|
||||
const typeDirectivesForFile = <Map<ResolvedTypeReferenceDirective>>JSON.parse(this.shimHost.getTypeReferenceDirectiveResolutionsForFile(containingFile));
|
||||
return map(typeDirectiveNames, name => lookUp(typeDirectivesForFile, name));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public log(s: string): void {
|
||||
@@ -899,6 +907,7 @@ namespace ts {
|
||||
|
||||
class CoreServicesShimObject extends ShimBase implements CoreServicesShim {
|
||||
private logPerformance = false;
|
||||
private getCanonicalFileName = createGetCanonicalFileName(false);
|
||||
|
||||
constructor(factory: ShimFactory, public logger: Logger, private host: CoreServicesShimHostAdapter) {
|
||||
super(factory);
|
||||
@@ -919,38 +928,57 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
public computeCompilationRoot(filesJson: string, currentDirectory: string, compilerOptionsJson: string): string {
|
||||
return this.forwardJSONCall("computeCompilationRoot", () => {
|
||||
const files = <string[]>JSON.parse(filesJson);
|
||||
const compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
|
||||
return computeCompilationRoot(files, currentDirectory, compilerOptions, this.getCanonicalFileName);
|
||||
});
|
||||
}
|
||||
|
||||
public resolveTypeReferenceDirective(fileName: string, typeReferenceDirective: string, compilationRoot: string, compilerOptionsJson: string): string {
|
||||
return this.forwardJSONCall(`resolveTypeReferenceDirective(${fileName})`, () => {
|
||||
const compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
|
||||
const result = resolveTypeReferenceDirective(typeReferenceDirective, normalizeSlashes(fileName), compilationRoot, compilerOptions, this.host);
|
||||
return {
|
||||
resolvedFileName: result.resolvedTypeReferenceDirective ? result.resolvedTypeReferenceDirective.resolvedFileName : undefined,
|
||||
primary: result.resolvedTypeReferenceDirective ? result.resolvedTypeReferenceDirective.primary : true,
|
||||
failedLookupLocations: result.failedLookupLocations
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public getPreProcessedFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
|
||||
return this.forwardJSONCall(
|
||||
"getPreProcessedFileInfo('" + fileName + "')",
|
||||
() => {
|
||||
// for now treat files as JavaScript
|
||||
const result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
|
||||
const convertResult = {
|
||||
referencedFiles: <IFileReference[]>[],
|
||||
importedFiles: <IFileReference[]>[],
|
||||
return {
|
||||
referencedFiles: this.convertFileReferences(result.referencedFiles),
|
||||
importedFiles: this.convertFileReferences(result.importedFiles),
|
||||
ambientExternalModules: result.ambientExternalModules,
|
||||
isLibFile: result.isLibFile
|
||||
isLibFile: result.isLibFile,
|
||||
typeReferenceDirectives: this.convertFileReferences(result.typeReferenceDirectives)
|
||||
};
|
||||
|
||||
forEach(result.referencedFiles, refFile => {
|
||||
convertResult.referencedFiles.push({
|
||||
path: normalizePath(refFile.fileName),
|
||||
position: refFile.pos,
|
||||
length: refFile.end - refFile.pos
|
||||
});
|
||||
});
|
||||
|
||||
forEach(result.importedFiles, importedFile => {
|
||||
convertResult.importedFiles.push({
|
||||
path: normalizeSlashes(importedFile.fileName),
|
||||
position: importedFile.pos,
|
||||
length: importedFile.end - importedFile.pos
|
||||
});
|
||||
});
|
||||
return convertResult;
|
||||
});
|
||||
}
|
||||
|
||||
private convertFileReferences(refs: FileReference[]): IFileReference[] {
|
||||
if (!refs) {
|
||||
return undefined;
|
||||
}
|
||||
const result: IFileReference[] = [];
|
||||
for (const ref of refs) {
|
||||
result.push({
|
||||
path: normalizeSlashes(ref.fileName),
|
||||
position: ref.pos,
|
||||
length: ref.end - ref.pos
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public getTSConfigFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
|
||||
return this.forwardJSONCall(
|
||||
`getTSConfigFileInfo('${fileName}')`,
|
||||
|
||||
Reference in New Issue
Block a user