From 950875c1ba867a59a7426158e496d911dd61a1e8 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Thu, 28 Jan 2021 14:43:37 -0600 Subject: [PATCH] refactor(language-service): pull out interfaces on package toplevel (#40621) Two motivations behind this change: 1. We would like to expose the types of the Language Service to external users (like the VSCode extension) via the npm package, on the top level of the package 2. We would like the View Engine and Ivy LS to share a common interface (notably after the inclusion of `getTcb`, the Ivy LS upholds a strict superset of `ts.LanguageService`; previously both VE and Ivy LS were aligned on `ts.LanguageService`.) To this end, this commit refactors the exports on the toplevel of the `language-service/` package to just be types common to both the VE and Ivy language services. The VE and Ivy build targets then import and use these types accordingly, and the expectation is that an external user will just import the relevant typings from the toplevel package without diving into either the VE or Ivy sources. Follow up on #40607 PR Close #40621 --- goldens/circular-deps/packages.json | 6 --- packages/language-service/BUILD.bazel | 14 ++++++- packages/language-service/api.ts | 42 +++++++++++++++++++ packages/language-service/index.ts | 3 +- packages/language-service/ivy/BUILD.bazel | 1 + .../language-service/ivy/language_service.ts | 20 +-------- packages/language-service/ivy/ts_plugin.ts | 7 +--- packages/language-service/language-service.ts | 19 --------- .../language-service/src/language_service.ts | 1 - packages/language-service/src/ts_plugin.ts | 9 +++- .../language-service/src/typescript_host.ts | 13 +----- 11 files changed, 69 insertions(+), 66 deletions(-) create mode 100644 packages/language-service/api.ts delete mode 100644 packages/language-service/language-service.ts diff --git a/goldens/circular-deps/packages.json b/goldens/circular-deps/packages.json index 06e4b0d60b..cae92bc971 100644 --- a/goldens/circular-deps/packages.json +++ b/goldens/circular-deps/packages.json @@ -382,12 +382,6 @@ "packages/forms/src/directives/validators.ts", "packages/forms/src/validators.ts" ], - [ - "packages/language-service/src/completions.ts", - "packages/language-service/src/template.ts", - "packages/language-service/src/typescript_host.ts", - "packages/language-service/src/language_service.ts" - ], [ "packages/language-service/src/template.ts", "packages/language-service/src/typescript_host.ts" diff --git a/packages/language-service/BUILD.bazel b/packages/language-service/BUILD.bazel index 1f2f3338dc..4a741ba60c 100644 --- a/packages/language-service/BUILD.bazel +++ b/packages/language-service/BUILD.bazel @@ -2,11 +2,20 @@ load("//tools:defaults.bzl", "pkg_npm", "ts_library") package(default_visibility = ["//visibility:public"]) +ts_library( + name = "api", + srcs = [ + "api.ts", + ], + deps = [ + "@npm//typescript", + ], +) + ts_library( name = "language-service", - srcs = glob( + srcs = ["index.ts"] + glob( [ - "*.ts", "src/**/*.ts", ], exclude = [ @@ -14,6 +23,7 @@ ts_library( ], ), deps = [ + ":api", ":ts_utils", "//packages:types", "//packages/compiler", diff --git a/packages/language-service/api.ts b/packages/language-service/api.ts new file mode 100644 index 0000000000..b74bc200d2 --- /dev/null +++ b/packages/language-service/api.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * @module + * @description + * Entry point for all public APIs of the language service package. + */ + +import * as ts from 'typescript'; + +export type GetTcbResponse = { + /** + * The filename of the SourceFile this typecheck block belongs to. + * The filename is entirely opaque and unstable, useful only for debugging + * purposes. + */ + fileName: string, + /** The content of the SourceFile this typecheck block belongs to. */ + content: string, + /** + * Spans over node(s) in the typecheck block corresponding to the + * TS code generated for template node under the current cursor position. + * + * When the cursor position is over a source for which there is no generated + * code, `selections` is empty. + */ + selections: ts.TextSpan[], +}|undefined; + +/** + * `NgLanguageService` describes an instance of an Angular language service, + * whose API surface is a strict superset of TypeScript's language service. + */ +export interface NgLanguageService extends ts.LanguageService { + getTcb(fileName: string, position: number): GetTcbResponse; +} diff --git a/packages/language-service/index.ts b/packages/language-service/index.ts index 89b3d0566a..0ed58aaa34 100644 --- a/packages/language-service/index.ts +++ b/packages/language-service/index.ts @@ -6,4 +6,5 @@ * found in the LICENSE file at https://angular.io/license */ -export * from './language-service'; +export * from './api'; +export {create, getExternalFiles} from './src/ts_plugin'; diff --git a/packages/language-service/ivy/BUILD.bazel b/packages/language-service/ivy/BUILD.bazel index 0a7aa1cb78..e87b920172 100644 --- a/packages/language-service/ivy/BUILD.bazel +++ b/packages/language-service/ivy/BUILD.bazel @@ -20,6 +20,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/typecheck", "//packages/compiler-cli/src/ngtsc/typecheck/api", "//packages/compiler-cli/src/ngtsc/util", + "//packages/language-service:api", "@npm//@types/node", "@npm//typescript", ], diff --git a/packages/language-service/ivy/language_service.ts b/packages/language-service/ivy/language_service.ts index 1b3a199b48..a69135b3f2 100644 --- a/packages/language-service/ivy/language_service.ts +++ b/packages/language-service/ivy/language_service.ts @@ -15,6 +15,7 @@ import {TypeCheckShimGenerator} from '@angular/compiler-cli/src/ngtsc/typecheck' import {OptimizeFor, TypeCheckingProgramStrategy} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import {findFirstMatchingNode} from '@angular/compiler-cli/src/ngtsc/typecheck/src/comments'; import * as ts from 'typescript/lib/tsserverlibrary'; +import {GetTcbResponse} from '../api'; import {LanguageServiceAdapter, LSParseConfigHost} from './adapters'; import {CompilerFactory} from './compiler_factory'; @@ -25,25 +26,6 @@ import {ReferencesAndRenameBuilder} from './references'; import {getTargetAtPosition, TargetContext, TargetNodeKind} from './template_target'; import {getTemplateInfoAtPosition, isTypeScriptFile} from './utils'; -export type GetTcbResponse = { - /** - * The filename of the SourceFile this typecheck block belongs to. - * The filename is entirely opaque and unstable, useful only for debugging - * purposes. - */ - fileName: string, - /** The content of the SourceFile this typecheck block belongs to. */ - content: string, - /** - * Spans over node(s) in the typecheck block corresponding to the - * TS code generated for template node under the current cursor position. - * - * When the cursor position is over a source for which there is no generated - * code, `selections` is empty. - */ - selections: ts.TextSpan[], -}|undefined; - export class LanguageService { private options: CompilerOptions; readonly compilerFactory: CompilerFactory; diff --git a/packages/language-service/ivy/ts_plugin.ts b/packages/language-service/ivy/ts_plugin.ts index 89f4c27a13..a4c9b08e5e 100644 --- a/packages/language-service/ivy/ts_plugin.ts +++ b/packages/language-service/ivy/ts_plugin.ts @@ -7,11 +7,8 @@ */ import * as ts from 'typescript/lib/tsserverlibrary'; -import {GetTcbResponse, LanguageService} from './language_service'; - -export interface NgLanguageService extends ts.LanguageService { - getTcb(fileName: string, position: number): GetTcbResponse; -} +import {GetTcbResponse, NgLanguageService} from '../api'; +import {LanguageService} from './language_service'; export function create(info: ts.server.PluginCreateInfo): NgLanguageService { const {project, languageService: tsLS, config} = info; diff --git a/packages/language-service/language-service.ts b/packages/language-service/language-service.ts deleted file mode 100644 index 4b37605cf5..0000000000 --- a/packages/language-service/language-service.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/// - -/** - * @module - * @description - * Entry point for all public APIs of the language service package. - */ -export {createLanguageService} from './src/language_service'; -export * from './src/ts_plugin'; -export {Declaration, Definition, Diagnostic, LanguageService, LanguageServiceHost, Span, TemplateSource} from './src/types'; -export {TypeScriptServiceHost, createLanguageServiceFromTypescript} from './src/typescript_host'; diff --git a/packages/language-service/src/language_service.ts b/packages/language-service/src/language_service.ts index 177a73f0d6..b26f499471 100644 --- a/packages/language-service/src/language_service.ts +++ b/packages/language-service/src/language_service.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import * as path from 'path'; import * as tss from 'typescript/lib/tsserverlibrary'; import {getTemplateCompletions} from './completions'; diff --git a/packages/language-service/src/ts_plugin.ts b/packages/language-service/src/ts_plugin.ts index 4320a7d553..fea31a3e28 100644 --- a/packages/language-service/src/ts_plugin.ts +++ b/packages/language-service/src/ts_plugin.ts @@ -7,6 +7,7 @@ */ import * as tss from 'typescript/lib/tsserverlibrary'; +import {NgLanguageService} from '../api'; import {createLanguageService} from './language_service'; import {TypeScriptServiceHost} from './typescript_host'; @@ -41,7 +42,7 @@ export function getExternalFiles(project: tss.server.Project): string[] { }); } -export function create(info: tss.server.PluginCreateInfo): tss.LanguageService { +export function create(info: tss.server.PluginCreateInfo): NgLanguageService { const {languageService: tsLS, languageServiceHost: tsLSHost, config, project} = info; // This plugin could operate under two different modes: // 1. TS + Angular @@ -135,6 +136,11 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService { return undefined; } + function getTcb(fileName: string, position: number) { + // Not implemented in VE Language Service + return undefined; + } + return { // First clone the original TS language service ...tsLS, @@ -147,5 +153,6 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService { getTypeDefinitionAtPosition, getReferencesAtPosition, findRenameLocations, + getTcb, }; } diff --git a/packages/language-service/src/typescript_host.ts b/packages/language-service/src/typescript_host.ts index 934501daf9..f3d24ca88a 100644 --- a/packages/language-service/src/typescript_host.ts +++ b/packages/language-service/src/typescript_host.ts @@ -11,21 +11,10 @@ import {SchemaMetadata, ViewEncapsulation, ɵConsole as Console} from '@angular/ import * as path from 'path'; import * as tss from 'typescript/lib/tsserverlibrary'; -import {createLanguageService} from './language_service'; import {ReflectorHost} from './reflector_host'; import {ExternalTemplate, InlineTemplate} from './template'; import {findTightestNode, getClassDeclFromDecoratorProp, getDirectiveClassLike, getPropertyAssignmentFromValue} from './ts_utils'; -import {AstResult, Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types'; - -/** - * Create a `LanguageServiceHost` - */ -export function createLanguageServiceFromTypescript( - host: tss.LanguageServiceHost, service: tss.LanguageService): LanguageService { - const ngHost = new TypeScriptServiceHost(host, service); - const ngServer = createLanguageService(ngHost); - return ngServer; -} +import {AstResult, Declaration, DeclarationError, DiagnosticMessageChain, LanguageServiceHost, Span, TemplateSource} from './types'; /** * The language service never needs the normalized versions of the metadata. To avoid parsing