From eba4d3673931dc748cdf5b0e4633646b23a0deb9 Mon Sep 17 00:00:00 2001 From: kingwl <805037171@163.com> Date: Thu, 14 Dec 2017 09:26:05 +0800 Subject: [PATCH 01/44] error if import empty object form module not existed --- src/compiler/checker.ts | 5 ++++- .../reference/importEmptyFromModuleNotExisted.errors.txt | 8 ++++++++ .../reference/importEmptyFromModuleNotExisted.js | 7 +++++++ .../reference/importEmptyFromModuleNotExisted.symbols | 4 ++++ .../reference/importEmptyFromModuleNotExisted.types | 4 ++++ .../es6/modules/importEmptyFromModuleNotExisted.ts | 1 + 6 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/importEmptyFromModuleNotExisted.errors.txt create mode 100644 tests/baselines/reference/importEmptyFromModuleNotExisted.js create mode 100644 tests/baselines/reference/importEmptyFromModuleNotExisted.symbols create mode 100644 tests/baselines/reference/importEmptyFromModuleNotExisted.types create mode 100644 tests/cases/conformance/es6/modules/importEmptyFromModuleNotExisted.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bb7f8d013fc..0c44bd928a7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23852,7 +23852,10 @@ namespace ts { checkImportBinding(importClause.namedBindings); } else { - forEach(importClause.namedBindings.elements, checkImportBinding); + const moduleExisted = resolveExternalModuleName(node, node.moduleSpecifier); + if (moduleExisted) { + forEach(importClause.namedBindings.elements, checkImportBinding); + } } } } diff --git a/tests/baselines/reference/importEmptyFromModuleNotExisted.errors.txt b/tests/baselines/reference/importEmptyFromModuleNotExisted.errors.txt new file mode 100644 index 00000000000..99ccd49c489 --- /dev/null +++ b/tests/baselines/reference/importEmptyFromModuleNotExisted.errors.txt @@ -0,0 +1,8 @@ +tests/cases/conformance/es6/modules/importEmptyFromModuleNotExisted.ts(1,16): error TS2307: Cannot find module 'module-not-existed'. + + +==== tests/cases/conformance/es6/modules/importEmptyFromModuleNotExisted.ts (1 errors) ==== + import {} from 'module-not-existed' + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2307: Cannot find module 'module-not-existed'. + \ No newline at end of file diff --git a/tests/baselines/reference/importEmptyFromModuleNotExisted.js b/tests/baselines/reference/importEmptyFromModuleNotExisted.js new file mode 100644 index 00000000000..aa08db2e99e --- /dev/null +++ b/tests/baselines/reference/importEmptyFromModuleNotExisted.js @@ -0,0 +1,7 @@ +//// [importEmptyFromModuleNotExisted.ts] +import {} from 'module-not-existed' + + +//// [importEmptyFromModuleNotExisted.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/importEmptyFromModuleNotExisted.symbols b/tests/baselines/reference/importEmptyFromModuleNotExisted.symbols new file mode 100644 index 00000000000..ea0425ff26c --- /dev/null +++ b/tests/baselines/reference/importEmptyFromModuleNotExisted.symbols @@ -0,0 +1,4 @@ +=== tests/cases/conformance/es6/modules/importEmptyFromModuleNotExisted.ts === +import {} from 'module-not-existed' +No type information for this code. +No type information for this code. \ No newline at end of file diff --git a/tests/baselines/reference/importEmptyFromModuleNotExisted.types b/tests/baselines/reference/importEmptyFromModuleNotExisted.types new file mode 100644 index 00000000000..ea0425ff26c --- /dev/null +++ b/tests/baselines/reference/importEmptyFromModuleNotExisted.types @@ -0,0 +1,4 @@ +=== tests/cases/conformance/es6/modules/importEmptyFromModuleNotExisted.ts === +import {} from 'module-not-existed' +No type information for this code. +No type information for this code. \ No newline at end of file diff --git a/tests/cases/conformance/es6/modules/importEmptyFromModuleNotExisted.ts b/tests/cases/conformance/es6/modules/importEmptyFromModuleNotExisted.ts new file mode 100644 index 00000000000..36bca3e9619 --- /dev/null +++ b/tests/cases/conformance/es6/modules/importEmptyFromModuleNotExisted.ts @@ -0,0 +1 @@ +import {} from 'module-not-existed' From b0fb73c47cc7202f2477a4140dd3e2303d6aefce Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 3 Apr 2018 13:53:19 -0700 Subject: [PATCH 02/44] Typings cache is internal data structure --- src/server/editorServices.ts | 3 ++- src/server/typingsCache.ts | 3 ++- tests/baselines/reference/api/tsserverlibrary.d.ts | 12 ------------ 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 1f8f9d3b21b..dc2fa086a93 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -312,7 +312,8 @@ namespace ts.server { export class ProjectService { - public readonly typingsCache: TypingsCache; + /*@internal*/ + readonly typingsCache: TypingsCache; private readonly documentRegistry: DocumentRegistry; diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index f2642230f2d..6418b5f9cbe 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -24,7 +24,7 @@ namespace ts.server { globalTypingsCacheLocation: undefined }; - class TypingsCacheEntry { + interface TypingsCacheEntry { readonly typeAcquisition: TypeAcquisition; readonly compilerOptions: CompilerOptions; readonly typings: SortedReadonlyArray; @@ -80,6 +80,7 @@ namespace ts.server { return !arrayIsEqualTo(imports1, imports2); } + /*@internal*/ export class TypingsCache { private readonly perProjectCache: Map = createMap(); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 2e8e3f3871d..d102f467ade 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7602,17 +7602,6 @@ declare namespace ts.server { readonly globalTypingsCacheLocation: string; } const nullTypingsInstaller: ITypingsInstaller; - class TypingsCache { - private readonly installer; - private readonly perProjectCache; - constructor(installer: ITypingsInstaller); - isKnownTypesPackageName(name: string): boolean; - installPackage(options: InstallPackageOptionsWithProject): Promise; - getTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray, forceRefresh: boolean): SortedReadonlyArray; - updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray, newTypings: string[]): void; - deleteTypingsForProject(projectName: string): void; - onProjectClosed(project: Project): void; - } } declare namespace ts.server { enum ProjectKind { @@ -7960,7 +7949,6 @@ declare namespace ts.server { syntaxOnly?: boolean; } class ProjectService { - readonly typingsCache: TypingsCache; private readonly documentRegistry; /** * Container of all known scripts From c9479f7263fbd51f454da2b6363238200e0abca8 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 12 Apr 2018 14:55:22 -0700 Subject: [PATCH 03/44] Remove the specialized type UnresolvedImportsMap which is just a redirection and helps only in test only --- src/harness/unittests/typingsInstaller.ts | 6 +-- src/server/project.ts | 42 +++---------------- .../reference/api/tsserverlibrary.d.ts | 12 ------ 3 files changed, 9 insertions(+), 51 deletions(-) diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 1a19c98f477..5ec7a9d7a17 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -999,7 +999,7 @@ namespace ts.projectSystem { proj.updateGraph(); assert.deepEqual( - proj.getCachedUnresolvedImportsPerFile_TestOnly().get(f1.path), + proj.cachedUnresolvedImportsPerFile.get(f1.path), ["foo", "foo", "foo", "@bar/router", "@bar/common", "@bar/common"] ); @@ -1029,7 +1029,7 @@ namespace ts.projectSystem { const projectService = session.getProjectService(); checkNumberOfProjects(projectService, { inferredProjects: 1 }); const proj = projectService.inferredProjects[0]; - const version1 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion(); + const version1 = proj.lastCachedUnresolvedImportsList; // make a change that should not affect the structure of the program const changeRequest: server.protocol.ChangeRequest = { @@ -1047,7 +1047,7 @@ namespace ts.projectSystem { }; session.executeCommand(changeRequest); host.checkTimeoutQueueLengthAndRun(2); // This enqueues the updategraph and refresh inferred projects - const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion(); + const version2 = proj.lastCachedUnresolvedImportsList; assert.notEqual(version1, version2, "set of unresolved imports should change"); }); diff --git a/src/server/project.ts b/src/server/project.ts index 1502d107dee..23583d9c71b 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -55,34 +55,6 @@ namespace ts.server { projectErrors: ReadonlyArray; } - export class UnresolvedImportsMap { - readonly perFileMap = createMap>(); - private version = 0; - - public clear() { - this.perFileMap.clear(); - this.version = 0; - } - - public getVersion() { - return this.version; - } - - public remove(path: Path) { - this.perFileMap.delete(path); - this.version++; - } - - public get(path: Path) { - return this.perFileMap.get(path); - } - - public set(path: Path, value: ReadonlyArray) { - this.perFileMap.set(path, value); - this.version++; - } - } - export interface PluginCreateInfo { project: Project; languageService: LanguageService; @@ -116,8 +88,10 @@ namespace ts.server { private missingFilesMap: Map; private plugins: PluginModule[] = []; - private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap(); - private lastCachedUnresolvedImportsList: SortedReadonlyArray; + /*@internal*/ + cachedUnresolvedImportsPerFile = createMap>(); + /*@internal*/ + lastCachedUnresolvedImportsList: SortedReadonlyArray; private lastFileExceededProgramSize: string | undefined; @@ -181,10 +155,6 @@ namespace ts.server { return hasOneOrMoreJsAndNoTsFiles(this); } - public getCachedUnresolvedImportsPerFile_TestOnly() { - return this.cachedUnresolvedImportsPerFile; - } - public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {} { const resolvedPath = normalizeSlashes(host.resolvePath(combinePaths(initialDir, "node_modules"))); log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`); @@ -742,7 +712,7 @@ namespace ts.server { else { this.resolutionCache.invalidateResolutionOfFile(info.path); } - this.cachedUnresolvedImportsPerFile.remove(info.path); + this.cachedUnresolvedImportsPerFile.delete(info.path); if (detachFromProject) { info.detachFromProject(this); @@ -812,7 +782,7 @@ namespace ts.server { for (const file of changedFiles) { // delete cached information for changed files - this.cachedUnresolvedImportsPerFile.remove(file); + this.cachedUnresolvedImportsPerFile.delete(file); } // update builder only if language service is enabled diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index d102f467ade..29959a89d02 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7611,15 +7611,6 @@ declare namespace ts.server { } function allRootFilesAreJsOrDts(project: Project): boolean; function allFilesAreJsOrDts(project: Project): boolean; - class UnresolvedImportsMap { - readonly perFileMap: Map>; - private version; - clear(): void; - getVersion(): number; - remove(path: Path): void; - get(path: Path): ReadonlyArray; - set(path: Path, value: ReadonlyArray): void; - } interface PluginCreateInfo { project: Project; languageService: LanguageService; @@ -7652,8 +7643,6 @@ declare namespace ts.server { private externalFiles; private missingFilesMap; private plugins; - private cachedUnresolvedImportsPerFile; - private lastCachedUnresolvedImportsList; private lastFileExceededProgramSize; protected languageService: LanguageService; languageServiceEnabled: boolean; @@ -7688,7 +7677,6 @@ declare namespace ts.server { private readonly cancellationToken; isNonTsProject(): boolean; isJsOnlyProject(): boolean; - getCachedUnresolvedImportsPerFile_TestOnly(): UnresolvedImportsMap; static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {}; isKnownTypesPackageName(name: string): boolean; installPackage(options: InstallPackageOptions): Promise; From 35abe268242207c58558d3e7d6077ffe09e2f779 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 12 Apr 2018 15:21:20 -0700 Subject: [PATCH 04/44] Force new typings resolution only if there are more or less script infos in the project. This helps in reducing number of forced typing installation requests We anyways use changes in unresolved import array to determine if we need to enqueue new typing request Hence there is no need to soley rely on hasChanges from updateGraph which just indicates that we didnt reused the program (that does not mean new files were added to the program or changes in unresolved imports) --- src/compiler/core.ts | 7 ++++--- src/server/project.ts | 6 +++++- src/server/scriptInfo.ts | 8 +++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index cdf68cb2dfa..b427c689c96 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2987,18 +2987,19 @@ namespace ts { } /** Remove the *first* occurrence of `item` from the array. */ - export function unorderedRemoveItem(array: T[], item: T): void { + export function unorderedRemoveItem(array: T[], item: T) { unorderedRemoveFirstItemWhere(array, element => element === item); } /** Remove the *first* element satisfying `predicate`. */ - function unorderedRemoveFirstItemWhere(array: T[], predicate: (element: T) => boolean): void { + function unorderedRemoveFirstItemWhere(array: T[], predicate: (element: T) => boolean) { for (let i = 0; i < array.length; i++) { if (predicate(array[i])) { unorderedRemoveItemAt(array, i); - break; + return true; } } + return false; } export type GetCanonicalFileName = (fileName: string) => string; diff --git a/src/server/project.ts b/src/server/project.ts index 23583d9c71b..47bfb03a189 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -92,6 +92,8 @@ namespace ts.server { cachedUnresolvedImportsPerFile = createMap>(); /*@internal*/ lastCachedUnresolvedImportsList: SortedReadonlyArray; + /*@internal*/ + hasMoreOrLessScriptInfos = false; private lastFileExceededProgramSize: string | undefined; @@ -777,6 +779,8 @@ namespace ts.server { this.resolutionCache.startRecordingFilesWithChangedResolutions(); let hasChanges = this.updateGraphWorker(); + const hasMoreOrLessScriptInfos = this.hasMoreOrLessScriptInfos; + this.hasMoreOrLessScriptInfos = false; const changedFiles: ReadonlyArray = this.resolutionCache.finishRecordingFilesWithChangedResolutions() || emptyArray; @@ -803,7 +807,7 @@ namespace ts.server { this.lastCachedUnresolvedImportsList = toDeduplicatedSortedArray(result); } - const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasChanges); + const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasMoreOrLessScriptInfos); if (!arrayIsEqualTo(this.typingFiles, cachedTypings)) { this.typingFiles = cachedTypings; this.markAsDirty(); diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index db56973d796..074fde298aa 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -304,6 +304,7 @@ namespace ts.server { const isNew = !this.isAttached(project); if (isNew) { this.containingProjects.push(project); + project.hasMoreOrLessScriptInfos = true; if (!project.getCompilerOptions().preserveSymlinks) { this.ensureRealPath(); } @@ -328,19 +329,24 @@ namespace ts.server { return; case 1: if (this.containingProjects[0] === project) { + project.hasMoreOrLessScriptInfos = true; this.containingProjects.pop(); } break; case 2: if (this.containingProjects[0] === project) { + project.hasMoreOrLessScriptInfos = true; this.containingProjects[0] = this.containingProjects.pop(); } else if (this.containingProjects[1] === project) { + project.hasMoreOrLessScriptInfos = true; this.containingProjects.pop(); } break; default: - unorderedRemoveItem(this.containingProjects, project); + if (unorderedRemoveItem(this.containingProjects, project)) { + project.hasMoreOrLessScriptInfos = true; + } break; } } From 60b19f5782b87af71a2ca567da9c3f9d50638df2 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 12 Apr 2018 16:47:40 -0700 Subject: [PATCH 05/44] Invalidate the unresolved import resolutions when typing files are set This has 3 changes: 1. In updateGraph when enqueue the typing installation request (depending on unresolved imports) 2. When ActionSet event is received, invalidate only files with unresolved imports and resolve those. 3. When ActionInvalidate event is received, typing installer has detected some change in global typing cache location, so just enqueue a new typing installation request. This will repeat the cycle of setting correct typings and pickiing unresolved imports --- src/compiler/resolutionCache.ts | 20 ++++- .../unittests/tsserverProjectSystem.ts | 1 - src/harness/unittests/typingsInstaller.ts | 75 ++++++++++++++++++- src/server/editorServices.ts | 10 +-- src/server/project.ts | 71 ++++++++++++------ src/server/typingsCache.ts | 16 ++-- .../reference/api/tsserverlibrary.d.ts | 5 +- 7 files changed, 153 insertions(+), 45 deletions(-) diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index c2b96649cf2..2cd39068de1 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -10,6 +10,7 @@ namespace ts { invalidateResolutionOfFile(filePath: Path): void; removeResolutionsOfFile(filePath: Path): void; + setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map): void; createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution; startCachingPerDirectoryResolution(): void; @@ -74,6 +75,7 @@ namespace ts { export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string, logChangesWhenResolvingModule: boolean): ResolutionCache { let filesWithChangedSetOfUnresolvedImports: Path[] | undefined; let filesWithInvalidatedResolutions: Map | undefined; + let filesWithInvalidatedNonRelativeUnresolvedImports: Map | undefined; let allFilesHaveInvalidatedResolution = false; const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory()); @@ -122,6 +124,7 @@ namespace ts { resolveTypeReferenceDirectives, removeResolutionsOfFile, invalidateResolutionOfFile, + setFilesWithInvalidatedNonRelativeUnresolvedImports, createHasInvalidatedResolution, updateTypeRootsWatch, closeTypeRootsWatch, @@ -173,7 +176,8 @@ namespace ts { } const collected = filesWithInvalidatedResolutions; filesWithInvalidatedResolutions = undefined; - return path => collected && collected.has(path); + return path => (collected && collected.has(path)) || + (filesWithInvalidatedNonRelativeUnresolvedImports && filesWithInvalidatedNonRelativeUnresolvedImports.has(path)); } function clearPerDirectoryResolutions() { @@ -184,6 +188,7 @@ namespace ts { function finishCachingPerDirectoryResolution() { allFilesHaveInvalidatedResolution = false; + filesWithInvalidatedNonRelativeUnresolvedImports = undefined; directoryWatchesOfFailedLookups.forEach((watcher, path) => { if (watcher.refCount === 0) { directoryWatchesOfFailedLookups.delete(path); @@ -237,13 +242,15 @@ namespace ts { const resolvedModules: R[] = []; const compilerOptions = resolutionHost.getCompilationSettings(); - + const hasInvalidatedNonRelativeUnresolvedImport = logChanges && filesWithInvalidatedNonRelativeUnresolvedImports && filesWithInvalidatedNonRelativeUnresolvedImports.has(path); const seenNamesInFile = createMap(); for (const name of names) { let resolution = resolutionsInFile.get(name); // Resolution is valid if it is present and not invalidated if (!seenNamesInFile.has(name) && - allFilesHaveInvalidatedResolution || !resolution || resolution.isInvalidated) { + allFilesHaveInvalidatedResolution || !resolution || resolution.isInvalidated || + // If the name is unresolved import that was invalidated, recalculate + (hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && !getResolutionWithResolvedFileName(resolution))) { const existingResolution = resolution; const resolutionInDirectory = perDirectoryResolution.get(name); if (resolutionInDirectory) { @@ -284,7 +291,7 @@ namespace ts { if (oldResolution === newResolution) { return true; } - if (!oldResolution || !newResolution || oldResolution.isInvalidated) { + if (!oldResolution || !newResolution) { return false; } const oldResult = getResolutionWithResolvedFileName(oldResolution); @@ -577,6 +584,11 @@ namespace ts { ); } + function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: Map) { + Debug.assert(filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined); + filesWithInvalidatedNonRelativeUnresolvedImports = filesMap; + } + function invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath: Path, isCreatingWatchedDirectory: boolean) { let isChangedFailedLookupLocation: (location: string) => boolean; if (isCreatingWatchedDirectory) { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index b167ed21b94..10912b4fb87 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -7294,7 +7294,6 @@ namespace ts.projectSystem { const host = createServerHost(files); const session = createSession(host); const projectService = session.getProjectService(); - debugger; session.executeCommandSeq({ command: protocol.CommandTypes.Open, arguments: { diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 5ec7a9d7a17..b6d5a20ef9a 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -1006,7 +1006,7 @@ namespace ts.projectSystem { installer.installAll(/*expectedCount*/ 1); }); - it("should recompute resolutions after typings are installed", () => { + it("cached unresolved typings are not recomputed if program structure did not change", () => { const host = createServerHost([]); const session = createSession(host); const f = { @@ -1048,7 +1048,7 @@ namespace ts.projectSystem { session.executeCommand(changeRequest); host.checkTimeoutQueueLengthAndRun(2); // This enqueues the updategraph and refresh inferred projects const version2 = proj.lastCachedUnresolvedImportsList; - assert.notEqual(version1, version2, "set of unresolved imports should change"); + assert.strictEqual(version1, version2, "set of unresolved imports should change"); }); it("expired cache entry (inferred project, should install typings)", () => { @@ -1621,4 +1621,75 @@ namespace ts.projectSystem { assert.deepEqual(commands, expectedCommands, "commands"); }); }); + + describe("recomputing resolutions of unresolved imports", () => { + const globalTypingsCacheLocation = "/tmp"; + const appPath = "/a/b/app.js" as Path; + const foooPath = "/a/b/node_modules/fooo/index.d.ts"; + function verifyResolvedModuleOfFooo(project: server.Project) { + const foooResolution = project.getLanguageService().getProgram().getSourceFileByPath(appPath).resolvedModules.get("fooo"); + assert.equal(foooResolution.resolvedFileName, foooPath); + return foooResolution; + } + + function verifyUnresolvedImportResolutions(appContents: string, typingNames: string[], typingFiles: FileOrFolder[]) { + const app: FileOrFolder = { + path: appPath, + content: `${appContents}import * as x from "fooo";` + }; + const fooo: FileOrFolder = { + path: foooPath, + content: `export var x: string;` + }; + const host = createServerHost([app, fooo]); + const installer = new (class extends Installer { + constructor() { + super(host, { globalTypingsCacheLocation, typesRegistry: createTypesRegistry("foo") }); + } + installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) { + executeCommand(this, host, typingNames, typingFiles, cb); + } + })(); + const projectService = createProjectService(host, { typingsInstaller: installer }); + projectService.openClientFile(app.path); + projectService.checkNumberOfProjects({ inferredProjects: 1 }); + + const proj = projectService.inferredProjects[0]; + checkProjectActualFiles(proj, [app.path, fooo.path]); + const foooResolution1 = verifyResolvedModuleOfFooo(proj); + + installer.installAll(/*expectedCount*/ 1); + host.checkTimeoutQueueLengthAndRun(2); + checkProjectActualFiles(proj, typingFiles.map(f => f.path).concat(app.path, fooo.path)); + const foooResolution2 = verifyResolvedModuleOfFooo(proj); + assert.strictEqual(foooResolution1, foooResolution2); + } + + it("correctly invalidate the resolutions with typing names", () => { + verifyUnresolvedImportResolutions('import * as a from "foo";', ["foo"], [{ + path: `${globalTypingsCacheLocation}/node_modules/foo/index.d.ts`, + content: "export function a(): void;" + }]); + }); + + it("correctly invalidate the resolutions with typing names that are trimmed", () => { + const fooAA: FileOrFolder = { + path: `${globalTypingsCacheLocation}/node_modules/foo/a/a.d.ts`, + content: "export function a (): void;" + }; + const fooAB: FileOrFolder = { + path: `${globalTypingsCacheLocation}/node_modules/foo/a/b.d.ts`, + content: "export function b (): void;" + }; + const fooAC: FileOrFolder = { + path: `${globalTypingsCacheLocation}/node_modules/foo/a/c.d.ts`, + content: "export function c (): void;" + }; + verifyUnresolvedImportResolutions(` + import * as a from "foo/a/a"; + import * as b from "foo/a/b"; + import * as c from "foo/a/c"; + `, ["foo"], [fooAA, fooAB, fooAC]); + }); + }); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index dc2fa086a93..1271b98c1c7 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -524,13 +524,13 @@ namespace ts.server { } switch (response.kind) { case ActionSet: - project.resolutionCache.clear(); - this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typeAcquisition, response.unresolvedImports, response.typings); + // Update the typing files and update the project + project.updateTypingFiles(this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typeAcquisition, response.unresolvedImports, response.typings)); break; case ActionInvalidate: - project.resolutionCache.clear(); - this.typingsCache.deleteTypingsForProject(response.projectName); - break; + // Do not clear resolution cache, there was changes detected in typings, so enque typing request and let it get us correct results + this.typingsCache.enqueueInstallTypingsForProject(project, project.lastCachedUnresolvedImportsList, /*forceRefresh*/ true); + return; } this.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project); } diff --git a/src/server/project.ts b/src/server/project.ts index 47bfb03a189..67469cc69d8 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -89,7 +89,18 @@ namespace ts.server { private plugins: PluginModule[] = []; /*@internal*/ + /** + * This is map from files to unresolved imports in it + * Maop does not contain entries for files that do not have unresolved imports + * This helps in containing the set of files to invalidate + */ cachedUnresolvedImportsPerFile = createMap>(); + + /** + * This is the set that has entry to true if file doesnt contain any unresolved import + */ + private filesWithNoUnresolvedImports = createMap(); + /*@internal*/ lastCachedUnresolvedImportsList: SortedReadonlyArray; /*@internal*/ @@ -143,7 +154,8 @@ namespace ts.server { /*@internal*/ hasChangedAutomaticTypeDirectiveNames = false; - private typingFiles: SortedReadonlyArray; + /*@internal*/ + typingFiles: SortedReadonlyArray = emptyArray; private readonly cancellationToken: ThrottledCancellationToken; @@ -554,6 +566,7 @@ namespace ts.server { this.resolutionCache.clear(); this.resolutionCache = undefined; this.cachedUnresolvedImportsPerFile = undefined; + this.filesWithNoUnresolvedImports = undefined; this.directoryStructureHost = undefined; // Clean up file watchers waiting for missing files @@ -714,6 +727,7 @@ namespace ts.server { else { this.resolutionCache.invalidateResolutionOfFile(info.path); } + this.filesWithNoUnresolvedImports.delete(info.path); this.cachedUnresolvedImportsPerFile.delete(info.path); if (detachFromProject) { @@ -735,16 +749,21 @@ namespace ts.server { } /* @internal */ - private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: Push, ambientModules: string[]) { + private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: string[] | undefined, ambientModules: string[]): string[] | undefined { + // No unresolve imports in this file + if (this.filesWithNoUnresolvedImports.has(file.path)) { + return result; + } + const cached = this.cachedUnresolvedImportsPerFile.get(file.path); if (cached) { // found cached result - use it and return for (const f of cached) { - result.push(f); + (result || (result = [])).push(f); } - return; + return result; } - let unresolvedImports: string[]; + let unresolvedImports: string[] | undefined; if (file.resolvedModules) { file.resolvedModules.forEach((resolvedModule, name) => { // pick unresolved non-relative names @@ -760,11 +779,17 @@ namespace ts.server { trimmed = trimmed.substr(0, i); } (unresolvedImports || (unresolvedImports = [])).push(trimmed); - result.push(trimmed); + (result || (result = [])).push(trimmed); } }); } - this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports || emptyArray); + if (unresolvedImports) { + this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports); + } + else { + this.filesWithNoUnresolvedImports.set(file.path, true); + } + return result; function isAmbientlyDeclaredModule(name: string) { return ambientModules.some(m => m === name); @@ -778,7 +803,7 @@ namespace ts.server { updateGraph(): boolean { this.resolutionCache.startRecordingFilesWithChangedResolutions(); - let hasChanges = this.updateGraphWorker(); + const hasChanges = this.updateGraphWorker(); const hasMoreOrLessScriptInfos = this.hasMoreOrLessScriptInfos; this.hasMoreOrLessScriptInfos = false; @@ -787,6 +812,7 @@ namespace ts.server { for (const file of changedFiles) { // delete cached information for changed files this.cachedUnresolvedImportsPerFile.delete(file); + this.filesWithNoUnresolvedImports.delete(file); } // update builder only if language service is enabled @@ -799,20 +825,15 @@ namespace ts.server { // (can reuse cached imports for files that were not changed) // 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch if (hasChanges || changedFiles.length) { - const result: string[] = []; + let result: string[] | undefined; const ambientModules = this.program.getTypeChecker().getAmbientModules().map(mod => stripQuotes(mod.getName())); for (const sourceFile of this.program.getSourceFiles()) { - this.extractUnresolvedImportsFromSourceFile(sourceFile, result, ambientModules); + result = this.extractUnresolvedImportsFromSourceFile(sourceFile, result, ambientModules); } - this.lastCachedUnresolvedImportsList = toDeduplicatedSortedArray(result); + this.lastCachedUnresolvedImportsList = result ? toDeduplicatedSortedArray(result) : emptyArray; } - const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasMoreOrLessScriptInfos); - if (!arrayIsEqualTo(this.typingFiles, cachedTypings)) { - this.typingFiles = cachedTypings; - this.markAsDirty(); - hasChanges = this.updateGraphWorker() || hasChanges; - } + this.projectService.typingsCache.enqueueInstallTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasMoreOrLessScriptInfos); } else { this.lastCachedUnresolvedImportsList = undefined; @@ -824,6 +845,13 @@ namespace ts.server { return !hasChanges; } + /*@internal*/ + updateTypingFiles(typingFiles: SortedReadonlyArray) { + this.typingFiles = typingFiles; + // Invalidate files with unresolved imports + this.resolutionCache.setFilesWithInvalidatedNonRelativeUnresolvedImports(this.cachedUnresolvedImportsPerFile); + } + /* @internal */ getCurrentProgram() { return this.program; @@ -959,15 +987,14 @@ namespace ts.server { setCompilerOptions(compilerOptions: CompilerOptions) { if (compilerOptions) { compilerOptions.allowNonTsExtensions = true; - if (changesAffectModuleResolution(this.compilerOptions, compilerOptions)) { - // reset cached unresolved imports if changes in compiler options affected module resolution - this.cachedUnresolvedImportsPerFile.clear(); - this.lastCachedUnresolvedImportsList = undefined; - } const oldOptions = this.compilerOptions; this.compilerOptions = compilerOptions; this.setInternalCompilerOptionsForEmittingJsFiles(); if (changesAffectModuleResolution(oldOptions, compilerOptions)) { + // reset cached unresolved imports if changes in compiler options affected module resolution + this.cachedUnresolvedImportsPerFile.clear(); + this.filesWithNoUnresolvedImports.clear(); + this.lastCachedUnresolvedImportsList = undefined; this.resolutionCache.clear(); } this.markAsDirty(); diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index 6418b5f9cbe..c255757481f 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -95,15 +95,14 @@ namespace ts.server { return this.installer.installPackage(options); } - getTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray, forceRefresh: boolean): SortedReadonlyArray { + enqueueInstallTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray, forceRefresh: boolean) { const typeAcquisition = project.getTypeAcquisition(); if (!typeAcquisition || !typeAcquisition.enable) { - return emptyArray; + return; } const entry = this.perProjectCache.get(project.getProjectName()); - const result: SortedReadonlyArray = entry ? entry.typings : emptyArray; if (forceRefresh || !entry || typeAcquisitionChanged(typeAcquisition, entry.typeAcquisition) || @@ -114,28 +113,25 @@ namespace ts.server { this.perProjectCache.set(project.getProjectName(), { compilerOptions: project.getCompilationSettings(), typeAcquisition, - typings: result, + typings: entry ? entry.typings : emptyArray, unresolvedImports, poisoned: true }); // something has been changed, issue a request to update typings this.installer.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports); } - return result; } updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray, newTypings: string[]) { + const typings = toSortedArray(newTypings); this.perProjectCache.set(projectName, { compilerOptions, typeAcquisition, - typings: toSortedArray(newTypings), + typings, unresolvedImports, poisoned: false }); - } - - deleteTypingsForProject(projectName: string) { - this.perProjectCache.delete(projectName); + return !typeAcquisition || !typeAcquisition.enable ? emptyArray : typings; } onProjectClosed(project: Project) { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 29959a89d02..53c23b1d382 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7643,6 +7643,10 @@ declare namespace ts.server { private externalFiles; private missingFilesMap; private plugins; + /** + * This is the set that has entry to true if file doesnt contain any unresolved import + */ + private filesWithNoUnresolvedImports; private lastFileExceededProgramSize; protected languageService: LanguageService; languageServiceEnabled: boolean; @@ -7673,7 +7677,6 @@ declare namespace ts.server { * This property is different from projectStructureVersion since in most cases edits don't affect set of files in the project */ private projectStateVersion; - private typingFiles; private readonly cancellationToken; isNonTsProject(): boolean; isJsOnlyProject(): boolean; From 82e9a7595b9c289875e15c706f608e913fce2fcb Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 13 Apr 2018 15:15:09 -0700 Subject: [PATCH 06/44] Invoked should be property on watchers map instead of local variable since watchers arent closed if they need to be reopened --- src/server/typingsInstaller/typingsInstaller.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 8d482241d1b..f79564f6f98 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -64,11 +64,13 @@ namespace ts.server.typingsInstaller { onRequestCompleted: RequestCompletedAction; } + type ProjectWatchers = Map & { isInvoked?: boolean; }; + export abstract class TypingsInstaller { private readonly packageNameToTypingLocation: Map = createMap(); private readonly missingTypingsSet: Map = createMap(); private readonly knownCachesSet: Map = createMap(); - private readonly projectWatchers = createMap>(); + private readonly projectWatchers = createMap(); private safeList: JsTyping.SafeList | undefined; readonly pendingRunRequests: PendingRequest[] = []; @@ -378,8 +380,8 @@ namespace ts.server.typingsInstaller { this.projectWatchers.set(projectName, watchers); } + watchers.isInvoked = false; // handler should be invoked once for the entire set of files since it will trigger full rediscovery of typings - let isInvoked = false; const isLoggingEnabled = this.log.isEnabled(); mutateMap( watchers, @@ -392,11 +394,11 @@ namespace ts.server.typingsInstaller { } const watcher = this.installTypingHost.watchFile(file, (f, eventKind) => { if (isLoggingEnabled) { - this.log.writeLine(`FileWatcher:: Triggered with ${f} eventKind: ${FileWatcherEventKind[eventKind]}:: WatchInfo: ${file}:: handler is already invoked '${isInvoked}'`); + this.log.writeLine(`FileWatcher:: Triggered with ${f} eventKind: ${FileWatcherEventKind[eventKind]}:: WatchInfo: ${file}:: handler is already invoked '${watchers.isInvoked}'`); } - if (!isInvoked) { + if (!watchers.isInvoked) { + watchers.isInvoked = true; this.sendResponse({ projectName, kind: ActionInvalidate }); - isInvoked = true; } }, /*pollingInterval*/ 2000); return isLoggingEnabled ? { From a60caba467597f2f49283a3735160f47adc9cd13 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 16 Apr 2018 11:22:24 -0700 Subject: [PATCH 07/44] In outliningElementsCollector, treat 'else if' as having same depth as the 'if' --- src/services/outliningElementsCollector.ts | 12 ++- .../fourslash/getOutliningSpansDepthElseIf.ts | 89 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/getOutliningSpansDepthElseIf.ts diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 90c53d47561..9d590ffef61 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -21,7 +21,17 @@ namespace ts.OutliningElementsCollector { if (span) out.push(span); depthRemaining--; - n.forEachChild(walk); + if (isIfStatement(n) && isIfStatement(n.elseStatement)) { + // Consider an 'else if' to be on the same depth as the 'if'. + walk(n.expression); + walk(n.thenStatement); + depthRemaining++; + walk(n.elseStatement); + depthRemaining--; + } + else { + n.forEachChild(walk); + } depthRemaining++; }); } diff --git a/tests/cases/fourslash/getOutliningSpansDepthElseIf.ts b/tests/cases/fourslash/getOutliningSpansDepthElseIf.ts new file mode 100644 index 00000000000..1e349e5b190 --- /dev/null +++ b/tests/cases/fourslash/getOutliningSpansDepthElseIf.ts @@ -0,0 +1,89 @@ +/// + +// Tests that each 'else if' does not count towards a higher nesting depth. + +////if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else if (1)[| { +//// 1; +////}|] else[| { +//// 1; +////}|] + +verify.outliningSpansInCurrentFile(test.ranges()); From 5ce6b6a2aea67c36f5a0820dd3cc24b2859b0865 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 16 Apr 2018 13:53:30 -0700 Subject: [PATCH 08/44] Convert gulp processDiagnostics builder to project compilation --- Gulpfile.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 1c6cfdd7ed1..26622f36fe8 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -362,20 +362,11 @@ const builtGeneratedDiagnosticMessagesJSON = path.join(builtLocalDirectory, "dia // processDiagnosticMessages script gulp.task(processDiagnosticMessagesJs, /*help*/ false, [], () => { - const settings: tsc.Settings = getCompilerSettings({ - target: "es5", - declaration: false, - removeComments: true, - noResolve: false, - stripInternal: false, - outFile: processDiagnosticMessagesJs - }, /*useBuiltCompiler*/ false); - return gulp.src(processDiagnosticMessagesTs) + const diagsProject = tsc.createProject('./scripts/processDiagnosticMessages.tsconfig.json'); + return diagsProject.src() .pipe(newer(processDiagnosticMessagesJs)) - .pipe(sourcemaps.init()) - .pipe(tsc(settings)) - .pipe(sourcemaps.write(".")) - .pipe(gulp.dest(".")); + .pipe(diagsProject()) + .pipe(gulp.dest(scriptsDirectory)); }); // The generated diagnostics map; built for the compiler and for the "generate-diagnostics" task From 1bafc49da07a970e728f2b553d68b8d41bb2f9c7 Mon Sep 17 00:00:00 2001 From: csigs Date: Mon, 16 Apr 2018 22:11:09 +0000 Subject: [PATCH 09/44] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ 4 files changed, 48 insertions(+) diff --git a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl index db17f37967b..0ef1aeed0cc 100644 --- a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3906,6 +3906,15 @@ + + + + + + + + + @@ -6018,6 +6027,9 @@ + + + diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 342918edc19..259d4b3b90e 100644 --- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3906,6 +3906,15 @@ + + + + + + + + + @@ -6018,6 +6027,9 @@ + + + diff --git a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl index a0f54926f3f..43d2f167e96 100644 --- a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3897,6 +3897,15 @@ + + + + + + + + + @@ -6009,6 +6018,9 @@ + + + diff --git a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl index b341aa7ea84..07d26979bf8 100644 --- a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3896,6 +3896,15 @@ + + + + + + + + + @@ -6008,6 +6017,9 @@ + + + From cae464058a5a36fb1de4988bd6e649260ef6ee74 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 16 Apr 2018 15:29:02 -0700 Subject: [PATCH 10/44] Delete "delete me" files (#23446) --- src/harness/tsconfig.json | 2 -- src/server/tsconfig.json | 2 -- src/server/tsconfig.library.json | 2 -- src/services/codefixes/fixes.ts | 1 - src/services/refactors/refactors.ts | 1 - src/services/tsconfig.json | 2 -- 6 files changed, 10 deletions(-) delete mode 100644 src/services/codefixes/fixes.ts delete mode 100644 src/services/refactors/refactors.ts diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 0d56447b28a..3ad653403cf 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -112,10 +112,8 @@ "../services/codefixes/fixInvalidImportSyntax.ts", "../services/codefixes/fixStrictClassInitialization.ts", "../services/codefixes/useDefaultImport.ts", - "../services/codefixes/fixes.ts", "../services/refactors/extractSymbol.ts", "../services/refactors/generateGetAccessorAndSetAccessor.ts", - "../services/refactors/refactors.ts", "../services/sourcemaps.ts", "../services/services.ts", "../services/breakpoints.ts", diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json index fe19cd418bd..dc732604ab6 100644 --- a/src/server/tsconfig.json +++ b/src/server/tsconfig.json @@ -108,10 +108,8 @@ "../services/codefixes/fixInvalidImportSyntax.ts", "../services/codefixes/fixStrictClassInitialization.ts", "../services/codefixes/useDefaultImport.ts", - "../services/codefixes/fixes.ts", "../services/refactors/extractSymbol.ts", "../services/refactors/generateGetAccessorAndSetAccessor.ts", - "../services/refactors/refactors.ts", "../services/sourcemaps.ts", "../services/services.ts", "../services/breakpoints.ts", diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json index 54353daf5c6..dd0196fab76 100644 --- a/src/server/tsconfig.library.json +++ b/src/server/tsconfig.library.json @@ -114,10 +114,8 @@ "../services/codefixes/fixInvalidImportSyntax.ts", "../services/codefixes/fixStrictClassInitialization.ts", "../services/codefixes/useDefaultImport.ts", - "../services/codefixes/fixes.ts", "../services/refactors/extractSymbol.ts", "../services/refactors/generateGetAccessorAndSetAccessor.ts", - "../services/refactors/refactors.ts", "../services/sourcemaps.ts", "../services/services.ts", "../services/breakpoints.ts", diff --git a/src/services/codefixes/fixes.ts b/src/services/codefixes/fixes.ts deleted file mode 100644 index bc845bd8dc0..00000000000 --- a/src/services/codefixes/fixes.ts +++ /dev/null @@ -1 +0,0 @@ -// Please delete me later. diff --git a/src/services/refactors/refactors.ts b/src/services/refactors/refactors.ts deleted file mode 100644 index bc845bd8dc0..00000000000 --- a/src/services/refactors/refactors.ts +++ /dev/null @@ -1 +0,0 @@ -// Please delete me later. diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index a54790df3be..31b35a44271 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -105,10 +105,8 @@ "codefixes/fixInvalidImportSyntax.ts", "codefixes/fixStrictClassInitialization.ts", "codefixes/useDefaultImport.ts", - "codefixes/fixes.ts", "refactors/extractSymbol.ts", "refactors/generateGetAccessorAndSetAccessor.ts", - "refactors/refactors.ts", "sourcemaps.ts", "services.ts", "breakpoints.ts", From 676983cb3e274870eff0d86d3f0f70121b4fcb4f Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Mon, 16 Apr 2018 13:26:21 -0700 Subject: [PATCH 11/44] Normalize paths to search for highlights --- src/server/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/session.ts b/src/server/session.ts index 1076d17e11f..ef2c9f92a83 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -793,7 +793,7 @@ namespace ts.server { private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); - const documentHighlights = project.getLanguageService().getDocumentHighlights(file, position, args.filesToSearch); + const documentHighlights = project.getLanguageService().getDocumentHighlights(file, position, map(args.filesToSearch, toNormalizedPath)); if (!documentHighlights) { return emptyArray; From 003c0a6743e1a0c41d7410b91c9cd6902a3f9397 Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Mon, 16 Apr 2018 15:58:25 -0700 Subject: [PATCH 12/44] Move normalization into services --- src/server/session.ts | 2 +- src/services/services.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/session.ts b/src/server/session.ts index ef2c9f92a83..1076d17e11f 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -793,7 +793,7 @@ namespace ts.server { private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); - const documentHighlights = project.getLanguageService().getDocumentHighlights(file, position, map(args.filesToSearch, toNormalizedPath)); + const documentHighlights = project.getLanguageService().getDocumentHighlights(file, position, args.filesToSearch); if (!documentHighlights) { return emptyArray; diff --git a/src/services/services.ts b/src/services/services.ts index bed301e452f..ab359c35323 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1673,6 +1673,7 @@ namespace ts { } function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray): DocumentHighlights[] { + filesToSearch = ts.map(filesToSearch, ts.normalizePath); Debug.assert(contains(filesToSearch, fileName)); synchronizeHostData(); const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f))); From e9d6f788ca64202a3fcefeb4b1aeee4b119ac1f2 Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Mon, 16 Apr 2018 15:58:39 -0700 Subject: [PATCH 13/44] Add test --- src/harness/fourslash.ts | 7 ++++++- src/harness/virtualFileSystem.ts | 2 +- tests/cases/fourslash/documentHighlights_windowsPath.ts | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/documentHighlights_windowsPath.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index b479b5596a8..f85f4b443de 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2910,6 +2910,11 @@ Actual: ${stringify(fullActual)}`); } private verifyDocumentHighlights(expectedRanges: Range[], fileNames: ReadonlyArray = [this.activeFile.fileName]) { + expectedRanges = ts.map(expectedRanges, r => { + r.fileName = ts.normalizePath(r.fileName); + return r; + }); + fileNames = ts.map(fileNames, ts.normalizePath); const documentHighlights = this.getDocumentHighlightsAtCurrentPosition(fileNames) || []; for (const dh of documentHighlights) { @@ -3219,7 +3224,7 @@ Actual: ${stringify(fullActual)}`); } } else if (ts.isString(indexOrName)) { - let name = indexOrName; + let name = ts.normalizePath(indexOrName); // names are stored in the compiler with this relative path, this allows people to use goTo.file on just the fileName name = name.indexOf("/") === -1 ? (this.basePath + "/" + name) : name; diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 16267a092fc..698f99616ca 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -125,7 +125,7 @@ namespace Utils { addFile(path: string, content?: Harness.LanguageService.ScriptInfo) { const absolutePath = ts.normalizePath(ts.getNormalizedAbsolutePath(path, this.currentDirectory)); - const fileName = ts.getBaseFileName(path); + const fileName = ts.getBaseFileName(absolutePath); const directoryPath = ts.getDirectoryPath(absolutePath); const directory = this.addDirectory(directoryPath); return directory ? directory.addFile(fileName, content) : undefined; diff --git a/tests/cases/fourslash/documentHighlights_windowsPath.ts b/tests/cases/fourslash/documentHighlights_windowsPath.ts new file mode 100644 index 00000000000..594f175745b --- /dev/null +++ b/tests/cases/fourslash/documentHighlights_windowsPath.ts @@ -0,0 +1,7 @@ +/// + +//@Filename: C:\a\b\c.ts +////var /*1*/[|x|] = 1; + +const range = test.ranges()[0]; +verify.documentHighlightsOf(range, [range], { filesToSearch: [range.fileName] }); \ No newline at end of file From 606492b6773b96a11e92d8fa7b3f41322d3638ea Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Mon, 16 Apr 2018 16:20:12 -0700 Subject: [PATCH 14/44] Fix lint error and normalization issue --- src/harness/fourslash.ts | 2 +- src/services/services.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index f85f4b443de..d2b389c4373 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -3231,7 +3231,7 @@ Actual: ${stringify(fullActual)}`); const availableNames: string[] = []; const result = ts.forEach(this.testData.files, file => { - const fn = file.fileName; + const fn = ts.normalizePath(file.fileName); if (fn) { if (fn === name) { return file; diff --git a/src/services/services.ts b/src/services/services.ts index ab359c35323..bceb7f572af 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1673,7 +1673,7 @@ namespace ts { } function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray): DocumentHighlights[] { - filesToSearch = ts.map(filesToSearch, ts.normalizePath); + filesToSearch = map(filesToSearch, normalizePath); Debug.assert(contains(filesToSearch, fileName)); synchronizeHostData(); const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f))); From 40fd6aebdcac8ecf730517bd5005591eef2f4bff Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 16 Apr 2018 16:46:27 -0700 Subject: [PATCH 15/44] Shrink span for convert-to-es6-module suggestion (#23441) --- src/services/suggestionDiagnostics.ts | 6 +++++- .../fourslash/refactorConvertToEs6Module_export_named.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/services/suggestionDiagnostics.ts b/src/services/suggestionDiagnostics.ts index 1fc1d671e43..d9f55a78d82 100644 --- a/src/services/suggestionDiagnostics.ts +++ b/src/services/suggestionDiagnostics.ts @@ -6,7 +6,7 @@ namespace ts { const diags: Diagnostic[] = []; if (sourceFile.commonJsModuleIndicator) { - diags.push(createDiagnosticForNode(sourceFile.commonJsModuleIndicator, Diagnostics.File_is_a_CommonJS_module_it_may_be_converted_to_an_ES6_module)); + diags.push(createDiagnosticForNode(getErrorNodeFromCommonJsIndicator(sourceFile.commonJsModuleIndicator), Diagnostics.File_is_a_CommonJS_module_it_may_be_converted_to_an_ES6_module)); } const isJsFile = isSourceFileJavaScript(sourceFile); @@ -61,4 +61,8 @@ namespace ts { return undefined; } } + + function getErrorNodeFromCommonJsIndicator(commonJsModuleIndicator: Node): Node { + return isBinaryExpression(commonJsModuleIndicator) ? commonJsModuleIndicator.left : commonJsModuleIndicator; + } } diff --git a/tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts b/tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts index 60d2436ddb0..a748b9844a6 100644 --- a/tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts +++ b/tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts @@ -3,7 +3,7 @@ // @allowJs: true // @Filename: /a.js -////[|exports.f = function() {}|]; +////[|exports.f|] = function() {}; ////exports.C = class {}; ////exports.x = 0; ////exports.a1 = () => {}; From 1e6e679a539c475dd7a7caa1b0f13dbf873f3a00 Mon Sep 17 00:00:00 2001 From: csigs Date: Tue, 17 Apr 2018 04:10:48 +0000 Subject: [PATCH 16/44] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl index 52881bd0f4e..d3663b384db 100644 --- a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3906,6 +3906,15 @@ + + + + + + + + + @@ -6018,6 +6027,9 @@ + + + diff --git a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl index 4b42ff8b2bf..0e70ff04cca 100644 --- a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3897,6 +3897,15 @@ + + + + + + + + + @@ -6009,6 +6018,9 @@ + + + diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl index 1ac1d8a83e6..72116417c1b 100644 --- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3890,6 +3890,15 @@ + + + + + + + + + @@ -6002,6 +6011,9 @@ + + + From 9dff614bd2fb6ec41ed3b1603a2879b761e0f4b4 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 17 Apr 2018 01:47:43 -0700 Subject: [PATCH 17/44] Combined the "Found X error(s)" and "Compilation" complete --watch messages --- src/compiler/diagnosticMessages.json | 8 ++------ src/compiler/watch.ts | 11 +++++------ src/harness/unittests/tscWatchMode.ts | 10 ++++------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c665744b1ea..235788258af 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2943,10 +2943,6 @@ "category": "Message", "code": 6040 }, - "Compilation complete. Watching for file changes.": { - "category": "Message", - "code": 6042 - }, "Generates corresponding '.map' file.": { "category": "Message", "code": 6043 @@ -3522,11 +3518,11 @@ "code": 6192, "reportsUnnecessary": true }, - "Found 1 error.": { + "Found 1 error. Watching for file changes.": { "category": "Message", "code": 6193 }, - "Found {0} errors.": { + "Found {0} errors. Watching for file changes.": { "category": "Message", "code": 6194 }, diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 7f3d078b790..37aa0118801 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -29,9 +29,8 @@ namespace ts { /** @internal */ export const nonClearingMessageCodes: number[] = [ - Diagnostics.Compilation_complete_Watching_for_file_changes.code, - Diagnostics.Found_1_error.code, - Diagnostics.Found_0_errors.code + Diagnostics.Found_1_error_Watching_for_file_changes.code, + Diagnostics.Found_0_errors_Watching_for_file_changes.code ]; function clearScreenIfNotWatchingForFileChanges(system: System, diagnostic: Diagnostic, options: CompilerOptions) { @@ -231,10 +230,10 @@ namespace ts { const reportSummary = (errorCount: number) => { if (errorCount === 1) { - onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_1_error, errorCount), newLine, compilerOptions); + onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions); } else { - onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_0_errors, errorCount, errorCount), newLine, compilerOptions); + onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions); } }; @@ -644,7 +643,7 @@ namespace ts { if (host.afterProgramCreate) { host.afterProgramCreate(builderProgram); } - reportWatchDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes); + return builderProgram; } diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 99bad3e3091..c8faba855dc 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -130,8 +130,8 @@ namespace ts.tscWatch { function createErrorsFoundCompilerDiagnostic(errors: ReadonlyArray) { return errors.length === 1 - ? createCompilerDiagnostic(Diagnostics.Found_1_error) - : createCompilerDiagnostic(Diagnostics.Found_0_errors, errors.length); + ? createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes) + : createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errors.length); } function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) { @@ -142,8 +142,7 @@ namespace ts.tscWatch { logsBeforeErrors, errors, disableConsoleClears, - createErrorsFoundCompilerDiagnostic(errors), - createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); + createErrorsFoundCompilerDiagnostic(errors)); } function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) { @@ -154,8 +153,7 @@ namespace ts.tscWatch { logsBeforeErrors, errors, disableConsoleClears, - createErrorsFoundCompilerDiagnostic(errors), - createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); + createErrorsFoundCompilerDiagnostic(errors)); } function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) { From e35336c1182360f0ed4cc2bc987f858abfc3372d Mon Sep 17 00:00:00 2001 From: csigs Date: Tue, 17 Apr 2018 10:10:39 +0000 Subject: [PATCH 18/44] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl index 473da1a4a01..3252fe24f15 100644 --- a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3887,6 +3887,15 @@ + + + + + + + + + @@ -5996,6 +6005,9 @@ + + + From bc285aa9a1ce8599d38dcf00583028e216f08c0d Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 07:53:43 -0700 Subject: [PATCH 19/44] findAllReferences: Mark *every* search symbol as seen, not just search.symbol (#23451) --- src/services/findAllReferences.ts | 26 ++++++++++--------- .../findAllReferencesDynamicImport3.ts | 16 ++++++------ 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 7c5922035ea..268af57e5a3 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -424,7 +424,8 @@ namespace ts.FindAllReferences.Core { readonly text: string; readonly escapedText: __String; /** Only set if `options.implementations` is true. These are the symbols checked to get the implementations of a property access. */ - readonly parents: Symbol[] | undefined; + readonly parents: ReadonlyArray | undefined; + readonly allSearchSymbols: ReadonlyArray; /** * Whether a symbol is in the search set. @@ -500,14 +501,11 @@ namespace ts.FindAllReferences.Core { // here appears to be intentional). const { text = stripQuotes(unescapeLeadingUnderscores((getLocalSymbolForExportDefault(symbol) || symbol).escapedName)), - allSearchSymbols, + allSearchSymbols = [symbol], } = searchOptions; const escapedText = escapeLeadingUnderscores(text); const parents = this.options.implementations && getParentSymbolsOfPropertyAccess(location, symbol, this.checker); - return { - symbol, comingFrom, text, escapedText, parents, - includes: referenceSymbol => allSearchSymbols ? contains(allSearchSymbols, referenceSymbol) : referenceSymbol === symbol, - }; + return { symbol, comingFrom, text, escapedText, parents, allSearchSymbols, includes: sym => contains(allSearchSymbols, sym) }; } private readonly symbolIdToReferences: Entry[][] = []; @@ -534,13 +532,17 @@ namespace ts.FindAllReferences.Core { } // Source file ID → symbol ID → Whether the symbol has been searched for in the source file. - private readonly sourceFileToSeenSymbols: true[][] = []; + private readonly sourceFileToSeenSymbols: Map[] = []; /** Returns `true` the first time we search for a symbol in a file and `false` afterwards. */ - markSearchedSymbol(sourceFile: SourceFile, symbol: Symbol): boolean { + markSearchedSymbols(sourceFile: SourceFile, symbols: ReadonlyArray): boolean { const sourceId = getNodeId(sourceFile); - const symbolId = getSymbolId(symbol); - const seenSymbols = this.sourceFileToSeenSymbols[sourceId] || (this.sourceFileToSeenSymbols[sourceId] = []); - return !seenSymbols[symbolId] && (seenSymbols[symbolId] = true); + const seenSymbols = this.sourceFileToSeenSymbols[sourceId] || (this.sourceFileToSeenSymbols[sourceId] = createMap()); + + let anyNewSymbols = false; + for (const sym of symbols) { + anyNewSymbols = addToSeen(seenSymbols, getSymbolId(sym)) || anyNewSymbols; + } + return anyNewSymbols; } } @@ -804,7 +806,7 @@ namespace ts.FindAllReferences.Core { * searchLocation: a node where the search value */ function getReferencesInContainer(container: Node, sourceFile: SourceFile, search: Search, state: State, addReferencesHere: boolean): void { - if (!state.markSearchedSymbol(sourceFile, search.symbol)) { + if (!state.markSearchedSymbols(sourceFile, search.allSearchSymbols)) { return; } diff --git a/tests/cases/fourslash/findAllReferencesDynamicImport3.ts b/tests/cases/fourslash/findAllReferencesDynamicImport3.ts index 21d2e1097db..636e640ceb3 100644 --- a/tests/cases/fourslash/findAllReferencesDynamicImport3.ts +++ b/tests/cases/fourslash/findAllReferencesDynamicImport3.ts @@ -1,13 +1,13 @@ /// // @Filename: foo.ts -//// export function [|bar|]() { return "bar"; } - -//// import('./foo').then(({ [|bar|] }) => undefined); +////export function [|{| "isWriteAccess": true, "isDefinition": true |}bar|]() { return "bar"; } +////import('./foo').then(({ [|{| "isWriteAccess": true, "isDefinition": true |}bar|] }) => undefined); const [r0, r1] = test.ranges(); -// This is because bindingElement at r1 are both name and value -verify.referencesOf(r0, [r1, r0, r1, r0]); -verify.referencesOf(r1, [r0, r1, r1, r0]); -verify.renameLocations(r0, [r0, r1]); -verify.renameLocations(r1, [r1, r0, r0, r1]); \ No newline at end of file +verify.referenceGroups(r0, [{ definition: "function bar(): string", ranges: [r0, r1] }]); +verify.referenceGroups(r1, [ + { definition: "function bar(): string", ranges: [r0] }, + { definition: "var bar: () => string", ranges: [r1] }, +]); +verify.rangesAreRenameLocations(); From 55f9a6ffc260798c87ab4420549456a9b28c8f02 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 08:59:07 -0700 Subject: [PATCH 20/44] Fix bug: VariableDeclaration initiazer may be undefined (#23439) --- src/services/codefixes/convertToEs6Module.ts | 32 +++++++++---------- ...orConvertToEs6Module_missingInitializer.ts | 14 ++++++++ 2 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 tests/cases/fourslash/refactorConvertToEs6Module_missingInitializer.ts diff --git a/src/services/codefixes/convertToEs6Module.ts b/src/services/codefixes/convertToEs6Module.ts index 8ffba3218c0..a8df21dc96d 100644 --- a/src/services/codefixes/convertToEs6Module.ts +++ b/src/services/codefixes/convertToEs6Module.ts @@ -130,23 +130,23 @@ namespace ts.codefix { let foundImport = false; const newNodes = flatMap(declarationList.declarations, decl => { const { name, initializer } = decl; - if (isExportsOrModuleExportsOrAlias(sourceFile, initializer)) { - // `const alias = module.exports;` can be removed. - foundImport = true; - return []; - } - if (isRequireCall(initializer, /*checkArgumentIsStringLiteralLike*/ true)) { - foundImport = true; - return convertSingleImport(sourceFile, name, initializer.arguments[0], changes, checker, identifiers, target); - } - else if (isPropertyAccessExpression(initializer) && isRequireCall(initializer.expression, /*checkArgumentIsStringLiteralLike*/ true)) { - foundImport = true; - return convertPropertyAccessImport(name, initializer.name.text, initializer.expression.arguments[0], identifiers); - } - else { - // Move it out to its own variable statement. (This will not be used if `!foundImport`) - return createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([decl], declarationList.flags)); + if (initializer) { + if (isExportsOrModuleExportsOrAlias(sourceFile, initializer)) { + // `const alias = module.exports;` can be removed. + foundImport = true; + return []; + } + else if (isRequireCall(initializer, /*checkArgumentIsStringLiteralLike*/ true)) { + foundImport = true; + return convertSingleImport(sourceFile, name, initializer.arguments[0], changes, checker, identifiers, target); + } + else if (isPropertyAccessExpression(initializer) && isRequireCall(initializer.expression, /*checkArgumentIsStringLiteralLike*/ true)) { + foundImport = true; + return convertPropertyAccessImport(name, initializer.name.text, initializer.expression.arguments[0], identifiers); + } } + // Move it out to its own variable statement. (This will not be used if `!foundImport`) + return createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([decl], declarationList.flags)); }); if (foundImport) { // useNonAdjustedEndPosition to ensure we don't eat the newline after the statement. diff --git a/tests/cases/fourslash/refactorConvertToEs6Module_missingInitializer.ts b/tests/cases/fourslash/refactorConvertToEs6Module_missingInitializer.ts new file mode 100644 index 00000000000..77c4ce0fc43 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertToEs6Module_missingInitializer.ts @@ -0,0 +1,14 @@ +/// + +// @allowJs: true + +// @Filename: /a.js +////require("m"); +////let x; x; + +verify.codeFix({ + description: "Convert to ES6 module", + newFileContent: +`import "m"; +let x; x;` +}); From e50b24a83beec5d4c1f81b9dcb8ea66534b20e49 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 09:15:05 -0700 Subject: [PATCH 21/44] convertFunctionToEs6Class: Copy comment from variable declaration (#23445) --- .../codefixes/convertFunctionToEs6Class.ts | 3 ++- ...ionToEs6Class_commentOnVariableDeclaration.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/convertFunctionToEs6Class_commentOnVariableDeclaration.ts diff --git a/src/services/codefixes/convertFunctionToEs6Class.ts b/src/services/codefixes/convertFunctionToEs6Class.ts index bf20aeda72b..d137785a7e7 100644 --- a/src/services/codefixes/convertFunctionToEs6Class.ts +++ b/src/services/codefixes/convertFunctionToEs6Class.ts @@ -34,13 +34,14 @@ namespace ts.codefix { case SyntaxKind.VariableDeclaration: precedingNode = ctorDeclaration.parent.parent; + newClassDeclaration = createClassFromVariableDeclaration(ctorDeclaration as VariableDeclaration); if ((ctorDeclaration.parent).declarations.length === 1) { + copyComments(precedingNode, newClassDeclaration, sourceFile); deleteNode(precedingNode); } else { deleteNode(ctorDeclaration, /*inList*/ true); } - newClassDeclaration = createClassFromVariableDeclaration(ctorDeclaration as VariableDeclaration); break; } diff --git a/tests/cases/fourslash/convertFunctionToEs6Class_commentOnVariableDeclaration.ts b/tests/cases/fourslash/convertFunctionToEs6Class_commentOnVariableDeclaration.ts new file mode 100644 index 00000000000..417bf0bc905 --- /dev/null +++ b/tests/cases/fourslash/convertFunctionToEs6Class_commentOnVariableDeclaration.ts @@ -0,0 +1,16 @@ +/// + +// @allowJs: true +// @Filename: /a.js +/////** Doc */ +////const C = function() { this.x = 0; } + +verify.codeFix({ + description: "Convert function to an ES2015 class", + newFileContent: +`/** Doc */ +class C { + constructor() { this.x = 0; } +} +`, +}); From 3a8d66ba29202030324c3f8a96fcdfcbbeca5190 Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Tue, 17 Apr 2018 09:20:34 -0700 Subject: [PATCH 22/44] Only normalize for purposes of assert --- src/services/services.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index bceb7f572af..11ccee93d6c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1673,8 +1673,7 @@ namespace ts { } function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray): DocumentHighlights[] { - filesToSearch = map(filesToSearch, normalizePath); - Debug.assert(contains(filesToSearch, fileName)); + Debug.assert(contains(map(filesToSearch, normalizePath), fileName)); synchronizeHostData(); const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f))); const sourceFile = getValidSourceFile(fileName); From 160356317b0bd475669e80a00fa4e7a72476a0ba Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Tue, 17 Apr 2018 09:20:52 -0700 Subject: [PATCH 23/44] Treat expectedRanges as readonly --- src/harness/fourslash.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index d2b389c4373..8800f78e6fd 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2910,10 +2910,6 @@ Actual: ${stringify(fullActual)}`); } private verifyDocumentHighlights(expectedRanges: Range[], fileNames: ReadonlyArray = [this.activeFile.fileName]) { - expectedRanges = ts.map(expectedRanges, r => { - r.fileName = ts.normalizePath(r.fileName); - return r; - }); fileNames = ts.map(fileNames, ts.normalizePath); const documentHighlights = this.getDocumentHighlightsAtCurrentPosition(fileNames) || []; @@ -2924,7 +2920,7 @@ Actual: ${stringify(fullActual)}`); } for (const fileName of fileNames) { - const expectedRangesInFile = expectedRanges.filter(r => r.fileName === fileName); + const expectedRangesInFile = expectedRanges.filter(r => ts.normalizePath(r.fileName) === fileName); const highlights = ts.find(documentHighlights, dh => dh.fileName === fileName); const spansInFile = highlights ? highlights.highlightSpans.sort((s1, s2) => s1.textSpan.start - s2.textSpan.start) : []; From 12f6e5232b4ca8b0a7b64fa328e46ac7099aeff1 Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Tue, 17 Apr 2018 09:41:26 -0700 Subject: [PATCH 24/44] Remove unnecessary allocation --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index 11ccee93d6c..a192231405a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1673,7 +1673,7 @@ namespace ts { } function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray): DocumentHighlights[] { - Debug.assert(contains(map(filesToSearch, normalizePath), fileName)); + Debug.assert(filesToSearch.some(f => normalizePath(f) === fileName)); synchronizeHostData(); const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f))); const sourceFile = getValidSourceFile(fileName); From d009d8efcffc85a0846b6c7008fc54f5bd635ab3 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 10:41:53 -0700 Subject: [PATCH 25/44] Fix bug: IfStatement#elseStatement can be undefined (#23467) --- src/services/outliningElementsCollector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 9d590ffef61..65888305d81 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -21,7 +21,7 @@ namespace ts.OutliningElementsCollector { if (span) out.push(span); depthRemaining--; - if (isIfStatement(n) && isIfStatement(n.elseStatement)) { + if (isIfStatement(n) && n.elseStatement && isIfStatement(n.elseStatement)) { // Consider an 'else if' to be on the same depth as the 'if'. walk(n.expression); walk(n.thenStatement); From db68075b670cc78a0db5ff8a62ab1e56a13aac25 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 17 Apr 2018 11:21:11 -0700 Subject: [PATCH 26/44] Add create-react-app and puppeteer user tests (#23471) * Add create-react-app * Add puppeteer Everything is broken now. I am going back to master to see if that fixes things. * Add puppeteer baseline * Add chalk override (gitignore was ignoring it) --- .gitmodules | 7 ++ src/harness/externalCompileRunner.ts | 2 +- .../reference/user/create-react-app.log | 79 ++++++++++++ tests/baselines/reference/user/puppeteer.log | 40 ++++++ tests/baselines/reference/user/uglify-js.log | 119 +++++++++--------- .../user/create-react-app/chalk-override.d.ts | 1 + .../user/create-react-app/create-react-app | 1 + tests/cases/user/create-react-app/test.json | 3 + .../cases/user/create-react-app/tsconfig.json | 20 +++ tests/cases/user/puppeteer/puppeteer | 1 + tests/cases/user/puppeteer/test.json | 3 + 11 files changed, 216 insertions(+), 60 deletions(-) create mode 100644 tests/baselines/reference/user/create-react-app.log create mode 100644 tests/baselines/reference/user/puppeteer.log create mode 100644 tests/cases/user/create-react-app/chalk-override.d.ts create mode 160000 tests/cases/user/create-react-app/create-react-app create mode 100644 tests/cases/user/create-react-app/test.json create mode 100644 tests/cases/user/create-react-app/tsconfig.json create mode 160000 tests/cases/user/puppeteer/puppeteer create mode 100644 tests/cases/user/puppeteer/test.json diff --git a/.gitmodules b/.gitmodules index f7632c4abbd..ccb2be81520 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,7 +18,14 @@ path = tests/cases/user/TypeScript-WeChat-Starter/TypeScript-WeChat-Starter url = https://github.com/Microsoft/TypeScript-WeChat-Starter.git ignore = all +[submodule "tests/cases/user/create-react-app/create-react-app"] + path = tests/cases/user/create-react-app/create-react-app + url = https://github.com/facebook/create-react-app.git + ignore = all [submodule "tests/cases/user/webpack/webpack"] path = tests/cases/user/webpack/webpack url = https://github.com/webpack/webpack.git ignore = all +[submodule "tests/cases/user/puppeteer/puppeteer"] + path = tests/cases/user/puppeteer/puppeteer + url = https://github.com/GoogleChrome/puppeteer.git diff --git a/src/harness/externalCompileRunner.ts b/src/harness/externalCompileRunner.ts index 1f945cfcc9c..3d07c69af85 100644 --- a/src/harness/externalCompileRunner.ts +++ b/src/harness/externalCompileRunner.ts @@ -66,7 +66,7 @@ abstract class ExternalCompileRunnerBase extends RunnerBase { if (fs.existsSync(path.join(cwd, "node_modules"))) { require("del").sync(path.join(cwd, "node_modules"), { force: true }); } - const install = cp.spawnSync(`npm`, ["i"], { cwd, timeout: timeout / 2, shell: true, stdio }); // NPM shouldn't take the entire timeout - if it takes a long time, it should be terminated and we should log the failure + const install = cp.spawnSync(`npm`, ["i", "--ignore-scripts"], { cwd, timeout: timeout / 2, shell: true, stdio }); // NPM shouldn't take the entire timeout - if it takes a long time, it should be terminated and we should log the failure if (install.status !== 0) throw new Error(`NPM Install for ${directoryName} failed: ${install.stderr.toString()}`); } const args = [path.join(__dirname, "tsc.js")]; diff --git a/tests/baselines/reference/user/create-react-app.log b/tests/baselines/reference/user/create-react-app.log new file mode 100644 index 00000000000..2fefa8b230e --- /dev/null +++ b/tests/baselines/reference/user/create-react-app.log @@ -0,0 +1,79 @@ +Exit Code: 1 +Standard output: +packages/babel-preset-react-app/dependencies.js(38,17): error TS2307: Cannot find module '@babel/preset-env'. +packages/babel-preset-react-app/dependencies.js(49,17): error TS2307: Cannot find module '@babel/preset-env'. +packages/babel-preset-react-app/index.js(52,17): error TS2307: Cannot find module '@babel/preset-env'. +packages/babel-preset-react-app/index.js(61,17): error TS2307: Cannot find module '@babel/preset-env'. +packages/babel-preset-react-app/index.js(74,17): error TS2307: Cannot find module '@babel/preset-react'. +packages/babel-preset-react-app/index.js(84,33): error TS2307: Cannot find module '@babel/preset-flow'. +packages/babel-preset-react-app/index.js(89,15): error TS2307: Cannot find module 'babel-plugin-macros'. +packages/babel-preset-react-app/index.js(93,15): error TS2307: Cannot find module '@babel/plugin-transform-destructuring'. +packages/babel-preset-react-app/index.js(98,17): error TS2307: Cannot find module '@babel/plugin-proposal-class-properties'. +packages/babel-preset-react-app/index.js(107,17): error TS2307: Cannot find module '@babel/plugin-proposal-object-rest-spread'. +packages/babel-preset-react-app/index.js(114,17): error TS2307: Cannot find module '@babel/plugin-transform-runtime'. +packages/babel-preset-react-app/index.js(123,17): error TS2307: Cannot find module 'babel-plugin-transform-react-remove-prop-types'. +packages/babel-preset-react-app/index.js(130,17): error TS2307: Cannot find module '@babel/plugin-transform-regenerator'. +packages/babel-preset-react-app/index.js(137,15): error TS2307: Cannot find module '@babel/plugin-syntax-dynamic-import'. +packages/babel-preset-react-app/index.js(140,17): error TS2307: Cannot find module 'babel-plugin-transform-dynamic-import'. +packages/confusing-browser-globals/test.js(14,1): error TS2304: Cannot find name 'it'. +packages/confusing-browser-globals/test.js(15,3): error TS2304: Cannot find name 'expect'. +packages/confusing-browser-globals/test.js(18,1): error TS2304: Cannot find name 'it'. +packages/confusing-browser-globals/test.js(19,3): error TS2304: Cannot find name 'expect'. +packages/create-react-app/createReactApp.js(37,37): error TS2307: Cannot find module 'validate-npm-package-name'. +packages/create-react-app/createReactApp.js(47,24): error TS2307: Cannot find module 'tar-pack'. +packages/create-react-app/createReactApp.js(49,28): error TS2307: Cannot find module 'hyperquest'. +packages/create-react-app/createReactApp.js(50,25): error TS2307: Cannot find module 'envinfo'. +packages/create-react-app/createReactApp.js(52,30): error TS2307: Cannot find module 'react-dev-utils/workspaceUtils'. +packages/create-react-app/createReactApp.js(53,29): error TS2307: Cannot find module './package.json'. +packages/create-react-app/createReactApp.js(771,20): error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'. + Type 'undefined' is not assignable to type 'string'. +packages/create-react-app/index.js(45,5): error TS2365: Operator '<' cannot be applied to types 'string' and 'number'. +packages/eslint-config-react-app/index.js(24,33): error TS2307: Cannot find module 'confusing-browser-globals'. +packages/react-dev-utils/FileSizeReporter.js(13,24): error TS2307: Cannot find module 'filesize'. +packages/react-dev-utils/FileSizeReporter.js(14,25): error TS2307: Cannot find module 'recursive-readdir'. +packages/react-dev-utils/FileSizeReporter.js(16,24): error TS2307: Cannot find module 'gzip-size'. +packages/react-dev-utils/WebpackDevServerUtils.js(9,25): error TS2307: Cannot find module 'address'. +packages/react-dev-utils/WebpackDevServerUtils.js(14,24): error TS2307: Cannot find module 'detect-port-alt'. +packages/react-dev-utils/WebpackDevServerUtils.js(15,24): error TS2307: Cannot find module 'is-root'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(12,1): error TS2304: Cannot find name 'describe'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(13,3): error TS2304: Cannot find name 'it'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(18,5): error TS2304: Cannot find name 'expect'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(19,5): error TS2304: Cannot find name 'expect'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(22,3): error TS2304: Cannot find name 'it'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(26,5): error TS2304: Cannot find name 'expect'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(29,3): error TS2304: Cannot find name 'it'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(36,5): error TS2304: Cannot find name 'expect'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(37,5): error TS2304: Cannot find name 'expect'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(40,3): error TS2304: Cannot find name 'it'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(46,5): error TS2304: Cannot find name 'expect'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(49,3): error TS2304: Cannot find name 'it'. +packages/react-dev-utils/__tests__/ignoredFiles.test.js(53,5): error TS2304: Cannot find name 'expect'. +packages/react-dev-utils/browsersHelper.js(9,30): error TS2307: Cannot find module 'browserslist'. +packages/react-dev-utils/browsersHelper.js(13,23): error TS2307: Cannot find module 'pkg-up'. +packages/react-dev-utils/browsersHelper.js(59,22): error TS2554: Expected 1 arguments, but got 0. +packages/react-dev-utils/browsersHelper.js(61,36): error TS2345: Argument of type 'Buffer' is not assignable to parameter of type 'string'. +packages/react-dev-utils/browsersHelper.js(97,25): error TS2538: Type 'undefined' cannot be used as an index type. +packages/react-dev-utils/checkRequiredFiles.js(19,34): error TS2339: Property 'F_OK' does not exist on type 'typeof import("fs")'. +packages/react-dev-utils/checkRequiredFiles.js(23,32): error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'. +packages/react-dev-utils/checkRequiredFiles.js(24,34): error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'. +packages/react-dev-utils/getProcessForPort.js(29,6): error TS2339: Property 'split' does not exist on type 'Buffer'. +packages/react-dev-utils/getProcessForPort.js(49,21): error TS2339: Property 'replace' does not exist on type 'Buffer'. +packages/react-dev-utils/getProcessForPort.js(63,5): error TS2339: Property 'trim' does not exist on type 'Buffer'. +packages/react-dev-utils/openBrowser.js(13,19): error TS2307: Cannot find module 'opn'. +packages/react-dev-utils/webpackHotDevClient.js(19,22): error TS2307: Cannot find module 'sockjs-client'. +packages/react-dev-utils/webpackHotDevClient.js(24,28): error TS2307: Cannot find module 'react-error-overlay'. +packages/react-dev-utils/webpackHotDevClient.js(31,14): error TS2339: Property 'encodeURIComponent' does not exist on type 'Window'. +packages/react-dev-utils/webpackHotDevClient.js(33,14): error TS2339: Property 'encodeURIComponent' does not exist on type 'Window'. +packages/react-dev-utils/webpackHotDevClient.js(35,14): error TS2339: Property 'encodeURIComponent' does not exist on type 'Window'. +packages/react-dev-utils/webpackHotDevClient.js(53,12): error TS2339: Property 'hot' does not exist on type 'NodeModule'. +packages/react-dev-utils/webpackHotDevClient.js(53,33): error TS2339: Property 'hot' does not exist on type 'NodeModule'. +packages/react-dev-utils/webpackHotDevClient.js(54,10): error TS2339: Property 'hot' does not exist on type 'NodeModule'. +packages/react-dev-utils/webpackHotDevClient.js(223,40): error TS2304: Cannot find name '__webpack_hash__'. +packages/react-dev-utils/webpackHotDevClient.js(228,17): error TS2339: Property 'hot' does not exist on type 'NodeModule'. +packages/react-dev-utils/webpackHotDevClient.js(233,15): error TS2339: Property 'hot' does not exist on type 'NodeModule'. +packages/react-dev-utils/webpackHotDevClient.js(261,23): error TS2339: Property 'hot' does not exist on type 'NodeModule'. +packages/react-dev-utils/workspaceUtils.js(11,25): error TS2307: Cannot find module 'find-pkg'. + + + +Standard error: diff --git a/tests/baselines/reference/user/puppeteer.log b/tests/baselines/reference/user/puppeteer.log new file mode 100644 index 00000000000..b2a53a08dd3 --- /dev/null +++ b/tests/baselines/reference/user/puppeteer.log @@ -0,0 +1,40 @@ +Exit Code: 1 +Standard output: +lib/Coverage.js(109,15): error TS2503: Cannot find namespace 'Protocol'. +lib/Coverage.js(196,15): error TS2503: Cannot find namespace 'Protocol'. +lib/ElementHandle.js(24,15): error TS2503: Cannot find namespace 'Protocol'. +lib/ElementHandle.js(83,29): error TS2503: Cannot find namespace 'Protocol'. +lib/EmulationManager.js(36,16): error TS2503: Cannot find namespace 'Protocol'. +lib/ExecutionContext.js(22,15): error TS2503: Cannot find namespace 'Protocol'. +lib/ExecutionContext.js(132,15): error TS2503: Cannot find namespace 'Protocol'. +lib/FrameManager.js(28,15): error TS2503: Cannot find namespace 'Protocol'. +lib/FrameManager.js(54,15): error TS2503: Cannot find namespace 'Protocol'. +lib/FrameManager.js(76,15): error TS2503: Cannot find namespace 'Protocol'. +lib/FrameManager.js(127,15): error TS2503: Cannot find namespace 'Protocol'. +lib/FrameManager.js(773,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(129,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(174,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(207,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(224,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(256,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(270,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(286,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(313,15): error TS2503: Cannot find namespace 'Protocol'. +lib/NetworkManager.js(640,13): error TS2503: Cannot find namespace 'Protocol'. +lib/Page.js(66,15): error TS2503: Cannot find namespace 'Protocol'. +lib/Page.js(185,15): error TS2503: Cannot find namespace 'Protocol'. +lib/Page.js(394,22): error TS2503: Cannot find namespace 'Protocol'. +lib/Page.js(407,15): error TS2503: Cannot find namespace 'Protocol'. +lib/Page.js(731,19): error TS2503: Cannot find namespace 'Protocol'. +lib/externs.d.ts(16,26): error TS2503: Cannot find namespace 'Protocol'. +lib/externs.d.ts(16,69): error TS2503: Cannot find namespace 'Protocol'. +lib/externs.d.ts(17,28): error TS2503: Cannot find namespace 'Protocol'. +lib/externs.d.ts(17,81): error TS2503: Cannot find namespace 'Protocol'. +lib/externs.d.ts(17,121): error TS2503: Cannot find namespace 'Protocol'. +lib/helper.js(59,15): error TS2503: Cannot find namespace 'Protocol'. +lib/helper.js(77,15): error TS2503: Cannot find namespace 'Protocol'. +lib/helper.js(101,15): error TS2503: Cannot find namespace 'Protocol'. + + + +Standard error: diff --git a/tests/baselines/reference/user/uglify-js.log b/tests/baselines/reference/user/uglify-js.log index b467514604a..66a1267e755 100644 --- a/tests/baselines/reference/user/uglify-js.log +++ b/tests/baselines/reference/user/uglify-js.log @@ -6,65 +6,66 @@ node_modules/uglify-js/lib/ast.js(858,5): error TS2322: Type '{ [x: string]: any Object literal may only specify known properties, but '_visit' does not exist in type 'TreeWalker'. Did you mean to write 'visit'? node_modules/uglify-js/lib/compress.js(165,27): error TS2554: Expected 0 arguments, but got 1. node_modules/uglify-js/lib/compress.js(453,26): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(722,18): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(964,38): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. -node_modules/uglify-js/lib/compress.js(990,51): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'true | ((node: any) => any)' has no compatible call signatures. -node_modules/uglify-js/lib/compress.js(1074,53): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. -node_modules/uglify-js/lib/compress.js(1114,112): error TS2532: Object is possibly 'undefined'. -node_modules/uglify-js/lib/compress.js(1115,29): error TS2532: Object is possibly 'undefined'. -node_modules/uglify-js/lib/compress.js(1124,87): error TS2322: Type 'false' is not assignable to type 'number'. -node_modules/uglify-js/lib/compress.js(1132,29): error TS2322: Type 'false' is not assignable to type 'never'. -node_modules/uglify-js/lib/compress.js(1185,38): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(1278,38): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. -node_modules/uglify-js/lib/compress.js(1374,27): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(1406,26): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(1820,44): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(1998,19): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(2258,27): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(2998,23): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(3011,33): error TS2322: Type '"f"' is not assignable to type 'boolean'. -node_modules/uglify-js/lib/compress.js(3148,18): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(3158,33): error TS2339: Property 'add' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3162,32): error TS2339: Property 'add' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3168,40): error TS2339: Property 'add' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3177,41): error TS2339: Property 'add' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3194,14): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(3196,40): error TS2339: Property 'get' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3204,33): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. -node_modules/uglify-js/lib/compress.js(3278,63): error TS2339: Property 'get' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3467,23): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(3484,36): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. -node_modules/uglify-js/lib/compress.js(3490,38): error TS2339: Property 'set' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3494,40): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. -node_modules/uglify-js/lib/compress.js(3519,22): error TS2339: Property 'each' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3524,30): error TS2339: Property 'del' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3529,30): error TS2339: Property 'set' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3540,41): error TS2339: Property 'has' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3542,48): error TS2339: Property 'get' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3554,41): error TS2339: Property 'has' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3556,48): error TS2339: Property 'get' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3640,21): error TS2403: Subsequent variable declarations must have the same type. Variable 'defs' must be of type 'Dictionary', but here has type 'any'. -node_modules/uglify-js/lib/compress.js(3642,36): error TS2339: Property 'get' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3659,22): error TS2339: Property 'set' does not exist on type 'Dictionary'. -node_modules/uglify-js/lib/compress.js(3679,17): error TS2447: The '|=' operator is not allowed for boolean types. Consider using '||' instead. -node_modules/uglify-js/lib/compress.js(3704,30): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(3855,22): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(4133,17): error TS2403: Subsequent variable declarations must have the same type. Variable 'body' must be of type 'any[]', but here has type 'any'. -node_modules/uglify-js/lib/compress.js(4217,22): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(4545,30): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(4552,25): error TS2403: Subsequent variable declarations must have the same type. Variable 'code' must be of type 'string', but here has type '{ [x: string]: any; get: () => string; toString: () => string; indent: () => void; indentation: (...'. -node_modules/uglify-js/lib/compress.js(4556,36): error TS2532: Object is possibly 'undefined'. -node_modules/uglify-js/lib/compress.js(4561,41): error TS2339: Property 'get' does not exist on type 'string'. -node_modules/uglify-js/lib/compress.js(5040,18): error TS2454: Variable 'is_strict_comparison' is used before being assigned. -node_modules/uglify-js/lib/compress.js(5505,32): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(5565,24): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(5637,24): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(5643,26): error TS2554: Expected 0 arguments, but got 1. -node_modules/uglify-js/lib/compress.js(5993,43): error TS2454: Variable 'property' is used before being assigned. -node_modules/uglify-js/lib/compress.js(6007,25): error TS2403: Subsequent variable declarations must have the same type. Variable 'value' must be of type 'number', but here has type 'any'. -node_modules/uglify-js/lib/compress.js(6010,46): error TS2339: Property 'has_side_effects' does not exist on type 'number'. -node_modules/uglify-js/lib/compress.js(6017,25): error TS2403: Subsequent variable declarations must have the same type. Variable 'value' must be of type 'number', but here has type 'any'. -node_modules/uglify-js/lib/compress.js(6070,19): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(734,18): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(976,38): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. +node_modules/uglify-js/lib/compress.js(1002,51): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'true | ((node: any) => any)' has no compatible call signatures. +node_modules/uglify-js/lib/compress.js(1086,53): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. +node_modules/uglify-js/lib/compress.js(1126,112): error TS2532: Object is possibly 'undefined'. +node_modules/uglify-js/lib/compress.js(1127,29): error TS2532: Object is possibly 'undefined'. +node_modules/uglify-js/lib/compress.js(1136,87): error TS2322: Type 'false' is not assignable to type 'number'. +node_modules/uglify-js/lib/compress.js(1144,29): error TS2322: Type 'false' is not assignable to type 'never'. +node_modules/uglify-js/lib/compress.js(1197,38): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(1290,38): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. +node_modules/uglify-js/lib/compress.js(1386,27): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(1418,26): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(1832,44): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(2024,19): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(2284,27): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(3024,23): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(3037,33): error TS2322: Type '"f"' is not assignable to type 'boolean'. +node_modules/uglify-js/lib/compress.js(3174,18): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(3184,33): error TS2339: Property 'add' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3188,32): error TS2339: Property 'add' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3194,40): error TS2339: Property 'add' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3203,41): error TS2339: Property 'add' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3220,14): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(3222,40): error TS2339: Property 'get' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3230,33): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. +node_modules/uglify-js/lib/compress.js(3304,63): error TS2339: Property 'get' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3493,23): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(3510,36): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. +node_modules/uglify-js/lib/compress.js(3516,38): error TS2339: Property 'set' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3520,40): error TS2339: Property 'parent' does not exist on type 'TreeTransformer'. +node_modules/uglify-js/lib/compress.js(3545,22): error TS2339: Property 'each' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3550,30): error TS2339: Property 'del' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3555,30): error TS2339: Property 'set' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3566,41): error TS2339: Property 'has' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3568,48): error TS2339: Property 'get' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3580,41): error TS2339: Property 'has' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3582,48): error TS2339: Property 'get' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3688,21): error TS2403: Subsequent variable declarations must have the same type. Variable 'defs' must be of type 'Dictionary', but here has type 'any'. +node_modules/uglify-js/lib/compress.js(3690,36): error TS2339: Property 'get' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3719,22): error TS2339: Property 'set' does not exist on type 'Dictionary'. +node_modules/uglify-js/lib/compress.js(3739,17): error TS2447: The '|=' operator is not allowed for boolean types. Consider using '||' instead. +node_modules/uglify-js/lib/compress.js(3764,30): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(3902,18): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(4201,17): error TS2403: Subsequent variable declarations must have the same type. Variable 'body' must be of type 'any[]', but here has type 'any'. +node_modules/uglify-js/lib/compress.js(4285,22): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(4613,30): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(4620,25): error TS2403: Subsequent variable declarations must have the same type. Variable 'code' must be of type 'string', but here has type '{ [x: string]: any; get: () => string; toString: () => string; indent: () => void; indentation: (...'. +node_modules/uglify-js/lib/compress.js(4624,36): error TS2532: Object is possibly 'undefined'. +node_modules/uglify-js/lib/compress.js(4629,41): error TS2339: Property 'get' does not exist on type 'string'. +node_modules/uglify-js/lib/compress.js(5114,18): error TS2454: Variable 'is_strict_comparison' is used before being assigned. +node_modules/uglify-js/lib/compress.js(5553,25): error TS2365: Operator '==' cannot be applied to types 'boolean' and '"f"'. +node_modules/uglify-js/lib/compress.js(5580,32): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(5640,24): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(5712,24): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(5718,26): error TS2554: Expected 0 arguments, but got 1. +node_modules/uglify-js/lib/compress.js(6068,43): error TS2454: Variable 'property' is used before being assigned. +node_modules/uglify-js/lib/compress.js(6082,25): error TS2403: Subsequent variable declarations must have the same type. Variable 'value' must be of type 'number', but here has type 'any'. +node_modules/uglify-js/lib/compress.js(6085,46): error TS2339: Property 'has_side_effects' does not exist on type 'number'. +node_modules/uglify-js/lib/compress.js(6092,25): error TS2403: Subsequent variable declarations must have the same type. Variable 'value' must be of type 'number', but here has type 'any'. +node_modules/uglify-js/lib/compress.js(6145,19): error TS2554: Expected 0 arguments, but got 1. node_modules/uglify-js/lib/minify.js(166,75): error TS2339: Property 'compress' does not exist on type 'Compressor'. node_modules/uglify-js/lib/mozilla-ast.js(569,18): error TS2554: Expected 0 arguments, but got 1. node_modules/uglify-js/lib/output.js(473,22): error TS2554: Expected 0 arguments, but got 1. diff --git a/tests/cases/user/create-react-app/chalk-override.d.ts b/tests/cases/user/create-react-app/chalk-override.d.ts new file mode 100644 index 00000000000..5adfd6d3cfb --- /dev/null +++ b/tests/cases/user/create-react-app/chalk-override.d.ts @@ -0,0 +1 @@ +declare module 'chalk'; diff --git a/tests/cases/user/create-react-app/create-react-app b/tests/cases/user/create-react-app/create-react-app new file mode 160000 index 00000000000..1d4fdc2dd49 --- /dev/null +++ b/tests/cases/user/create-react-app/create-react-app @@ -0,0 +1 @@ +Subproject commit 1d4fdc2dd4950011beacf1883900bf5d8da7079e diff --git a/tests/cases/user/create-react-app/test.json b/tests/cases/user/create-react-app/test.json new file mode 100644 index 00000000000..e0d4d26bdca --- /dev/null +++ b/tests/cases/user/create-react-app/test.json @@ -0,0 +1,3 @@ +{ + "types": [] +} diff --git a/tests/cases/user/create-react-app/tsconfig.json b/tests/cases/user/create-react-app/tsconfig.json new file mode 100644 index 00000000000..0bb5948b878 --- /dev/null +++ b/tests/cases/user/create-react-app/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "noImplicitAny": false, + "noImplicitThis": false, + "maxNodeModuleJsDepth": 0, + "strict": true, + "noEmit": true, + "allowJs": true, + "checkJs": true, + "types": ["node"], + "lib": ["esnext", "dom"], + "baseUrl": "./", + "paths": { + "chalk": ["chalk-override"] + } + }, + "include": ["create-react-app"], + "exclude": ["create-react-app/packages/react-error-overlay", + "create-react-app/packages/react-scripts"] +} diff --git a/tests/cases/user/puppeteer/puppeteer b/tests/cases/user/puppeteer/puppeteer new file mode 160000 index 00000000000..98bb2615adb --- /dev/null +++ b/tests/cases/user/puppeteer/puppeteer @@ -0,0 +1 @@ +Subproject commit 98bb2615adb6815c91efcc59593b49e2ec8c3935 diff --git a/tests/cases/user/puppeteer/test.json b/tests/cases/user/puppeteer/test.json new file mode 100644 index 00000000000..e0d4d26bdca --- /dev/null +++ b/tests/cases/user/puppeteer/test.json @@ -0,0 +1,3 @@ +{ + "types": [] +} From 0c17a2b2a89c05d6f0a5da14706ab029456a0bd1 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 11:28:12 -0700 Subject: [PATCH 27/44] goToDefinition: Reduce duplicate code around createDefinitionInfo (#23473) * goToDefinition: Reduce duplicate code around createDefinitionInfo * Use '[]' instead of 'emptyArray' * Remove comment --- src/services/goToDefinition.ts | 44 ++++++++++------------------------ 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 226fe74ef10..2a1d829e608 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -50,16 +50,7 @@ namespace ts.GoToDefinition { // assignment. This case and others are handled by the following code. if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); - if (!shorthandSymbol) { - return []; - } - - const shorthandDeclarations = shorthandSymbol.getDeclarations(); - const shorthandSymbolKind = SymbolDisplay.getSymbolKind(typeChecker, shorthandSymbol, node); - const shorthandSymbolName = typeChecker.symbolToString(shorthandSymbol); - const shorthandContainerName = typeChecker.symbolToString(symbol.parent, node); - return map(shorthandDeclarations, - declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); + return shorthandSymbol ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node)) : []; } // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the @@ -191,8 +182,7 @@ namespace ts.GoToDefinition { } function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] { - const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, symbol, node); - return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(symbol.declarations, declaration => createDefinitionInfo(declaration, symbolKind, symbolName, containerName)); + return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(symbol.declarations, declaration => createDefinitionInfo(declaration, typeChecker, symbol, node)); function getConstructSignatureDefinition(): DefinitionInfo[] | undefined { // Applicable only if we are in a new expression, or we are on a constructor declaration @@ -215,7 +205,7 @@ namespace ts.GoToDefinition { } const declarations = signatureDeclarations.filter(selectConstructors ? isConstructorDeclaration : isSignatureDeclaration); return declarations.length - ? [createDefinitionInfo(find(declarations, d => !!(d).body) || last(declarations), symbolKind, symbolName, containerName)] + ? [createDefinitionInfo(find(declarations, d => !!(d).body) || last(declarations), typeChecker, symbol, node)] : undefined; } } @@ -234,12 +224,16 @@ namespace ts.GoToDefinition { } /** Creates a DefinitionInfo from a Declaration, using the declaration's name if possible. */ - function createDefinitionInfo(node: Declaration, symbolKind: ScriptElementKind, symbolName: string, containerName: string): DefinitionInfo { - return createDefinitionInfoFromName(getNameOfDeclaration(node) || node, symbolKind, symbolName, containerName); + function createDefinitionInfo(declaration: Declaration, checker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo { + const symbolName = checker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol + const symbolKind = SymbolDisplay.getSymbolKind(checker, symbol, node); + const containerName = symbol.parent ? checker.symbolToString(symbol.parent, node) : ""; + return createDefinitionInfoFromName(declaration, symbolKind, symbolName, containerName); } /** Creates a DefinitionInfo directly from the name of a declaration. */ - function createDefinitionInfoFromName(name: Node, symbolKind: ScriptElementKind, symbolName: string, containerName: string): DefinitionInfo { + function createDefinitionInfoFromName(declaration: Declaration, symbolKind: ScriptElementKind, symbolName: string, containerName: string): DefinitionInfo { + const name = getNameOfDeclaration(declaration) || declaration; const sourceFile = name.getSourceFile(); return { fileName: sourceFile.fileName, @@ -251,26 +245,12 @@ namespace ts.GoToDefinition { }; } - function getSymbolInfo(typeChecker: TypeChecker, symbol: Symbol, node: Node) { - return { - symbolName: typeChecker.symbolToString(symbol), // Do not get scoped name, just the name of the symbol - symbolKind: SymbolDisplay.getSymbolKind(typeChecker, symbol, node), - containerName: symbol.parent ? typeChecker.symbolToString(symbol.parent, node) : "" - }; - } - function createDefinitionFromSignatureDeclaration(typeChecker: TypeChecker, decl: SignatureDeclaration): DefinitionInfo { - const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, decl.symbol, decl); - return createDefinitionInfo(decl, symbolKind, symbolName, containerName); + return createDefinitionInfo(decl, typeChecker, decl.symbol, decl); } export function findReferenceInPosition(refs: ReadonlyArray, pos: number): FileReference | undefined { - for (const ref of refs) { - if (ref.pos <= pos && pos <= ref.end) { - return ref; - } - } - return undefined; + return find(refs, ref => ref.pos <= pos && pos <= ref.end); } function getDefinitionInfoForFileReference(name: string, targetFileName: string): DefinitionInfo { From f6510bd6b9619d9c196d4de97dd23aeb658fc6a2 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 11:53:41 -0700 Subject: [PATCH 28/44] goToDefinition: Remove isSignatureDeclaration, use isFunctionLike (#23475) --- src/services/goToDefinition.ts | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 2a1d829e608..f97a8338b05 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -203,26 +203,13 @@ namespace ts.GoToDefinition { if (!signatureDeclarations) { return undefined; } - const declarations = signatureDeclarations.filter(selectConstructors ? isConstructorDeclaration : isSignatureDeclaration); + const declarations = signatureDeclarations.filter(selectConstructors ? isConstructorDeclaration : isFunctionLike); return declarations.length ? [createDefinitionInfo(find(declarations, d => !!(d).body) || last(declarations), typeChecker, symbol, node)] : undefined; } } - function isSignatureDeclaration(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.Constructor: - case SyntaxKind.ConstructSignature: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - return true; - default: - return false; - } - } - /** Creates a DefinitionInfo from a Declaration, using the declaration's name if possible. */ function createDefinitionInfo(declaration: Declaration, checker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo { const symbolName = checker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol @@ -278,13 +265,7 @@ namespace ts.GoToDefinition { function tryGetSignatureDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined { const callLike = getAncestorCallLikeExpression(node); const signature = callLike && typeChecker.getResolvedSignature(callLike); - if (signature) { - const decl = signature.declaration; - if (decl && isSignatureDeclaration(decl)) { - return decl; - } - } // Don't go to a function type, go to the value having that type. - return undefined; + return tryCast(signature && signature.declaration, (d): d is SignatureDeclaration => isFunctionLike(d) && !isFunctionTypeNode(d)); } } From 56d83f95723d6dc238da7a7e629a8e7f96b4d3ce Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 12:41:39 -0700 Subject: [PATCH 29/44] Rename `pushTypePart` to `pushSymbolKind` (#23481) --- src/services/symbolDisplay.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 4693a5089f1..66367905af8 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -178,7 +178,7 @@ namespace ts.SymbolDisplay { } else if (symbolFlags & SymbolFlags.Alias) { symbolKind = ScriptElementKind.alias; - pushTypePart(symbolKind); + pushSymbolKind(symbolKind); displayParts.push(spacePart()); if (useConstructSignatures) { displayParts.push(keywordPart(SyntaxKind.NewKeyword)); @@ -258,7 +258,7 @@ namespace ts.SymbolDisplay { // Special case for class expressions because we would like to indicate that // the class name is local to the class body (similar to function expression) // (local class) class - pushTypePart(ScriptElementKind.localClassElement); + pushSymbolKind(ScriptElementKind.localClassElement); } else { // Class declaration has name which is not local. @@ -531,7 +531,7 @@ namespace ts.SymbolDisplay { function addAliasPrefixIfNecessary() { if (alias) { - pushTypePart(ScriptElementKind.alias); + pushSymbolKind(ScriptElementKind.alias); displayParts.push(spacePart()); } } @@ -554,7 +554,7 @@ namespace ts.SymbolDisplay { function addPrefixForAnyFunctionOrVar(symbol: Symbol, symbolKind: string) { prefixNextMeaning(); if (symbolKind) { - pushTypePart(symbolKind); + pushSymbolKind(symbolKind); if (symbol && !some(symbol.declarations, d => isArrowFunction(d) || (isFunctionExpression(d) || isClassExpression(d)) && !d.name)) { displayParts.push(spacePart()); addFullSymbolName(symbol); @@ -562,7 +562,7 @@ namespace ts.SymbolDisplay { } } - function pushTypePart(symbolKind: string) { + function pushSymbolKind(symbolKind: string) { switch (symbolKind) { case ScriptElementKind.variableElement: case ScriptElementKind.functionElement: From b00e37060501fad1d7e7b3c4387128b61d8372ce Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 12:42:23 -0700 Subject: [PATCH 30/44] Add '?' in quick info for optional properties/methods (#23480) --- src/services/symbolDisplay.ts | 4 ++++ tests/cases/fourslash/findAllRefsForObjectSpread.ts | 4 ++-- .../findAllRefsObjectBindingElementPropertyName10.ts | 2 +- .../quickInfoForObjectBindingElementPropertyName03.ts | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 66367905af8..7431393df87 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -549,6 +549,10 @@ namespace ts.SymbolDisplay { const fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbolToDisplay, enclosingDeclaration || sourceFile, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing | SymbolFormatFlags.AllowAnyNodeKind); addRange(displayParts, fullSymbolDisplayParts); + + if (symbol.flags & SymbolFlags.Optional) { + displayParts.push(punctuationPart(SyntaxKind.QuestionToken)); + } } function addPrefixForAnyFunctionOrVar(symbol: Symbol, symbolKind: string) { diff --git a/tests/cases/fourslash/findAllRefsForObjectSpread.ts b/tests/cases/fourslash/findAllRefsForObjectSpread.ts index 970806922ed..c6f111672c8 100644 --- a/tests/cases/fourslash/findAllRefsForObjectSpread.ts +++ b/tests/cases/fourslash/findAllRefsForObjectSpread.ts @@ -12,12 +12,12 @@ const [r0, r1, r2, r3] = ranges; // members of spread types only refer to themselves and the resulting property verify.referenceGroups(r0, [{ definition: "(property) A1.a: string", ranges: [r0, r2, r3] }]); -verify.referenceGroups(r1, [{ definition: "(property) A2.a: number", ranges: [r1, r2] }]); +verify.referenceGroups(r1, [{ definition: "(property) A2.a?: number", ranges: [r1, r2] }]); // but the resulting property refers to everything verify.referenceGroups(r2, [ { definition: "(property) A1.a: string", ranges: [r0, r2, r3] }, - { definition: "(property) A2.a: number", ranges: [r1] }, + { definition: "(property) A2.a?: number", ranges: [r1] }, ]); verify.referenceGroups(r3, [{ definition: "(property) A1.a: string", ranges: [r0, r2, r3] }]); diff --git a/tests/cases/fourslash/findAllRefsObjectBindingElementPropertyName10.ts b/tests/cases/fourslash/findAllRefsObjectBindingElementPropertyName10.ts index 1c79d326dbc..500ca7fe3cf 100644 --- a/tests/cases/fourslash/findAllRefsObjectBindingElementPropertyName10.ts +++ b/tests/cases/fourslash/findAllRefsObjectBindingElementPropertyName10.ts @@ -8,4 +8,4 @@ ////function f ({ [|next|]: { [|next|]: x} }: Recursive) { ////} -verify.singleReferenceGroup("(property) Recursive.next: Recursive"); +verify.singleReferenceGroup("(property) Recursive.next?: Recursive"); diff --git a/tests/cases/fourslash/quickInfoForObjectBindingElementPropertyName03.ts b/tests/cases/fourslash/quickInfoForObjectBindingElementPropertyName03.ts index c29a7265eab..9378c99126c 100644 --- a/tests/cases/fourslash/quickInfoForObjectBindingElementPropertyName03.ts +++ b/tests/cases/fourslash/quickInfoForObjectBindingElementPropertyName03.ts @@ -9,5 +9,5 @@ ////} for (const marker of test.markerNames()) { - verify.quickInfoAt(marker, "(property) Recursive.next: Recursive"); + verify.quickInfoAt(marker, "(property) Recursive.next?: Recursive"); } \ No newline at end of file From c645f1753f588605aec5cc938f59fbdccdcd98f0 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 17 Apr 2018 15:42:31 -0400 Subject: [PATCH 31/44] Update configurePrerelease to not utilize ts internals (#23476) * update configure nightly to not utilize ts internals * Nightly -> Prerelease * Remove alias --- Gulpfile.ts | 17 ++++----- Jakefile.js | 2 +- scripts/configurePrerelease.ts | 36 ++++++++++--------- .../TypeScript-Node-Starter | 2 +- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 26622f36fe8..7f7e525564c 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -215,8 +215,8 @@ for (const i in libraryTargets) { .pipe(gulp.dest("."))); } -const configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js"); -const configureNightlyTs = path.join(scriptsDirectory, "configureNightly.ts"); +const configurePreleleaseJs = path.join(scriptsDirectory, "configurePrerelease.js"); +const configurePreleleaseTs = path.join(scriptsDirectory, "configurePrerelease.ts"); const packageJson = "package.json"; const versionFile = path.join(compilerDirectory, "core.ts"); @@ -301,24 +301,25 @@ function getCompilerSettings(base: tsc.Settings, useBuiltCompiler?: boolean): ts return copy; } -gulp.task(configureNightlyJs, /*help*/ false, [], () => { +gulp.task(configurePreleleaseJs, /*help*/ false, [], () => { const settings: tsc.Settings = { declaration: false, removeComments: true, noResolve: false, stripInternal: false, + module: "commonjs" }; - return gulp.src(configureNightlyTs) + return gulp.src(configurePreleleaseTs) .pipe(sourcemaps.init()) .pipe(tsc(settings)) - .pipe(sourcemaps.write(path.dirname(configureNightlyJs))) - .pipe(gulp.dest(path.dirname(configureNightlyJs))); + .pipe(sourcemaps.write(".")) + .pipe(gulp.dest("./scripts")); }); // Nightly management tasks -gulp.task("configure-nightly", "Runs scripts/configureNightly.ts to prepare a build for nightly publishing", [configureNightlyJs], (done) => { - exec(host, [configureNightlyJs, packageJson, versionFile], done, done); +gulp.task("configure-nightly", "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing", [configurePreleleaseJs], (done) => { + exec(host, [configurePreleleaseJs, "dev", packageJson, versionFile], done, done); }); gulp.task("publish-nightly", "Runs `npm publish --tag next` to create a new nightly build on npm", ["LKG"], () => { return runSequence("clean", "useDebugMode", "runtests-parallel", (done) => { diff --git a/Jakefile.js b/Jakefile.js index bda270b0b4c..3eb8736445b 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -491,7 +491,7 @@ compileFile(/*outfile*/configurePrereleaseJs, /*prereqs*/[configurePrereleaseTs], /*prefixes*/[], /*useBuiltCompiler*/ false, - { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); + { noOutFile: true, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); task("setDebugMode", function () { useDebugMode = true; diff --git a/scripts/configurePrerelease.ts b/scripts/configurePrerelease.ts index d17ddb963b1..da1984c13e0 100644 --- a/scripts/configurePrerelease.ts +++ b/scripts/configurePrerelease.ts @@ -1,4 +1,9 @@ -/// +/// +import { normalize } from "path"; +import assert = require("assert"); +import { readFileSync, writeFileSync } from "fs"; +const args = process.argv.slice(2); + /** * A minimal description for a parsed package.json object. @@ -10,28 +15,27 @@ interface PackageJson { } function main(): void { - const sys = ts.sys; - if (sys.args.length < 3) { - sys.write("Usage:" + sys.newLine) - sys.write("\tnode configureNightly.js " + sys.newLine); + if (args.length < 3) { + console.log("Usage:"); + console.log("\tnode configureNightly.js "); return; } - const tag = sys.args[0]; + const tag = args[0]; if (tag !== "dev" && tag !== "insiders") { throw new Error(`Unexpected tag name '${tag}'.`); } // Acquire the version from the package.json file and modify it appropriately. - const packageJsonFilePath = ts.normalizePath(sys.args[1]); - const packageJsonValue: PackageJson = JSON.parse(sys.readFile(packageJsonFilePath)); + const packageJsonFilePath = normalize(args[1]); + const packageJsonValue: PackageJson = JSON.parse(readFileSync(packageJsonFilePath).toString()); const { majorMinor, patch } = parsePackageJsonVersion(packageJsonValue.version); const prereleasePatch = getPrereleasePatch(tag, patch); // Acquire and modify the source file that exposes the version string. - const tsFilePath = ts.normalizePath(sys.args[2]); - const tsFileContents = ts.sys.readFile(tsFilePath); + const tsFilePath = normalize(args[2]); + const tsFileContents = readFileSync(tsFilePath).toString(); const modifiedTsFileContents = updateTsFile(tsFilePath, tsFileContents, majorMinor, patch, prereleasePatch); // Ensure we are actually changing something - the user probably wants to know that the update failed. @@ -44,20 +48,20 @@ function main(): void { // Finally write the changes to disk. // Modify the package.json structure packageJsonValue.version = `${majorMinor}.${prereleasePatch}`; - sys.writeFile(packageJsonFilePath, JSON.stringify(packageJsonValue, /*replacer:*/ undefined, /*space:*/ 4)) - sys.writeFile(tsFilePath, modifiedTsFileContents); + writeFileSync(packageJsonFilePath, JSON.stringify(packageJsonValue, /*replacer:*/ undefined, /*space:*/ 4)) + writeFileSync(tsFilePath, modifiedTsFileContents); } function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: string, patch: string, nightlyPatch: string): string { const majorMinorRgx = /export const versionMajorMinor = "(\d+\.\d+)"/; const majorMinorMatch = majorMinorRgx.exec(tsFileContents); - ts.Debug.assert(majorMinorMatch !== null, "", () => `The file seems to no longer have a string matching '${majorMinorRgx}'.`); + assert(majorMinorMatch !== null, `The file seems to no longer have a string matching '${majorMinorRgx}'.`); const parsedMajorMinor = majorMinorMatch[1]; - ts.Debug.assert(parsedMajorMinor === majorMinor, "versionMajorMinor does not match.", () => `${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`); + assert(parsedMajorMinor === majorMinor, `versionMajorMinor does not match. ${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`); const versionRgx = /export const version = `\$\{versionMajorMinor\}\.(\d)(-dev)?`;/; const patchMatch = versionRgx.exec(tsFileContents); - ts.Debug.assert(patchMatch !== null, "The file seems to no longer have a string matching", () => versionRgx.toString()); + assert(patchMatch !== null, "The file seems to no longer have a string matching " + versionRgx.toString()); const parsedPatch = patchMatch[1]; if (parsedPatch !== patch) { throw new Error(`patch does not match. ${tsFilePath}: '${parsedPatch}; package.json: '${patch}'`); @@ -69,7 +73,7 @@ function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: st function parsePackageJsonVersion(versionString: string): { majorMinor: string, patch: string } { const versionRgx = /(\d+\.\d+)\.(\d+)($|\-)/; const match = versionString.match(versionRgx); - ts.Debug.assert(match !== null, "package.json 'version' should match", () => versionRgx.toString()); + assert(match !== null, "package.json 'version' should match " + versionRgx.toString()); return { majorMinor: match[1], patch: match[2] }; } diff --git a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter index 6b9706810b5..40bdb4eadab 160000 --- a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter +++ b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter @@ -1 +1 @@ -Subproject commit 6b9706810b55af326a93b9aa59cb17815a30bb32 +Subproject commit 40bdb4eadabc9fbed7d83e3f26817a931c0763b6 From d1fde3786c4a1509b5172540bbaac02c05c22387 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 14:07:59 -0700 Subject: [PATCH 32/44] Symbol kind for a method on a mapped type should still be 'method' (#23478) --- src/services/symbolDisplay.ts | 9 +++++++++ .../{server => }/quickInfoMappedSpreadTypes.ts | 2 +- tests/cases/fourslash/quickInfoMappedType.ts | 11 +++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) rename tests/cases/fourslash/{server => }/quickInfoMappedSpreadTypes.ts (89%) create mode 100644 tests/cases/fourslash/quickInfoMappedType.ts diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 7431393df87..f79c048efad 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -26,6 +26,15 @@ namespace ts.SymbolDisplay { } function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind { + const roots = typeChecker.getRootSymbols(symbol); + // If this is a method from a mapped type, leave as a method so long as it still has a call signature. + if (roots.length === 1 + && first(roots).flags & SymbolFlags.Method + // Ensure the mapped version is still a method, as opposed to `{ [K in keyof I]: number }`. + && typeChecker.getTypeOfSymbolAtLocation(symbol, location).getNonNullableType().getCallSignatures().length !== 0) { + return ScriptElementKind.memberFunctionElement; + } + if (typeChecker.isUndefinedSymbol(symbol)) { return ScriptElementKind.variableElement; } diff --git a/tests/cases/fourslash/server/quickInfoMappedSpreadTypes.ts b/tests/cases/fourslash/quickInfoMappedSpreadTypes.ts similarity index 89% rename from tests/cases/fourslash/server/quickInfoMappedSpreadTypes.ts rename to tests/cases/fourslash/quickInfoMappedSpreadTypes.ts index 2a0c1668763..c1e34f49bfc 100644 --- a/tests/cases/fourslash/server/quickInfoMappedSpreadTypes.ts +++ b/tests/cases/fourslash/quickInfoMappedSpreadTypes.ts @@ -1,4 +1,4 @@ -/// +/// ////interface Foo { //// /** Doc */ diff --git a/tests/cases/fourslash/quickInfoMappedType.ts b/tests/cases/fourslash/quickInfoMappedType.ts new file mode 100644 index 00000000000..c4afb444155 --- /dev/null +++ b/tests/cases/fourslash/quickInfoMappedType.ts @@ -0,0 +1,11 @@ +/// + +////interface I { m(): void; } +////declare const o: { [K in keyof I]: number }; +////o.m/*0*/; +//// +////declare const p: { [K in keyof I]: I[K] }; +////p.m/*1*/; + +verify.quickInfoAt("0", "(property) m: number"); +verify.quickInfoAt("1", "(method) m(): void"); From d64f2483e4c8838ff7bc46fa106c69a05901769d Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 17 Apr 2018 14:17:15 -0700 Subject: [PATCH 33/44] Update to respond to PR feedback --- src/compiler/core.ts | 2 +- src/compiler/resolutionCache.ts | 20 +++-- src/server/project.ts | 82 ++++++++----------- src/server/scriptInfo.ts | 10 +-- .../reference/api/tsserverlibrary.d.ts | 8 +- 5 files changed, 57 insertions(+), 65 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b427c689c96..0e53ae2946d 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2988,7 +2988,7 @@ namespace ts { /** Remove the *first* occurrence of `item` from the array. */ export function unorderedRemoveItem(array: T[], item: T) { - unorderedRemoveFirstItemWhere(array, element => element === item); + return unorderedRemoveFirstItemWhere(array, element => element === item); } /** Remove the *first* element satisfying `predicate`. */ diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 2cd39068de1..35e056546c0 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -10,7 +10,7 @@ namespace ts { invalidateResolutionOfFile(filePath: Path): void; removeResolutionsOfFile(filePath: Path): void; - setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map): void; + setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map>): void; createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution; startCachingPerDirectoryResolution(): void; @@ -75,7 +75,7 @@ namespace ts { export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string, logChangesWhenResolvingModule: boolean): ResolutionCache { let filesWithChangedSetOfUnresolvedImports: Path[] | undefined; let filesWithInvalidatedResolutions: Map | undefined; - let filesWithInvalidatedNonRelativeUnresolvedImports: Map | undefined; + let filesWithInvalidatedNonRelativeUnresolvedImports: Map> | undefined; let allFilesHaveInvalidatedResolution = false; const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory()); @@ -168,6 +168,16 @@ namespace ts { return collected; } + function isFileWithInvalidatedNonRelativeUnresolvedImports(path: Path) { + if (!filesWithInvalidatedNonRelativeUnresolvedImports) { + return false; + } + + // Invalidated if file has unresolved imports + const value = filesWithInvalidatedNonRelativeUnresolvedImports.get(path); + return value && !!value.length; + } + function createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution { if (allFilesHaveInvalidatedResolution || forceAllFilesAsInvalidated) { // Any file asked would have invalidated resolution @@ -177,7 +187,7 @@ namespace ts { const collected = filesWithInvalidatedResolutions; filesWithInvalidatedResolutions = undefined; return path => (collected && collected.has(path)) || - (filesWithInvalidatedNonRelativeUnresolvedImports && filesWithInvalidatedNonRelativeUnresolvedImports.has(path)); + isFileWithInvalidatedNonRelativeUnresolvedImports(path); } function clearPerDirectoryResolutions() { @@ -242,7 +252,7 @@ namespace ts { const resolvedModules: R[] = []; const compilerOptions = resolutionHost.getCompilationSettings(); - const hasInvalidatedNonRelativeUnresolvedImport = logChanges && filesWithInvalidatedNonRelativeUnresolvedImports && filesWithInvalidatedNonRelativeUnresolvedImports.has(path); + const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path); const seenNamesInFile = createMap(); for (const name of names) { let resolution = resolutionsInFile.get(name); @@ -584,7 +594,7 @@ namespace ts { ); } - function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: Map) { + function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: Map>) { Debug.assert(filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined); filesWithInvalidatedNonRelativeUnresolvedImports = filesMap; } diff --git a/src/server/project.ts b/src/server/project.ts index 67469cc69d8..653f2943d0a 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -96,15 +96,10 @@ namespace ts.server { */ cachedUnresolvedImportsPerFile = createMap>(); - /** - * This is the set that has entry to true if file doesnt contain any unresolved import - */ - private filesWithNoUnresolvedImports = createMap(); - /*@internal*/ lastCachedUnresolvedImportsList: SortedReadonlyArray; /*@internal*/ - hasMoreOrLessScriptInfos = false; + private hasMoreOrLessFiles = false; private lastFileExceededProgramSize: string | undefined; @@ -136,10 +131,10 @@ namespace ts.server { */ private lastReportedVersion = 0; /** - * Current project structure version. + * Current project's program version. (incremented everytime new program is created that is not complete reuse from the old one) * This property is changed in 'updateGraph' based on the set of files in program */ - private projectStructureVersion = 0; + private projectProgramVersion = 0; /** * Current version of the project state. It is changed when: * - new root file was added/removed @@ -566,7 +561,6 @@ namespace ts.server { this.resolutionCache.clear(); this.resolutionCache = undefined; this.cachedUnresolvedImportsPerFile = undefined; - this.filesWithNoUnresolvedImports = undefined; this.directoryStructureHost = undefined; // Clean up file watchers waiting for missing files @@ -727,7 +721,6 @@ namespace ts.server { else { this.resolutionCache.invalidateResolutionOfFile(info.path); } - this.filesWithNoUnresolvedImports.delete(info.path); this.cachedUnresolvedImportsPerFile.delete(info.path); if (detachFromProject) { @@ -749,19 +742,11 @@ namespace ts.server { } /* @internal */ - private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: string[] | undefined, ambientModules: string[]): string[] | undefined { - // No unresolve imports in this file - if (this.filesWithNoUnresolvedImports.has(file.path)) { - return result; - } - + private extractUnresolvedImportsFromSourceFile(file: SourceFile, ambientModules: string[]): ReadonlyArray { const cached = this.cachedUnresolvedImportsPerFile.get(file.path); if (cached) { - // found cached result - use it and return - for (const f of cached) { - (result || (result = [])).push(f); - } - return result; + // found cached result, return + return cached; } let unresolvedImports: string[] | undefined; if (file.resolvedModules) { @@ -779,23 +764,23 @@ namespace ts.server { trimmed = trimmed.substr(0, i); } (unresolvedImports || (unresolvedImports = [])).push(trimmed); - (result || (result = [])).push(trimmed); } }); } - if (unresolvedImports) { - this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports); - } - else { - this.filesWithNoUnresolvedImports.set(file.path, true); - } - return result; + + this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports || emptyArray); + return unresolvedImports || emptyArray; function isAmbientlyDeclaredModule(name: string) { return ambientModules.some(m => m === name); } } + /* @internal */ + setHasMoreOrLessFiles() { + this.hasMoreOrLessFiles = true; + } + /** * Updates set of files that contribute to this project * @returns: true if set of files in the project stays the same and false - otherwise. @@ -803,16 +788,15 @@ namespace ts.server { updateGraph(): boolean { this.resolutionCache.startRecordingFilesWithChangedResolutions(); - const hasChanges = this.updateGraphWorker(); - const hasMoreOrLessScriptInfos = this.hasMoreOrLessScriptInfos; - this.hasMoreOrLessScriptInfos = false; + const hasNewProgram = this.updateGraphWorker(); + const hasMoreOrLessFiles = this.hasMoreOrLessFiles; + this.hasMoreOrLessFiles = false; const changedFiles: ReadonlyArray = this.resolutionCache.finishRecordingFilesWithChangedResolutions() || emptyArray; for (const file of changedFiles) { // delete cached information for changed files this.cachedUnresolvedImportsPerFile.delete(file); - this.filesWithNoUnresolvedImports.delete(file); } // update builder only if language service is enabled @@ -824,25 +808,28 @@ namespace ts.server { // 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files // (can reuse cached imports for files that were not changed) // 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch - if (hasChanges || changedFiles.length) { + if (hasNewProgram || changedFiles.length) { let result: string[] | undefined; const ambientModules = this.program.getTypeChecker().getAmbientModules().map(mod => stripQuotes(mod.getName())); for (const sourceFile of this.program.getSourceFiles()) { - result = this.extractUnresolvedImportsFromSourceFile(sourceFile, result, ambientModules); + const unResolved = this.extractUnresolvedImportsFromSourceFile(sourceFile, ambientModules); + if (unResolved !== emptyArray) { + (result || (result = [])).push(...unResolved); + } } this.lastCachedUnresolvedImportsList = result ? toDeduplicatedSortedArray(result) : emptyArray; } - this.projectService.typingsCache.enqueueInstallTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasMoreOrLessScriptInfos); + this.projectService.typingsCache.enqueueInstallTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasMoreOrLessFiles); } else { this.lastCachedUnresolvedImportsList = undefined; } - if (hasChanges) { - this.projectStructureVersion++; + if (hasNewProgram) { + this.projectProgramVersion++; } - return !hasChanges; + return !hasNewProgram; } /*@internal*/ @@ -878,9 +865,9 @@ namespace ts.server { // bump up the version if // - oldProgram is not set - this is a first time updateGraph is called // - newProgram is different from the old program and structure of the old program was not reused. - const hasChanges = this.program && (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely))); + const hasNewProgram = this.program && (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely))); this.hasChangedAutomaticTypeDirectiveNames = false; - if (hasChanges) { + if (hasNewProgram) { if (oldProgram) { for (const f of oldProgram.getSourceFiles()) { if (this.program.getSourceFileByPath(f.path)) { @@ -918,8 +905,8 @@ namespace ts.server { removed => this.detachScriptInfoFromProject(removed) ); const elapsed = timestamp() - start; - this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} Version: ${this.getProjectVersion()} structureChanged: ${hasChanges} Elapsed: ${elapsed}ms`); - return hasChanges; + this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} Version: ${this.getProjectVersion()} structureChanged: ${hasNewProgram} Elapsed: ${elapsed}ms`); + return hasNewProgram; } private detachScriptInfoFromProject(uncheckedFileName: string) { @@ -993,7 +980,6 @@ namespace ts.server { if (changesAffectModuleResolution(oldOptions, compilerOptions)) { // reset cached unresolved imports if changes in compiler options affected module resolution this.cachedUnresolvedImportsPerFile.clear(); - this.filesWithNoUnresolvedImports.clear(); this.lastCachedUnresolvedImportsList = undefined; this.resolutionCache.clear(); } @@ -1007,7 +993,7 @@ namespace ts.server { const info: protocol.ProjectVersionInfo = { projectName: this.getProjectName(), - version: this.projectStructureVersion, + version: this.projectProgramVersion, isInferred: this.projectKind === ProjectKind.Inferred, options: this.getCompilationSettings(), languageServiceDisabled: !this.languageServiceEnabled, @@ -1018,7 +1004,7 @@ namespace ts.server { // check if requested version is the same that we have reported last time if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) { // if current structure version is the same - return info without any changes - if (this.projectStructureVersion === this.lastReportedVersion && !updatedFileNames) { + if (this.projectProgramVersion === this.lastReportedVersion && !updatedFileNames) { return { info, projectErrors: this.getGlobalProjectErrors() }; } // compute and return the difference @@ -1041,7 +1027,7 @@ namespace ts.server { } }); this.lastReportedFileNames = currentFiles; - this.lastReportedVersion = this.projectStructureVersion; + this.lastReportedVersion = this.projectProgramVersion; return { info, changes: { added, removed, updated }, projectErrors: this.getGlobalProjectErrors() }; } else { @@ -1050,7 +1036,7 @@ namespace ts.server { const externalFiles = this.getExternalFiles().map(f => toNormalizedPath(f)); const allFiles = projectFileNames.concat(externalFiles); this.lastReportedFileNames = arrayToSet(allFiles); - this.lastReportedVersion = this.projectStructureVersion; + this.lastReportedVersion = this.projectProgramVersion; return { info, files: allFiles, projectErrors: this.getGlobalProjectErrors() }; } } diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 074fde298aa..cc4ebd519e5 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -304,7 +304,7 @@ namespace ts.server { const isNew = !this.isAttached(project); if (isNew) { this.containingProjects.push(project); - project.hasMoreOrLessScriptInfos = true; + project.setHasMoreOrLessFiles(); if (!project.getCompilerOptions().preserveSymlinks) { this.ensureRealPath(); } @@ -329,23 +329,23 @@ namespace ts.server { return; case 1: if (this.containingProjects[0] === project) { - project.hasMoreOrLessScriptInfos = true; + project.setHasMoreOrLessFiles(); this.containingProjects.pop(); } break; case 2: if (this.containingProjects[0] === project) { - project.hasMoreOrLessScriptInfos = true; + project.setHasMoreOrLessFiles(); this.containingProjects[0] = this.containingProjects.pop(); } else if (this.containingProjects[1] === project) { - project.hasMoreOrLessScriptInfos = true; + project.setHasMoreOrLessFiles(); this.containingProjects.pop(); } break; default: if (unorderedRemoveItem(this.containingProjects, project)) { - project.hasMoreOrLessScriptInfos = true; + project.setHasMoreOrLessFiles(); } break; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 53c23b1d382..a81f1c66c22 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7643,10 +7643,6 @@ declare namespace ts.server { private externalFiles; private missingFilesMap; private plugins; - /** - * This is the set that has entry to true if file doesnt contain any unresolved import - */ - private filesWithNoUnresolvedImports; private lastFileExceededProgramSize; protected languageService: LanguageService; languageServiceEnabled: boolean; @@ -7666,10 +7662,10 @@ declare namespace ts.server { */ private lastReportedVersion; /** - * Current project structure version. + * Current project's program version. (incremented everytime new program is created that is not complete reuse from the old one) * This property is changed in 'updateGraph' based on the set of files in program */ - private projectStructureVersion; + private projectProgramVersion; /** * Current version of the project state. It is changed when: * - new root file was added/removed From 391c0565d7c8138b2d9b3541faef648dfa9debd9 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 17 Apr 2018 17:19:42 -0400 Subject: [PATCH 34/44] Use ts-check instead of ts-node to avoid transpilation overhead on gulp startup (#23486) --- Gulpfile.ts => Gulpfile.js | 223 +++++++++++++++++++------------------ package.json | 1 - 2 files changed, 113 insertions(+), 111 deletions(-) rename Gulpfile.ts => Gulpfile.js (87%) diff --git a/Gulpfile.ts b/Gulpfile.js similarity index 87% rename from Gulpfile.ts rename to Gulpfile.js index 7f7e525564c..afa7e775dcd 100644 --- a/Gulpfile.ts +++ b/Gulpfile.js @@ -1,35 +1,27 @@ /// -import * as cp from "child_process"; -import * as path from "path"; -import * as fs from "fs"; -import child_process = require("child_process"); -import originalGulp = require("gulp"); -import helpMaker = require("gulp-help"); -import runSequence = require("run-sequence"); -import concat = require("gulp-concat"); -import clone = require("gulp-clone"); -import newer = require("gulp-newer"); -import tsc = require("gulp-typescript"); -declare module "gulp-typescript" { - interface Settings { - pretty?: boolean; - newLine?: string; - noImplicitThis?: boolean; - stripInternal?: boolean; - types?: string[]; - } -} -import * as insert from "gulp-insert"; -import * as sourcemaps from "gulp-sourcemaps"; -import Q = require("q"); -import del = require("del"); -import mkdirP = require("mkdirp"); -import minimist = require("minimist"); -import browserify = require("browserify"); -import through2 = require("through2"); -import merge2 = require("merge2"); -import * as os from "os"; -import fold = require("travis-fold"); +// @ts-check +const cp = require("child_process"); +const path = require("path"); +const fs = require("fs"); +const child_process = require("child_process"); +const originalGulp = require("gulp"); +const helpMaker = require("gulp-help"); +const runSequence = require("run-sequence"); +const concat = require("gulp-concat"); +const clone = require("gulp-clone"); +const newer = require("gulp-newer"); +const tsc = require("gulp-typescript"); +const insert = require("gulp-insert"); +const sourcemaps = require("gulp-sourcemaps"); +const Q = require("q"); +const del = require("del"); +const mkdirP = require("mkdirp"); +const minimist = require("minimist"); +const browserify = require("browserify"); +const through2 = require("through2"); +const merge2 = require("merge2"); +const os = require("os"); +const fold = require("travis-fold"); const gulp = helpMaker(originalGulp); Error.stackTraceLimit = 1000; @@ -73,17 +65,26 @@ const cmdLineOptions = minimist(process.argv.slice(2), { }); const noop = () => {}; // tslint:disable-line no-empty -function exec(cmd: string, args: string[], complete: () => void = noop, error: (e: any, status: number) => void = noop) { +/** + * @param {string} cmd + * @param {string[]} args + * @param {() => void} complete + * @param {(e: *, status: number) => void} error + */ +function exec(cmd, args, complete = noop, error = noop) { console.log(`${cmd} ${args.join(" ")}`); // TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition const subshellFlag = isWin ? "/c" : "-c"; const command = isWin ? [possiblyQuote(cmd), ...args] : [`${cmd} ${args.join(" ")}`]; - const ex = cp.spawn(isWin ? "cmd" : "/bin/sh", [subshellFlag, ...command], { stdio: "inherit", windowsVerbatimArguments: true } as any); + const ex = cp.spawn(isWin ? "cmd" : "/bin/sh", [subshellFlag, ...command], { stdio: "inherit", windowsVerbatimArguments: true }); ex.on("exit", (code) => code === 0 ? complete() : error(/*e*/ undefined, code)); ex.on("error", error); } -function possiblyQuote(cmd: string) { +/** + * @param {string} cmd + */ +function possiblyQuote(cmd) { return cmd.indexOf(" ") >= 0 ? `"${cmd}"` : cmd; } @@ -220,7 +221,12 @@ const configurePreleleaseTs = path.join(scriptsDirectory, "configurePrerelease.t const packageJson = "package.json"; const versionFile = path.join(compilerDirectory, "core.ts"); -function needsUpdate(source: string | string[], dest: string | string[]): boolean { +/** + * @param {string | string[]} source + * @param {string | string[]} dest + * @returns {boolean} + */ +function needsUpdate(source, dest) { if (typeof source === "string" && typeof dest === "string") { if (fs.existsSync(dest)) { const {mtime: outTime} = fs.statSync(dest); @@ -283,8 +289,13 @@ function needsUpdate(source: string | string[], dest: string | string[]): boolea return true; } -function getCompilerSettings(base: tsc.Settings, useBuiltCompiler?: boolean): tsc.Settings { - const copy: tsc.Settings = {}; +/** + * @param {tsc.Settings} base + * @param {boolean=} useBuiltCompiler + * @returns {tsc.Settings} + */ +function getCompilerSettings(base, useBuiltCompiler) { + const copy = /** @type {tsc.Settings} */ ({}); for (const key in base) { copy[key] = base[key]; } @@ -293,16 +304,17 @@ function getCompilerSettings(base: tsc.Settings, useBuiltCompiler?: boolean): ts } copy.newLine = "lf"; if (useBuiltCompiler === true) { - copy.typescript = require("./built/local/typescript.js"); + copy.typescript = /** @type {*} */ (require("./built/local/typescript.js")); } else if (useBuiltCompiler === false) { - copy.typescript = require("./lib/typescript.js"); + copy.typescript = /** @type {*} */ (require("./lib/typescript.js")); } return copy; } gulp.task(configurePreleleaseJs, /*help*/ false, [], () => { - const settings: tsc.Settings = { + /** @type {tsc.Settings} */ + const settings = { declaration: false, removeComments: true, noResolve: false, @@ -332,7 +344,8 @@ const importDefinitelyTypedTestsJs = path.join(importDefinitelyTypedTestsDirecto const importDefinitelyTypedTestsTs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.ts"); gulp.task(importDefinitelyTypedTestsJs, /*help*/ false, [], () => { - const settings: tsc.Settings = getCompilerSettings({ + /** @type {tsc.Settings} */ + const settings = getCompilerSettings({ declaration: false, removeComments: true, noResolve: false, @@ -394,7 +407,8 @@ const generateLocalizedDiagnosticMessagesJs = path.join(scriptsDirectory, "gener const generateLocalizedDiagnosticMessagesTs = path.join(scriptsDirectory, "generateLocalizedDiagnosticMessages.ts"); gulp.task(generateLocalizedDiagnosticMessagesJs, /*help*/ false, [], () => { - const settings: tsc.Settings = getCompilerSettings({ + /** @type {tsc.Settings} */ + const settings = getCompilerSettings({ target: "es5", declaration: false, removeComments: true, @@ -425,8 +439,12 @@ const nodePackageFile = path.join(builtLocalDirectory, "typescript.js"); const nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts"); const nodeStandaloneDefinitionsFile = path.join(builtLocalDirectory, "typescript_standalone.d.ts"); -let copyrightContent: string; -function prependCopyright(outputCopyright: boolean = !useDebugMode) { +/** @type {string} */ +let copyrightContent; +/** + * @param {boolean} outputCopyright + */ +function prependCopyright(outputCopyright = !useDebugMode) { return insert.prepend(outputCopyright ? (copyrightContent || (copyrightContent = fs.readFileSync(copyright).toString())) : ""); } @@ -518,9 +536,10 @@ const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverli gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile, typesMapJson], (done) => { const serverLibraryProject = tsc.createProject("src/server/tsconfig.library.json", getCompilerSettings({ removeComments: false }, /*useBuiltCompiler*/ true)); - const {js, dts}: { js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream } = serverLibraryProject.src() + /** @type {{ js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream }} */ + const {js, dts} = serverLibraryProject.src() .pipe(sourcemaps.init()) - .pipe(newer({ dest: tsserverLibraryFile, extra: ["src/compiler/**/*.ts", "src/services/**/*.ts"] })) + .pipe(newer(/** @type {*} */({ dest: tsserverLibraryFile, extra: ["src/compiler/**/*.ts", "src/services/**/*.ts"] }))) .pipe(serverLibraryProject()); return merge2([ @@ -555,7 +574,8 @@ const specWord = path.join(docDirectory, "TypeScript Language Specification.docx const specMd = path.join(docDirectory, "spec.md"); gulp.task(word2mdJs, /*help*/ false, [], () => { - const settings: tsc.Settings = getCompilerSettings({ + /** @type {tsc.Settings} */ + const settings = getCompilerSettings({ outFile: word2mdJs }, /*useBuiltCompiler*/ false); return gulp.src(word2mdTs) @@ -634,7 +654,8 @@ function deleteTemporaryProjectOutput() { return del(path.join(localBaseline, "projectOutput/")); } -let savedNodeEnv: string; +/** @type {string} */ +let savedNodeEnv; function setNodeEnvToDevelopment() { savedNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = "development"; @@ -644,7 +665,12 @@ function restoreSavedNodeEnv() { process.env.NODE_ENV = savedNodeEnv; } -function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: (e?: any) => void) { +/** + * @param {string} defaultReporter + * @param {boolean} runInParallel + * @param {(e?: any) => void} done + */ +function runConsoleTests(defaultReporter, runInParallel, done) { const lintFlag = cmdLineOptions.lint; cleanTestDirs((err) => { if (err) { console.error(err); failWithStatus(err, 1); } @@ -719,7 +745,11 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: } }); - function failWithStatus(err?: any, status?: number) { + /** + * @param {any=} err + * @param {number=} status + */ + function failWithStatus(err, status) { if (err || status) { process.exit(typeof status === "number" ? status : 2); } @@ -735,7 +765,11 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: } } - function finish(error?: any, errorStatus?: number) { + /** + * @param {any=} error + * @param {number=} errorStatus + */ + function finish(error, errorStatus) { restoreSavedNodeEnv(); deleteTestConfig().then(deleteTemporaryProjectOutput).then(() => { if (error !== undefined || errorStatus !== undefined) { @@ -765,7 +799,8 @@ gulp.task("runtests", const nodeServerOutFile = "tests/webTestServer.js"; const nodeServerInFile = "tests/webTestServer.ts"; gulp.task(nodeServerOutFile, /*help*/ false, [servicesFile], () => { - const settings: tsc.Settings = getCompilerSettings({ module: "commonjs" }, /*useBuiltCompiler*/ true); + /** @type {tsc.Settings} */ + const settings = getCompilerSettings({ module: "commonjs" }, /*useBuiltCompiler*/ true); return gulp.src(nodeServerInFile) .pipe(newer(nodeServerOutFile)) .pipe(sourcemaps.init()) @@ -774,16 +809,18 @@ gulp.task(nodeServerOutFile, /*help*/ false, [servicesFile], () => { .pipe(gulp.dest(path.dirname(nodeServerOutFile))); }); -import convertMap = require("convert-source-map"); -import sorcery = require("sorcery"); -import Vinyl = require("vinyl"); +const convertMap = require("convert-source-map"); +const sorcery = require("sorcery"); +const Vinyl = require("vinyl"); const bundlePath = path.resolve("built/local/bundle.js"); gulp.task("browserify", "Runs browserify on run.js to produce a file suitable for running tests in the browser", [servicesFile], (done) => { const testProject = tsc.createProject("src/harness/tsconfig.json", getCompilerSettings({ outFile: bundlePath, inlineSourceMap: true }, /*useBuiltCompiler*/ true)); - let originalMap: any; - let prebundledContent: string; + /** @type {*} */ + let originalMap; + /** @type {string} */ + let prebundledContent; browserify(testProject.src() .pipe(newer(bundlePath)) .pipe(sourcemaps.init()) @@ -847,8 +884,10 @@ gulp.task("browserify", "Runs browserify on run.js to produce a file suitable fo }); }); - -function cleanTestDirs(done: (e?: any) => void) { +/** + * @param {(e?: any) => void} done + */ +function cleanTestDirs(done) { // Clean the local baselines & Rwc baselines directories del([ localBaseline, @@ -864,8 +903,17 @@ function cleanTestDirs(done: (e?: any) => void) { }); } -// used to pass data from jake command line directly to run.js -function writeTestConfigFile(tests: string, runners: string, light: boolean, taskConfigsFolder?: string, workerCount?: number, stackTraceLimit?: string, timeout?: number) { +/** + * used to pass data from jake command line directly to run.js + * @param {string} tests + * @param {string} runners + * @param {boolean} light + * @param {string=} taskConfigsFolder + * @param {number=} workerCount + * @param {string=} stackTraceLimit + * @param {number=} timeout + */ +function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, timeout) { const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, runner: runners ? runners.split(",") : undefined, @@ -966,7 +1014,7 @@ gulp.task("baseline-accept-test262", "Makes the most recent test262 test results const webhostPath = "tests/webhost/webtsc.ts"; const webhostJsPath = "tests/webhost/webtsc.js"; gulp.task(webhostJsPath, /*help*/ false, [servicesFile], () => { - const settings: tsc.Settings = getCompilerSettings({ + const settings = getCompilerSettings({ outFile: webhostJsPath }, /*useBuiltCompiler*/ true); return gulp.src(webhostPath) @@ -986,7 +1034,7 @@ gulp.task("webhost", "Builds the tsc web host", [webhostJsPath], () => { const perftscPath = "tests/perftsc.ts"; const perftscJsPath = "built/local/perftsc.js"; gulp.task(perftscJsPath, /*help*/ false, [servicesFile], () => { - const settings: tsc.Settings = getCompilerSettings({ + const settings = getCompilerSettings({ outFile: perftscJsPath }, /*useBuiltCompiler*/ true); return gulp.src(perftscPath) @@ -1017,7 +1065,7 @@ gulp.task(loggedIOJsPath, /*help*/ false, [], (done) => { const instrumenterPath = path.join(harnessDirectory, "instrumenter.ts"); const instrumenterJsPath = path.join(builtLocalDirectory, "instrumenter.js"); gulp.task(instrumenterJsPath, /*help*/ false, [servicesFile], () => { - const settings: tsc.Settings = getCompilerSettings({ + const settings = getCompilerSettings({ module: "commonjs", target: "es5", lib: [ @@ -1044,7 +1092,7 @@ gulp.task("update-sublime", "Updates the sublime plugin's tsserver", ["local", s }); gulp.task("build-rules", "Compiles tslint rules to js", () => { - const settings: tsc.Settings = getCompilerSettings({ module: "commonjs", lib: ["es6"] }, /*useBuiltCompiler*/ false); + const settings = getCompilerSettings({ module: "commonjs", lib: ["es6"] }, /*useBuiltCompiler*/ false); const dest = path.join(builtLocalDirectory, "tslint"); return gulp.src("scripts/tslint/**/*.ts") .pipe(newer({ @@ -1057,51 +1105,6 @@ gulp.task("build-rules", "Compiles tslint rules to js", () => { .pipe(gulp.dest(dest)); }); -const lintTargets = [ - "Gulpfile.ts", - "src/compiler/**/*.ts", - "src/harness/**/*.ts", - "!src/harness/unittests/services/formatting/**/*.ts", - "src/server/**/*.ts", - "scripts/tslint/**/*.ts", - "src/services/**/*.ts", - "tests/*.ts", "tests/webhost/*.ts" // Note: does *not* descend recursively -]; - -function sendNextFile(files: {path: string}[], child: cp.ChildProcess, callback: (failures: number) => void, failures: number) { - const file = files.pop(); - if (file) { - console.log(`Linting '${file.path}'.`); - child.send({ kind: "file", name: file.path }); - } - else { - child.send({ kind: "close" }); - callback(failures); - } -} - -function spawnLintWorker(files: {path: string}[], callback: (failures: number) => void) { - const child = cp.fork("./scripts/parallel-lint"); - let failures = 0; - child.on("message", data => { - switch (data.kind) { - case "result": - if (data.failures > 0) { - failures += data.failures; - console.log(data.output); - } - sendNextFile(files, child, callback, failures); - break; - case "error": - console.error(data.error); - failures++; - sendNextFile(files, child, callback, failures); - break; - } - }); - sendNextFile(files, child, callback, failures); -} - gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex", ["build-rules"], () => { if (fold.isTravis()) console.log(fold.start("lint")); for (const project of ["scripts/tslint/tsconfig.json", "src/tsconfig-base.json"]) { diff --git a/package.json b/package.json index 5837f72317f..71b5ed71029 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,6 @@ "source-map-support": "latest", "through2": "latest", "travis-fold": "latest", - "ts-node": "latest", "tslint": "latest", "vinyl": "latest", "chalk": "latest", From f5101e21c36f19cbd30ae32b6945618a3e9dc98d Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 17 Apr 2018 15:01:36 -0700 Subject: [PATCH 35/44] Find-all-references: Don't crash on 'typeof import' (#23448) * Find-all-references: Don't crash on 'typeof import' * Move `| ImportTypeNode` out of `AnyImportOrReExport` --- src/compiler/types.ts | 3 ++- src/compiler/utilities.ts | 8 +++++++- src/services/importTracker.ts | 6 +++++- tests/baselines/reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + tests/cases/fourslash/findAllRefsTypeofImport.ts | 8 ++++++++ 6 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/findAllRefsTypeofImport.ts diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 63720d55900..d038d4281b0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3193,7 +3193,8 @@ namespace ts { export type AnyValidImportOrReExport = | (ImportDeclaration | ExportDeclaration) & { moduleSpecifier: StringLiteral } | ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteral } } - | RequireOrImportCall; + | RequireOrImportCall + | ImportTypeNode & { argument: LiteralType }; /* @internal */ export type RequireOrImportCall = CallExpression & { arguments: [StringLiteralLike] }; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f975e27dca3..e9d6f026b4e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1710,8 +1710,10 @@ namespace ts { return (node.parent as ExternalModuleReference).parent as AnyValidImportOrReExport; case SyntaxKind.CallExpression: return node.parent as AnyValidImportOrReExport; + case SyntaxKind.LiteralType: + return cast(node.parent.parent, isImportTypeNode) as ImportTypeNode & { argument: LiteralType }; default: - return Debug.fail(Debug.showSyntaxKind(node)); + return Debug.fail(Debug.showSyntaxKind(node.parent)); } } @@ -4926,6 +4928,10 @@ namespace ts { return node.kind === SyntaxKind.LiteralType; } + export function isImportTypeNode(node: Node): node is ImportTypeNode { + return node.kind === SyntaxKind.ImportType; + } + // Binding patterns export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern { diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index 82f9fc00af6..80f0721e642 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -33,7 +33,7 @@ namespace ts.FindAllReferences { interface AmbientModuleDeclaration extends ModuleDeclaration { body?: ModuleBlock; } type SourceFileLike = SourceFile | AmbientModuleDeclaration; // Identifier for the case of `const x = require("y")`. - type Importer = AnyImportOrReExport | Identifier; + type Importer = AnyImportOrReExport | ImportTypeNode | Identifier; type ImporterOrCallExpression = Importer | CallExpression; /** Returns import statements that directly reference the exporting module, and a list of files that may access the module through a namespace. */ @@ -215,6 +215,10 @@ namespace ts.FindAllReferences { return; } + if (decl.kind === SyntaxKind.ImportType) { + return; + } + // Ignore if there's a grammar error if (decl.moduleSpecifier.kind !== SyntaxKind.StringLiteral) { return; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 04fabe6ff09..f5f60338aa9 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3169,6 +3169,7 @@ declare namespace ts { function isIndexedAccessTypeNode(node: Node): node is IndexedAccessTypeNode; function isMappedTypeNode(node: Node): node is MappedTypeNode; function isLiteralTypeNode(node: Node): node is LiteralTypeNode; + function isImportTypeNode(node: Node): node is ImportTypeNode; function isObjectBindingPattern(node: Node): node is ObjectBindingPattern; function isArrayBindingPattern(node: Node): node is ArrayBindingPattern; function isBindingElement(node: Node): node is BindingElement; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 506aa6b15c6..b64ef40646c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3169,6 +3169,7 @@ declare namespace ts { function isIndexedAccessTypeNode(node: Node): node is IndexedAccessTypeNode; function isMappedTypeNode(node: Node): node is MappedTypeNode; function isLiteralTypeNode(node: Node): node is LiteralTypeNode; + function isImportTypeNode(node: Node): node is ImportTypeNode; function isObjectBindingPattern(node: Node): node is ObjectBindingPattern; function isArrayBindingPattern(node: Node): node is ArrayBindingPattern; function isBindingElement(node: Node): node is BindingElement; diff --git a/tests/cases/fourslash/findAllRefsTypeofImport.ts b/tests/cases/fourslash/findAllRefsTypeofImport.ts new file mode 100644 index 00000000000..77bd2d51a8c --- /dev/null +++ b/tests/cases/fourslash/findAllRefsTypeofImport.ts @@ -0,0 +1,8 @@ +/// + +// @Filename: /a.ts +////export const [|{| "isWriteAccess": true, "isDefinition": true |}x|] = 0; +////declare const a: typeof import("./a"); +////a.[|x|]; + +verify.singleReferenceGroup("const x: 0"); From e26745f129404109948f91d6803e92f5f14c24b6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 17 Apr 2018 15:20:48 -0700 Subject: [PATCH 36/44] Add axios' source to user tests (#23490) * Add axios' source to user tests We already have the npm-installed version in order to test their d.ts so that we don't break their users. * Just compile lib, plus fix some config lint --- .gitmodules | 5 +++ tests/baselines/reference/user/axios-src.log | 44 ++++++++++++++++++++ tests/cases/user/axios-src/axios-src | 1 + tests/cases/user/axios-src/test.json | 3 ++ tests/cases/user/axios-src/tsconfig.json | 14 +++++++ 5 files changed, 67 insertions(+) create mode 100644 tests/baselines/reference/user/axios-src.log create mode 160000 tests/cases/user/axios-src/axios-src create mode 100644 tests/cases/user/axios-src/test.json create mode 100644 tests/cases/user/axios-src/tsconfig.json diff --git a/.gitmodules b/.gitmodules index ccb2be81520..fdf474a693d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -29,3 +29,8 @@ [submodule "tests/cases/user/puppeteer/puppeteer"] path = tests/cases/user/puppeteer/puppeteer url = https://github.com/GoogleChrome/puppeteer.git + ignore = all +[submodule "tests/cases/user/axios-src/axios-src"] + path = tests/cases/user/axios-src/axios-src + url = https://github.com/axios/axios.git + ignore = all diff --git a/tests/baselines/reference/user/axios-src.log b/tests/baselines/reference/user/axios-src.log new file mode 100644 index 00000000000..ad8bc285bbb --- /dev/null +++ b/tests/baselines/reference/user/axios-src.log @@ -0,0 +1,44 @@ +Exit Code: 1 +Standard output: +lib/adapters/http.js(12,19): error TS2307: Cannot find module './../../package.json'. +lib/adapters/http.js(83,22): error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'. + Type 'undefined' is not assignable to type 'string'. +lib/adapters/http.js(189,23): error TS2345: Argument of type 'null' is not assignable to parameter of type 'string | undefined'. +lib/adapters/http.js(195,44): error TS2345: Argument of type 'null' is not assignable to parameter of type 'string | undefined'. +lib/adapters/http.js(201,13): error TS2322: Type 'string' is not assignable to type 'Buffer'. +lib/adapters/http.js(213,40): error TS2345: Argument of type 'null' is not assignable to parameter of type 'string | undefined'. +lib/adapters/http.js(237,42): error TS2345: Argument of type 'null' is not assignable to parameter of type 'string | undefined'. +lib/adapters/xhr.js(29,16): error TS2339: Property 'XDomainRequest' does not exist on type 'Window'. +lib/adapters/xhr.js(31,28): error TS2339: Property 'XDomainRequest' does not exist on type 'Window'. +lib/adapters/xhr.js(80,7): error TS2322: Type 'null' is not assignable to type 'XMLHttpRequest'. +lib/adapters/xhr.js(92,7): error TS2322: Type 'null' is not assignable to type 'XMLHttpRequest'. +lib/adapters/xhr.js(99,51): error TS2345: Argument of type 'null' is not assignable to parameter of type 'string | undefined'. +lib/adapters/xhr.js(102,7): error TS2322: Type 'null' is not assignable to type 'XMLHttpRequest'. +lib/adapters/xhr.js(111,7): error TS2322: Type 'null' is not assignable to type 'XMLHttpRequest'. +lib/adapters/xhr.js(181,9): error TS2322: Type 'null' is not assignable to type 'XMLHttpRequest'. +lib/axios.js(23,3): error TS2554: Expected 3 arguments, but got 2. +lib/axios.js(25,3): error TS2322: Type '(...args: any[]) => any' is not assignable to type 'Axios'. + Property 'defaults' is missing in type '(...args: any[]) => any'. +lib/axios.js(32,7): error TS2339: Property 'Axios' does not exist on type 'Axios'. +lib/axios.js(35,7): error TS2339: Property 'create' does not exist on type 'Axios'. +lib/axios.js(40,7): error TS2339: Property 'Cancel' does not exist on type 'Axios'. +lib/axios.js(41,7): error TS2339: Property 'CancelToken' does not exist on type 'Axios'. +lib/axios.js(42,7): error TS2339: Property 'isCancel' does not exist on type 'Axios'. +lib/axios.js(45,7): error TS2339: Property 'all' does not exist on type 'Axios'. +lib/axios.js(48,7): error TS2339: Property 'spread' does not exist on type 'Axios'. +lib/cancel/CancelToken.js(37,12): error TS2339: Property 'reason' does not exist on type 'CancelToken'. +lib/cancel/CancelToken.js(38,16): error TS2339: Property 'reason' does not exist on type 'CancelToken'. +lib/core/enhanceError.js(14,9): error TS2339: Property 'config' does not exist on type 'Error'. +lib/core/enhanceError.js(16,11): error TS2339: Property 'code' does not exist on type 'Error'. +lib/core/enhanceError.js(18,9): error TS2339: Property 'request' does not exist on type 'Error'. +lib/core/enhanceError.js(19,9): error TS2339: Property 'response' does not exist on type 'Error'. +lib/core/settle.js(21,7): error TS2345: Argument of type 'null' is not assignable to parameter of type 'string | undefined'. +lib/helpers/btoa.js(11,13): error TS2339: Property 'code' does not exist on type 'Error'. +lib/helpers/btoa.js(31,13): error TS2532: Object is possibly 'undefined'. +lib/helpers/cookies.js(16,56): error TS2551: Property 'toGMTString' does not exist on type 'Date'. Did you mean 'toUTCString'? +lib/utils.js(244,20): error TS8029: JSDoc '@param' tag has name 'obj1', but there is no parameter with that name. It would match 'arguments' if it had an array type. +lib/utils.js(268,20): error TS8029: JSDoc '@param' tag has name 'obj1', but there is no parameter with that name. It would match 'arguments' if it had an array type. + + + +Standard error: diff --git a/tests/cases/user/axios-src/axios-src b/tests/cases/user/axios-src/axios-src new file mode 160000 index 00000000000..0b3db5d87a6 --- /dev/null +++ b/tests/cases/user/axios-src/axios-src @@ -0,0 +1 @@ +Subproject commit 0b3db5d87a60a1ad8b0dce9669dbc10483ec33da diff --git a/tests/cases/user/axios-src/test.json b/tests/cases/user/axios-src/test.json new file mode 100644 index 00000000000..b6495a1b80b --- /dev/null +++ b/tests/cases/user/axios-src/test.json @@ -0,0 +1,3 @@ +{ + "types": ["node"] +} diff --git a/tests/cases/user/axios-src/tsconfig.json b/tests/cases/user/axios-src/tsconfig.json new file mode 100644 index 00000000000..ef3f63dc126 --- /dev/null +++ b/tests/cases/user/axios-src/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "noImplicitAny": false, + "noImplicitThis": false, + "maxNodeModuleJsDepth": 0, + "strict": true, + "noEmit": true, + "allowJs": true, + "checkJs": true, + "types": ["node"], + "lib": ["esnext", "dom"], + }, + "include": ["axios-src/lib"] +} From 26c836b12a56063ddde7586cdf37127ba174a7f5 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 17 Apr 2018 19:44:52 -0700 Subject: [PATCH 37/44] Propagage reportsUnnecessary in convertToDiagnosticsWithLinePosition --- src/server/session.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/session.ts b/src/server/session.ts index 1076d17e11f..27044ec369d 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -634,7 +634,8 @@ namespace ts.server { code: d.code, source: d.source, startLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start), - endLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start + d.length) + endLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start + d.length), + reportsUnnecessary: d.reportsUnnecessary }); } From 81b347d61da5ba100bd09fef6d863520e6c48a21 Mon Sep 17 00:00:00 2001 From: csigs Date: Wed, 18 Apr 2018 04:10:18 +0000 Subject: [PATCH 38/44] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl index 58326d7ccb6..b2ffdf585e0 100644 --- a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -3894,6 +3894,15 @@ + + + + + + + + + @@ -6003,6 +6012,9 @@ + + + From afde2b5bf3d90526493dbb2ef40553be0ca48d17 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 18 Apr 2018 07:55:57 -0700 Subject: [PATCH 39/44] MissingDeclaration is only ever a Statement (#23485) --- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 9 +++------ tests/baselines/reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d038d4281b0..19034c3469e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1892,7 +1892,7 @@ namespace ts { kind: SyntaxKind.DebuggerStatement; } - export interface MissingDeclaration extends DeclarationStatement, ClassElement, ObjectLiteralElement, TypeElement { + export interface MissingDeclaration extends DeclarationStatement { kind: SyntaxKind.MissingDeclaration; name?: Identifier; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e9d6f026b4e..3fe255fb612 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5612,8 +5612,7 @@ namespace ts { || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor || kind === SyntaxKind.IndexSignature - || kind === SyntaxKind.SemicolonClassElement - || kind === SyntaxKind.MissingDeclaration; + || kind === SyntaxKind.SemicolonClassElement; } export function isClassLike(node: Node): node is ClassLikeDeclaration { @@ -5644,8 +5643,7 @@ namespace ts { || kind === SyntaxKind.CallSignature || kind === SyntaxKind.PropertySignature || kind === SyntaxKind.MethodSignature - || kind === SyntaxKind.IndexSignature - || kind === SyntaxKind.MissingDeclaration; + || kind === SyntaxKind.IndexSignature; } export function isClassOrTypeElement(node: Node): node is ClassElement | TypeElement { @@ -5659,8 +5657,7 @@ namespace ts { || kind === SyntaxKind.SpreadAssignment || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor - || kind === SyntaxKind.MissingDeclaration; + || kind === SyntaxKind.SetAccessor; } // Type diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index f5f60338aa9..fe1f28e1181 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1160,7 +1160,7 @@ declare namespace ts { interface DebuggerStatement extends Statement { kind: SyntaxKind.DebuggerStatement; } - interface MissingDeclaration extends DeclarationStatement, ClassElement, ObjectLiteralElement, TypeElement { + interface MissingDeclaration extends DeclarationStatement { kind: SyntaxKind.MissingDeclaration; name?: Identifier; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index b64ef40646c..16edf86582e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1160,7 +1160,7 @@ declare namespace ts { interface DebuggerStatement extends Statement { kind: SyntaxKind.DebuggerStatement; } - interface MissingDeclaration extends DeclarationStatement, ClassElement, ObjectLiteralElement, TypeElement { + interface MissingDeclaration extends DeclarationStatement { kind: SyntaxKind.MissingDeclaration; name?: Identifier; } From 7c5f5249ae0a7b5be78d4f3549860e532208073d Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 18 Apr 2018 11:05:56 -0700 Subject: [PATCH 40/44] Renames as per PR feedback --- src/server/project.ts | 12 ++++++------ src/server/scriptInfo.ts | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/server/project.ts b/src/server/project.ts index 653f2943d0a..92645cfca52 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -99,7 +99,7 @@ namespace ts.server { /*@internal*/ lastCachedUnresolvedImportsList: SortedReadonlyArray; /*@internal*/ - private hasMoreOrLessFiles = false; + private hasAddedorRemovedFiles = false; private lastFileExceededProgramSize: string | undefined; @@ -777,8 +777,8 @@ namespace ts.server { } /* @internal */ - setHasMoreOrLessFiles() { - this.hasMoreOrLessFiles = true; + onFileAddedOrRemoved() { + this.hasAddedorRemovedFiles = true; } /** @@ -789,8 +789,8 @@ namespace ts.server { this.resolutionCache.startRecordingFilesWithChangedResolutions(); const hasNewProgram = this.updateGraphWorker(); - const hasMoreOrLessFiles = this.hasMoreOrLessFiles; - this.hasMoreOrLessFiles = false; + const hasAddedorRemovedFiles = this.hasAddedorRemovedFiles; + this.hasAddedorRemovedFiles = false; const changedFiles: ReadonlyArray = this.resolutionCache.finishRecordingFilesWithChangedResolutions() || emptyArray; @@ -820,7 +820,7 @@ namespace ts.server { this.lastCachedUnresolvedImportsList = result ? toDeduplicatedSortedArray(result) : emptyArray; } - this.projectService.typingsCache.enqueueInstallTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasMoreOrLessFiles); + this.projectService.typingsCache.enqueueInstallTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasAddedorRemovedFiles); } else { this.lastCachedUnresolvedImportsList = undefined; diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index cc4ebd519e5..589975769b1 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -304,7 +304,7 @@ namespace ts.server { const isNew = !this.isAttached(project); if (isNew) { this.containingProjects.push(project); - project.setHasMoreOrLessFiles(); + project.onFileAddedOrRemoved(); if (!project.getCompilerOptions().preserveSymlinks) { this.ensureRealPath(); } @@ -329,23 +329,23 @@ namespace ts.server { return; case 1: if (this.containingProjects[0] === project) { - project.setHasMoreOrLessFiles(); + project.onFileAddedOrRemoved(); this.containingProjects.pop(); } break; case 2: if (this.containingProjects[0] === project) { - project.setHasMoreOrLessFiles(); + project.onFileAddedOrRemoved(); this.containingProjects[0] = this.containingProjects.pop(); } else if (this.containingProjects[1] === project) { - project.setHasMoreOrLessFiles(); + project.onFileAddedOrRemoved(); this.containingProjects.pop(); } break; default: if (unorderedRemoveItem(this.containingProjects, project)) { - project.setHasMoreOrLessFiles(); + project.onFileAddedOrRemoved(); } break; } From 0e9b815956b0c3d7a110008dd3f1a1ac165259d6 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 18 Apr 2018 12:58:16 -0700 Subject: [PATCH 41/44] Improve performance of duplicate check (#23516) --- src/harness/fourslash.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 8800f78e6fd..75915c915ed 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2103,14 +2103,11 @@ Actual: ${stringify(fullActual)}`); this.raiseError("verifyRangesInImplementationList failed - expected to find at least one implementation location but got 0"); } - for (let i = 0; i < implementations.length; i++) { - for (let j = 0; j < implementations.length; j++) { - if (i !== j && implementationsAreEqual(implementations[i], implementations[j])) { - const { textSpan, fileName } = implementations[i]; - const end = textSpan.start + textSpan.length; - this.raiseError(`Duplicate implementations returned for range (${textSpan.start}, ${end}) in ${fileName}`); - } - } + const duplicate = findDuplicatedElement(implementations, implementationsAreEqual); + if (duplicate) { + const { textSpan, fileName } = duplicate; + const end = textSpan.start + textSpan.length; + this.raiseError(`Duplicate implementations returned for range (${textSpan.start}, ${end}) in ${fileName}`); } const ranges = this.getRanges(); @@ -3756,6 +3753,16 @@ ${code} function stripWhitespace(s: string): string { return s.replace(/\s/g, ""); } + + function findDuplicatedElement(a: ReadonlyArray, equal: (a: T, b: T) => boolean): T { + for (let i = 0; i < a.length; i++) { + for (let j = i + 1; j < a.length; j++) { + if (equal(a[i], a[j])) { + return a[i]; + } + } + } + } } namespace FourSlashInterface { From b271df1639102dc301ef9ab735915cc7eddb91ab Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 18 Apr 2018 12:58:40 -0700 Subject: [PATCH 42/44] Simplify getParentSymbolsOfPropertyAccess (#23513) --- src/services/findAllReferences.ts | 32 +++++++------------------------ 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 268af57e5a3..2812c051bd3 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1030,10 +1030,6 @@ namespace ts.FindAllReferences.Core { } } - function getPropertyAccessExpressionFromRightHandSide(node: Node): PropertyAccessExpression { - return isRightSideOfPropertyAccess(node) && node.parent; - } - /** * `classSymbol` is the class where the constructor was defined. * Reference the constructor and all calls to `new this()`. @@ -1130,18 +1126,6 @@ namespace ts.FindAllReferences.Core { } } - function getSymbolsForClassAndInterfaceComponents(type: UnionOrIntersectionType, result: Symbol[] = []): Symbol[] { - for (const componentType of type.types) { - if (componentType.symbol && componentType.symbol.getFlags() & (SymbolFlags.Class | SymbolFlags.Interface)) { - result.push(componentType.symbol); - } - if (componentType.isUnionOrIntersection()) { - getSymbolsForClassAndInterfaceComponents(componentType, result); - } - } - return result; - } - function getContainingTypeReference(node: Node): Node { let topLevelTypeReference: Node; @@ -1462,7 +1446,7 @@ namespace ts.FindAllReferences.Core { ? rootSymbol && !(getCheckFlags(sym) & CheckFlags.Synthetic) ? rootSymbol : sym : undefined, /*allowBaseTypes*/ rootSymbol => - !(search.parents && !some(search.parents, parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, checker)))); + !(search.parents && !search.parents.some(parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, checker)))); } /** Gets all symbols for one property. Does not get symbols for every property. */ @@ -1549,13 +1533,11 @@ namespace ts.FindAllReferences.Core { * symbol may have a different parent symbol if the local type's symbol does not declare the property * being accessed (i.e. it is declared in some parent class or interface) */ - function getParentSymbolsOfPropertyAccess(location: Node, symbol: Symbol, checker: TypeChecker): Symbol[] | undefined { - const propertyAccessExpression = getPropertyAccessExpressionFromRightHandSide(location); - const localParentType = propertyAccessExpression && checker.getTypeAtLocation(propertyAccessExpression.expression); - return localParentType && localParentType.symbol && localParentType.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) && localParentType.symbol !== symbol.parent - ? [localParentType.symbol] - : localParentType && localParentType.isUnionOrIntersection() - ? getSymbolsForClassAndInterfaceComponents(localParentType) - : undefined; + function getParentSymbolsOfPropertyAccess(location: Node, symbol: Symbol, checker: TypeChecker): ReadonlyArray | undefined { + const propertyAccessExpression = isRightSideOfPropertyAccess(location) ? location.parent : undefined; + const lhsType = propertyAccessExpression && checker.getTypeAtLocation(propertyAccessExpression.expression); + const res = mapDefined(lhsType && (lhsType.isUnionOrIntersection() ? lhsType.types : lhsType.symbol === symbol.parent ? undefined : [lhsType]), t => + t.symbol && t.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? t.symbol : undefined); + return res.length === 0 ? undefined : res; } } From 8f1bdc7e18ec7ffa7ae62571f4d5733dac193470 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 18 Apr 2018 15:24:02 -0700 Subject: [PATCH 43/44] findAllReferences: Reduce node.getSourceFile() calls (#23524) * findAllReferences: Reduce node.getSourceFile() calls * Don't create extra object --- src/services/findAllReferences.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 2812c051bd3..b7baf7166b7 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -130,8 +130,7 @@ namespace ts.FindAllReferences { const { node, name, kind, displayParts } = info; const sourceFile = node.getSourceFile(); - const textSpan = getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile); - return { containerKind: ScriptElementKind.unknown, containerName: "", fileName: sourceFile.fileName, kind, name, textSpan, displayParts }; + return { containerKind: ScriptElementKind.unknown, containerName: "", fileName: sourceFile.fileName, kind, name, textSpan: getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile), displayParts }; } function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, node: Node): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } { @@ -148,21 +147,23 @@ namespace ts.FindAllReferences { } const { node, isInString } = entry; + const sourceFile = node.getSourceFile(); return { - fileName: node.getSourceFile().fileName, - textSpan: getTextSpan(node), + fileName: sourceFile.fileName, + textSpan: getTextSpan(node, sourceFile), isWriteAccess: isWriteAccessForReference(node), isDefinition: node.kind === SyntaxKind.DefaultKeyword || isAnyDeclarationName(node) || isLiteralComputedPropertyDeclarationName(node), - isInString + isInString, }; } function toImplementationLocation(entry: Entry, checker: TypeChecker): ImplementationLocation { if (entry.type === "node") { const { node } = entry; - return { textSpan: getTextSpan(node), fileName: node.getSourceFile().fileName, ...implementationKindDisplayParts(node, checker) }; + const sourceFile = node.getSourceFile(); + return { textSpan: getTextSpan(node, sourceFile), fileName: sourceFile.fileName, ...implementationKindDisplayParts(node, checker) }; } else { const { textSpan, fileName } = entry; @@ -199,17 +200,17 @@ namespace ts.FindAllReferences { } const { node, isInString } = entry; - const fileName = entry.node.getSourceFile().fileName; + const sourceFile = node.getSourceFile(); const writeAccess = isWriteAccessForReference(node); const span: HighlightSpan = { - textSpan: getTextSpan(node), + textSpan: getTextSpan(node, sourceFile), kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference, isInString }; - return { fileName, span }; + return { fileName: sourceFile.fileName, span }; } - function getTextSpan(node: Node, sourceFile?: SourceFile): TextSpan { + function getTextSpan(node: Node, sourceFile: SourceFile): TextSpan { let start = node.getStart(sourceFile); let end = node.getEnd(); if (node.kind === SyntaxKind.StringLiteral) { From 2f6b59eab9e9e77c133798097b0359e5be009c53 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 18 Apr 2018 15:24:19 -0700 Subject: [PATCH 44/44] Misc. improvements to addImplementationReferences (#23507) * Misc. improvements to addImplementationReferences * Test typeHavingNode.type === typeNode * Fix bug: refNode.parent -> refNode --- src/services/findAllReferences.ts | 60 +++++++++++-------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index b7baf7166b7..60f53c2f527 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1102,57 +1102,37 @@ namespace ts.FindAllReferences.Core { } // If we got a type reference, try and see if the reference applies to any expressions that can implement an interface - const containingTypeReference = getContainingTypeReference(refNode); - if (containingTypeReference && state.markSeenContainingTypeReference(containingTypeReference)) { - const parent = containingTypeReference.parent; - if (hasType(parent) && parent.type === containingTypeReference && hasInitializer(parent) && isImplementationExpression(parent.initializer)) { - addReference(parent.initializer); + // Find the first node whose parent isn't a type node -- i.e., the highest type node. + const typeNode = findAncestor(refNode, a => !isQualifiedName(a.parent) && !isTypeNode(a.parent) && !isTypeElement(a.parent)); + const typeHavingNode = typeNode.parent; + if (hasType(typeHavingNode) && typeHavingNode.type === typeNode && state.markSeenContainingTypeReference(typeHavingNode)) { + if (hasInitializer(typeHavingNode)) { + addIfImplementation(typeHavingNode.initializer); } - else if (isFunctionLike(parent) && parent.type === containingTypeReference && (parent as FunctionLikeDeclaration).body) { - const body = (parent as FunctionLikeDeclaration).body; + else if (isFunctionLike(typeHavingNode) && (typeHavingNode as FunctionLikeDeclaration).body) { + const body = (typeHavingNode as FunctionLikeDeclaration).body; if (body.kind === SyntaxKind.Block) { forEachReturnStatement(body, returnStatement => { - if (returnStatement.expression && isImplementationExpression(returnStatement.expression)) { - addReference(returnStatement.expression); - } + if (returnStatement.expression) addIfImplementation(returnStatement.expression); }); } - else if (isImplementationExpression(body)) { - addReference(body); + else { + addIfImplementation(body); } } - else if (isAssertionExpression(parent) && isImplementationExpression(parent.expression)) { - addReference(parent.expression); + else if (isAssertionExpression(typeHavingNode)) { + addIfImplementation(typeHavingNode.expression); } } + + function addIfImplementation(e: Expression): void { + if (isImplementationExpression(e)) addReference(e); + } } - function getContainingTypeReference(node: Node): Node { - let topLevelTypeReference: Node; - - while (node) { - if (isTypeNode(node)) { - topLevelTypeReference = node; - } - node = node.parent; - } - - return topLevelTypeReference; - } - - function getContainingClassIfInHeritageClause(node: Node): ClassLikeDeclaration { - if (node && node.parent) { - if (node.kind === SyntaxKind.ExpressionWithTypeArguments - && node.parent.kind === SyntaxKind.HeritageClause - && isClassLike(node.parent.parent)) { - return node.parent.parent; - } - - else if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) { - return getContainingClassIfInHeritageClause(node.parent); - } - } - return undefined; + function getContainingClassIfInHeritageClause(node: Node): ClassLikeDeclaration | InterfaceDeclaration { + return isIdentifier(node) || isPropertyAccessExpression(node) ? getContainingClassIfInHeritageClause(node.parent) + : isExpressionWithTypeArguments(node) ? tryCast(node.parent.parent, isClassLike) : undefined; } /**