Merge pull request #9095 from RyanCavanaugh/implicitTypeReferences

Implicit type inclusion changes
This commit is contained in:
Ryan Cavanaugh
2016-06-13 16:33:22 -07:00
committed by GitHub
100 changed files with 603 additions and 523 deletions

View File

@@ -337,8 +337,13 @@ namespace ts {
}
},
{
name: "typesRoot",
type: "string"
name: "typeRoots",
type: "list",
element: {
name: "typeRoots",
type: "string",
isFilePath: true
}
},
{
name: "types",

View File

@@ -1931,6 +1931,10 @@
"category": "Error",
"code": 2687
},
"Cannot find type definition file for '{0}'.": {
"category": "Error",
"code": 2688
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000

View File

@@ -9,16 +9,11 @@ namespace ts {
/* @internal */ export let ioWriteTime = 0;
/** The version of the TypeScript compiler release */
export const version = "1.9.0";
const emptyArray: any[] = [];
const defaultLibrarySearchPaths = [
"types/",
"node_modules/",
"node_modules/@types/",
];
export const version = "1.9.0";
const defaultTypeRoots = ["node_modules/@types"];
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean): string {
while (true) {
@@ -183,6 +178,11 @@ namespace ts {
const typeReferenceExtensions = [".d.ts"];
function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) {
return options.typeRoots ||
defaultTypeRoots.map(d => combinePaths(options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory(), d));
}
/**
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
@@ -197,24 +197,22 @@ namespace ts {
traceEnabled
};
// use typesRoot and fallback to directory that contains tsconfig or current directory if typesRoot is not set
const rootDir = options.typesRoot || (options.configFilePath ? getDirectoryPath(options.configFilePath) : (host.getCurrentDirectory && host.getCurrentDirectory()));
const typeRoots = getEffectiveTypeRoots(options, host);
if (traceEnabled) {
if (containingFile === undefined) {
if (rootDir === undefined) {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, rootDir);
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots);
}
}
else {
if (rootDir === undefined) {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, rootDir);
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots);
}
}
}
@@ -222,14 +220,13 @@ namespace ts {
const failedLookupLocations: string[] = [];
// Check primary library paths
if (rootDir !== undefined) {
const effectivePrimarySearchPaths = options.typesSearchPaths || defaultLibrarySearchPaths;
for (const searchPath of effectivePrimarySearchPaths) {
const primaryPath = combinePaths(rootDir, searchPath);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, primaryPath);
}
const candidate = combinePaths(primaryPath, typeReferenceDirectiveName);
if (typeRoots.length) {
if (traceEnabled) {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", "));
}
const primarySearchPaths = typeRoots;
for (const typeRoot of primarySearchPaths) {
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
const candidateDirectory = getDirectoryPath(candidate);
const resolvedFile = loadNodeModuleFromDirectory(typeReferenceExtensions, candidate, failedLookupLocations,
!directoryProbablyExists(candidateDirectory, host), moduleResolutionState);
@@ -256,9 +253,6 @@ namespace ts {
if (containingFile) {
initialLocationForSecondaryLookup = getDirectoryPath(containingFile);
}
else {
initialLocationForSecondaryLookup = rootDir;
}
if (initialLocationForSecondaryLookup !== undefined) {
// check secondary locations
@@ -937,19 +931,6 @@ namespace ts {
}
}
function getDefaultTypeDirectiveNames(rootPath: string): string[] {
const localTypes = combinePaths(rootPath, "types");
const npmTypes = combinePaths(rootPath, "node_modules/@types");
let result: string[] = [];
if (sys.directoryExists(localTypes)) {
result = result.concat(sys.getDirectories(localTypes));
}
if (sys.directoryExists(npmTypes)) {
result = result.concat(sys.getDirectories(npmTypes));
}
return result;
}
function getDefaultLibLocation(): string {
return getDirectoryPath(normalizePath(sys.getExecutingFilePath()));
}
@@ -958,7 +939,6 @@ namespace ts {
const realpath = sys.realpath && ((path: string) => sys.realpath(path));
return {
getDefaultTypeDirectiveNames,
getSourceFile,
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
@@ -971,6 +951,7 @@ namespace ts {
readFile: fileName => sys.readFile(fileName),
trace: (s: string) => sys.write(s + newLine),
directoryExists: directoryName => sys.directoryExists(directoryName),
getDirectories: (path: string) => sys.getDirectories(path),
realpath
};
}
@@ -1034,21 +1015,35 @@ namespace ts {
return resolutions;
}
export function getDefaultTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] {
function getInferredTypesRoot(options: CompilerOptions, rootFiles: string[], host: CompilerHost) {
return computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
}
/**
* Given a set of options and a set of root files, returns the set of type directive names
* that should be included for this program automatically.
* This list could either come from the config file,
* or from enumerating the types root + initial secondary types lookup location.
* More type directives might appear in the program later as a result of loading actual source files;
* this list is only the set of defaults that are implicitly included.
*/
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] {
// Use explicit type list from tsconfig.json
if (options.types) {
return options.types;
}
// or load all types from the automatic type import fields
if (host && host.getDefaultTypeDirectiveNames) {
const commonRoot = computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
if (commonRoot) {
return host.getDefaultTypeDirectiveNames(commonRoot);
// Walk the primary type lookup locations
let result: string[] = [];
if (host.directoryExists && host.getDirectories) {
const typeRoots = getEffectiveTypeRoots(options, host);
for (const root of typeRoots) {
if (host.directoryExists(root)) {
result = result.concat(host.getDirectories(root));
}
}
}
return undefined;
return result;
}
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
@@ -1100,11 +1095,13 @@ namespace ts {
if (!tryReuseStructureFromOldProgram()) {
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
// load type declarations specified via 'types' argument
const typeReferences: string[] = getDefaultTypeDirectiveNames(options, rootNames, host);
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, rootNames, host);
if (typeReferences) {
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, /*containingFile*/ undefined);
const inferredRoot = getInferredTypesRoot(options, rootNames, host);
const containingFilename = combinePaths(inferredRoot, "__inferred type names__.ts");
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
for (let i = 0; i < typeReferences.length; i++) {
processTypeReferenceDirective(typeReferences[i], resolutions[i]);
}
@@ -1212,10 +1209,9 @@ namespace ts {
(oldOptions.jsx !== options.jsx) ||
(oldOptions.allowJs !== options.allowJs) ||
(oldOptions.rootDir !== options.rootDir) ||
(oldOptions.typesSearchPaths !== options.typesSearchPaths) ||
(oldOptions.configFilePath !== options.configFilePath) ||
(oldOptions.baseUrl !== options.baseUrl) ||
(oldOptions.typesRoot !== options.typesRoot) ||
!arrayIsEqualTo(oldOptions.typeRoots, oldOptions.typeRoots) ||
!arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) ||
!mapIsEqualTo(oldOptions.paths, options.paths)) {
return false;
@@ -1970,7 +1966,7 @@ namespace ts {
}
}
else {
fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd, Diagnostics.Cannot_find_name_0, typeReferenceDirective));
fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd, Diagnostics.Cannot_find_type_definition_file_for_0, typeReferenceDirective));
}
if (saveResolution) {

View File

@@ -2562,7 +2562,8 @@ namespace ts {
target?: ScriptTarget;
traceResolution?: boolean;
types?: string[];
/* @internal */ typesRoot?: string;
/** Paths used to used to compute primary types search locations */
typeRoots?: string[];
typesSearchPaths?: string[];
/*@internal*/ version?: boolean;
/*@internal*/ watch?: boolean;
@@ -2871,6 +2872,7 @@ namespace ts {
getDefaultTypeDirectiveNames?(rootPath: string): string[];
writeFile: WriteFileCallback;
getCurrentDirectory(): string;
getDirectories(path: string): string[];
getCanonicalFileName(fileName: string): string;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;

View File

@@ -246,8 +246,8 @@ namespace FourSlash {
// Create a new Services Adapter
this.cancellationToken = new TestCancellationToken();
const compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
if (compilationOptions.typesRoot) {
compilationOptions.typesRoot = ts.getNormalizedAbsolutePath(compilationOptions.typesRoot, this.basePath);
if (compilationOptions.typeRoots) {
compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath));
}
const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);

View File

@@ -432,6 +432,7 @@ namespace Harness {
readFile(path: string): string;
writeFile(path: string, contents: string): void;
directoryName(path: string): string;
getDirectories(path: string): string[];
createDirectory(path: string): void;
fileExists(fileName: string): boolean;
directoryExists(path: string): boolean;
@@ -477,6 +478,7 @@ namespace Harness {
export const readFile: typeof IO.readFile = path => ts.sys.readFile(path);
export const writeFile: typeof IO.writeFile = (path, content) => ts.sys.writeFile(path, content);
export const directoryName: typeof IO.directoryName = fso.GetParentFolderName;
export const getDirectories: typeof IO.getDirectories = dir => ts.sys.getDirectories(dir);
export const directoryExists: typeof IO.directoryExists = fso.FolderExists;
export const fileExists: typeof IO.fileExists = fso.FileExists;
export const log: typeof IO.log = global.WScript && global.WScript.StdOut.WriteLine;
@@ -543,6 +545,7 @@ namespace Harness {
export const args = () => ts.sys.args;
export const getExecutingFilePath = () => ts.sys.getExecutingFilePath();
export const exit = (exitCode: number) => ts.sys.exit(exitCode);
export const getDirectories: typeof IO.getDirectories = path => ts.sys.getDirectories(path);
export const readFile: typeof IO.readFile = path => ts.sys.readFile(path);
export const writeFile: typeof IO.writeFile = (path, content) => ts.sys.writeFile(path, content);
@@ -616,6 +619,7 @@ namespace Harness {
export const args = () => <string[]>[];
export const getExecutingFilePath = () => "";
export const exit = (exitCode: number) => { };
export const getDirectories = () => <string[]>[];
export let log = (s: string) => console.log(s);
@@ -861,7 +865,7 @@ namespace Harness {
// Local get canonical file name function, that depends on passed in parameter for useCaseSensitiveFileNames
const getCanonicalFileName = ts.createGetCanonicalFileName(useCaseSensitiveFileNames);
let realPathMap: ts.FileMap<string>;
const realPathMap: ts.FileMap<string> = ts.createFileMap<string>();
const fileMap: ts.FileMap<() => ts.SourceFile> = ts.createFileMap<() => ts.SourceFile>();
for (const file of inputFiles) {
if (file.content !== undefined) {
@@ -870,9 +874,6 @@ namespace Harness {
if (file.fileOptions && file.fileOptions["symlink"]) {
const link = file.fileOptions["symlink"];
const linkPath = ts.toPath(link, currentDirectory, getCanonicalFileName);
if (!realPathMap) {
realPathMap = ts.createFileMap<string>();
}
realPathMap.set(linkPath, fileName);
fileMap.set(path, (): ts.SourceFile => { throw new Error("Symlinks should always be resolved to a realpath first"); });
}
@@ -906,20 +907,6 @@ namespace Harness {
return {
getDefaultTypeDirectiveNames: (path: string) => {
const results: string[] = [];
fileMap.forEachValue((key, value) => {
const rx = /node_modules\/@types\/(\w+)/;
const typeNameResult = rx.exec(key);
if (typeNameResult) {
const typeName = typeNameResult[1];
if (results.indexOf(typeName) < 0) {
results.push(typeName);
}
}
});
return results;
},
getCurrentDirectory: () => currentDirectory,
getSourceFile,
getDefaultLibFileName,
@@ -937,7 +924,37 @@ namespace Harness {
realpath: realPathMap && ((f: string) => {
const path = ts.toPath(f, currentDirectory, getCanonicalFileName);
return realPathMap.contains(path) ? realPathMap.get(path) : path;
})
}),
directoryExists: dir => {
let path = ts.toPath(dir, currentDirectory, getCanonicalFileName);
// Strip trailing /, which may exist if the path is a drive root
if (path[path.length - 1] === "/") {
path = <ts.Path>path.substr(0, path.length - 1);
}
let exists = false;
fileMap.forEachValue(key => {
if (key.indexOf(path) === 0 && key[path.length] === "/") {
exists = true;
}
});
return exists;
},
getDirectories: d => {
const path = ts.toPath(d, currentDirectory, getCanonicalFileName);
const result: string[] = [];
fileMap.forEachValue((key, value) => {
if (key.indexOf(path) === 0 && key.lastIndexOf("/") > path.length) {
let dirName = key.substr(path.length, key.indexOf("/", path.length + 1) - path.length);
if (dirName[0] === "/") {
dirName = dirName.substr(1);
}
if (result.indexOf(dirName) < 0) {
result.push(dirName);
}
}
});
return result;
}
};
}
@@ -1036,7 +1053,9 @@ namespace Harness {
options.noErrorTruncation = true;
options.skipDefaultLibCheck = true;
currentDirectory = currentDirectory || Harness.IO.getCurrentDirectory();
if (typeof currentDirectory === "undefined") {
currentDirectory = Harness.IO.getCurrentDirectory();
}
// Parse settings
if (harnessSettings) {

View File

@@ -246,16 +246,21 @@ namespace Harness.LanguageService {
};
this.getTypeReferenceDirectiveResolutionsForFile = (fileName) => {
const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
const resolutions: ts.Map<ts.ResolvedTypeReferenceDirective> = {};
const settings = this.nativeHost.getCompilationSettings();
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost);
if (resolutionInfo.resolvedTypeReferenceDirective.resolvedFileName) {
resolutions[typeReferenceDirective.fileName] = resolutionInfo.resolvedTypeReferenceDirective;
if (scriptInfo) {
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
const resolutions: ts.Map<ts.ResolvedTypeReferenceDirective> = {};
const settings = this.nativeHost.getCompilationSettings();
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost);
if (resolutionInfo.resolvedTypeReferenceDirective.resolvedFileName) {
resolutions[typeReferenceDirective.fileName] = resolutionInfo.resolvedTypeReferenceDirective;
}
}
return JSON.stringify(resolutions);
}
else {
return "[]";
}
return JSON.stringify(resolutions);
};
}
}

View File

@@ -185,7 +185,8 @@ class ProjectRunner extends RunnerBase {
useCaseSensitiveFileNames: () => Harness.IO.useCaseSensitiveFileNames(),
getNewLine: () => Harness.IO.newLine(),
fileExists: fileName => fileName === Harness.Compiler.defaultLibFileName || getSourceFileText(fileName) !== undefined,
readFile: fileName => Harness.IO.readFile(fileName)
readFile: fileName => Harness.IO.readFile(fileName),
getDirectories: path => Harness.IO.getDirectories(path)
};
}
}

View File

@@ -315,6 +315,10 @@ namespace ts.server {
return this.host.directoryExists(path);
}
getDirectories(path: string): string[] {
return this.host.getDirectories(path);
}
/**
* @param line 1 based index
*/

View File

@@ -1077,6 +1077,7 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
directoryExists?(directoryName: string): boolean;
getDirectories?(directoryName: string): string[];
}
//
@@ -2038,7 +2039,8 @@ namespace ts {
getNewLine: () => newLine,
fileExists: (fileName): boolean => fileName === inputFileName,
readFile: (fileName): string => "",
directoryExists: directoryExists => true
directoryExists: directoryExists => true,
getDirectories: (path: string) => []
};
const program = createProgram([inputFileName], options, compilerHost);
@@ -2139,7 +2141,7 @@ namespace ts {
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${settings.typesRoot}|${settings.typesSearchPaths}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${JSON.stringify(settings.typeRoots)}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
}
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
@@ -2999,8 +3001,10 @@ namespace ts {
return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength());
},
directoryExists: directoryName => {
Debug.assert(!host.resolveModuleNames || !host.resolveTypeReferenceDirectives);
return directoryProbablyExists(directoryName, host);
},
getDirectories: path => {
return host.getDirectories ? host.getDirectories(path) : [];
}
};
if (host.trace) {