From 49ba408e4fed08e328dfff2614611c500ee53bb0 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 16 Jul 2019 10:14:06 -0700 Subject: [PATCH 01/16] Handle scoped package names in typing installer Fixes #32075 --- src/jsTyping/jsTyping.ts | 75 +++++++++++++------ .../unittests/tsserver/typingsInstaller.ts | 64 +++++++++++----- src/tsserver/server.ts | 2 +- src/typingsInstallerCore/typingsInstaller.ts | 29 +++---- 4 files changed, 114 insertions(+), 56 deletions(-) diff --git a/src/jsTyping/jsTyping.ts b/src/jsTyping/jsTyping.ts index c2b2ad3f5b3..172d041cc01 100644 --- a/src/jsTyping/jsTyping.ts +++ b/src/jsTyping/jsTyping.ts @@ -289,9 +289,8 @@ namespace ts.JsTyping { } - export const enum PackageNameValidationResult { + export const enum NameValidationResult { Ok, - ScopedPackagesNotSupported, EmptyName, NameTooLong, NameStartsWithDot, @@ -301,49 +300,77 @@ namespace ts.JsTyping { const maxPackageNameLength = 214; + export interface ScopedPackageNameValidationResult { + name: string; + isScopeName: boolean; + result: NameValidationResult; + } + export type PackageNameValidationResult = NameValidationResult | ScopedPackageNameValidationResult; + /** * Validates package name using rules defined at https://docs.npmjs.com/files/package.json */ export function validatePackageName(packageName: string): PackageNameValidationResult { + return validatePackageNameWorker(packageName, /*supportScopedPackage*/ true); + } + + function validatePackageNameWorker(packageName: string, supportScopedPackage: false): NameValidationResult; + function validatePackageNameWorker(packageName: string, supportScopedPackage: true): PackageNameValidationResult; + function validatePackageNameWorker(packageName: string, supportScopedPackage: boolean): PackageNameValidationResult { if (!packageName) { - return PackageNameValidationResult.EmptyName; + return NameValidationResult.EmptyName; } if (packageName.length > maxPackageNameLength) { - return PackageNameValidationResult.NameTooLong; + return NameValidationResult.NameTooLong; } if (packageName.charCodeAt(0) === CharacterCodes.dot) { - return PackageNameValidationResult.NameStartsWithDot; + return NameValidationResult.NameStartsWithDot; } if (packageName.charCodeAt(0) === CharacterCodes._) { - return PackageNameValidationResult.NameStartsWithUnderscore; + return NameValidationResult.NameStartsWithUnderscore; } // check if name is scope package like: starts with @ and has one '/' in the middle // scoped packages are not currently supported - // TODO: when support will be added we'll need to split and check both scope and package name - if (/^@[^/]+\/[^/]+$/.test(packageName)) { - return PackageNameValidationResult.ScopedPackagesNotSupported; + if (supportScopedPackage) { + const matches = /^@([^/]+)\/([^/]+)$/.exec(packageName); + if (matches) { + const scopeResult = validatePackageNameWorker(matches[1], /*supportScopedPackage*/ false); + if (scopeResult !== NameValidationResult.Ok) { + return { name: matches[1], isScopeName: true, result: scopeResult }; + } + const packageResult = validatePackageNameWorker(matches[2], /*supportScopedPackage*/ false); + if (packageResult !== NameValidationResult.Ok) { + return { name: matches[2], isScopeName: false, result: packageResult }; + } + return NameValidationResult.Ok; + } } if (encodeURIComponent(packageName) !== packageName) { - return PackageNameValidationResult.NameContainsNonURISafeCharacters; + return NameValidationResult.NameContainsNonURISafeCharacters; } - return PackageNameValidationResult.Ok; + return NameValidationResult.Ok; } export function renderPackageNameValidationFailure(result: PackageNameValidationResult, typing: string): string { + return typeof result === "object" ? + renderPackageNameValidationFailureWorker(typing, result.result, result.name, result.isScopeName) : + renderPackageNameValidationFailureWorker(typing, result, typing, /*isScopeName*/ false); + } + + function renderPackageNameValidationFailureWorker(typing: string, result: NameValidationResult, name: string, isScopeName: boolean): string { + const kind = isScopeName ? "Scope" : "Package"; switch (result) { - case PackageNameValidationResult.EmptyName: - return `Package name '${typing}' cannot be empty`; - case PackageNameValidationResult.NameTooLong: - return `Package name '${typing}' should be less than ${maxPackageNameLength} characters`; - case PackageNameValidationResult.NameStartsWithDot: - return `Package name '${typing}' cannot start with '.'`; - case PackageNameValidationResult.NameStartsWithUnderscore: - return `Package name '${typing}' cannot start with '_'`; - case PackageNameValidationResult.ScopedPackagesNotSupported: - return `Package '${typing}' is scoped and currently is not supported`; - case PackageNameValidationResult.NameContainsNonURISafeCharacters: - return `Package name '${typing}' contains non URI safe characters`; - case PackageNameValidationResult.Ok: + case NameValidationResult.EmptyName: + return `'${typing}':: ${kind} name '${name}' cannot be empty`; + case NameValidationResult.NameTooLong: + return `'${typing}':: ${kind} name '${name}' should be less than ${maxPackageNameLength} characters`; + case NameValidationResult.NameStartsWithDot: + return `'${typing}':: ${kind} name '${name}' cannot start with '.'`; + case NameValidationResult.NameStartsWithUnderscore: + return `'${typing}':: ${kind} name '${name}' cannot start with '_'`; + case NameValidationResult.NameContainsNonURISafeCharacters: + return `'${typing}':: ${kind} name '${name}' contains non URI safe characters`; + case NameValidationResult.Ok: return Debug.fail(); // Shouldn't have called this. default: throw Debug.assertNever(result); diff --git a/src/testRunner/unittests/tsserver/typingsInstaller.ts b/src/testRunner/unittests/tsserver/typingsInstaller.ts index b02adbbd094..79b4f01aa06 100644 --- a/src/testRunner/unittests/tsserver/typingsInstaller.ts +++ b/src/testRunner/unittests/tsserver/typingsInstaller.ts @@ -1,6 +1,6 @@ namespace ts.projectSystem { import validatePackageName = JsTyping.validatePackageName; - import PackageNameValidationResult = JsTyping.PackageNameValidationResult; + import NameValidationResult = JsTyping.NameValidationResult; interface InstallerParams { globalTypingsCacheLocation?: string; @@ -948,7 +948,8 @@ namespace ts.projectSystem { path: "/a/b/app.js", content: ` import * as fs from "fs"; - import * as commander from "commander";` + import * as commander from "commander"; + import * as component from "@ember/component";` }; const cachePath = "/a/cache"; const node = { @@ -959,14 +960,19 @@ namespace ts.projectSystem { path: cachePath + "/node_modules/@types/commander/index.d.ts", content: "export let y: string" }; + const emberComponentDirectory = "ember__component"; + const emberComponent = { + path: `${cachePath}/node_modules/@types/${emberComponentDirectory}/index.d.ts`, + content: "export let x: number" + }; const host = createServerHost([file]); const installer = new (class extends Installer { constructor() { super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("node", "commander") }); } installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) { - const installedTypings = ["@types/node", "@types/commander"]; - const typingFiles = [node, commander]; + const installedTypings = ["@types/node", "@types/commander", `@types/${emberComponentDirectory}`]; + const typingFiles = [node, commander, emberComponent]; executeCommand(this, host, installedTypings, typingFiles, cb); } })(); @@ -980,9 +986,10 @@ namespace ts.projectSystem { assert.isTrue(host.fileExists(node.path), "typings for 'node' should be created"); assert.isTrue(host.fileExists(commander.path), "typings for 'commander' should be created"); + assert.isTrue(host.fileExists(emberComponent.path), "typings for 'commander' should be created"); host.checkTimeoutQueueLengthAndRun(2); - checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path]); + checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path, emberComponent.path]); }); it("should redo resolution that resolved to '.js' file after typings are installed", () => { @@ -1263,21 +1270,44 @@ namespace ts.projectSystem { for (let i = 0; i < 8; i++) { packageName += packageName; } - assert.equal(validatePackageName(packageName), PackageNameValidationResult.NameTooLong); + assert.equal(validatePackageName(packageName), NameValidationResult.NameTooLong); }); - it("name cannot start with dot", () => { - assert.equal(validatePackageName(".foo"), PackageNameValidationResult.NameStartsWithDot); + it("package name cannot start with dot", () => { + assert.equal(validatePackageName(".foo"), NameValidationResult.NameStartsWithDot); }); - it("name cannot start with underscore", () => { - assert.equal(validatePackageName("_foo"), PackageNameValidationResult.NameStartsWithUnderscore); + it("package name cannot start with underscore", () => { + assert.equal(validatePackageName("_foo"), NameValidationResult.NameStartsWithUnderscore); }); - it("scoped packages not supported", () => { - assert.equal(validatePackageName("@scope/bar"), PackageNameValidationResult.ScopedPackagesNotSupported); + it("package non URI safe characters are not supported", () => { + assert.equal(validatePackageName(" scope "), NameValidationResult.NameContainsNonURISafeCharacters); + assert.equal(validatePackageName("; say ‘Hello from TypeScript!’ #"), NameValidationResult.NameContainsNonURISafeCharacters); + assert.equal(validatePackageName("a/b/c"), NameValidationResult.NameContainsNonURISafeCharacters); }); - it("non URI safe characters are not supported", () => { - assert.equal(validatePackageName(" scope "), PackageNameValidationResult.NameContainsNonURISafeCharacters); - assert.equal(validatePackageName("; say ‘Hello from TypeScript!’ #"), PackageNameValidationResult.NameContainsNonURISafeCharacters); - assert.equal(validatePackageName("a/b/c"), PackageNameValidationResult.NameContainsNonURISafeCharacters); + it("scoped package name is supported", () => { + assert.equal(validatePackageName("@scope/bar"), NameValidationResult.Ok); + }); + it("scoped name in scoped package name cannot start with dot", () => { + assert.deepEqual(validatePackageName("@.scope/bar"), { name: ".scope", isScopeName: true, result: NameValidationResult.NameStartsWithDot }); + assert.deepEqual(validatePackageName("@.scope/.bar"), { name: ".scope", isScopeName: true, result: NameValidationResult.NameStartsWithDot }); + }); + it("scope name in scoped package name cannot start with underscore", () => { + assert.deepEqual(validatePackageName("@_scope/bar"), { name: "_scope", isScopeName: true, result: NameValidationResult.NameStartsWithUnderscore }); + assert.deepEqual(validatePackageName("@_scope/_bar"), { name: "_scope", isScopeName: true, result: NameValidationResult.NameStartsWithUnderscore }); + }); + it("scope name in scoped package name with non URI safe characters are not supported", () => { + assert.deepEqual(validatePackageName("@ scope /bar"), { name: " scope ", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters }); + assert.deepEqual(validatePackageName("@; say ‘Hello from TypeScript!’ #/bar"), { name: "; say ‘Hello from TypeScript!’ #", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters }); + assert.deepEqual(validatePackageName("@ scope / bar "), { name: " scope ", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters }); + }); + it("package name in scoped package name cannot start with dot", () => { + assert.deepEqual(validatePackageName("@scope/.bar"), { name: ".bar", isScopeName: false, result: NameValidationResult.NameStartsWithDot }); + }); + it("package name in scoped package name cannot start with underscore", () => { + assert.deepEqual(validatePackageName("@scope/_bar"), { name: "_bar", isScopeName: false, result: NameValidationResult.NameStartsWithUnderscore }); + }); + it("package name in scoped package name with non URI safe characters are not supported", () => { + assert.deepEqual(validatePackageName("@scope/ bar "), { name: " bar ", isScopeName: false, result: NameValidationResult.NameContainsNonURISafeCharacters }); + assert.deepEqual(validatePackageName("@scope/; say ‘Hello from TypeScript!’ #"), { name: "; say ‘Hello from TypeScript!’ #", isScopeName: false, result: NameValidationResult.NameContainsNonURISafeCharacters }); }); }); @@ -1309,7 +1339,7 @@ namespace ts.projectSystem { projectService.openClientFile(f1.path); installer.checkPendingCommands(/*expectedCount*/ 0); - assert.isTrue(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name"); + assert.isTrue(messages.indexOf("'; say ‘Hello from TypeScript!’ #':: Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name"); }); }); diff --git a/src/tsserver/server.ts b/src/tsserver/server.ts index 43dc4638418..a9fbf2f3b6a 100644 --- a/src/tsserver/server.ts +++ b/src/tsserver/server.ts @@ -248,7 +248,7 @@ namespace ts.server { isKnownTypesPackageName(name: string): boolean { // We want to avoid looking this up in the registry as that is expensive. So first check that it's actually an NPM package. const validationResult = JsTyping.validatePackageName(name); - if (validationResult !== JsTyping.PackageNameValidationResult.Ok) { + if (validationResult !== JsTyping.NameValidationResult.Ok) { return false; } diff --git a/src/typingsInstallerCore/typingsInstaller.ts b/src/typingsInstallerCore/typingsInstaller.ts index df83f1a677c..17dae3b4dcb 100644 --- a/src/typingsInstallerCore/typingsInstaller.ts +++ b/src/typingsInstallerCore/typingsInstaller.ts @@ -268,27 +268,28 @@ namespace ts.server.typingsInstaller { } private filterTypings(typingsToInstall: ReadonlyArray): ReadonlyArray { - return typingsToInstall.filter(typing => { - if (this.missingTypingsSet.get(typing)) { - if (this.log.isEnabled()) this.log.writeLine(`'${typing}' is in missingTypingsSet - skipping...`); - return false; + return mapDefined(typingsToInstall, typing => { + const typingKey = mangleScopedPackageName(typing); + if (this.missingTypingsSet.get(typingKey)) { + if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: '${typingKey}' is in missingTypingsSet - skipping...`); + return undefined; } const validationResult = JsTyping.validatePackageName(typing); - if (validationResult !== JsTyping.PackageNameValidationResult.Ok) { + if (validationResult !== JsTyping.NameValidationResult.Ok) { // add typing name to missing set so we won't process it again - this.missingTypingsSet.set(typing, true); + this.missingTypingsSet.set(typingKey, true); if (this.log.isEnabled()) this.log.writeLine(JsTyping.renderPackageNameValidationFailure(validationResult, typing)); - return false; + return undefined; } - if (!this.typesRegistry.has(typing)) { - if (this.log.isEnabled()) this.log.writeLine(`Entry for package '${typing}' does not exist in local types registry - skipping...`); - return false; + if (!this.typesRegistry.has(typingKey)) { + if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: Entry for package '${typingKey}' does not exist in local types registry - skipping...`); + return undefined; } - if (this.packageNameToTypingLocation.get(typing) && JsTyping.isTypingUpToDate(this.packageNameToTypingLocation.get(typing)!, this.typesRegistry.get(typing)!)) { - if (this.log.isEnabled()) this.log.writeLine(`'${typing}' already has an up-to-date typing - skipping...`); - return false; + if (this.packageNameToTypingLocation.get(typingKey) && JsTyping.isTypingUpToDate(this.packageNameToTypingLocation.get(typingKey)!, this.typesRegistry.get(typingKey)!)) { + if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: '${typingKey}' already has an up-to-date typing - skipping...`); + return undefined; } - return true; + return typingKey; }); } From 049618f7daf63332d7f972eb0ae4e6af3bcd55bd Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 16 Jul 2019 17:16:21 -0700 Subject: [PATCH 02/16] Get contextual type of yield from contextual signature of containing function (#32433) * Get contextual type of yield from contextual signature of containing function * Add missing baseline --- src/compiler/checker.ts | 6 +++ .../reference/generatorTypeCheck25.types | 6 +-- .../reference/generatorTypeCheck28.types | 2 +- .../reference/generatorTypeCheck45.types | 2 +- .../reference/generatorTypeCheck46.types | 2 +- .../reference/generatorTypeCheck62.types | 6 +-- .../reference/generatorTypeCheck63.types | 4 +- .../generatorYieldContextualType.symbols | 44 +++++++++++++++++++ .../generatorYieldContextualType.types | 38 ++++++++++++++++ .../types.asyncGenerators.es2018.1.types | 12 ++--- .../types.asyncGenerators.es2018.2.types | 6 +-- tests/baselines/reference/uniqueSymbols.types | 4 +- .../reference/uniqueSymbolsDeclarations.types | 4 +- .../generatorYieldContextualType.ts | 14 ++++++ 14 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 tests/baselines/reference/generatorYieldContextualType.symbols create mode 100644 tests/baselines/reference/generatorYieldContextualType.types create mode 100644 tests/cases/conformance/generators/generatorYieldContextualType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 06395d935b7..20a4246f372 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24740,6 +24740,12 @@ namespace ts { || anyType; } + const contextualReturnType = getContextualReturnType(func); + if (contextualReturnType) { + return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, contextualReturnType, isAsync) + || anyType; + } + return anyType; } diff --git a/tests/baselines/reference/generatorTypeCheck25.types b/tests/baselines/reference/generatorTypeCheck25.types index d0498c2da16..12dbfeaede9 100644 --- a/tests/baselines/reference/generatorTypeCheck25.types +++ b/tests/baselines/reference/generatorTypeCheck25.types @@ -17,15 +17,15 @@ var g3: () => Iterable = function* () { >function* () { yield; yield new Bar; yield new Baz; yield *[new Bar]; yield *[new Baz];} : () => Generator yield; ->yield : any +>yield : undefined yield new Bar; ->yield new Bar : any +>yield new Bar : undefined >new Bar : Bar >Bar : typeof Bar yield new Baz; ->yield new Baz : any +>yield new Baz : undefined >new Baz : Baz >Baz : typeof Baz diff --git a/tests/baselines/reference/generatorTypeCheck28.types b/tests/baselines/reference/generatorTypeCheck28.types index 9cd4e5e82ce..6921c946a39 100644 --- a/tests/baselines/reference/generatorTypeCheck28.types +++ b/tests/baselines/reference/generatorTypeCheck28.types @@ -14,7 +14,7 @@ function* g(): IterableIterator<(x: string) => number> { >iterator : symbol yield x => x.length; ->yield x => x.length : any +>yield x => x.length : undefined >x => x.length : (x: string) => number >x : string >x.length : number diff --git a/tests/baselines/reference/generatorTypeCheck45.types b/tests/baselines/reference/generatorTypeCheck45.types index cf409241947..18d3e9a5fc2 100644 --- a/tests/baselines/reference/generatorTypeCheck45.types +++ b/tests/baselines/reference/generatorTypeCheck45.types @@ -12,7 +12,7 @@ foo("", function* () { yield x => x.length }, p => undefined); // T is fixed, sh >foo : (x: T, fun: () => Iterator<(x: T) => U, any, undefined>, fun2: (y: U) => T) => T >"" : "" >function* () { yield x => x.length } : () => Generator<(x: string) => number, void, unknown> ->yield x => x.length : any +>yield x => x.length : undefined >x => x.length : (x: string) => number >x : string >x.length : number diff --git a/tests/baselines/reference/generatorTypeCheck46.types b/tests/baselines/reference/generatorTypeCheck46.types index cd565d55911..283188193b7 100644 --- a/tests/baselines/reference/generatorTypeCheck46.types +++ b/tests/baselines/reference/generatorTypeCheck46.types @@ -24,7 +24,7 @@ foo("", function* () { >iterator : symbol yield x => x.length ->yield x => x.length : any +>yield x => x.length : undefined >x => x.length : (x: string) => number >x : string >x.length : number diff --git a/tests/baselines/reference/generatorTypeCheck62.types b/tests/baselines/reference/generatorTypeCheck62.types index be5635a07fc..ed957295c31 100644 --- a/tests/baselines/reference/generatorTypeCheck62.types +++ b/tests/baselines/reference/generatorTypeCheck62.types @@ -32,7 +32,7 @@ export function strategy(stratName: string, gen: (a: T >stratName : string } yield next; ->yield next : any +>yield next : undefined >next : T } } @@ -70,7 +70,7 @@ export const Nothing2: Strategy = strategy("Nothing", function*(state: St >state : State yield state; ->yield state : any +>yield state : undefined >state : State }); @@ -84,7 +84,7 @@ export const Nothing3: Strategy = strategy("Nothing", function* (state: S >state : State yield ; ->yield : any +>yield : undefined return state; >state : State diff --git a/tests/baselines/reference/generatorTypeCheck63.types b/tests/baselines/reference/generatorTypeCheck63.types index 8a1d03dcb16..64d67083e61 100644 --- a/tests/baselines/reference/generatorTypeCheck63.types +++ b/tests/baselines/reference/generatorTypeCheck63.types @@ -32,7 +32,7 @@ export function strategy(stratName: string, gen: (a: T >stratName : string } yield next; ->yield next : any +>yield next : undefined >next : T } } @@ -97,7 +97,7 @@ export const Nothing3: Strategy = strategy("Nothing", function* (state: S >state : State yield state; ->yield state : any +>yield state : undefined >state : State return 1; diff --git a/tests/baselines/reference/generatorYieldContextualType.symbols b/tests/baselines/reference/generatorYieldContextualType.symbols new file mode 100644 index 00000000000..80d20b90d75 --- /dev/null +++ b/tests/baselines/reference/generatorYieldContextualType.symbols @@ -0,0 +1,44 @@ +=== tests/cases/conformance/generators/generatorYieldContextualType.ts === +declare function f1(gen: () => Generator): void; +>f1 : Symbol(f1, Decl(generatorYieldContextualType.ts, 0, 0)) +>T : Symbol(T, Decl(generatorYieldContextualType.ts, 0, 20)) +>R : Symbol(R, Decl(generatorYieldContextualType.ts, 0, 22)) +>S : Symbol(S, Decl(generatorYieldContextualType.ts, 0, 25)) +>gen : Symbol(gen, Decl(generatorYieldContextualType.ts, 0, 29)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +>R : Symbol(R, Decl(generatorYieldContextualType.ts, 0, 22)) +>T : Symbol(T, Decl(generatorYieldContextualType.ts, 0, 20)) +>S : Symbol(S, Decl(generatorYieldContextualType.ts, 0, 25)) + +f1<0, 0, 1>(function* () { +>f1 : Symbol(f1, Decl(generatorYieldContextualType.ts, 0, 0)) + + const a = yield 0; +>a : Symbol(a, Decl(generatorYieldContextualType.ts, 2, 6)) + + return 0; +}); + +declare function f2(gen: () => Generator | AsyncGenerator): void; +>f2 : Symbol(f2, Decl(generatorYieldContextualType.ts, 4, 3)) +>T : Symbol(T, Decl(generatorYieldContextualType.ts, 6, 20)) +>R : Symbol(R, Decl(generatorYieldContextualType.ts, 6, 22)) +>S : Symbol(S, Decl(generatorYieldContextualType.ts, 6, 25)) +>gen : Symbol(gen, Decl(generatorYieldContextualType.ts, 6, 29)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +>R : Symbol(R, Decl(generatorYieldContextualType.ts, 6, 22)) +>T : Symbol(T, Decl(generatorYieldContextualType.ts, 6, 20)) +>S : Symbol(S, Decl(generatorYieldContextualType.ts, 6, 25)) +>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --)) +>R : Symbol(R, Decl(generatorYieldContextualType.ts, 6, 22)) +>T : Symbol(T, Decl(generatorYieldContextualType.ts, 6, 20)) +>S : Symbol(S, Decl(generatorYieldContextualType.ts, 6, 25)) + +f2<0, 0, 1>(async function* () { +>f2 : Symbol(f2, Decl(generatorYieldContextualType.ts, 4, 3)) + + const a = yield 0; +>a : Symbol(a, Decl(generatorYieldContextualType.ts, 8, 6)) + + return 0; +}); diff --git a/tests/baselines/reference/generatorYieldContextualType.types b/tests/baselines/reference/generatorYieldContextualType.types new file mode 100644 index 00000000000..5caccffa933 --- /dev/null +++ b/tests/baselines/reference/generatorYieldContextualType.types @@ -0,0 +1,38 @@ +=== tests/cases/conformance/generators/generatorYieldContextualType.ts === +declare function f1(gen: () => Generator): void; +>f1 : (gen: () => Generator) => void +>gen : () => Generator + +f1<0, 0, 1>(function* () { +>f1<0, 0, 1>(function* () { const a = yield 0; return 0;}) : void +>f1 : (gen: () => Generator) => void +>function* () { const a = yield 0; return 0;} : () => Generator<0, 0, unknown> + + const a = yield 0; +>a : 1 +>yield 0 : 1 +>0 : 0 + + return 0; +>0 : 0 + +}); + +declare function f2(gen: () => Generator | AsyncGenerator): void; +>f2 : (gen: () => Generator | AsyncGenerator) => void +>gen : () => Generator | AsyncGenerator + +f2<0, 0, 1>(async function* () { +>f2<0, 0, 1>(async function* () { const a = yield 0; return 0;}) : void +>f2 : (gen: () => Generator | AsyncGenerator) => void +>async function* () { const a = yield 0; return 0;} : () => AsyncGenerator<0, 0, unknown> + + const a = yield 0; +>a : 1 +>yield 0 : 1 +>0 : 0 + + return 0; +>0 : 0 + +}); diff --git a/tests/baselines/reference/types.asyncGenerators.es2018.1.types b/tests/baselines/reference/types.asyncGenerators.es2018.1.types index ad35f2a796f..24da9312c15 100644 --- a/tests/baselines/reference/types.asyncGenerators.es2018.1.types +++ b/tests/baselines/reference/types.asyncGenerators.es2018.1.types @@ -78,7 +78,7 @@ const assignability1: () => AsyncIterableIterator = async function * () >async function * () { yield 1;} : () => AsyncGenerator yield 1; ->yield 1 : any +>yield 1 : undefined >1 : 1 }; @@ -87,7 +87,7 @@ const assignability2: () => AsyncIterableIterator = async function * () >async function * () { yield Promise.resolve(1);} : () => AsyncGenerator yield Promise.resolve(1); ->yield Promise.resolve(1) : any +>yield Promise.resolve(1) : undefined >Promise.resolve(1) : Promise >Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } >Promise : PromiseConstructor @@ -138,7 +138,7 @@ const assignability6: () => AsyncIterable = async function * () { >async function * () { yield 1;} : () => AsyncGenerator yield 1; ->yield 1 : any +>yield 1 : undefined >1 : 1 }; @@ -147,7 +147,7 @@ const assignability7: () => AsyncIterable = async function * () { >async function * () { yield Promise.resolve(1);} : () => AsyncGenerator yield Promise.resolve(1); ->yield Promise.resolve(1) : any +>yield Promise.resolve(1) : undefined >Promise.resolve(1) : Promise >Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } >Promise : PromiseConstructor @@ -198,7 +198,7 @@ const assignability11: () => AsyncIterator = async function * () { >async function * () { yield 1;} : () => AsyncGenerator yield 1; ->yield 1 : any +>yield 1 : undefined >1 : 1 }; @@ -207,7 +207,7 @@ const assignability12: () => AsyncIterator = async function * () { >async function * () { yield Promise.resolve(1);} : () => AsyncGenerator yield Promise.resolve(1); ->yield Promise.resolve(1) : any +>yield Promise.resolve(1) : undefined >Promise.resolve(1) : Promise >Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } >Promise : PromiseConstructor diff --git a/tests/baselines/reference/types.asyncGenerators.es2018.2.types b/tests/baselines/reference/types.asyncGenerators.es2018.2.types index 23bf7225eb9..022ddd297a2 100644 --- a/tests/baselines/reference/types.asyncGenerators.es2018.2.types +++ b/tests/baselines/reference/types.asyncGenerators.es2018.2.types @@ -32,7 +32,7 @@ const assignability1: () => AsyncIterableIterator = async function * () >async function * () { yield "a";} : () => AsyncGenerator yield "a"; ->yield "a" : any +>yield "a" : undefined >"a" : "a" }; @@ -65,7 +65,7 @@ const assignability4: () => AsyncIterable = async function * () { >async function * () { yield "a";} : () => AsyncGenerator yield "a"; ->yield "a" : any +>yield "a" : undefined >"a" : "a" }; @@ -98,7 +98,7 @@ const assignability7: () => AsyncIterator = async function * () { >async function * () { yield "a";} : () => AsyncGenerator yield "a"; ->yield "a" : any +>yield "a" : undefined >"a" : "a" }; diff --git a/tests/baselines/reference/uniqueSymbols.types b/tests/baselines/reference/uniqueSymbols.types index da2f9895218..02ededf7b1f 100644 --- a/tests/baselines/reference/uniqueSymbols.types +++ b/tests/baselines/reference/uniqueSymbols.types @@ -839,7 +839,7 @@ const o3: Context = { >method3 : () => AsyncGenerator yield s; // yield type should not widen due to contextual type ->yield s : any +>yield s : undefined >s : unique symbol }, @@ -847,7 +847,7 @@ const o3: Context = { >method4 : () => Generator yield s; // yield type should not widen due to contextual type ->yield s : any +>yield s : undefined >s : unique symbol }, diff --git a/tests/baselines/reference/uniqueSymbolsDeclarations.types b/tests/baselines/reference/uniqueSymbolsDeclarations.types index b8c32385f4b..db198153153 100644 --- a/tests/baselines/reference/uniqueSymbolsDeclarations.types +++ b/tests/baselines/reference/uniqueSymbolsDeclarations.types @@ -832,7 +832,7 @@ const o4: Context = { >method3 : () => AsyncGenerator yield s; // yield type should not widen due to contextual type ->yield s : any +>yield s : undefined >s : unique symbol }, @@ -840,7 +840,7 @@ const o4: Context = { >method4 : () => Generator yield s; // yield type should not widen due to contextual type ->yield s : any +>yield s : undefined >s : unique symbol }, diff --git a/tests/cases/conformance/generators/generatorYieldContextualType.ts b/tests/cases/conformance/generators/generatorYieldContextualType.ts new file mode 100644 index 00000000000..20cad6a9189 --- /dev/null +++ b/tests/cases/conformance/generators/generatorYieldContextualType.ts @@ -0,0 +1,14 @@ +// @target: esnext +// @strict: true +// @noEmit: true +declare function f1(gen: () => Generator): void; +f1<0, 0, 1>(function* () { + const a = yield 0; + return 0; +}); + +declare function f2(gen: () => Generator | AsyncGenerator): void; +f2<0, 0, 1>(async function* () { + const a = yield 0; + return 0; +}); \ No newline at end of file From e6c723dd2aefc851642ba3b7c534986ae33f3d9b Mon Sep 17 00:00:00 2001 From: csigs Date: Wed, 17 Jul 2019 16:10:08 +0000 Subject: [PATCH 03/16] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl index 2c25256b224..07839d24475 100644 --- a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -1,4 +1,4 @@ - + @@ -3301,7 +3301,7 @@ - + @@ -4123,7 +4123,7 @@ - + From 246610957772c8801457ec9c8f32b0686edc0716 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 17 Jul 2019 13:07:10 -0700 Subject: [PATCH 04/16] Fix build/lint due to differences in master and LKG (#32450) --- src/compiler/emitter.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a1207c9b892..8681a452e2a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -411,7 +411,11 @@ namespace ts { } ); if (emitOnlyDtsFiles && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) { - const sourceFile = declarationTransform.transformed[0] as SourceFile; + // Improved narrowing in master/3.6 makes this cast unnecessary, triggering a lint rule. + // But at the same time, the LKG (3.5) necessitates it because it doesn’t narrow. + // Once the LKG is updated to 3.6, this comment, the cast to `SourceFile`, and the + // tslint directive can be all be removed. + const sourceFile = declarationTransform.transformed[0] as SourceFile; // tslint:disable-line exportedModulesFromDeclarationEmit = sourceFile.exportedModulesFromDeclarationEmit; } } From 8f2ed0ded88a978c283eab9a9184729b6f7e009f Mon Sep 17 00:00:00 2001 From: Milosz Piechocki Date: Wed, 17 Jul 2019 22:22:53 +0200 Subject: [PATCH 05/16] addTypeToIntersection performance improvement (#32388) --- src/compiler/checker.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 20a4246f372..1d6b9e842cc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9890,7 +9890,7 @@ namespace ts { return links.resolvedType; } - function addTypeToIntersection(typeSet: Type[], includes: TypeFlags, type: Type) { + function addTypeToIntersection(typeSet: Map, includes: TypeFlags, type: Type) { const flags = type.flags; if (flags & TypeFlags.Intersection) { return addTypesToIntersection(typeSet, includes, (type).types); @@ -9898,20 +9898,20 @@ namespace ts { if (isEmptyAnonymousObjectType(type)) { if (!(includes & TypeFlags.IncludesEmptyObject)) { includes |= TypeFlags.IncludesEmptyObject; - typeSet.push(type); + typeSet.set(type.id.toString(), type); } } else { if (flags & TypeFlags.AnyOrUnknown) { if (type === wildcardType) includes |= TypeFlags.IncludesWildcard; } - else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !contains(typeSet, type)) { + else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !typeSet.has(type.id.toString())) { if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) { // We have seen two distinct unit types which means we should reduce to an // empty intersection. Adding TypeFlags.NonPrimitive causes that to happen. includes |= TypeFlags.NonPrimitive; } - typeSet.push(type); + typeSet.set(type.id.toString(), type); } includes |= flags & TypeFlags.IncludesMask; } @@ -9920,7 +9920,7 @@ namespace ts { // Add the given types to the given type set. Order is preserved, freshness is removed from literal // types, duplicates are removed, and nested types of the given kind are flattened into the set. - function addTypesToIntersection(typeSet: Type[], includes: TypeFlags, types: ReadonlyArray) { + function addTypesToIntersection(typeSet: Map, includes: TypeFlags, types: ReadonlyArray) { for (const type of types) { includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type)); } @@ -10027,8 +10027,9 @@ namespace ts { // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution // for intersections of types with signatures can be deterministic. function getIntersectionType(types: ReadonlyArray, aliasSymbol?: Symbol, aliasTypeArguments?: ReadonlyArray): Type { - const typeSet: Type[] = []; - const includes = addTypesToIntersection(typeSet, 0, types); + const typeMembershipMap: Map = createMap(); + const includes = addTypesToIntersection(typeMembershipMap, 0, types); + const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); // An intersection type is considered empty if it contains // the type never, or // more than one unit type or, From 387c917765793773a6f7184bab84e6f5956f44fc Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 17 Jul 2019 14:02:18 -0700 Subject: [PATCH 06/16] =?UTF-8?q?Revert=20"Proposal:=20If=20there=E2=80=99?= =?UTF-8?q?s=20a=20package.json,=20only=20auto-import=20things=20in=20it,?= =?UTF-8?q?=20more=20or=20less=20(#31893)"=20(#32448)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 60a1b1dc1a93ca792cf12bb0432cf7bc134c3ad1. --- src/compiler/utilities.ts | 2 +- src/harness/fourslash.ts | 2 +- src/services/codefixes/importFixes.ts | 124 +------------- src/services/completions.ts | 152 ++++-------------- src/services/services.ts | 2 +- src/services/stringCompletions.ts | 49 ++++++ src/services/utilities.ts | 49 ------ ...rt_filteredByPackageJson_@typesImplicit.ts | 44 ----- ...Import_filteredByPackageJson_@typesOnly.ts | 44 ----- ...onsImport_filteredByPackageJson_ambient.ts | 30 ---- ...ionsImport_filteredByPackageJson_direct.ts | 46 ------ ...ionsImport_filteredByPackageJson_nested.ts | 66 -------- ...nsImport_filteredByPackageJson_reexport.ts | 58 ------- ...sImport_filteredByPackageJson_reexport2.ts | 58 ------- ...sImport_filteredByPackageJson_reexport3.ts | 48 ------ ...sImport_filteredByPackageJson_reexport4.ts | 57 ------- .../fourslash/completionsImport_ofAlias.ts | 17 +- .../importNameCodeFixNewImportNodeModules8.ts | 2 +- 18 files changed, 98 insertions(+), 752 deletions(-) delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_@typesImplicit.ts delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_@typesOnly.ts delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_ambient.ts delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_direct.ts delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_nested.ts delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport.ts delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport2.ts delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport3.ts delete mode 100644 tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport4.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fb5c69c6375..f3282d43723 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -7485,7 +7485,7 @@ namespace ts { export function getDirectoryPath(path: Path): Path; /** * Returns the path except for its basename. Semantics align with NodeJS's `path.dirname` - * except that we support URLs as well. + * except that we support URL's as well. * * ```ts * getDirectoryPath("/path/to/file.ext") === "/path/to" diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 919352e1391..aa764e74cdc 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -798,7 +798,7 @@ namespace FourSlash { const name = typeof include === "string" ? include : include.name; const found = nameToEntries.get(name); if (!found) throw this.raiseError(`No completion ${name} found`); - assert(found.length === 1, `Must use 'exact' for multiple completions with same name: '${name}'`); + assert(found.length === 1); // Must use 'exact' for multiple completions with same name this.verifyCompletionEntry(ts.first(found), include); } } diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 2fcc8cf5ccb..8006d1a0cfd 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -283,25 +283,13 @@ namespace ts.codefix { preferences: UserPreferences, ): ReadonlyArray { const isJs = isSourceFileJS(sourceFile); - const { allowsImporting } = createLazyPackageJsonDependencyReader(sourceFile, host); const choicesForEachExportingModule = flatMap(moduleSymbols, ({ moduleSymbol, importKind, exportedSymbolIsTypeOnly }) => moduleSpecifiers.getModuleSpecifiers(moduleSymbol, program.getCompilerOptions(), sourceFile, host, program.getSourceFiles(), preferences, program.redirectTargetsMap) .map((moduleSpecifier): FixAddNewImport | FixUseImportType => // `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types. exportedSymbolIsTypeOnly && isJs ? { kind: ImportFixKind.ImportType, moduleSpecifier, position: Debug.assertDefined(position) } : { kind: ImportFixKind.AddNew, moduleSpecifier, importKind })); - - // Sort by presence in package.json, then shortest paths first - return sort(choicesForEachExportingModule, (a, b) => { - const allowsImportingA = allowsImporting(a.moduleSpecifier); - const allowsImportingB = allowsImporting(b.moduleSpecifier); - if (allowsImportingA && !allowsImportingB) { - return -1; - } - if (allowsImportingB && !allowsImportingA) { - return 1; - } - return a.moduleSpecifier.length - b.moduleSpecifier.length; - }); + // Sort to keep the shortest paths first + return sort(choicesForEachExportingModule, (a, b) => a.moduleSpecifier.length - b.moduleSpecifier.length); } function getFixesForAddImport( @@ -392,8 +380,7 @@ namespace ts.codefix { // "default" is a keyword and not a legal identifier for the import, so we don't expect it here Debug.assert(symbolName !== InternalSymbolName.Default); - const exportInfos = getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, checker, program, preferences, host); - const fixes = arrayFrom(flatMapIterator(exportInfos.entries(), ([_, exportInfos]) => + const fixes = arrayFrom(flatMapIterator(getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, checker, program).entries(), ([_, exportInfos]) => getFixForImport(exportInfos, symbolName, symbolToken.getStart(sourceFile), program, sourceFile, host, preferences))); return { fixes, symbolName }; } @@ -406,8 +393,6 @@ namespace ts.codefix { sourceFile: SourceFile, checker: TypeChecker, program: Program, - preferences: UserPreferences, - host: LanguageServiceHost ): ReadonlyMap> { // For each original symbol, keep all re-exports of that symbol together so we can call `getCodeActionsForImport` on the whole group at once. // Maps symbol id to info for modules providing that symbol (original export + re-exports). @@ -415,7 +400,7 @@ namespace ts.codefix { function addSymbol(moduleSymbol: Symbol, exportedSymbol: Symbol, importKind: ImportKind): void { originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { moduleSymbol, importKind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exportedSymbol, checker) }); } - forEachExternalModuleToImportFrom(checker, host, preferences, program.redirectTargetsMap, sourceFile, program.getSourceFiles(), moduleSymbol => { + forEachExternalModuleToImportFrom(checker, sourceFile, program.getSourceFiles(), moduleSymbol => { cancellationToken.throwIfCancellationRequested(); const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, program.getCompilerOptions()); @@ -576,44 +561,12 @@ namespace ts.codefix { return some(declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)); } - export function forEachExternalModuleToImportFrom(checker: TypeChecker, host: LanguageServiceHost, preferences: UserPreferences, redirectTargetsMap: RedirectTargetsMap, from: SourceFile, allSourceFiles: ReadonlyArray, cb: (module: Symbol) => void) { - const { allowsImporting } = createLazyPackageJsonDependencyReader(from, host); - const compilerOptions = host.getCompilationSettings(); - const getCanonicalFileName = hostGetCanonicalFileName(host); + export function forEachExternalModuleToImportFrom(checker: TypeChecker, from: SourceFile, allSourceFiles: ReadonlyArray, cb: (module: Symbol) => void) { forEachExternalModule(checker, allSourceFiles, (module, sourceFile) => { - if (sourceFile === undefined && allowsImporting(stripQuotes(module.getName()))) { + if (sourceFile === undefined || sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName)) { cb(module); } - else if (sourceFile && sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName)) { - const moduleSpecifier = getNodeModulesPackageNameFromFileName(sourceFile.fileName); - if (!moduleSpecifier || allowsImporting(moduleSpecifier)) { - cb(module); - } - } }); - - function getNodeModulesPackageNameFromFileName(importedFileName: string): string | undefined { - const specifier = moduleSpecifiers.getModuleSpecifier( - compilerOptions, - from, - toPath(from.fileName, /*basePath*/ undefined, getCanonicalFileName), - importedFileName, - host, - allSourceFiles, - preferences, - redirectTargetsMap); - - // Paths here are not node_modules, so we don’t care about them; - // returning anything will trigger a lookup in package.json. - if (!pathIsRelative(specifier) && !isRootedDiskPath(specifier)) { - const components = getPathComponents(getPackageNameFromTypesPackageName(specifier)).slice(1); - // Scoped packages - if (startsWith(components[0], "@")) { - return `${components[0]}/${components[1]}`; - } - return components[0]; - } - } } function forEachExternalModule(checker: TypeChecker, allSourceFiles: ReadonlyArray, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) { @@ -667,69 +620,4 @@ namespace ts.codefix { // Need `|| "_"` to ensure result isn't empty. return !isStringANonContextualKeyword(res) ? res || "_" : `_${res}`; } - - function createLazyPackageJsonDependencyReader(fromFile: SourceFile, host: LanguageServiceHost) { - const packageJsonPaths = findPackageJsons(getDirectoryPath(fromFile.fileName), host); - const dependencyIterator = readPackageJsonDependencies(host, packageJsonPaths); - let seenDeps: Map | undefined; - let usesNodeCoreModules: boolean | undefined; - return { allowsImporting }; - - function containsDependency(dependency: string) { - if ((seenDeps || (seenDeps = createMap())).has(dependency)) { - return true; - } - let packageName: string | void; - while (packageName = dependencyIterator.next().value) { - seenDeps.set(packageName, true); - if (packageName === dependency) { - return true; - } - } - return false; - } - - function allowsImporting(moduleSpecifier: string): boolean { - if (!packageJsonPaths.length) { - return true; - } - - // If we’re in JavaScript, it can be difficult to tell whether the user wants to import - // from Node core modules or not. We can start by seeing if the user is actually using - // any node core modules, as opposed to simply having @types/node accidentally as a - // dependency of a dependency. - if (isSourceFileJS(fromFile) && JsTyping.nodeCoreModules.has(moduleSpecifier)) { - if (usesNodeCoreModules === undefined) { - usesNodeCoreModules = consumesNodeCoreModules(fromFile); - } - if (usesNodeCoreModules) { - return true; - } - } - - return containsDependency(moduleSpecifier) - || containsDependency(getTypesPackageName(moduleSpecifier)); - } - } - - function *readPackageJsonDependencies(host: LanguageServiceHost, packageJsonPaths: string[]) { - type PackageJson = Record | undefined>; - const dependencyKeys = ["dependencies", "devDependencies", "optionalDependencies"] as const; - for (const fileName of packageJsonPaths) { - const content = readJson(fileName, { readFile: host.readFile ? host.readFile.bind(host) : sys.readFile }) as PackageJson; - for (const key of dependencyKeys) { - const dependencies = content[key]; - if (!dependencies) { - continue; - } - for (const packageName in dependencies) { - yield packageName; - } - } - } - } - - function consumesNodeCoreModules(sourceFile: SourceFile): boolean { - return some(sourceFile.imports, ({ text }) => JsTyping.nodeCoreModules.has(text)); - } } diff --git a/src/services/completions.ts b/src/services/completions.ts index d7b40d14587..b5c412a788a 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -64,7 +64,7 @@ namespace ts.Completions { return getLabelCompletionAtPosition(contextToken.parent); } - const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, preferences, /*detailsEntryId*/ undefined, host); + const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, preferences, /*detailsEntryId*/ undefined); if (!completionData) { return undefined; } @@ -407,10 +407,10 @@ namespace ts.Completions { previousToken: Node | undefined; readonly isJsxInitializer: IsJsxInitializer; } - function getSymbolCompletionFromEntryId(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier, host: LanguageServiceHost + function getSymbolCompletionFromEntryId(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier, ): SymbolCompletion | { type: "request", request: Request } | { type: "literal", literal: string | number | PseudoBigInt } | { type: "none" } { const compilerOptions = program.getCompilerOptions(); - const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId, host); + const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId); if (!completionData) { return { type: "none" }; } @@ -472,7 +472,7 @@ namespace ts.Completions { } // Compute all the completion symbols again. - const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host); + const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId); switch (symbolCompletion.type) { case "request": { const { request } = symbolCompletion; @@ -557,8 +557,8 @@ namespace ts.Completions { return { sourceDisplay: [textPart(moduleSpecifier)], codeActions: [codeAction] }; } - export function getCompletionEntrySymbol(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier, host: LanguageServiceHost): Symbol | undefined { - const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host); + export function getCompletionEntrySymbol(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier): Symbol | undefined { + const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId); return completion.type === "symbol" ? completion.symbol : undefined; } @@ -657,7 +657,6 @@ namespace ts.Completions { position: number, preferences: Pick, detailsEntryId: CompletionEntryIdentifier | undefined, - host: LanguageServiceHost ): CompletionData | Request | undefined { const typeChecker = program.getTypeChecker(); @@ -1150,7 +1149,7 @@ namespace ts.Completions { } if (shouldOfferImportCompletions()) { - getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", program.getCompilerOptions().target!, host); + getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", program.getCompilerOptions().target!); } filterGlobalCompletion(symbols); } @@ -1268,64 +1267,12 @@ namespace ts.Completions { typeChecker.getExportsOfModule(sym).some(e => symbolCanBeReferencedAtTypeLocation(e, seenModules)); } - /** - * Gathers symbols that can be imported from other files, deduplicating along the way. Symbols can be “duplicates” - * if re-exported from another module, e.g. `export { foo } from "./a"`. That syntax creates a fresh symbol, but - * it’s just an alias to the first, and both have the same name, so we generally want to filter those aliases out, - * if and only if the the first can be imported (it may be excluded due to package.json filtering in - * `codefix.forEachExternalModuleToImportFrom`). - * - * Example. Imagine a chain of node_modules re-exporting one original symbol: - * - * ```js - * node_modules/x/index.js node_modules/y/index.js node_modules/z/index.js - * +-----------------------+ +--------------------------+ +--------------------------+ - * | | | | | | - * | export const foo = 0; | <--- | export { foo } from 'x'; | <--- | export { foo } from 'y'; | - * | | | | | | - * +-----------------------+ +--------------------------+ +--------------------------+ - * ``` - * - * Also imagine three buckets, which we’ll reference soon: - * - * ```md - * | | | | | | - * | **Bucket A** | | **Bucket B** | | **Bucket C** | - * | Symbols to | | Aliases to symbols | | Symbols to return | - * | definitely | | in Buckets A or C | | if nothing better | - * | return | | (don’t return these) | | comes along | - * |__________________| |______________________| |___________________| - * ``` - * - * We _probably_ want to show `foo` from 'x', but not from 'y' or 'z'. However, if 'x' is not in a package.json, it - * will not appear in a `forEachExternalModuleToImportFrom` iteration. Furthermore, the order of iterations is not - * guaranteed, as it is host-dependent. Therefore, when presented with the symbol `foo` from module 'y' alone, we - * may not be sure whether or not it should go in the list. So, we’ll take the following steps: - * - * 1. Resolve alias `foo` from 'y' to the export declaration in 'x', get the symbol there, and see if that symbol is - * already in Bucket A (symbols we already know will be returned). If it is, put `foo` from 'y' in Bucket B - * (symbols that are aliases to symbols in Bucket A). If it’s not, put it in Bucket C. - * 2. Next, imagine we see `foo` from module 'z'. Again, we resolve the alias to the nearest export, which is in 'y'. - * At this point, if that nearest export from 'y' is in _any_ of the three buckets, we know the symbol in 'z' - * should never be returned in the final list, so put it in Bucket B. - * 3. Next, imagine we see `foo` from module 'x', the original. Syntactically, it doesn’t look like a re-export, so - * we can just check Bucket C to see if we put any aliases to the original in there. If they exist, throw them out. - * Put this symbol in Bucket A. - * 4. After we’ve iterated through every symbol of every module, any symbol left in Bucket C means that step 3 didn’t - * occur for that symbol---that is, the original symbol is not in Bucket A, so we should include the alias. Move - * everything from Bucket C to Bucket A. - * - * Note: Bucket A is passed in as the parameter `symbols` and mutated. - */ - function getSymbolsFromOtherSourceFileExports(/** Bucket A */ symbols: Symbol[], tokenText: string, target: ScriptTarget, host: LanguageServiceHost): void { + function getSymbolsFromOtherSourceFileExports(symbols: Symbol[], tokenText: string, target: ScriptTarget): void { const tokenTextLowerCase = tokenText.toLowerCase(); - const seenResolvedModules = createMap(); - /** Bucket B */ - const aliasesToAlreadyIncludedSymbols = createMap(); - /** Bucket C */ - const aliasesToReturnIfOriginalsAreMissing = createMap<{ alias: Symbol, moduleSymbol: Symbol }>(); - codefix.forEachExternalModuleToImportFrom(typeChecker, host, preferences, program.redirectTargetsMap, sourceFile, program.getSourceFiles(), moduleSymbol => { + const seenResolvedModules = createMap(); + + codefix.forEachExternalModuleToImportFrom(typeChecker, sourceFile, program.getSourceFiles(), moduleSymbol => { // Perf -- ignore other modules if this is a request for details if (detailsEntryId && detailsEntryId.source && stripQuotes(moduleSymbol.name) !== detailsEntryId.source) { return; @@ -1346,59 +1293,33 @@ namespace ts.Completions { symbolToOriginInfoMap[getSymbolId(resolvedModuleSymbol)] = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport: false }; } - for (const symbol of typeChecker.getExportsOfModule(moduleSymbol)) { - // If this is `export { _break as break };` (a keyword) -- skip this and prefer the keyword completion. - if (some(symbol.declarations, d => isExportSpecifier(d) && !!d.propertyName && isIdentifierANonContextualKeyword(d.name))) { + for (let symbol of typeChecker.getExportsOfModule(moduleSymbol)) { + // Don't add a completion for a re-export, only for the original. + // The actual import fix might end up coming from a re-export -- we don't compute that until getting completion details. + // This is just to avoid adding duplicate completion entries. + // + // If `symbol.parent !== ...`, this is an `export * from "foo"` re-export. Those don't create new symbols. + if (typeChecker.getMergedSymbol(symbol.parent!) !== resolvedModuleSymbol + || some(symbol.declarations, d => + // If `!!d.name.originalKeywordKind`, this is `export { _break as break };` -- skip this and prefer the keyword completion. + // If `!!d.parent.parent.moduleSpecifier`, this is `export { foo } from "foo"` re-export, which creates a new symbol (thus isn't caught by the first check). + isExportSpecifier(d) && (d.propertyName ? isIdentifierANonContextualKeyword(d.name) : !!d.parent.parent.moduleSpecifier))) { continue; } - // If `symbol.parent !== moduleSymbol`, this is an `export * from "foo"` re-export. Those don't create new symbols. - const isExportStarFromReExport = typeChecker.getMergedSymbol(symbol.parent!) !== resolvedModuleSymbol; - // If `!!d.parent.parent.moduleSpecifier`, this is `export { foo } from "foo"` re-export, which creates a new symbol (thus isn't caught by the first check). - if (isExportStarFromReExport || some(symbol.declarations, d => isExportSpecifier(d) && !d.propertyName && !!d.parent.parent.moduleSpecifier)) { - // Walk the export chain back one module (step 1 or 2 in diagrammed example). - // Or, in the case of `export * from "foo"`, `symbol` already points to the original export, so just use that. - const nearestExportSymbolId = getSymbolId(isExportStarFromReExport ? symbol : Debug.assertDefined(getNearestExportSymbol(symbol))); - const symbolHasBeenSeen = !!symbolToOriginInfoMap[nearestExportSymbolId] || aliasesToAlreadyIncludedSymbols.has(nearestExportSymbolId.toString()); - if (!symbolHasBeenSeen) { - aliasesToReturnIfOriginalsAreMissing.set(nearestExportSymbolId.toString(), { alias: symbol, moduleSymbol }); - aliasesToAlreadyIncludedSymbols.set(getSymbolId(symbol).toString(), true); - } - else { - // Perf - we know this symbol is an alias to one that’s already covered in `symbols`, so store it here - // in case another symbol re-exports this one; that way we can short-circuit as soon as we see this symbol id. - addToSeen(aliasesToAlreadyIncludedSymbols, getSymbolId(symbol)); - } + + const isDefaultExport = symbol.escapedName === InternalSymbolName.Default; + if (isDefaultExport) { + symbol = getLocalSymbolForExportDefault(symbol) || symbol; } - else { - // This is not a re-export, so see if we have any aliases pending and remove them (step 3 in diagrammed example) - aliasesToReturnIfOriginalsAreMissing.delete(getSymbolId(symbol).toString()); - pushSymbol(symbol, moduleSymbol); + + const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport }; + if (detailsEntryId || stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) { + symbols.push(symbol); + symbolToSortTextMap[getSymbolId(symbol)] = SortText.AutoImportSuggestions; + symbolToOriginInfoMap[getSymbolId(symbol)] = origin; } } }); - - // By this point, any potential duplicates that were actually duplicates have been - // removed, so the rest need to be added. (Step 4 in diagrammed example) - aliasesToReturnIfOriginalsAreMissing.forEach(({ alias, moduleSymbol }) => pushSymbol(alias, moduleSymbol)); - - function pushSymbol(symbol: Symbol, moduleSymbol: Symbol) { - const isDefaultExport = symbol.escapedName === InternalSymbolName.Default; - if (isDefaultExport) { - symbol = getLocalSymbolForExportDefault(symbol) || symbol; - } - const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport }; - if (detailsEntryId || stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) { - symbols.push(symbol); - symbolToSortTextMap[getSymbolId(symbol)] = SortText.AutoImportSuggestions; - symbolToOriginInfoMap[getSymbolId(symbol)] = origin; - } - } - } - - function getNearestExportSymbol(fromSymbol: Symbol) { - return findAlias(typeChecker, fromSymbol, alias => { - return some(alias.declarations, d => isExportSpecifier(d) || !!d.localSymbol); - }); } /** @@ -2322,13 +2243,4 @@ namespace ts.Completions { function binaryExpressionMayBeOpenTag({ left }: BinaryExpression): boolean { return nodeIsMissing(left); } - - function findAlias(typeChecker: TypeChecker, symbol: Symbol, predicate: (symbol: Symbol) => boolean): Symbol | undefined { - let currentAlias: Symbol | undefined = symbol; - while (currentAlias.flags & SymbolFlags.Alias && (currentAlias = typeChecker.getImmediateAliasedSymbol(currentAlias))) { - if (predicate(currentAlias)) { - return currentAlias; - } - } - } } diff --git a/src/services/services.ts b/src/services/services.ts index 2f000bc0f2e..fab6f88b779 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1453,7 +1453,7 @@ namespace ts { function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol | undefined { synchronizeHostData(); - return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host); + return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }); } function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined { diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index 58195b5cb3c..b287ebdb406 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -627,6 +627,30 @@ namespace ts.Completions.StringCompletions { } } + function findPackageJsons(directory: string, host: LanguageServiceHost): string[] { + const paths: string[] = []; + forEachAncestorDirectory(directory, ancestor => { + const currentConfigPath = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json"); + if (!currentConfigPath) { + return true; // break out + } + paths.push(currentConfigPath); + }); + return paths; + } + + function findPackageJson(directory: string, host: LanguageServiceHost): string | undefined { + let packageJson: string | undefined; + forEachAncestorDirectory(directory, ancestor => { + if (ancestor === "node_modules") return true; + packageJson = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json"); + if (packageJson) { + return true; // break out + } + }); + return packageJson; + } + function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string): ReadonlyArray { if (!host.readFile || !host.fileExists) return emptyArray; @@ -682,6 +706,31 @@ namespace ts.Completions.StringCompletions { const nodeModulesDependencyKeys: ReadonlyArray = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]; + function tryGetDirectories(host: LanguageServiceHost, directoryName: string): string[] { + return tryIOAndConsumeErrors(host, host.getDirectories, directoryName) || []; + } + + function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray): ReadonlyArray { + return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include) || emptyArray; + } + + function tryFileExists(host: LanguageServiceHost, path: string): boolean { + return tryIOAndConsumeErrors(host, host.fileExists, path); + } + + function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean { + return tryAndIgnoreErrors(() => directoryProbablyExists(path, host)) || false; + } + + function tryIOAndConsumeErrors(host: LanguageServiceHost, toApply: ((...a: any[]) => T) | undefined, ...args: any[]) { + return tryAndIgnoreErrors(() => toApply && toApply.apply(host, args)); + } + + function tryAndIgnoreErrors(cb: () => T): T | undefined { + try { return cb(); } + catch { return undefined; } + } + function containsSlash(fragment: string) { return stringContains(fragment, directorySeparator); } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 98406c6879c..852d22106a2 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2022,53 +2022,4 @@ namespace ts { // If even 2/5 places have a semicolon, the user probably wants semicolons return withSemicolon / withoutSemicolon > 1 / nStatementsToObserve; } - - export function tryGetDirectories(host: LanguageServiceHost, directoryName: string): string[] { - return tryIOAndConsumeErrors(host, host.getDirectories, directoryName) || []; - } - - export function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray): ReadonlyArray { - return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include) || emptyArray; - } - - export function tryFileExists(host: LanguageServiceHost, path: string): boolean { - return tryIOAndConsumeErrors(host, host.fileExists, path); - } - - export function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean { - return tryAndIgnoreErrors(() => directoryProbablyExists(path, host)) || false; - } - - export function tryAndIgnoreErrors(cb: () => T): T | undefined { - try { return cb(); } - catch { return undefined; } - } - - export function tryIOAndConsumeErrors(host: LanguageServiceHost, toApply: ((...a: any[]) => T) | undefined, ...args: any[]) { - return tryAndIgnoreErrors(() => toApply && toApply.apply(host, args)); - } - - export function findPackageJsons(directory: string, host: LanguageServiceHost): string[] { - const paths: string[] = []; - forEachAncestorDirectory(directory, ancestor => { - const currentConfigPath = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json"); - if (!currentConfigPath) { - return true; // break out - } - paths.push(currentConfigPath); - }); - return paths; - } - - export function findPackageJson(directory: string, host: LanguageServiceHost): string | undefined { - let packageJson: string | undefined; - forEachAncestorDirectory(directory, ancestor => { - if (ancestor === "node_modules") return true; - packageJson = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json"); - if (packageJson) { - return true; // break out - } - }); - return packageJson; - } } diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_@typesImplicit.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_@typesImplicit.ts deleted file mode 100644 index 539a9cc8ff6..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_@typesImplicit.ts +++ /dev/null @@ -1,44 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "dependencies": { -//// "react": "*" -//// } -////} - -//@Filename: /node_modules/@types/react/index.d.ts -////export declare var React: any; - -//@Filename: /node_modules/@types/react/package.json -////{ -//// "name": "@types/react" -////} - -//@Filename: /node_modules/@types/fake-react/index.d.ts -////export declare var ReactFake: any; - -//@Filename: /node_modules/@types/fake-react/package.json -////{ -//// "name": "@types/fake-react" -////} - -//@Filename: /src/index.ts -////const x = Re/**/ - -verify.completions({ - marker: test.marker(""), - isNewIdentifierLocation: true, - includes: { - name: "React", - hasAction: true, - source: "/node_modules/@types/react/index", - sortText: completion.SortText.AutoImportSuggestions - }, - excludes: "ReactFake", - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_@typesOnly.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_@typesOnly.ts deleted file mode 100644 index b0d2c01e3db..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_@typesOnly.ts +++ /dev/null @@ -1,44 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "devDependencies": { -//// "@types/react": "*" -//// } -////} - -//@Filename: /node_modules/@types/react/index.d.ts -////export declare var React: any; - -//@Filename: /node_modules/@types/react/package.json -////{ -//// "name": "@types/react" -////} - -//@Filename: /node_modules/@types/fake-react/index.d.ts -////export declare var ReactFake: any; - -//@Filename: /node_modules/@types/fake-react/package.json -////{ -//// "name": "@types/fake-react" -////} - -//@Filename: /src/index.ts -////const x = Re/**/ - -verify.completions({ - marker: test.marker(""), - isNewIdentifierLocation: true, - includes: { - name: "React", - hasAction: true, - source: "/node_modules/@types/react/index", - sortText: completion.SortText.AutoImportSuggestions - }, - excludes: "ReactFake", - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_ambient.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_ambient.ts deleted file mode 100644 index 3dcb9eb6690..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_ambient.ts +++ /dev/null @@ -1,30 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "dependencies": { -//// } -////} - -//@Filename: /node_modules/@types/node/timers.d.ts -////declare module "timers" { -//// function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timeout; -////} - -//@Filename: /node_modules/@types/node/package.json -////{ -//// "name": "@types/node", -////} - -//@Filename: /src/index.ts -////setTimeo/**/ - -verify.completions({ - marker: test.marker(""), - exact: completion.globals, - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_direct.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_direct.ts deleted file mode 100644 index aa7845daed3..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_direct.ts +++ /dev/null @@ -1,46 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "dependencies": { -//// "react": "*" -//// } -////} - -//@Filename: /node_modules/react/index.d.ts -////export declare var React: any; - -//@Filename: /node_modules/react/package.json -////{ -//// "name": "react", -//// "types": "./index.d.ts" -////} - -//@Filename: /node_modules/fake-react/index.d.ts -////export declare var ReactFake: any; - -//@Filename: /node_modules/fake-react/package.json -////{ -//// "name": "fake-react", -//// "types": "./index.d.ts" -////} - -//@Filename: /src/index.ts -////const x = Re/**/ - -verify.completions({ - marker: test.marker(""), - isNewIdentifierLocation: true, - includes: { - name: "React", - hasAction: true, - source: "/node_modules/react/index", - sortText: completion.SortText.AutoImportSuggestions - }, - excludes: "ReactFake", - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_nested.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_nested.ts deleted file mode 100644 index e940c43e32c..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_nested.ts +++ /dev/null @@ -1,66 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "dependencies": { -//// "react": "*" -//// } -////} - -//@Filename: /node_modules/react/index.d.ts -////export declare var React: any; - -//@Filename: /node_modules/react/package.json -////{ -//// "name": "react", -//// "types": "./index.d.ts" -////} - -//@Filename: /dir/package.json -////{ -//// "dependencies": { -//// "redux": "*" -//// } -////} - -//@Filename: /dir/node_modules/redux/package.json -////{ -//// "name": "redux", -//// "types": "./index.d.ts" -////} - -//@Filename: /dir/node_modules/redux/index.d.ts -////export declare var Redux: any; - -//@Filename: /dir/index.ts -////const x = Re/**/ - -verify.completions({ - marker: test.marker(""), - isNewIdentifierLocation: true, - includes: { - name: "React", - hasAction: true, - source: "/node_modules/react/index", - sortText: completion.SortText.AutoImportSuggestions - }, - preferences: { - includeCompletionsForModuleExports: true - } -}); - -verify.completions({ - marker: test.marker(""), - isNewIdentifierLocation: true, - includes: { - name: "Redux", - hasAction: true, - source: "/dir/node_modules/redux/index", - sortText: completion.SortText.AutoImportSuggestions - }, - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport.ts deleted file mode 100644 index 8e17a3c3a44..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport.ts +++ /dev/null @@ -1,58 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "dependencies": { -//// "@emotion/core": "*" -//// } -////} - -//@Filename: /node_modules/@emotion/css/index.d.ts -////export declare const css: any; -////const css2: any; -////export { css2 }; - -//@Filename: /node_modules/@emotion/css/package.json -////{ -//// "name": "@emotion/css", -//// "types": "./index.d.ts" -////} - -//@Filename: /node_modules/@emotion/core/index.d.ts -////import { css2 } from "@emotion/css"; -////export { css } from "@emotion/css"; -////export { css2 }; - -//@Filename: /node_modules/@emotion/core/package.json -////{ -//// "name": "@emotion/core", -//// "types": "./index.d.ts" -////} - -//@Filename: /src/index.ts -////cs/**/ - -verify.completions({ - marker: test.marker(""), - includes: [ - completion.undefinedVarEntry, - { - name: "css", - source: "/node_modules/@emotion/core/index", - hasAction: true, - sortText: completion.SortText.AutoImportSuggestions - }, - { - name: "css2", - source: "/node_modules/@emotion/core/index", - hasAction: true, - sortText: completion.SortText.AutoImportSuggestions - }, - ...completion.statementKeywordsWithTypes - ], - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport2.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport2.ts deleted file mode 100644 index eb946ce17b4..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport2.ts +++ /dev/null @@ -1,58 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "dependencies": { -//// "b_": "*", -//// "_c": "*" -//// } -////} - -//@Filename: /node_modules/a/index.d.ts -////export const foo = 0; - -//@Filename: /node_modules/a/package.json -////{ -//// "name": "a", -//// "types": "./index.d.ts" -////} - -//@Filename: /node_modules/b_/index.d.ts -////export { foo } from "a"; - -//@Filename: /node_modules/b_/package.json -////{ -//// "name": "b_", -//// "types": "./index.d.ts" -////} - -//@Filename: /node_modules/_c/index.d.ts -////export { foo } from "b_"; - -//@Filename: /node_modules/_c/package.json -////{ -//// "name": "_c", -//// "types": "./index.d.ts" -////} - -//@Filename: /src/index.ts -////fo/**/ - -verify.completions({ - marker: test.marker(""), - includes: [ - completion.undefinedVarEntry, - { - name: "foo", - source: "/node_modules/b_/index", - hasAction: true, - sortText: completion.SortText.AutoImportSuggestions - }, - ...completion.statementKeywordsWithTypes - ], - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport3.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport3.ts deleted file mode 100644 index 8533461e0b8..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport3.ts +++ /dev/null @@ -1,48 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "dependencies": { -//// "b": "*", -//// } -////} - -//@Filename: /node_modules/a/index.d.ts -////export const foo = 0; - -//@Filename: /node_modules/a/package.json -////{ -//// "name": "a", -//// "types": "./index.d.ts" -////} - -//@Filename: /node_modules/b/index.d.ts -////export * from "a"; - -//@Filename: /node_modules/b/package.json -////{ -//// "name": "b", -//// "types": "./index.d.ts" -////} - -//@Filename: /src/index.ts -////fo/**/ - -verify.completions({ - marker: test.marker(""), - includes: [ - completion.undefinedVarEntry, - { - name: "foo", - source: "/node_modules/b/index", - hasAction: true, - sortText: completion.SortText.AutoImportSuggestions - }, - ...completion.statementKeywordsWithTypes - ], - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport4.ts b/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport4.ts deleted file mode 100644 index 83ac6526b25..00000000000 --- a/tests/cases/fourslash/completionsImport_filteredByPackageJson_reexport4.ts +++ /dev/null @@ -1,57 +0,0 @@ -/// - -//@noEmit: true - -//@Filename: /package.json -////{ -//// "dependencies": { -//// "c": "*", -//// } -////} - -//@Filename: /node_modules/a/index.d.ts -////export const foo = 0; - -//@Filename: /node_modules/a/package.json -////{ -//// "name": "a", -//// "types": "./index.d.ts" -////} - -//@Filename: /node_modules/b/index.d.ts -////export * from "a"; - -//@Filename: /node_modules/b/package.json -////{ -//// "name": "b", -//// "types": "./index.d.ts" -////} - -//@Filename: /node_modules/c/index.d.ts -////export * from "a"; - -//@Filename: /node_modules/c/package.json -////{ -//// "name": "c", -//// "types": "./index.d.ts" -////} - -//@Filename: /src/index.ts -////fo/**/ - -verify.completions({ - marker: test.marker(""), - includes: [ - completion.undefinedVarEntry, - { - name: "foo", - source: "/node_modules/c/index", - hasAction: true, - sortText: completion.SortText.AutoImportSuggestions - }, - ...completion.statementKeywordsWithTypes - ], - preferences: { - includeCompletionsForModuleExports: true - } -}); diff --git a/tests/cases/fourslash/completionsImport_ofAlias.ts b/tests/cases/fourslash/completionsImport_ofAlias.ts index 1319391eb0b..9a9cb4a2b18 100644 --- a/tests/cases/fourslash/completionsImport_ofAlias.ts +++ b/tests/cases/fourslash/completionsImport_ofAlias.ts @@ -16,9 +16,6 @@ // @Filename: /a_reexport_2.ts ////export * from "./a"; -// @Filename: /a_reexport_3.ts -////export { foo } from "./a_reexport"; - // @Filename: /b.ts ////fo/**/ @@ -27,13 +24,13 @@ verify.completions({ includes: [ completion.undefinedVarEntry, { - name: "foo", - source: "/a", - sourceDisplay: "./a", - text: "(alias) const foo: 0\nexport foo", - kind: "alias", - hasAction: true, - sortText: completion.SortText.AutoImportSuggestions + name: "foo", + source: "/a", + sourceDisplay: "./a", + text: "(alias) const foo: 0\nexport foo", + kind: "alias", + hasAction: true, + sortText: completion.SortText.AutoImportSuggestions }, ...completion.statementKeywordsWithTypes, ], diff --git a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules8.ts b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules8.ts index acfddd587f7..f048f0d30d2 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportNodeModules8.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportNodeModules8.ts @@ -3,7 +3,7 @@ //// [|f1/*0*/('');|] // @Filename: package.json -//// { "dependencies": { "@scope/package-name": "latest" } } +//// { "dependencies": { "package-name": "latest" } } // @Filename: node_modules/@scope/package-name/bin/lib/index.d.ts //// export function f1(text: string): string; From 69ec5e03663bc2ad25ae11b20baf0eb767ce3d5d Mon Sep 17 00:00:00 2001 From: csigs Date: Wed, 17 Jul 2019 22:10:20 +0000 Subject: [PATCH 07/16] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl index 8d7a0a4acbd..5ba07d067e1 100644 --- a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -1,4 +1,4 @@ - + @@ -3300,7 +3300,7 @@ - + @@ -4122,7 +4122,7 @@ - + From 7f071d2a1bda299cf3ac4eacb8abae951a095534 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Wed, 17 Jul 2019 18:21:53 -0400 Subject: [PATCH 08/16] Set the ScriptTarget of ESNext to be 99 so it doesn't change between releases --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 439dfb5f98e..6d46d36d167 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4808,7 +4808,7 @@ namespace ts { ES2018 = 5, ES2019 = 6, ES2020 = 7, - ESNext = 8, + ESNext = 99, JSON = 100, Latest = ESNext, } From a24e4b0d2ca34b6ad12aae9218ea4e2f9ddd32f4 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Wed, 17 Jul 2019 18:24:35 -0400 Subject: [PATCH 09/16] Undo accidental push to master --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6d46d36d167..439dfb5f98e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4808,7 +4808,7 @@ namespace ts { ES2018 = 5, ES2019 = 6, ES2020 = 7, - ESNext = 99, + ESNext = 8, JSON = 100, Latest = ESNext, } From 5f6cdf17ea0441f42190610254c9d7f0c024459e Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Wed, 17 Jul 2019 18:27:29 -0400 Subject: [PATCH 10/16] Set the ScriptTarget of ESNext to be 99 so it doesn't change between releases --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 439dfb5f98e..6d46d36d167 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4808,7 +4808,7 @@ namespace ts { ES2018 = 5, ES2019 = 6, ES2020 = 7, - ESNext = 8, + ESNext = 99, JSON = 100, Latest = ESNext, } From 282e72419b2421c2d3b86d18a15256602df530a1 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Wed, 17 Jul 2019 22:56:28 -0400 Subject: [PATCH 11/16] Set the ModuleKind value for ESNext to be 99 so it doesn't change between releases (and yet another module system?!) --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6d46d36d167..67c85147aed 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4760,7 +4760,7 @@ namespace ts { UMD = 3, System = 4, ES2015 = 5, - ESNext = 6 + ESNext = 99 } export const enum JsxEmit { From 0c4422e47203cc3a5b3680aac443ab031c03c1fb Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Thu, 18 Jul 2019 11:08:39 -0400 Subject: [PATCH 12/16] Adds baseline updates --- tests/baselines/reference/api/tsserverlibrary.d.ts | 6 +++--- tests/baselines/reference/api/typescript.d.ts | 6 +++--- .../sample1/initial-Build/when-target-option-changes.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 1810cb020e3..c2bad61505b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2600,7 +2600,7 @@ declare namespace ts { UMD = 3, System = 4, ES2015 = 5, - ESNext = 6 + ESNext = 99 } enum JsxEmit { None = 0, @@ -2640,9 +2640,9 @@ declare namespace ts { ES2018 = 5, ES2019 = 6, ES2020 = 7, - ESNext = 8, + ESNext = 99, JSON = 100, - Latest = 8 + Latest = 99 } enum LanguageVariant { Standard = 0, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index f74b4b14683..7a2559bbc22 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2600,7 +2600,7 @@ declare namespace ts { UMD = 3, System = 4, ES2015 = 5, - ESNext = 6 + ESNext = 99 } enum JsxEmit { None = 0, @@ -2640,9 +2640,9 @@ declare namespace ts { ES2018 = 5, ES2019 = 6, ES2020 = 7, - ESNext = 8, + ESNext = 99, JSON = 100, - Latest = 8 + Latest = 99 } enum LanguageVariant { Standard = 0, diff --git a/tests/baselines/reference/tsbuild/sample1/initial-Build/when-target-option-changes.js b/tests/baselines/reference/tsbuild/sample1/initial-Build/when-target-option-changes.js index 895ac5e4bc5..c31d39c0647 100644 --- a/tests/baselines/reference/tsbuild/sample1/initial-Build/when-target-option-changes.js +++ b/tests/baselines/reference/tsbuild/sample1/initial-Build/when-target-option-changes.js @@ -70,7 +70,7 @@ export function multiply(a, b) { return a * b; } "incremental": true, "listFiles": true, "listEmittedFiles": true, - "target": 8, + "target": 99, "configFilePath": "./tsconfig.json" }, "referencedMap": {}, From c30ba7884c76a9e94772c0c89604cb6cbfaced99 Mon Sep 17 00:00:00 2001 From: Jake Boone Date: Thu, 18 Jul 2019 12:38:14 -0700 Subject: [PATCH 13/16] Fix capitalization in parseInt description --- src/lib/es5.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 0d1481dd7d2..2594344decc 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -12,7 +12,7 @@ declare var Infinity: number; declare function eval(x: string): any; /** - * Converts A string to an integer. + * Converts a string to an integer. * @param s A string to convert into a number. * @param radix A value between 2 and 36 that specifies the base of the number in numString. * If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal. From 90afd6d620f30bc47e862970a931ca0e207e6b28 Mon Sep 17 00:00:00 2001 From: TypeScript Bot Date: Fri, 19 Jul 2019 09:01:05 -0700 Subject: [PATCH 14/16] Update user baselines (#32483) --- .../baselines/reference/docker/azure-sdk.log | 58 ++++++------------- .../reference/docker/office-ui-fabric.log | 16 ++--- .../reference/user/adonis-framework.log | 2 +- .../user/chrome-devtools-frontend.log | 3 +- tests/baselines/reference/user/lodash.log | 2 - tests/baselines/reference/user/npmlog.log | 6 +- 6 files changed, 33 insertions(+), 54 deletions(-) diff --git a/tests/baselines/reference/docker/azure-sdk.log b/tests/baselines/reference/docker/azure-sdk.log index a1e33524e6e..a75af93aee1 100644 --- a/tests/baselines/reference/docker/azure-sdk.log +++ b/tests/baselines/reference/docker/azure-sdk.log @@ -5,16 +5,11 @@ Rush Multi-Project Build Tool 5.10.1 - https://rushjs.io Starting "rush rebuild" Executing a maximum of 1 simultaneous processes... [@azure/cosmos] started -npm ERR! code ELIFECYCLE -npm ERR! errno 2 -npm ERR! @azure/cosmos@X.X.X compile: `echo Using TypeScript && tsc --version && tsc -p tsconfig.prod.json --pretty` -npm ERR! Exit status 2 -npm ERR! -npm ERR! Failed at the @azure/cosmos@X.X.X compile script. -npm ERR! This is probably not a problem with npm. There is likely additional logging output above. -npm ERR! A complete log of this run can be found in: -npm ERR! /root/.npm/_logs/2019-07-15T13_35_10_789Z-debug.log +XX of XX: [@azure/cosmos] completed successfully in ? seconds +[@azure/event-processor-host] started +XX of XX: [@azure/event-processor-host] completed successfully in ? seconds [@azure/service-bus] started +Warning: You have changed the public API signature for this project. Updating review/service-bus.api.md [@azure/storage-blob] started XX of XX: [@azure/storage-blob] completed successfully in ? seconds [@azure/storage-file] started @@ -23,6 +18,8 @@ XX of XX: [@azure/storage-file] completed successfully in ? seconds XX of XX: [@azure/storage-queue] completed successfully in ? seconds [@azure/template] started XX of XX: [@azure/template] completed successfully in ? seconds +[testhub] started +XX of XX: [testhub] completed successfully in ? seconds [@azure/abort-controller] started XX of XX: [@azure/abort-controller] completed successfully in ? seconds [@azure/core-asynciterator-polyfill] started @@ -38,7 +35,7 @@ npm ERR! npm ERR! Failed at the @azure/core-http@X.X.X-preview.1 build:tsc script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: -npm ERR! /root/.npm/_logs/2019-07-15T13_36_24_862Z-debug.log +npm ERR! /root/.npm/_logs/2019-07-19T13_40_31_496Z-debug.log ERROR: "build:tsc" exited with 2. npm ERR! code ELIFECYCLE npm ERR! errno 1 @@ -48,20 +45,17 @@ npm ERR! npm ERR! Failed at the @azure/core-http@X.X.X-preview.1 build:lib script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: -npm ERR! /root/.npm/_logs/2019-07-15T13_36_24_938Z-debug.log +npm ERR! /root/.npm/_logs/2019-07-19T13_40_31_533Z-debug.log ERROR: "build:lib" exited with 1. [@azure/core-paging] started XX of XX: [@azure/core-paging] completed successfully in ? seconds -[@azure/event-processor-host] started -XX of XX: [@azure/event-processor-host] completed successfully in ? seconds -[testhub] started -XX of XX: [testhub] completed successfully in ? seconds -SUCCESS (10) +SUCCESS (11) ================================ @azure/abort-controller (? seconds) @azure/core-asynciterator-polyfill (? seconds) @azure/core-auth (? seconds) @azure/core-paging (? seconds) +@azure/cosmos (? seconds) @azure/event-processor-host (? seconds) @azure/storage-blob (? seconds) @azure/storage-file (? seconds) @@ -69,6 +63,11 @@ SUCCESS (10) @azure/template (? seconds) testhub (? seconds) ================================ +SUCCESS WITH WARNINGS (1) +================================ +@azure/service-bus (? seconds) +Warning: You have changed the public API signature for this project. Updating review/service-bus.api.md +================================ BLOCKED (7) ================================ @azure/core-amqp @@ -79,7 +78,7 @@ BLOCKED (7) @azure/keyvault-keys @azure/keyvault-secrets ================================ -FAILURE (3) +FAILURE (1) ================================ @azure/core-http (? seconds) npm ERR! code ELIFECYCLE @@ -90,7 +89,7 @@ npm ERR! npm ERR! Failed at the @azure/core-http@X.X.X-preview.1 build:tsc script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: -npm ERR! /root/.npm/_logs/2019-07-15T13_36_24_862Z-debug.log +npm ERR! /root/.npm/_logs/2019-07-19T13_40_31_496Z-debug.log ERROR: "build:tsc" exited with 2. npm ERR! code ELIFECYCLE npm ERR! errno 1 @@ -100,24 +99,8 @@ npm ERR! npm ERR! Failed at the @azure/core-http@X.X.X-preview.1 build:lib script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: -npm ERR! /root/.npm/_logs/2019-07-15T13_36_24_938Z-debug.log +npm ERR! /root/.npm/_logs/2019-07-19T13_40_31_533Z-debug.log ERROR: "build:lib" exited with 1. -@azure/cosmos ( ? seconds) -npm ERR! code ELIFECYCLE -npm ERR! errno 2 -npm ERR! @azure/cosmos@X.X.X compile: `echo Using TypeScript && tsc --version && tsc -p tsconfig.prod.json --pretty` -npm ERR! Exit status 2 -npm ERR! -npm ERR! Failed at the @azure/cosmos@X.X.X compile script. -npm ERR! This is probably not a problem with npm. There is likely additional logging output above. -npm ERR! A complete log of this run can be found in: -npm ERR! /root/.npm/_logs/2019-07-15T13_35_10_789Z-debug.log -@azure/service-bus ( ? seconds) ->>> @azure/service-bus -tsc -p . && rollup -c 2>&1 && npm run extract-api -error TS2318: Cannot find global type 'AsyncGenerator'. -src/receiver.ts(193,32): error TS2739: Type '{}' is missing the following properties from type 'AsyncIterableIterator': [Symbol.asyncIterator], next -src/receiver.ts(742,32): error TS2322: Type '{}' is not assignable to type 'AsyncIterableIterator'. ================================ Error: Project(s) failed to build rush rebuild - Errors! ( ? seconds) @@ -126,8 +109,7 @@ rush rebuild - Errors! ( ? seconds) Standard error: Your version of Node.js (X.X.X) has not been tested with this release of Rush. The Rush team will not accept issue reports for it. Please consider upgrading Rush or downgrading Node.js. -XX of XX: [@azure/cosmos] failed to build! -XX of XX: [@azure/service-bus] failed to build! +XX of XX: [@azure/service-bus] completed with warnings in ? seconds XX of XX: [@azure/core-http] failed to build! XX of XX: [@azure/core-arm] blocked by [@azure/core-http]! XX of XX: [@azure/identity] blocked by [@azure/core-http]! @@ -137,5 +119,3 @@ XX of XX: [@azure/keyvault-certificates] blocked by [@azure/core-http]! XX of XX: [@azure/keyvault-keys] blocked by [@azure/core-http]! XX of XX: [@azure/keyvault-secrets] blocked by [@azure/core-http]! [@azure/core-http] Returned error code: 1 -[@azure/cosmos] Returned error code: 2 -[@azure/service-bus] Returned error code: 2 diff --git a/tests/baselines/reference/docker/office-ui-fabric.log b/tests/baselines/reference/docker/office-ui-fabric.log index c3a55fc3f6b..888e20cac4b 100644 --- a/tests/baselines/reference/docker/office-ui-fabric.log +++ b/tests/baselines/reference/docker/office-ui-fabric.log @@ -12,11 +12,11 @@ XX of XX: [@uifabric/tslint-rules] completed successfully in ? seconds ts-jest[versions] (WARN) Version X.X.X-insiders.xxxxxxxx of typescript installed has not been tested with ts-jest. If you're experiencing issues, consider using a supported version (>=2.7.0 <4.0.0). Please do not report issues in ts-jest if you are using unsupported versions. PASS src/__tests__/codepenTransform.test.ts codepen transform - ✓ handles examples with function components (225ms) + ✓ handles examples with function components (256ms) ✓ handles examples with class components (38ms) - ✓ handles examples importing exampleData (115ms) - ✓ handles examples importing TestImages (45ms) - ✓ handles examples importing PeopleExampleData (288ms) + ✓ handles examples importing exampleData (125ms) + ✓ handles examples importing TestImages (33ms) + ✓ handles examples importing PeopleExampleData (270ms) Test Suites: 1 passed, 1 total Tests: 5 passed, 5 total Snapshots: 4 passed, 4 total @@ -246,11 +246,11 @@ SUCCESS WITH WARNINGS (5) ts-jest[versions] (WARN) Version X.X.X-insiders.xxxxxxxx of typescript installed has not been tested with ts-jest. If you're experiencing issues, consider using a supported version (>=2.7.0 <4.0.0). Please do not report issues in ts-jest if you are using unsupported versions. PASS src/__tests__/codepenTransform.test.ts codepen transform - ✓ handles examples with function components (225ms) + ✓ handles examples with function components (256ms) ✓ handles examples with class components (38ms) - ✓ handles examples importing exampleData (115ms) - ✓ handles examples importing TestImages (45ms) - ✓ handles examples importing PeopleExampleData (288ms) + ✓ handles examples importing exampleData (125ms) + ✓ handles examples importing TestImages (33ms) + ✓ handles examples importing PeopleExampleData (270ms) Test Suites: 1 passed, 1 total Tests: 5 passed, 5 total Snapshots: 4 passed, 4 total diff --git a/tests/baselines/reference/user/adonis-framework.log b/tests/baselines/reference/user/adonis-framework.log index e3b19b5de04..6243a7a15db 100644 --- a/tests/baselines/reference/user/adonis-framework.log +++ b/tests/baselines/reference/user/adonis-framework.log @@ -30,7 +30,7 @@ node_modules/adonis-framework/src/Encryption/index.js(87,15): error TS2304: Cann node_modules/adonis-framework/src/Encryption/index.js(101,21): error TS2769: No overload matches this call. Overload 1 of 4, '(data: Binary, input_encoding: undefined, output_encoding: Utf8AsciiBinaryEncoding): string', gave the following error. Argument of type '"base64"' is not assignable to parameter of type 'undefined'. - Overload 2 of 4, '(data: string, input_encoding: "binary" | "base64" | "hex" | undefined, output_encoding: Utf8AsciiBinaryEncoding): string', gave the following error. + Overload 2 of 4, '(data: string, input_encoding: "base64" | "binary" | "hex" | undefined, output_encoding: Utf8AsciiBinaryEncoding): string', gave the following error. Argument of type 'string' is not assignable to parameter of type 'Utf8AsciiBinaryEncoding'. node_modules/adonis-framework/src/Encryption/index.js(114,15): error TS2304: Cannot find name 'Mixed'. node_modules/adonis-framework/src/Encryption/index.js(119,23): error TS2554: Expected 2 arguments, but got 1. diff --git a/tests/baselines/reference/user/chrome-devtools-frontend.log b/tests/baselines/reference/user/chrome-devtools-frontend.log index 3f4fa8d5b95..2d4e456d759 100644 --- a/tests/baselines/reference/user/chrome-devtools-frontend.log +++ b/tests/baselines/reference/user/chrome-devtools-frontend.log @@ -6501,7 +6501,8 @@ node_modules/chrome-devtools-frontend/front_end/formatter_worker/acorn/acorn_loo node_modules/chrome-devtools-frontend/front_end/formatter_worker/acorn/acorn_loose.js(258,55): error TS2339: Property 'end' does not exist on type 'true'. node_modules/chrome-devtools-frontend/front_end/formatter_worker/acorn/acorn_loose.js(1365,5): error TS2339: Property 'next' does not exist on type 'LooseParser'. node_modules/chrome-devtools-frontend/front_end/formatter_worker/acorn/acorn_loose.js(1366,12): error TS2339: Property 'parseTopLevel' does not exist on type 'LooseParser'. -node_modules/chrome-devtools-frontend/front_end/har_importer/HARImporter.js(26,11): error TS2403: Subsequent variable declarations must have the same type. Variable 'page' must be of type 'any', but here has type 'HARPage'. +node_modules/chrome-devtools-frontend/front_end/har_importer/HARImporter.js(16,32): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +node_modules/chrome-devtools-frontend/front_end/har_importer/HARImporter.js(16,52): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. node_modules/chrome-devtools-frontend/front_end/har_importer/HARImporter.js(46,5): error TS2322: Type 'Date' is not assignable to type 'number'. node_modules/chrome-devtools-frontend/front_end/heap_profiler_test_runner/HeapProfilerTestRunner.js(320,70): error TS2339: Property 'heap_profiler' does not exist on type 'any[]'. node_modules/chrome-devtools-frontend/front_end/heap_profiler_test_runner/HeapProfilerTestRunner.js(321,35): error TS2339: Property 'heap_profiler' does not exist on type 'any[]'. diff --git a/tests/baselines/reference/user/lodash.log b/tests/baselines/reference/user/lodash.log index 954077ce729..1c358238f8d 100644 --- a/tests/baselines/reference/user/lodash.log +++ b/tests/baselines/reference/user/lodash.log @@ -382,8 +382,6 @@ node_modules/lodash/nthArg.js(28,26): error TS2345: Argument of type 'number | u node_modules/lodash/omit.js(48,32): error TS2345: Argument of type 'number' is not assignable to parameter of type 'boolean'. node_modules/lodash/orderBy.js(18,10): error TS1003: Identifier expected. node_modules/lodash/orderBy.js(18,10): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name. -node_modules/lodash/org.js(8,22): error TS2307: Cannot find module 'moment'. -node_modules/lodash/org.js(9,19): error TS2307: Cannot find module 'ncp'. node_modules/lodash/parseInt.js(24,10): error TS1003: Identifier expected. node_modules/lodash/parseInt.js(24,10): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name. node_modules/lodash/partial.js(48,9): error TS2339: Property 'placeholder' does not exist on type 'Function'. diff --git a/tests/baselines/reference/user/npmlog.log b/tests/baselines/reference/user/npmlog.log index 1e6284674e3..6b6e315d996 100644 --- a/tests/baselines/reference/user/npmlog.log +++ b/tests/baselines/reference/user/npmlog.log @@ -8,9 +8,9 @@ node_modules/npmlog/log.js(194,37): error TS2345: Argument of type 'any[]' is no Property '0' is missing in type 'any[]' but required in type '[any, ...any[]]'. node_modules/npmlog/log.js(218,12): error TS2551: Property '_paused' does not exist on type 'typeof EventEmitter'. Did you mean 'pause'? node_modules/npmlog/log.js(271,16): error TS2769: No overload matches this call. - Overload 1 of 2, '(buffer: string | Uint8Array | Buffer, cb?: ((err?: Error | null | undefined) => void) | undefined): boolean', gave the following error. - Argument of type 'string | undefined' is not assignable to parameter of type 'string | Uint8Array | Buffer'. - Type 'undefined' is not assignable to type 'string | Uint8Array | Buffer'. + Overload 1 of 2, '(buffer: string | Uint8Array, cb?: ((err?: Error | null | undefined) => void) | undefined): boolean', gave the following error. + Argument of type 'string | undefined' is not assignable to parameter of type 'string | Uint8Array'. + Type 'undefined' is not assignable to type 'string | Uint8Array'. Overload 2 of 2, '(str: string, encoding?: string | undefined, cb?: ((err?: Error | null | undefined) => void) | undefined): boolean', gave the following error. Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string'. From e543d8bc5a17bdee931ac1a0d2b9ddd32a7164a9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 19 Jul 2019 15:22:04 -0700 Subject: [PATCH 15/16] Fix type keyword completions (#32474) * Fix type keyword completions 1. In functions, type keywords were omitted. 2. In All context, no keywords were omitted. (1) fixes #28737 (2) removes 17 keywords that should not be suggested, even at the toplevel of a typescript file: * private * protected * public * static * abstract * as * constructor * get * infer * is * namespace * require * set * type * from * global * of I don't know whether we have a bug tracking this or not. * Change keyword filter in filterGlobalCompletion Instead of changing FunctionLikeBodyKeywords * Add more tests cases * Make type-only completions after < more common Because isPossiblyTypeArgumentPosition doesn't give false positives now that it uses type information. --- src/harness/fourslash.ts | 43 +------------------ src/services/completions.ts | 38 +++++++++------- ...FunctionLikeBody_includesPrimitiveTypes.ts | 27 ++++++++++++ .../completionListInUnclosedTypeArguments.ts | 9 ++-- .../completionListIsGlobalCompletion.ts | 2 +- ...mpletionsIsPossiblyTypeArgumentPosition.ts | 17 +++----- tests/cases/fourslash/fourslash.ts | 1 - tests/cases/user/prettier/prettier | 2 +- 8 files changed, 64 insertions(+), 75 deletions(-) create mode 100644 tests/cases/fourslash/completionInFunctionLikeBody_includesPrimitiveTypes.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index aa764e74cdc..f12506be2fb 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -797,7 +797,7 @@ namespace FourSlash { for (const include of toArray(options.includes)) { const name = typeof include === "string" ? include : include.name; const found = nameToEntries.get(name); - if (!found) throw this.raiseError(`No completion ${name} found`); + if (!found) throw this.raiseError(`Includes: completion '${name}' not found.`); assert(found.length === 1); // Must use 'exact' for multiple completions with same name this.verifyCompletionEntry(ts.first(found), include); } @@ -806,7 +806,7 @@ namespace FourSlash { for (const exclude of toArray(options.excludes)) { assert(typeof exclude === "string"); if (nameToEntries.has(exclude)) { - this.raiseError(`Did not expect to get a completion named ${exclude}`); + this.raiseError(`Excludes: unexpected completion '${exclude}' found.`); } } } @@ -4827,40 +4827,23 @@ namespace FourSlashInterface { "interface", "let", "package", - "private", - "protected", - "public", - "static", "yield", - "abstract", - "as", "any", "async", "await", "boolean", - "constructor", "declare", - "get", - "infer", - "is", "keyof", "module", - "namespace", "never", "readonly", - "require", "number", "object", - "set", "string", "symbol", - "type", "unique", "unknown", - "from", - "global", "bigint", - "of", ].map(keywordEntry); export const statementKeywords: ReadonlyArray = statementKeywordsWithTypes.filter(k => { @@ -5041,40 +5024,23 @@ namespace FourSlashInterface { "interface", "let", "package", - "private", - "protected", - "public", - "static", "yield", - "abstract", - "as", "any", "async", "await", "boolean", - "constructor", "declare", - "get", - "infer", - "is", "keyof", "module", - "namespace", "never", "readonly", - "require", "number", "object", - "set", "string", "symbol", - "type", "unique", "unknown", - "from", - "global", "bigint", - "of", ].map(keywordEntry); export const globalInJsKeywords = getInJsKeywords(globalKeywords); @@ -5127,11 +5093,6 @@ namespace FourSlashInterface { export const insideMethodInJsKeywords = getInJsKeywords(insideMethodKeywords); - export const globalKeywordsPlusUndefined: ReadonlyArray = (() => { - const i = ts.findIndex(globalKeywords, x => x.name === "unique"); - return [...globalKeywords.slice(0, i), keywordEntry("undefined"), ...globalKeywords.slice(i)]; - })(); - export const globals: ReadonlyArray = [ globalThisEntry, ...globalsVars, diff --git a/src/services/completions.ts b/src/services/completions.ts index b5c412a788a..e9a38fb1516 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -947,11 +947,13 @@ namespace ts.Completions { // Right of dot member completion list completionKind = CompletionKind.PropertyAccess; - // Since this is qualified name check its a type node location + // Since this is qualified name check it's a type node location const isImportType = isLiteralImportTypeNode(node); - const isTypeLocation = insideJsDocTagTypeExpression || (isImportType && !(node as ImportTypeNode).isTypeOf) || isPartOfTypeNode(node.parent); + const isTypeLocation = insideJsDocTagTypeExpression + || (isImportType && !(node as ImportTypeNode).isTypeOf) + || isPartOfTypeNode(node.parent) + || isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker); const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node); - const allowTypeOrValue = isRhsOfImportDeclaration || (!isTypeLocation && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker)); if (isEntityName(node) || isImportType) { const isNamespaceName = isModuleDeclaration(node.parent); if (isNamespaceName) isNewIdentifierLocation = true; @@ -968,7 +970,7 @@ namespace ts.Completions { isNamespaceName // At `namespace N.M/**/`, if this is the only declaration of `M`, don't include `M` as a completion. ? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations.every(d => d.parent === node.parent) - : allowTypeOrValue ? + : isRhsOfImportDeclaration ? // Any kind is allowed when dotting off namespace in internal import equals declaration symbol => isValidTypeAccess(symbol) || isValidValueAccess(symbol) : isTypeLocation ? isValidTypeAccess : isValidValueAccess; @@ -1181,7 +1183,6 @@ namespace ts.Completions { function filterGlobalCompletion(symbols: Symbol[]): void { const isTypeOnly = isTypeOnlyCompletion(); - const allowTypes = isTypeOnly || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker); if (isTypeOnly) { keywordFilters = isTypeAssertion() ? KeywordCompletionFilters.TypeAssertionKeywords @@ -1202,12 +1203,9 @@ namespace ts.Completions { return !!(symbol.flags & SymbolFlags.Namespace); } - if (allowTypes) { - // Its a type, but you can reach it by namespace.type as well - const symbolAllowedAsType = symbolCanBeReferencedAtTypeLocation(symbol); - if (symbolAllowedAsType || isTypeOnly) { - return symbolAllowedAsType; - } + if (isTypeOnly) { + // It's a type, but you can reach it by namespace.type as well + return symbolCanBeReferencedAtTypeLocation(symbol); } } @@ -1221,7 +1219,11 @@ namespace ts.Completions { } function isTypeOnlyCompletion(): boolean { - return insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken)); + return insideJsDocTagTypeExpression + || !isContextTokenValueLocation(contextToken) && + (isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker) + || isPartOfTypeNode(location) + || isContextTokenTypeLocation(contextToken)); } function isContextTokenValueLocation(contextToken: Node) { @@ -2060,16 +2062,18 @@ namespace ts.Completions { case KeywordCompletionFilters.None: return false; case KeywordCompletionFilters.All: - return kind === SyntaxKind.AsyncKeyword || SyntaxKind.AwaitKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind) || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.ModuleKeyword + return isFunctionLikeBodyKeyword(kind) + || kind === SyntaxKind.DeclareKeyword + || kind === SyntaxKind.ModuleKeyword || isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword; + case KeywordCompletionFilters.FunctionLikeBodyKeywords: + return isFunctionLikeBodyKeyword(kind); case KeywordCompletionFilters.ClassElementKeywords: return isClassMemberCompletionKeyword(kind); case KeywordCompletionFilters.InterfaceElementKeywords: return isInterfaceOrTypeLiteralCompletionKeyword(kind); case KeywordCompletionFilters.ConstructorParameterKeywords: return isParameterPropertyModifier(kind); - case KeywordCompletionFilters.FunctionLikeBodyKeywords: - return isFunctionLikeBodyKeyword(kind); case KeywordCompletionFilters.TypeAssertionKeywords: return isTypeKeyword(kind) || kind === SyntaxKind.ConstKeyword; case KeywordCompletionFilters.TypeKeywords: @@ -2132,7 +2136,9 @@ namespace ts.Completions { } function isFunctionLikeBodyKeyword(kind: SyntaxKind) { - return kind === SyntaxKind.AsyncKeyword || kind === SyntaxKind.AwaitKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind); + return kind === SyntaxKind.AsyncKeyword + || kind === SyntaxKind.AwaitKeyword + || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind); } function keywordForNode(node: Node): SyntaxKind { diff --git a/tests/cases/fourslash/completionInFunctionLikeBody_includesPrimitiveTypes.ts b/tests/cases/fourslash/completionInFunctionLikeBody_includesPrimitiveTypes.ts new file mode 100644 index 00000000000..bc94936ab43 --- /dev/null +++ b/tests/cases/fourslash/completionInFunctionLikeBody_includesPrimitiveTypes.ts @@ -0,0 +1,27 @@ +/// + +//// class Foo { } +//// class Bar { } +//// function includesTypes() { +//// new Foo ////f -////f(); +////f(); //// ////f2 ////f2 -////f2(); +////f2(); //// ////f2 { const markerName = test.markerName(marker) || ""; - const typeOnly = markerName.endsWith("TypeOnly") || marker.data && marker.data.typeOnly; const valueOnly = markerName.endsWith("ValueOnly"); verify.completions({ marker, - includes: typeOnly ? "Type" : valueOnly ? "x" : ["Type", "x"], - excludes: typeOnly ? "x" : valueOnly ? "Type" : [], + includes: valueOnly ? "x" : "Type", + excludes: valueOnly ? "Type" : "x", isNewIdentifierLocation: marker.data && marker.data.newId || false, }); }); diff --git a/tests/cases/fourslash/completionListIsGlobalCompletion.ts b/tests/cases/fourslash/completionListIsGlobalCompletion.ts index ea89155a771..91cd1a0f9c7 100644 --- a/tests/cases/fourslash/completionListIsGlobalCompletion.ts +++ b/tests/cases/fourslash/completionListIsGlobalCompletion.ts @@ -48,5 +48,5 @@ verify.completions( { marker: "13", exact: globals, isGlobalCompletion: false }, { marker: "15", exact: globals, isGlobalCompletion: true, isNewIdentifierLocation: true }, { marker: "16", exact: [...x, completion.globalThisEntry, ...completion.globalsVars, completion.undefinedVarEntry], isGlobalCompletion: false }, - { marker: "17", exact: completion.globalKeywordsPlusUndefined, isGlobalCompletion: false }, + { marker: "17", exact: completion.globalKeywords, isGlobalCompletion: false }, ); diff --git a/tests/cases/fourslash/completionsIsPossiblyTypeArgumentPosition.ts b/tests/cases/fourslash/completionsIsPossiblyTypeArgumentPosition.ts index 1ea8416d70b..704fe0d8347 100644 --- a/tests/cases/fourslash/completionsIsPossiblyTypeArgumentPosition.ts +++ b/tests/cases/fourslash/completionsIsPossiblyTypeArgumentPosition.ts @@ -9,25 +9,22 @@ ////x + {| "valueOnly": true |} ////x < {| "valueOnly": true |} ////f < {| "valueOnly": true |} -////g < {| "valueOnly": false |} -////const something: C<{| "typeOnly": true |}; -////const something2: C(): callAndConstruct; (): string; }; ////interface callAndConstruct {} ////new callAndConstruct; export const insideMethodKeywords: ReadonlyArray; export const insideMethodInJsKeywords: ReadonlyArray; - export const globalKeywordsPlusUndefined: ReadonlyArray; export const globalsVars: ReadonlyArray; export function globalsInsideFunction(plus: ReadonlyArray): ReadonlyArray; export function globalsInJsInsideFunction(plus: ReadonlyArray): ReadonlyArray; diff --git a/tests/cases/user/prettier/prettier b/tests/cases/user/prettier/prettier index 1e471a00796..7f938c71ffd 160000 --- a/tests/cases/user/prettier/prettier +++ b/tests/cases/user/prettier/prettier @@ -1 +1 @@ -Subproject commit 1e471a007968b7490563b91ed6909ae6046f3fe8 +Subproject commit 7f938c71ffda293eb1b69adf8bd12b7c11f9113b From aab3069e643952f5ddfe750fe3e5f196c16c3915 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 19 Jul 2019 15:55:22 -0700 Subject: [PATCH 16/16] Fix the assert of reporting file infos still attached to the project for circular json reference --- src/server/editorServices.ts | 19 ++++++++++++++++++- src/testRunner/unittests/tsserver/projects.ts | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 81c56bb179b..df9f0e3ee01 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1087,7 +1087,24 @@ namespace ts.server { project.close(); if (Debug.shouldAssert(AssertionLevel.Normal)) { - this.filenameToScriptInfo.forEach(info => Debug.assert(!info.isAttached(project), "Found script Info still attached to project", () => `${project.projectName}: ScriptInfos still attached: ${JSON.stringify(mapDefined(arrayFrom(this.filenameToScriptInfo.values()), info => info.isAttached(project) ? info : undefined))}`)); + this.filenameToScriptInfo.forEach(info => Debug.assert( + !info.isAttached(project), + "Found script Info still attached to project", + () => `${project.projectName}: ScriptInfos still attached: ${JSON.stringify( + arrayFrom( + mapDefinedIterator( + this.filenameToScriptInfo.values(), + info => info.isAttached(project) ? + { + fileName: info.fileName, + projects: info.containingProjects.map(p => p.projectName), + hasMixedContent: info.hasMixedContent + } : undefined + ) + ), + /*replacer*/ undefined, + " " + )}`)); } // Remove the project from pending project updates this.pendingProjectUpdates.delete(project.getProjectName()); diff --git a/src/testRunner/unittests/tsserver/projects.ts b/src/testRunner/unittests/tsserver/projects.ts index 409ce5c525f..abb21669f8a 100644 --- a/src/testRunner/unittests/tsserver/projects.ts +++ b/src/testRunner/unittests/tsserver/projects.ts @@ -1467,5 +1467,22 @@ var x = 10;` openFilesForSession([{ file, projectRootPath }], session); } }); + + it("assert when removing project", () => { + const host = createServerHost([commonFile1, commonFile2, libFile]); + const service = createProjectService(host); + service.openClientFile(commonFile1.path); + const project = service.inferredProjects[0]; + checkProjectActualFiles(project, [commonFile1.path, libFile.path]); + // Intentionally create scriptinfo and attach it to project + const info = service.getOrCreateScriptInfoForNormalizedPath(commonFile2.path as server.NormalizedPath, /*openedByClient*/ false)!; + info.attachToProject(project); + try { + service.applyChangesInOpenFiles(/*openFiles*/ undefined, /*changedFiles*/ undefined, [commonFile1.path]); + } + catch (e) { + assert.isTrue(e.message.indexOf("Debug Failure. False expression: Found script Info still attached to project") === 0); + } + }); }); }