diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts
index 0bea9468432..178578ec452 100644
--- a/src/compiler/commandLineParser.ts
+++ b/src/compiler/commandLineParser.ts
@@ -1992,7 +1992,7 @@ namespace ts {
}
if (include && include.length > 0) {
- for (const file of host.readDirectory(basePath, supportedExtensions, exclude, include)) {
+ for (const file of host.readDirectory(basePath, supportedExtensions, exclude, include, /*depth*/ undefined)) {
// If we have already included a literal or wildcard path with a
// higher priority extension, we should skip this file.
//
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index 86ad483ccc3..48e16ab16b4 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -2,8 +2,9 @@
///
namespace ts {
+ export const versionMajorMinor = "2.5";
/** The version of the TypeScript compiler release */
- export const version = "2.5.0";
+ export const version = `${versionMajorMinor}.0`;
}
/* @internal */
@@ -2016,7 +2017,7 @@ namespace ts {
};
}
- export function matchFiles(path: string, extensions: string[], excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string, getFileSystemEntries: (path: string) => FileSystemEntries): string[] {
+ export function matchFiles(path: string, extensions: string[], excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] {
path = normalizePath(path);
currentDirectory = normalizePath(currentDirectory);
@@ -2033,15 +2034,14 @@ namespace ts {
const comparer = useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive;
for (const basePath of patterns.basePaths) {
- visitDirectory(basePath, combinePaths(currentDirectory, basePath));
+ visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth);
}
return flatten(results);
- function visitDirectory(path: string, absolutePath: string) {
+ function visitDirectory(path: string, absolutePath: string, depth: number | undefined) {
let { files, directories } = getFileSystemEntries(path);
files = files.slice().sort(comparer);
- directories = directories.slice().sort(comparer);
for (const current of files) {
const name = combinePaths(path, current);
@@ -2059,12 +2059,20 @@ namespace ts {
}
}
+ if (depth !== undefined) {
+ depth--;
+ if (depth === 0) {
+ return;
+ }
+ }
+
+ directories = directories.slice().sort(comparer);
for (const current of directories) {
const name = combinePaths(path, current);
const absoluteName = combinePaths(absolutePath, current);
if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) &&
(!excludeRegex || !excludeRegex.test(absoluteName))) {
- visitDirectory(name, absoluteName);
+ visitDirectory(name, absoluteName, depth);
}
}
}
diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts
index cd14f3b2801..18ddba9c917 100644
--- a/src/compiler/sys.ts
+++ b/src/compiler/sys.ts
@@ -33,7 +33,7 @@ namespace ts {
getExecutingFilePath(): string;
getCurrentDirectory(): string;
getDirectories(path: string): string[];
- readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[];
+ readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[];
getModifiedTime?(path: string): Date;
/**
* This should be cryptographically secure.
@@ -281,8 +281,8 @@ namespace ts {
}
}
- function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[]): string[] {
- return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), getAccessibleFileSystemEntries);
+ function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[], depth?: number): string[] {
+ return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries);
}
const enum FileSystemEntryKind {
@@ -475,7 +475,7 @@ namespace ts {
getCurrentDirectory: () => ChakraHost.currentDirectory,
getDirectories: ChakraHost.getDirectories,
getEnvironmentVariable: ChakraHost.getEnvironmentVariable || (() => ""),
- readDirectory: (path: string, extensions?: string[], excludes?: string[], includes?: string[]) => {
+ readDirectory(path, extensions, excludes, includes, _depth) {
const pattern = getFileMatcherPatterns(path, excludes, includes, !!ChakraHost.useCaseSensitiveFileNames, ChakraHost.currentDirectory);
return ChakraHost.readDirectory(path, extensions, pattern.basePaths, pattern.excludePattern, pattern.includeFilePattern, pattern.includeDirectoryPattern);
},
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 85ab8a65d5d..4b79c4e99ed 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2390,7 +2390,7 @@ namespace ts {
export interface ParseConfigHost {
useCaseSensitiveFileNames: boolean;
- readDirectory(rootDir: string, extensions: string[], excludes: string[], includes: string[]): string[];
+ readDirectory(rootDir: string, extensions: string[], excludes: string[], includes: string[], depth: number): string[];
/**
* Gets a value indicating whether the specified path exists and is a file.
diff --git a/src/harness/harness.ts b/src/harness/harness.ts
index 97d49ca134d..026ff2de85f 100644
--- a/src/harness/harness.ts
+++ b/src/harness/harness.ts
@@ -493,7 +493,7 @@ namespace Harness {
args(): string[];
getExecutingFilePath(): string;
exit(exitCode?: number): void;
- readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]): string[];
+ readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[], depth?: number): string[];
tryEnableSourceMapsForHost?(): void;
getEnvironmentVariable?(name: string): string;
}
@@ -537,7 +537,7 @@ namespace Harness {
ts.sys.tryEnableSourceMapsForHost();
}
}
- export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include) => ts.sys.readDirectory(path, extension, exclude, include);
+ export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include, depth) => ts.sys.readDirectory(path, extension, exclude, include, depth);
export function createDirectory(path: string) {
if (!directoryExists(path)) {
@@ -733,12 +733,12 @@ namespace Harness {
Http.writeToServerSync(serverRoot + path, "WRITE", contents);
}
- export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]) {
+ export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[], depth?: number) {
const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames());
for (const file of listFiles(path)) {
fs.addFile(file);
}
- return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), path => {
+ return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), depth, path => {
const entry = fs.traversePath(path);
if (entry && entry.isDirectory()) {
const directory = entry;
diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts
index 774c43556ca..82f0b87be94 100644
--- a/src/harness/harnessLanguageService.ts
+++ b/src/harness/harnessLanguageService.ts
@@ -208,10 +208,11 @@ namespace Harness.LanguageService {
const script = this.getScriptSnapshot(fileName);
return script !== undefined;
}
- readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
+ readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
return ts.matchFiles(path, extensions, exclude, include,
/*useCaseSensitiveFileNames*/ false,
this.getCurrentDirectory(),
+ depth,
(p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p));
}
readFile(path: string): string {
@@ -312,9 +313,7 @@ namespace Harness.LanguageService {
getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); }
getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); }
- readDirectory(_rootDir: string, _extension: string): string {
- return ts.notImplemented();
- }
+ readDirectory = ts.notImplemented;
readDirectoryNames = ts.notImplemented;
readFileNames = ts.notImplemented;
fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; }
@@ -668,9 +667,7 @@ namespace Harness.LanguageService {
return ts.sys.getEnvironmentVariable(name);
}
- readDirectory(_path: string, _extension?: string[], _exclude?: string[], _include?: string[]): string[] {
- return ts.notImplemented();
- }
+ readDirectory() { return ts.notImplemented(); }
watchFile(): ts.FileWatcher {
return { close: ts.noop };
diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts
index 76f585b623a..d4a1e2e2bd0 100644
--- a/src/harness/loggedIO.ts
+++ b/src/harness/loggedIO.ts
@@ -67,6 +67,7 @@ interface IOLog {
extensions: string[],
exclude: string[],
include: string[],
+ depth: number,
result: string[]
}[];
}
@@ -220,10 +221,9 @@ namespace Playback {
memoize(path => findFileByPath(replayLog.filesRead, path, /*throwFileNotFoundError*/ true).contents));
wrapper.readDirectory = recordReplay(wrapper.readDirectory, underlying)(
- (path, extensions, exclude, include) => {
- const result = (underlying).readDirectory(path, extensions, exclude, include);
- const logEntry = { path, extensions, exclude, include, result };
- recordLog.directoriesRead.push(logEntry);
+ (path, extensions, exclude, include, depth) => {
+ const result = (underlying).readDirectory(path, extensions, exclude, include, depth);
+ recordLog.directoriesRead.push({ path, extensions, exclude, include, depth, result });
return result;
},
path => {
diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts
index b02164034fa..169d2ef539b 100644
--- a/src/harness/projectsRunner.ts
+++ b/src/harness/projectsRunner.ts
@@ -275,8 +275,8 @@ class ProjectRunner extends RunnerBase {
: ts.normalizeSlashes(testCase.projectRoot) + "/" + ts.normalizeSlashes(fileName);
}
- function readDirectory(rootDir: string, extension: string[], exclude: string[], include: string[]): string[] {
- const harnessReadDirectoryResult = Harness.IO.readDirectory(getFileNameInTheProjectTest(rootDir), extension, exclude, include);
+ function readDirectory(rootDir: string, extension: string[], exclude: string[], include: string[], depth: number): string[] {
+ const harnessReadDirectoryResult = Harness.IO.readDirectory(getFileNameInTheProjectTest(rootDir), extension, exclude, include, depth);
const result: string[] = [];
for (let i = 0; i < harnessReadDirectoryResult.length; i++) {
result[i] = ts.getRelativePathToDirectoryOrUrl(testCase.projectRoot, harnessReadDirectoryResult[i],
diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts
index 067cd351ec7..e8e54a0d6d0 100644
--- a/src/harness/unittests/session.ts
+++ b/src/harness/unittests/session.ts
@@ -19,7 +19,7 @@ namespace ts.server {
getExecutingFilePath(): string { return void 0; },
getCurrentDirectory(): string { return void 0; },
getEnvironmentVariable(): string { return ""; },
- readDirectory(): string[] { return []; },
+ readDirectory() { return []; },
exit: noop,
setTimeout() { return 0; },
clearTimeout: noop,
diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts
index 2f6ba7aa3bb..c0a0d21f854 100644
--- a/src/harness/unittests/tsserverProjectSystem.ts
+++ b/src/harness/unittests/tsserverProjectSystem.ts
@@ -438,14 +438,13 @@ namespace ts.projectSystem {
}
}
- readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
- const that = this;
- return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), (dir) => {
+ readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
+ return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => {
const result: FileSystemEntries = {
directories: [],
files: []
};
- const dirEntry = that.fs.get(that.toPath(dir));
+ const dirEntry = this.fs.get(this.toPath(dir));
if (isFolder(dirEntry)) {
dirEntry.entries.forEach((entry) => {
if (isFolder(entry)) {
diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts
index 9483308287f..7479c532c08 100644
--- a/src/harness/unittests/typingsInstaller.ts
+++ b/src/harness/unittests/typingsInstaller.ts
@@ -663,16 +663,22 @@ namespace ts.projectSystem {
path: "/node_modules/jquery/package.json",
content: JSON.stringify({ name: "jquery" })
};
+ // Should not search deeply in node_modules.
+ const nestedPackage = {
+ path: "/node_modules/jquery/nested/package.json",
+ content: JSON.stringify({ name: "nested" }),
+ };
const jqueryDTS = {
path: "/tmp/node_modules/@types/jquery/index.d.ts",
content: ""
};
- const host = createServerHost([app, jsconfig, jquery, jqueryPackage]);
+ const host = createServerHost([app, jsconfig, jquery, jqueryPackage, nestedPackage]);
const installer = new (class extends Installer {
constructor() {
- super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") });
+ super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery", "nested") });
}
- installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
+ installWorker(_requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
+ assert.deepEqual(args, [`@types/jquery@ts${versionMajorMinor}`]);
const installedTypings = ["@types/jquery"];
const typingFiles = [jqueryDTS];
executeCommand(this, host, installedTypings, typingFiles, cb);
@@ -1058,6 +1064,29 @@ namespace ts.projectSystem {
assert.deepEqual(result.cachedTypingPaths, [node.path]);
assert.deepEqual(result.newTypingNames, ["bar"]);
});
+
+ it("should search only 2 levels deep", () => {
+ const app = {
+ path: "/app.js",
+ content: "",
+ };
+ const a = {
+ path: "/node_modules/a/package.json",
+ content: JSON.stringify({ name: "a" }),
+ };
+ const b = {
+ path: "/node_modules/a/b/package.json",
+ content: JSON.stringify({ name: "b" }),
+ };
+ const host = createServerHost([app, a, b]);
+ const cache = createMap();
+ const result = JsTyping.discoverTypings(host, [app.path], getDirectoryPath(app.path), /*safeListPath*/ undefined, cache, { enable: true }, /*unresolvedImports*/ []);
+ assert.deepEqual(result, {
+ cachedTypingPaths: [],
+ newTypingNames: ["a"], // But not "b"
+ filesToWatch: ["/bower_components", "/node_modules"],
+ });
+ });
});
describe("telemetry events", () => {
diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts
index b89f4c7232d..3904a27bb1c 100644
--- a/src/harness/virtualFileSystem.ts
+++ b/src/harness/virtualFileSystem.ts
@@ -216,8 +216,8 @@ namespace Utils {
}
}
- readDirectory(path: string, extensions: string[], excludes: string[], includes: string[]) {
- return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, (path: string) => this.getAccessibleFileSystemEntries(path));
+ readDirectory(path: string, extensions: string[], excludes: string[], includes: string[], depth: number) {
+ return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, depth, (path: string) => this.getAccessibleFileSystemEntries(path));
}
}
}
\ No newline at end of file
diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts
index b8f9807139f..fa972a0c930 100644
--- a/src/server/lsHost.ts
+++ b/src/server/lsHost.ts
@@ -222,8 +222,8 @@ namespace ts.server {
return this.host.directoryExists(path);
}
- readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
- return this.host.readDirectory(path, extensions, exclude, include);
+ readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
+ return this.host.readDirectory(path, extensions, exclude, include, depth);
}
getDirectories(path: string): string[] {
diff --git a/src/server/server.ts b/src/server/server.ts
index 5f2b7054755..a7063fb1ad3 100644
--- a/src/server/server.ts
+++ b/src/server/server.ts
@@ -35,7 +35,6 @@ namespace ts.server {
} = require("os");
function getGlobalTypingsCacheLocation() {
- const versionMajorMinor = ts.version.match(/\d+\.\d+/)[0];
switch (process.platform) {
case "win32": {
const basePath = process.env.LOCALAPPDATA ||
diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts
index fa533450b26..cf1e1a836a0 100644
--- a/src/server/typingsInstaller/typingsInstaller.ts
+++ b/src/server/typingsInstaller/typingsInstaller.ts
@@ -434,5 +434,4 @@ namespace ts.server.typingsInstaller {
export function typingsName(packageName: string): string {
return `@types/${packageName}@ts${versionMajorMinor}`;
}
- const versionMajorMinor = version.split(".").slice(0, 2).join(".");
}
\ No newline at end of file
diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts
index 008fe357ec5..f3ee1e007cc 100644
--- a/src/services/jsTyping.ts
+++ b/src/services/jsTyping.ts
@@ -208,6 +208,7 @@ namespace ts.JsTyping {
return;
}
+ // depth of 2, so we access `node_modules/foo` but not `node_modules/foo/bar`
const fileNames = host.readDirectory(packagesFolderPath, [".json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2);
for (const fileName of fileNames) {
const normalizedFileName = normalizePath(fileName);
diff --git a/src/services/shims.ts b/src/services/shims.ts
index 1c37f598021..0a0b2c82c5b 100644
--- a/src/services/shims.ts
+++ b/src/services/shims.ts
@@ -467,7 +467,7 @@ namespace ts {
}
}
- export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost {
+ export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost, JsTyping.TypingResolutionHost {
public directoryExists: (directoryName: string) => boolean;
public realpath: (path: string) => string;
@@ -484,33 +484,17 @@ namespace ts {
}
public readDirectory(rootDir: string, extensions: string[], exclude: string[], include: string[], depth?: number): string[] {
- // Wrap the API changes for 2.0 release. This try/catch
- // should be removed once TypeScript 2.0 has shipped.
- try {
- const pattern = getFileMatcherPatterns(rootDir, exclude, include,
- this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory());
- return JSON.parse(this.shimHost.readDirectory(
- rootDir,
- JSON.stringify(extensions),
- JSON.stringify(pattern.basePaths),
- pattern.excludePattern,
- pattern.includeFilePattern,
- pattern.includeDirectoryPattern,
- depth
- ));
- }
- catch (e) {
- const results: string[] = [];
- for (const extension of extensions) {
- for (const file of this.readDirectoryFallback(rootDir, extension, exclude))
- {
- if (!contains(results, file)) {
- results.push(file);
- }
- }
- }
- return results;
- }
+ const pattern = getFileMatcherPatterns(rootDir, exclude, include,
+ this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory());
+ return JSON.parse(this.shimHost.readDirectory(
+ rootDir,
+ JSON.stringify(extensions),
+ JSON.stringify(pattern.basePaths),
+ pattern.excludePattern,
+ pattern.includeFilePattern,
+ pattern.includeDirectoryPattern,
+ depth
+ ));
}
public fileExists(fileName: string): boolean {
@@ -521,10 +505,6 @@ namespace ts {
return this.shimHost.readFile(fileName);
}
- private readDirectoryFallback(rootDir: string, extension: string, exclude: string[]) {
- return JSON.parse(this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)));
- }
-
public getDirectories(path: string): string[] {
return JSON.parse(this.shimHost.getDirectories(path));
}
diff --git a/src/services/types.ts b/src/services/types.ts
index 2d47da2fd1d..447029a2f66 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -163,7 +163,7 @@ namespace ts {
* LS host can optionally implement these methods to support completions for module specifiers.
* Without these methods, only completions for ambient modules will be provided.
*/
- readDirectory?(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[];
+ readDirectory?(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[];
readFile?(path: string, encoding?: string): string;
fileExists?(path: string): boolean;