From ed8052bdffbe8e0c902a15bfdf16aaabc3a218a0 Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Sun, 4 Jun 2017 23:27:41 +0200 Subject: [PATCH 01/11] read and resolve typeRoots so ensureBasicFiles can add content --- src/project-manager.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index a5ef62198..a5d0f81fb 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -734,6 +734,11 @@ export class ProjectConfiguration { */ private expectedFilePaths = new Set(); + /** + * List of resolved extra root directories to allow global typings to be loaded from. + */ + private typeRoots: string[]; + /** * @param fs file system to use * @param documentRegistry Shared DocumentRegistry that manages SourceFile objects @@ -838,6 +843,10 @@ export class ProjectConfiguration { this.expectedFilePaths = new Set(configParseResult.fileNames); const options = configParseResult.options; + this.typeRoots = options.typeRoots ? + options.typeRoots.map((r: string) => path.resolve(this.rootFilePath, r)) : + []; + if (/(^|\/)jsconfig\.json$/.test(this.configFilePath)) { options.allowJs = true; } @@ -864,6 +873,17 @@ export class ProjectConfiguration { private ensuredBasicFiles = false; + private isTypeRootDeclaration(path: string) { + if (isDeclarationFile(path)) { + for (const base of this.typeRoots) { + if (path.startsWith(base)) { + return true; + } + } + } + return false; + } + /** * Ensures we added basic files (global TS files, dependencies, declarations) */ @@ -882,7 +902,9 @@ export class ProjectConfiguration { // Add all global declaration files from the workspace and all declarations from the project for (const uri of this.fs.uris()) { const fileName = uri2path(uri); - if (isGlobalTSFile(fileName) || (isDeclarationFile(fileName) && this.expectedFilePaths.has(toUnixPath(fileName)))) { + if (isGlobalTSFile(fileName) || + this.isTypeRootDeclaration(fileName) || + (isDeclarationFile(fileName) && this.expectedFilePaths.has(toUnixPath(fileName)))) { const sourceFile = program.getSourceFile(fileName); if (!sourceFile) { this.getHost().addFile(fileName); From e0addb00fb8386f4f1ed71faa56296358f05d444 Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Tue, 29 Aug 2017 08:41:03 +0200 Subject: [PATCH 02/11] fix: clean up ensureBasicFiles logic --- src/project-manager.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index a5d0f81fb..7b2149a3b 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -735,7 +735,7 @@ export class ProjectConfiguration { private expectedFilePaths = new Set(); /** - * List of resolved extra root directories to allow global typings to be loaded from. + * List of resolved extra root directories to allow global type declaration files to be loaded from. */ private typeRoots: string[]; @@ -873,15 +873,17 @@ export class ProjectConfiguration { private ensuredBasicFiles = false; - private isTypeRootDeclaration(path: string) { - if (isDeclarationFile(path)) { - for (const base of this.typeRoots) { - if (path.startsWith(base)) { - return true; - } - } + /** + * [isExpectedDeclarationFile description] + * @param {string} fileName [description] + */ + private isExpectedDeclarationFile(fileName: string) { + if (isDeclarationFile(fileName)) { + return this.expectedFilePaths.has(toUnixPath(fileName)) || + this.typeRoots.some(root => fileName.startsWith(root)) + } else { + return false; } - return false; } /** @@ -903,8 +905,7 @@ export class ProjectConfiguration { for (const uri of this.fs.uris()) { const fileName = uri2path(uri); if (isGlobalTSFile(fileName) || - this.isTypeRootDeclaration(fileName) || - (isDeclarationFile(fileName) && this.expectedFilePaths.has(toUnixPath(fileName)))) { + this.isExpectedDeclarationFile(fileName)) { const sourceFile = program.getSourceFile(fileName); if (!sourceFile) { this.getHost().addFile(fileName); From 5a10ad3c6e2b6413bcc279f27f2a8048876ca590 Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Tue, 29 Aug 2017 19:04:29 +0200 Subject: [PATCH 03/11] fix: Lint fix --- src/project-manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index 7b2149a3b..3dc7a67c1 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -880,7 +880,7 @@ export class ProjectConfiguration { private isExpectedDeclarationFile(fileName: string) { if (isDeclarationFile(fileName)) { return this.expectedFilePaths.has(toUnixPath(fileName)) || - this.typeRoots.some(root => fileName.startsWith(root)) + this.typeRoots.some(root => fileName.startsWith(root)); } else { return false; } From c520bb21a8224b60299caa4ace1936da45badf6e Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Thu, 31 Aug 2017 08:42:50 +0200 Subject: [PATCH 04/11] fix: Add (broken) test --- src/test/project-manager.test.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/project-manager.test.ts b/src/test/project-manager.test.ts index ee40fabfe..508f0edb9 100644 --- a/src/test/project-manager.test.ts +++ b/src/test/project-manager.test.ts @@ -24,6 +24,28 @@ describe('ProjectManager', () => { assert.isDefined(configs.find(config => config.configFilePath === '/foo/tsconfig.json')); }); + describe('ensureBasicFiles', () => { + beforeEach(async () => { + memfs = new InMemoryFileSystem('/'); + const localfs = new MapFileSystem(new Map([ + ['file:///project/package.json', '{"name": "package-name-1"}'], + ['file:///project/tsconfig.json', '{ "compilerOptions": { "typeRoots": ["../types"]} }'], + ['file:///project/file.ts', 'console.log(GLOBALCONSTANT);'], + ['file:///types/types.d.ts', 'declare var GLOBALCONSTANT=1;'] + + ])); + const updater = new FileSystemUpdater(localfs, memfs); + projectManager = new ProjectManager('/', memfs, updater, true); + }); + it('loads files from typeRoots', async () => { + await projectManager.ensureReferencedFiles('file:///project/file.ts').toPromise(); + projectManager.invalidateModuleStructure(); + await projectManager.ensureModuleStructure().toPromise(); + memfs.getContent('file:///project/file.ts'); + memfs.getContent('file:///types/types.d.ts'); + }); + }); + describe('getPackageName()', () => { beforeEach(async () => { memfs = new InMemoryFileSystem('/'); From e19ffc2079c3a4efd17caf209231f72e2e5aaaa0 Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Thu, 7 Sep 2017 22:00:04 +0200 Subject: [PATCH 05/11] fix: Add ensureConfigDependencies to fix test --- src/project-manager.ts | 21 ++++++++++++++++++++- src/test/project-manager.test.ts | 2 -- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index 3dc7a67c1..0f1c78e25 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -321,6 +321,7 @@ export class ProjectManager implements Disposable { span.addTags({ uri, maxDepth }); ignore.add(uri); return this.ensureModuleStructure(span) + .concat(Observable.defer(() => this.ensureConfigDependencies())) // If max depth was reached, don't go any further .concat(Observable.defer(() => maxDepth === 0 ? Observable.empty() : this.resolveReferencedFiles(uri))) // Prevent cycles @@ -337,6 +338,24 @@ export class ProjectManager implements Disposable { }); } + isConfigDependency(uri: string): boolean { + for (const config of this.configurations()) { + config.ensureConfigFile(); + if (config.isExpectedDeclarationFile(uri)) { + return true; + } + } + return false; + } + + ensureConfigDependencies(): Observable { + return observableFromIterable(this.inMemoryFs.uris()) + .filter(uri => this.isConfigDependency(uri2path(uri))) + .mergeMap(uri => this.updater.ensure(uri)) + .publishReplay() + .refCount(); + } + /** * Invalidates a cache entry for `resolveReferencedFiles` (e.g. because the file changed) * @@ -877,7 +896,7 @@ export class ProjectConfiguration { * [isExpectedDeclarationFile description] * @param {string} fileName [description] */ - private isExpectedDeclarationFile(fileName: string) { + public isExpectedDeclarationFile(fileName: string) { if (isDeclarationFile(fileName)) { return this.expectedFilePaths.has(toUnixPath(fileName)) || this.typeRoots.some(root => fileName.startsWith(root)); diff --git a/src/test/project-manager.test.ts b/src/test/project-manager.test.ts index 508f0edb9..aa8e8392b 100644 --- a/src/test/project-manager.test.ts +++ b/src/test/project-manager.test.ts @@ -39,8 +39,6 @@ describe('ProjectManager', () => { }); it('loads files from typeRoots', async () => { await projectManager.ensureReferencedFiles('file:///project/file.ts').toPromise(); - projectManager.invalidateModuleStructure(); - await projectManager.ensureModuleStructure().toPromise(); memfs.getContent('file:///project/file.ts'); memfs.getContent('file:///types/types.d.ts'); }); From 5cf7be33b059e62741fde55aa15e4572d3b7509c Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Thu, 7 Sep 2017 22:14:08 +0200 Subject: [PATCH 06/11] fix: variable naming and windows slashes --- src/project-manager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index 0f1c78e25..e81591519 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -338,10 +338,10 @@ export class ProjectManager implements Disposable { }); } - isConfigDependency(uri: string): boolean { + isConfigDependency(filePath: string): boolean { for (const config of this.configurations()) { config.ensureConfigFile(); - if (config.isExpectedDeclarationFile(uri)) { + if (config.isExpectedDeclarationFile(toUnixPath(filePath))) { return true; } } From 1d7507322cd5649ca81c534879f9eb3f1da85769 Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Fri, 8 Sep 2017 00:12:40 +0200 Subject: [PATCH 07/11] fix: typeroot resolving on windows --- src/project-manager.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index e81591519..4b9d1846d 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -341,7 +341,7 @@ export class ProjectManager implements Disposable { isConfigDependency(filePath: string): boolean { for (const config of this.configurations()) { config.ensureConfigFile(); - if (config.isExpectedDeclarationFile(toUnixPath(filePath))) { + if (config.isExpectedDeclarationFile(filePath)) { return true; } } @@ -862,8 +862,9 @@ export class ProjectConfiguration { this.expectedFilePaths = new Set(configParseResult.fileNames); const options = configParseResult.options; + const pathResolver = /[a-z]:\//i.test(base) ? path.win32 : path.posix; this.typeRoots = options.typeRoots ? - options.typeRoots.map((r: string) => path.resolve(this.rootFilePath, r)) : + options.typeRoots.map((r: string) => pathResolver.resolve(this.rootFilePath, r)) : []; if (/(^|\/)jsconfig\.json$/.test(this.configFilePath)) { From c259701079da39d6a1f30b89ea916be512a736de Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Sat, 9 Sep 2017 13:20:40 +0200 Subject: [PATCH 08/11] fix: Check for drive prefix at start of string only, complete doc template --- src/project-manager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index 4b9d1846d..25cafba5d 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -862,7 +862,7 @@ export class ProjectConfiguration { this.expectedFilePaths = new Set(configParseResult.fileNames); const options = configParseResult.options; - const pathResolver = /[a-z]:\//i.test(base) ? path.win32 : path.posix; + const pathResolver = /^[a-z]:\//i.test(base) ? path.win32 : path.posix; this.typeRoots = options.typeRoots ? options.typeRoots.map((r: string) => pathResolver.resolve(this.rootFilePath, r)) : []; @@ -894,8 +894,8 @@ export class ProjectConfiguration { private ensuredBasicFiles = false; /** - * [isExpectedDeclarationFile description] - * @param {string} fileName [description] + * Determines if a fileName is a declaration file within expected files or type roots + * @param fileName */ public isExpectedDeclarationFile(fileName: string) { if (isDeclarationFile(fileName)) { From 34739938cc452893ad18fd188d09d2bc155e82a5 Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Sat, 16 Sep 2017 23:26:03 +0200 Subject: [PATCH 09/11] fix: Add doctypes, remove publishReplay&refCount, simplify --- src/project-manager.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index 25cafba5d..e76f30375 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -338,6 +338,10 @@ export class ProjectManager implements Disposable { }); } + /** + * Determines if a tsconfig/jsconfig needs additional declaration files loaded. + * @param filePath + */ isConfigDependency(filePath: string): boolean { for (const config of this.configurations()) { config.ensureConfigFile(); @@ -348,12 +352,13 @@ export class ProjectManager implements Disposable { return false; } + /** + * Loads files determined by tsconfig to be needed into the file system + */ ensureConfigDependencies(): Observable { return observableFromIterable(this.inMemoryFs.uris()) .filter(uri => this.isConfigDependency(uri2path(uri))) - .mergeMap(uri => this.updater.ensure(uri)) - .publishReplay() - .refCount(); + .mergeMap(uri => this.updater.ensure(uri)); } /** @@ -898,12 +903,9 @@ export class ProjectConfiguration { * @param fileName */ public isExpectedDeclarationFile(fileName: string) { - if (isDeclarationFile(fileName)) { - return this.expectedFilePaths.has(toUnixPath(fileName)) || - this.typeRoots.some(root => fileName.startsWith(root)); - } else { - return false; - } + return isDeclarationFile(fileName) && + (this.expectedFilePaths.has(toUnixPath(fileName)) || + this.typeRoots.some(root => fileName.startsWith(root))); } /** From 14224573883d8078edd81ec6a6bea2317fe61d39 Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Mon, 25 Sep 2017 22:17:49 +0200 Subject: [PATCH 10/11] fix: memoize ensureConfigDependencies --- src/project-manager.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index e76f30375..76b1a120e 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -74,6 +74,11 @@ export class ProjectManager implements Disposable { */ private ensuredModuleStructure?: Observable; + /** + * Observable that completes when extra dependencies pointed to by tsconfig.json have been loaded. + */ + private ensuredConfigDependencies?: Observable; + /** * Observable that completes when `ensureAllFiles` completed */ @@ -252,6 +257,7 @@ export class ProjectManager implements Disposable { */ invalidateModuleStructure(): void { this.ensuredModuleStructure = undefined; + this.ensuredConfigDependencies = undefined; this.ensuredAllFiles = undefined; this.ensuredOwnFiles = undefined; } @@ -355,10 +361,20 @@ export class ProjectManager implements Disposable { /** * Loads files determined by tsconfig to be needed into the file system */ - ensureConfigDependencies(): Observable { - return observableFromIterable(this.inMemoryFs.uris()) - .filter(uri => this.isConfigDependency(uri2path(uri))) - .mergeMap(uri => this.updater.ensure(uri)); + ensureConfigDependencies(childOf = new Span()): Observable { + return traceObservable('Ensure config dependencies', childOf, span => { + if (!this.ensuredConfigDependencies) { + this.ensuredConfigDependencies = observableFromIterable(this.inMemoryFs.uris()) + .filter(uri => this.isConfigDependency(uri2path(uri))) + .mergeMap(uri => this.updater.ensure(uri)) + .do(noop, err => { + this.ensuredConfigDependencies = undefined; + }) + .publishReplay() + .refCount(); + } + return this.ensuredConfigDependencies; + }); } /** From 08eec1c5723b2ccf6af4c79733efaff63f48df26 Mon Sep 17 00:00:00 2001 From: Tom van Ommeren Date: Tue, 26 Sep 2017 08:03:32 +0200 Subject: [PATCH 11/11] fix: add ts2.4 compile fix to ensureConfigDependencies --- src/project-manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/project-manager.ts b/src/project-manager.ts index 76b1a120e..d25327d6e 100644 --- a/src/project-manager.ts +++ b/src/project-manager.ts @@ -371,7 +371,7 @@ export class ProjectManager implements Disposable { this.ensuredConfigDependencies = undefined; }) .publishReplay() - .refCount(); + .refCount() as Observable; } return this.ensuredConfigDependencies; });