verbose module resolution

This commit is contained in:
Vladimir Matveev 2015-11-19 21:33:33 -08:00
parent 6bed1ba53a
commit 6844285782
6 changed files with 292 additions and 37 deletions

View file

@ -302,6 +302,11 @@ namespace ts {
type: "object",
isTSConfigOnly: true
},
{
name: "traceModuleResolution",
type: "boolean",
description: Diagnostics.Enable_tracing_of_the_module_resolution_process
},
{
name: "allowJs",
type: "boolean",

View file

@ -410,6 +410,17 @@ namespace ts {
};
}
/* internal */
export function formatMessage(dummy: any, message: DiagnosticMessage): string {
let text = getLocaleSpecificMessage(message);
if (arguments.length > 2) {
text = formatStringFromArgs(text, arguments, 2);
}
return text;
}
export function createCompilerDiagnostic(message: DiagnosticMessage, ...args: any[]): Diagnostic;
export function createCompilerDiagnostic(message: DiagnosticMessage): Diagnostic {
let text = getLocaleSpecificMessage(message);
@ -860,4 +871,4 @@ namespace ts {
}
return copiedList;
}
}
}

View file

@ -2220,10 +2220,10 @@
"category": "Error",
"code": 6046
},
"Argument for '--target' option must be 'ES3', 'ES5', or 'ES2015'.": {
"category": "Error",
"code": 6047
},
"Argument for '--target' option must be 'ES3', 'ES5', or 'ES2015'.": {
"category": "Error",
"code": 6047
},
"Locale must be of the form <language> or <language>-<territory>. For example '{0}' or '{1}'.": {
"category": "Error",
"code": 6048
@ -2353,14 +2353,114 @@
"category": "Error",
"code": 6082
},
"Allow javascript files to be compiled.": {
"Base directory to resolve relative module names.": {
"category": "Message",
"code": 6083
},
"Base directory to resolve relative module names.": {
"Enable tracing of the module resolution process.": {
"category": "Message",
"code": 6084
},
},
"======== Resolving module '{0}' from '{1}'. ========": {
"category": "Message",
"code": 6085
},
"Explicitly specified module resolution kind: '{0}'.": {
"category": "Message",
"code": 6086
},
"Module resolution kind is not specified, using '{0}'.": {
"category": "Message",
"code": 6087
},
"======== Module name '{0}' was successfully resolved to '{1}'. ========": {
"category": "Message",
"code": 6088
},
"======== Module name '{0}' was not resolved. ========": {
"category": "Message",
"code": 6089
},
"Resolving rooted module name '{0}' - use it as a candidate location.": {
"category": "Message",
"code": 6090
},
"Resolving relative module name '{0}'.": {
"category": "Message",
"code": 6091
},
"Resolving non-relative module name '{0}'.": {
"category": "Message",
"code": 6092
},
"Converting relative module name '{0}' to absolute using '{1}' as a base directory => '{2}'.": {
"category": "Message",
"code": 6093
},
"'rootDirs' option is specified, searching for a longest matching prefix...": {
"category": "Message",
"code": 6094
},
"Found longest matching rootDir '{0}', 'converted relative path '{1}', to non-relative path '{2}'": {
"category": "Message",
"code": 6095
},
"'rootDirs' option is not specified, using '{0}' as candidate location.": {
"category": "Message",
"code": 6096
},
"'paths' option is specified, looking for a pattern to match module name '{0}'.": {
"category": "Message",
"code": 6097
},
"Module name '{0}', matched pattern '{1}'.": {
"category": "Message",
"code": 6098
},
"Trying substitution '{0}', candidate module location: '{1}'.": {
"category": "Message",
"code": 6099
},
"Resolving module name '{0}' relative to base url '{1}' - '{2}'.": {
"category": "Message",
"code": 6100
},
"Loading module '{0}' as file / folder, candidate module location '{1}'.": {
"category": "Message",
"code": 6101
},
"File '{0}' does not exist.": {
"category": "Message",
"code": 6102
},
"File '{0}' exist - use it as a module resolution result.": {
"category": "Message",
"code": 6103
},
"Loading module '{0}' from 'node_modules' folder.": {
"category": "Message",
"code": 6104
},
"Found 'package.json' at '{0}'.": {
"category": "Message",
"code": 6105
},
"'package.json' does not have 'typings' field.": {
"category": "Message",
"code": 6106
},
"'package.json' has 'typings' field '{0}' that references '{1}'.": {
"category": "Message",
"code": 6107
},
"Module name '{0}' contains '!' character.": {
"category": "Message",
"code": 6108
},
"Allow javascript files to be compiled.": {
"category": "Message",
"code": 6109
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005

View file

@ -36,7 +36,21 @@ namespace ts {
return normalizePath(referencedFileName);
}
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;
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
}
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
if (compilerOptions.module === ModuleKind.CommonJS) {
@ -48,13 +62,40 @@ namespace ts {
else {
moduleResolution = ModuleResolutionKind.Classic;
}
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
}
}
let result: ResolvedModuleWithFailedLookupLocations;
switch (moduleResolution) {
case ModuleResolutionKind.NodeJs: return nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
case ModuleResolutionKind.Classic: return classicNameResolver(moduleName, containingFile, compilerOptions, host);
case ModuleResolutionKind.BaseUrl: return baseUrlModuleNameResolver(moduleName, containingFile, compilerOptions, host);
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.BaseUrl:
result = baseUrlModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
}
if (traceEnabled) {
if (result.resolvedModule) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
}
else {
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
}
}
return result;
}
// Path mapping based module resolution strategy uses base url to resolve relative file names. This resolution strategy can be enabled
@ -135,28 +176,52 @@ namespace ts {
Debug.assert(baseUrl !== undefined);
const supportedExtensions = getSupportedExtensions(compilerOptions);
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (isRootedDiskPath(moduleName)) {
return { resolvedModule: { resolvedFileName: moduleName }, failedLookupLocations: emptyArray };
if (traceEnabled) {
trace(host, Diagnostics.Resolving_rooted_module_name_0_use_it_as_a_candidate_location, moduleName);
}
const failedLookupLocations: string[] = [];
const resolvedFileName = loadModuleFromFile(supportedExtensions, moduleName, failedLookupLocations, host, traceEnabled);
return {
resolvedModule: resolvedFileName ? { resolvedFileName } : undefined,
failedLookupLocations
};
}
if (nameStartsWithDotSlashOrDotDotSlash(moduleName)) {
// relative name
return baseUrlResolveRelativeModuleName(moduleName, containingFile, baseUrl, compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_relative_module_name_0, moduleName);
}
return baseUrlResolveRelativeModuleName(moduleName, containingFile, baseUrl, supportedExtensions, compilerOptions, host, traceEnabled);
}
else {
// non-relative name
return baseUrlResolveNonRelativeModuleName(moduleName, baseUrl, supportedExtensions, compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_non_relative_module_name_0, moduleName);
}
return baseUrlResolveNonRelativeModuleName(moduleName, baseUrl, supportedExtensions, compilerOptions, host, traceEnabled);
}
}
export function baseUrlResolveRelativeModuleName(moduleName: string, containingFile: string, baseUrl: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
function baseUrlResolveRelativeModuleName(moduleName: string, containingFile: string, baseUrl: string, supportedExtensions: string[], compilerOptions: CompilerOptions, host: ModuleResolutionHost, traceEnabled: boolean): ResolvedModuleWithFailedLookupLocations {
const failedLookupLocations: string[] = [];
const containingDirectory = getDirectoryPath(containingFile);
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
const supportedExtensions = getSupportedExtensions(compilerOptions);
if (traceEnabled) {
trace(host, Diagnostics.Converting_relative_module_name_0_to_absolute_using_1_as_a_base_directory_2, moduleName, containingDirectory, candidate);
}
if (compilerOptions.rootDirs) {
if (traceEnabled) {
trace(host, Diagnostics.rootDirs_option_is_specified_searching_for_a_longest_matching_prefix);
}
let matchedPrefix: string;
for (const rootDir of compilerOptions.rootDirs) {
let normalizedRoot = getNormalizedAbsolutePath(rootDir, baseUrl);
@ -169,12 +234,20 @@ namespace ts {
}
if (matchedPrefix) {
const suffix = candidate.substr(matchedPrefix.length);
return baseUrlResolveNonRelativeModuleName(suffix, baseUrl, supportedExtensions, compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Found_longest_matching_rootDir_0_converted_relative_path_1_to_non_relative_path_2, matchedPrefix, moduleName, suffix);
}
return baseUrlResolveNonRelativeModuleName(suffix, baseUrl, supportedExtensions, compilerOptions, host, traceEnabled);
}
return { resolvedModule: undefined, failedLookupLocations };
}
else {
const resolvedFileName = loadModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host);
if (traceEnabled) {
trace(host, Diagnostics.rootDirs_option_is_not_specified_using_0_as_candidate_location, candidate);
}
const resolvedFileName = loadModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host, traceEnabled);
return {
resolvedModule: resolvedFileName ? { resolvedFileName } : undefined,
failedLookupLocations
@ -182,14 +255,18 @@ namespace ts {
}
}
export function baseUrlResolveNonRelativeModuleName(moduleName: string, baseUrl: string, supportedExtensions: string[], options: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
function baseUrlResolveNonRelativeModuleName(moduleName: string, baseUrl: string, supportedExtensions: string[], compilerOptions: CompilerOptions, host: ModuleResolutionHost, traceEnabled: boolean): ResolvedModuleWithFailedLookupLocations {
let longestMatchPrefixLength = -1;
let matchedPattern: string;
let matchedStar: string;
const failedLookupLocations: string[] = [];
if (options.paths) {
for (const key in options.paths) {
if (compilerOptions.paths) {
if (traceEnabled) {
trace(host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
}
for (const key in compilerOptions.paths) {
const pattern: string = key;
const indexOfStar = pattern.indexOf("*");
if (indexOfStar !== -1) {
@ -217,10 +294,19 @@ namespace ts {
}
if (matchedPattern) {
for (const subst of options.paths[matchedPattern]) {
if (traceEnabled) {
trace(host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPattern);
}
for (const subst of compilerOptions.paths[matchedPattern]) {
const path = matchedStar ? subst.replace("\*", matchedStar) : subst;
const candidate = normalizePath(combinePaths(baseUrl, path));
const resolvedFileName = loadModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host);
if (traceEnabled) {
trace(host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
}
const resolvedFileName = loadModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host, traceEnabled);
if (resolvedFileName) {
return { resolvedModule: { resolvedFileName }, failedLookupLocations };
}
@ -230,7 +316,12 @@ namespace ts {
}
else {
const candidate = normalizePath(combinePaths(baseUrl, moduleName));
const resolvedFileName = loadModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, baseUrl, candidate);
}
const resolvedFileName = loadModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host, traceEnabled);
return {
resolvedModule: resolvedFileName ? { resolvedFileName } : undefined,
failedLookupLocations
@ -250,44 +341,64 @@ namespace ts {
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const containingDirectory = getDirectoryPath(containingFile);
const supportedExtensions = getSupportedExtensions(compilerOptions);
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (isRootedDiskPath(moduleName) || nameStartsWithDotSlashOrDotDotSlash(moduleName)) {
const failedLookupLocations: string[] = [];
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
let resolvedFileName = loadModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host);
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_as_file_Slash_folder_candidate_module_location_1, moduleName, candidate);
}
let resolvedFileName = loadModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host, traceEnabled);
if (resolvedFileName) {
return { resolvedModule: { resolvedFileName }, failedLookupLocations };
}
resolvedFileName = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, host);
resolvedFileName = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, host, traceEnabled);
return resolvedFileName
? { resolvedModule: { resolvedFileName }, failedLookupLocations }
: { resolvedModule: undefined, failedLookupLocations };
}
else {
return loadModuleFromNodeModules(moduleName, containingDirectory, host);
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
}
return loadModuleFromNodeModules(moduleName, containingDirectory, host, traceEnabled);
}
}
function loadModuleFromFile(extensions: string[], candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost): string {
function loadModuleFromFile(extensions: string[], candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost, traceEnabled: boolean): string {
return forEach(extensions, tryLoad);
function tryLoad(ext: string): string {
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
if (host.fileExists(fileName)) {
if (traceEnabled) {
trace(host, Diagnostics.File_0_exist_use_it_as_a_module_resolution_result, fileName);
}
return fileName;
}
else {
if (traceEnabled) {
trace(host, Diagnostics.File_0_does_not_exist, fileName);
}
failedLookupLocation.push(fileName);
return undefined;
}
}
}
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost): string {
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost, traceEnabled: boolean): string {
const packageJsonPath = combinePaths(candidate, "package.json");
if (host.fileExists(packageJsonPath)) {
if (traceEnabled) {
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
let jsonContent: { typings?: string };
@ -301,21 +412,33 @@ namespace ts {
}
if (jsonContent.typings) {
const result = loadModuleFromFile(extensions, normalizePath(combinePaths(candidate, jsonContent.typings)), failedLookupLocation, host);
const typingsFile = normalizePath(combinePaths(candidate, jsonContent.typings));
if (traceEnabled) {
trace(host, Diagnostics.package_json_has_typings_field_0_that_references_1, jsonContent.typings, typingsFile);
}
const result = loadModuleFromFile(extensions, typingsFile , failedLookupLocation, host, traceEnabled);
if (result) {
return result;
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.package_json_does_not_have_typings_field);
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.package_json_does_not_have_typings_field);
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocation.push(packageJsonPath);
}
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocation, host);
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocation, host, traceEnabled);
}
function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost, traceEnabled: boolean): ResolvedModuleWithFailedLookupLocations {
const failedLookupLocations: string[] = [];
directory = normalizeSlashes(directory);
while (true) {
@ -324,12 +447,12 @@ namespace ts {
const nodeModulesFolder = combinePaths(directory, "node_modules");
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
// Load only typescript files irrespective of allowJs option if loading from node modules
let result = loadModuleFromFile(supportedTypeScriptExtensions, candidate, failedLookupLocations, host);
let result = loadModuleFromFile(supportedTypeScriptExtensions, candidate, failedLookupLocations, host, traceEnabled);
if (result) {
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations };
}
result = loadNodeModuleFromDirectory(supportedTypeScriptExtensions, candidate, failedLookupLocations, host);
result = loadNodeModuleFromDirectory(supportedTypeScriptExtensions, candidate, failedLookupLocations, host, traceEnabled);
if (result) {
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations };
}
@ -369,8 +492,13 @@ namespace ts {
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
// module names that contain '!' are used to reference resources and are not resolved to actual files on disk
if (moduleName.indexOf("!") != -1) {
if (traceEnabled) {
trace(host, Diagnostics.Module_name_0_contains_character, moduleName);
}
return { resolvedModule: undefined, failedLookupLocations: [] };
}
@ -392,9 +520,15 @@ namespace ts {
const candidate = searchName + extension;
if (host.fileExists(candidate)) {
if (traceEnabled) {
trace(host, Diagnostics.File_0_exist_use_it_as_a_module_resolution_result, candidate);
}
return candidate;
}
else {
if (traceEnabled) {
trace(host, Diagnostics.File_0_does_not_exist, candidate);
}
failedLookupLocations.push(candidate);
}
});
@ -498,7 +632,8 @@ namespace ts {
getCanonicalFileName,
getNewLine: () => newLine,
fileExists: fileName => sys.fileExists(fileName),
readFile: fileName => sys.readFile(fileName)
readFile: fileName => sys.readFile(fileName),
trace: (s: string) => sys.write(s + newLine)
};
}

View file

@ -2308,7 +2308,7 @@ namespace ts {
Message,
}
export const enum ModuleResolutionKind {
export enum ModuleResolutionKind {
Classic = 1,
NodeJs = 2,
BaseUrl = 3
@ -2368,6 +2368,7 @@ namespace ts {
baseUrl?: string;
paths?: PathSubstitutions;
rootDirs?: RootPaths;
traceModuleResolution?: boolean;
allowJs?: boolean;
/* @internal */ stripInternal?: boolean;
@ -2379,7 +2380,7 @@ namespace ts {
[option: string]: string | number | boolean | TsConfigOnlyOptions;
}
export const enum ModuleKind {
export enum ModuleKind {
None = 0,
CommonJS = 1,
AMD = 2,
@ -2606,6 +2607,7 @@ namespace ts {
// readFile function is used to read arbitrary text files on disk, i.e. when resolution procedure needs the content of 'package.json'
// to determine location of bundled typings for node module
readFile(fileName: string): string;
trace?(s: string): void;
}
export interface ResolvedModule {

View file

@ -73,6 +73,8 @@ namespace ts {
* when enumerating the directory.
*/
readDirectory(rootDir: string, extension: string, exclude?: string): string;
trace(s: string): void;
}
///