Skip to content

Commit

Permalink
Merge pull request #11147 from Microsoft/classic_at_types
Browse files Browse the repository at this point in the history
Have classic module resolution use @types as a fallback
  • Loading branch information
Andy committed Oct 6, 2016
2 parents 32eddcf + 4907fd1 commit 02493de
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 25 deletions.
67 changes: 42 additions & 25 deletions src/compiler/moduleNameResolver.ts
Expand Up @@ -95,7 +95,7 @@ namespace ts {
currentDirectory = host.getCurrentDirectory();
}

return currentDirectory && getDefaultTypeRoots(currentDirectory, host);
return currentDirectory !== undefined && getDefaultTypeRoots(currentDirectory, host);
}

/**
Expand Down Expand Up @@ -675,23 +675,33 @@ namespace ts {

/* @internal */
export function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, checkOneLevel: boolean): string {
return loadModuleFromNodeModulesWorker(moduleName, directory, failedLookupLocations, state, checkOneLevel, /*typesOnly*/ false);
}

function loadModuleFromNodeModulesAtTypes(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
return loadModuleFromNodeModulesWorker(moduleName, directory, failedLookupLocations, state, /*checkOneLevel*/ false, /*typesOnly*/ true);
}

function loadModuleFromNodeModulesWorker(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState, checkOneLevel: boolean, typesOnly: boolean): string {
directory = normalizeSlashes(directory);
while (true) {
const baseName = getBaseFileName(directory);
if (baseName !== "node_modules") {
// Try to load source from the package
const packageResult = loadModuleFromNodeModulesFolder(moduleName, directory, failedLookupLocations, state);
if (packageResult && hasTypeScriptFileExtension(packageResult)) {
// Always prefer a TypeScript (.ts, .tsx, .d.ts) file shipped with the package
return packageResult;
}
else {
// Else prefer a types package over non-TypeScript results (e.g. JavaScript files)
const typesResult = loadModuleFromNodeModulesFolder(combinePaths("@types", moduleName), directory, failedLookupLocations, state);
if (typesResult || packageResult) {
return typesResult || packageResult;
let packageResult: string | undefined;
if (!typesOnly) {
// Try to load source from the package
packageResult = loadModuleFromNodeModulesFolder(moduleName, directory, failedLookupLocations, state);
if (packageResult && hasTypeScriptFileExtension(packageResult)) {
// Always prefer a TypeScript (.ts, .tsx, .d.ts) file shipped with the package
return packageResult;
}
}

// Else prefer a types package over non-TypeScript results (e.g. JavaScript files)
const typesResult = loadModuleFromNodeModulesFolder(combinePaths("@types", moduleName), directory, failedLookupLocations, state);
if (typesResult || packageResult) {
return typesResult || packageResult;
}
}

const parentPath = getDirectoryPath(directory);
Expand All @@ -709,7 +719,7 @@ namespace ts {
const state = { compilerOptions, host, traceEnabled, skipTsx: !compilerOptions.jsx };
const failedLookupLocations: string[] = [];
const supportedExtensions = getSupportedExtensions(compilerOptions);
let containingDirectory = getDirectoryPath(containingFile);
const containingDirectory = getDirectoryPath(containingFile);

const resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, supportedExtensions, state);
if (resolvedFileName) {
Expand All @@ -718,18 +728,9 @@ namespace ts {

let referencedSourceFile: string;
if (moduleHasNonRelativeName(moduleName)) {
while (true) {
const searchName = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(searchName, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (referencedSourceFile) {
break;
}
const parentPath = getDirectoryPath(containingDirectory);
if (parentPath === containingDirectory) {
break;
}
containingDirectory = parentPath;
}
referencedSourceFile = referencedSourceFile = loadModuleFromAncestorDirectories(moduleName, containingDirectory, supportedExtensions, failedLookupLocations, state) ||
// If we didn't find the file normally, look it up in @types.
loadModuleFromNodeModulesAtTypes(moduleName, containingDirectory, failedLookupLocations, state);
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
Expand All @@ -741,4 +742,20 @@ namespace ts {
? { resolvedModule: { resolvedFileName: referencedSourceFile }, failedLookupLocations }
: { resolvedModule: undefined, failedLookupLocations };
}

/** Climb up parent directories looking for a module. */
function loadModuleFromAncestorDirectories(moduleName: string, containingDirectory: string, supportedExtensions: string[], failedLookupLocations: string[], state: ModuleResolutionState): string | undefined {
while (true) {
const searchName = normalizePath(combinePaths(containingDirectory, moduleName));
const referencedSourceFile = loadModuleFromFile(searchName, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (referencedSourceFile) {
return referencedSourceFile;
}
const parentPath = getDirectoryPath(containingDirectory);
if (parentPath === containingDirectory) {
return undefined;
}
containingDirectory = parentPath;
}
}
}
18 changes: 18 additions & 0 deletions tests/baselines/reference/typingsLookupAmd.js
@@ -0,0 +1,18 @@
//// [tests/cases/conformance/typings/typingsLookupAmd.ts] ////

//// [index.d.ts]

export declare class A {}

//// [index.d.ts]
import {A} from "a";
export declare class B extends A {}

//// [foo.ts]
import {B} from "b";


//// [foo.js]
define(["require", "exports"], function (require, exports) {
"use strict";
});
17 changes: 17 additions & 0 deletions tests/baselines/reference/typingsLookupAmd.symbols
@@ -0,0 +1,17 @@
=== /x/y/foo.ts ===
import {B} from "b";
>B : Symbol(B, Decl(foo.ts, 0, 8))

=== /node_modules/@types/a/index.d.ts ===

export declare class A {}
>A : Symbol(A, Decl(index.d.ts, 0, 0))

=== /x/node_modules/@types/b/index.d.ts ===
import {A} from "a";
>A : Symbol(A, Decl(index.d.ts, 0, 8))

export declare class B extends A {}
>B : Symbol(B, Decl(index.d.ts, 0, 20))
>A : Symbol(A, Decl(index.d.ts, 0, 8))

59 changes: 59 additions & 0 deletions tests/baselines/reference/typingsLookupAmd.trace.json
@@ -0,0 +1,59 @@
[
"======== Resolving module 'b' from '/x/y/foo.ts'. ========",
"Module resolution kind is not specified, using 'Classic'.",
"File '/x/y/b.ts' does not exist.",
"File '/x/y/b.d.ts' does not exist.",
"File '/x/b.ts' does not exist.",
"File '/x/b.d.ts' does not exist.",
"File '/b.ts' does not exist.",
"File '/b.d.ts' does not exist.",
"File '/x/y/node_modules/@types/b.ts' does not exist.",
"File '/x/y/node_modules/@types/b.d.ts' does not exist.",
"File '/x/y/node_modules/@types/b/package.json' does not exist.",
"File '/x/y/node_modules/@types/b/index.ts' does not exist.",
"File '/x/y/node_modules/@types/b/index.d.ts' does not exist.",
"File '/x/node_modules/@types/b.ts' does not exist.",
"File '/x/node_modules/@types/b.d.ts' does not exist.",
"File '/x/node_modules/@types/b/package.json' does not exist.",
"File '/x/node_modules/@types/b/index.ts' does not exist.",
"File '/x/node_modules/@types/b/index.d.ts' exist - use it as a name resolution result.",
"======== Module name 'b' was successfully resolved to '/x/node_modules/@types/b/index.d.ts'. ========",
"======== Resolving module 'a' from '/x/node_modules/@types/b/index.d.ts'. ========",
"Module resolution kind is not specified, using 'Classic'.",
"File '/x/node_modules/@types/b/a.ts' does not exist.",
"File '/x/node_modules/@types/b/a.d.ts' does not exist.",
"File '/x/node_modules/@types/a.ts' does not exist.",
"File '/x/node_modules/@types/a.d.ts' does not exist.",
"File '/x/node_modules/a.ts' does not exist.",
"File '/x/node_modules/a.d.ts' does not exist.",
"File '/x/a.ts' does not exist.",
"File '/x/a.d.ts' does not exist.",
"File '/a.ts' does not exist.",
"File '/a.d.ts' does not exist.",
"File '/x/node_modules/@types/b/node_modules/@types/a.ts' does not exist.",
"File '/x/node_modules/@types/b/node_modules/@types/a.d.ts' does not exist.",
"File '/x/node_modules/@types/b/node_modules/@types/a/package.json' does not exist.",
"File '/x/node_modules/@types/b/node_modules/@types/a/index.ts' does not exist.",
"File '/x/node_modules/@types/b/node_modules/@types/a/index.d.ts' does not exist.",
"File '/x/node_modules/@types/node_modules/@types/a.ts' does not exist.",
"File '/x/node_modules/@types/node_modules/@types/a.d.ts' does not exist.",
"File '/x/node_modules/@types/node_modules/@types/a/package.json' does not exist.",
"File '/x/node_modules/@types/node_modules/@types/a/index.ts' does not exist.",
"File '/x/node_modules/@types/node_modules/@types/a/index.d.ts' does not exist.",
"File '/x/node_modules/@types/a.ts' does not exist.",
"File '/x/node_modules/@types/a.d.ts' does not exist.",
"File '/x/node_modules/@types/a/package.json' does not exist.",
"File '/x/node_modules/@types/a/index.ts' does not exist.",
"File '/x/node_modules/@types/a/index.d.ts' does not exist.",
"File '/node_modules/@types/a.ts' does not exist.",
"File '/node_modules/@types/a.d.ts' does not exist.",
"File '/node_modules/@types/a/package.json' does not exist.",
"File '/node_modules/@types/a/index.ts' does not exist.",
"File '/node_modules/@types/a/index.d.ts' exist - use it as a name resolution result.",
"======== Module name 'a' was successfully resolved to '/node_modules/@types/a/index.d.ts'. ========",
"======== Resolving type reference directive 'a', containing file '/__inferred type names__.ts', root directory '/node_modules/@types'. ========",
"Resolving with primary search path '/node_modules/@types'",
"File '/node_modules/@types/a/package.json' does not exist.",
"File '/node_modules/@types/a/index.d.ts' exist - use it as a name resolution result.",
"======== Type reference directive 'a' was successfully resolved to '/node_modules/@types/a/index.d.ts', primary: true. ========"
]
17 changes: 17 additions & 0 deletions tests/baselines/reference/typingsLookupAmd.types
@@ -0,0 +1,17 @@
=== /x/y/foo.ts ===
import {B} from "b";
>B : typeof B

=== /node_modules/@types/a/index.d.ts ===

export declare class A {}
>A : A

=== /x/node_modules/@types/b/index.d.ts ===
import {A} from "a";
>A : typeof A

export declare class B extends A {}
>B : B
>A : A

14 changes: 14 additions & 0 deletions tests/cases/conformance/typings/typingsLookupAmd.ts
@@ -0,0 +1,14 @@
// @traceResolution: true
// @noImplicitReferences: true
// @currentDirectory: /
// @module: amd

// @filename: /node_modules/@types/a/index.d.ts
export declare class A {}

// @filename: /x/node_modules/@types/b/index.d.ts
import {A} from "a";
export declare class B extends A {}

// @filename: /x/y/foo.ts
import {B} from "b";

0 comments on commit 02493de

Please sign in to comment.