Skip to content

Commit

Permalink
fix(language-service): use single entry point for Ivy and View Engine (
Browse files Browse the repository at this point in the history
…#40967)

Currently there are two entry points for the `@angular/language-service`
package:

- `@angular/language-service`
  This default entry point is for View Engine LS. Through the redirection
  of `main` field in `package.json`, it resolves to
  `./bundles/language-service.js`.
- `@angular/language-service/bundles/ivy.js`
  This secondary entry point is for Ivy LS.

TypeScript recently changed the behavior of tsserver to allow only package
names as plugin names [1] for security reasons. This means the secondary
entry point for Ivy LS can no longer be used.
We implemented a quick hack in the module resolver (in the extension repo)
to fix this, but the long term fix should be in `@angular/language-service`.

Here, the `main` field in `package.json` is changed to `index.js`, and in the
index file we conditionally load View Engine or Ivy based on the input config.
This eliminates the need for multiple entry points.

As part of this PR, I also removed all source code for View Engine and Ivy
included in the NPM package. Consumers of this package should run the bundled
output and nothing else. This would help us prevent an accidental import that
results in execution of unbundled code.

[1]: microsoft/TypeScript#42713

PR Close #40967
  • Loading branch information
kyliau authored and zarend committed Feb 24, 2021
1 parent 51a7977 commit e986a97
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 6 deletions.
7 changes: 4 additions & 3 deletions packages/language-service/BUILD.bazel
Expand Up @@ -6,15 +6,17 @@ ts_library(
name = "api",
srcs = [
"api.ts",
"index.ts",
],
deps = [
"@npm//@types/node",
"@npm//typescript",
],
)

ts_library(
name = "language-service",
srcs = ["index.ts"] + glob(
srcs = glob(
[
"src/**/*.ts",
],
Expand Down Expand Up @@ -54,8 +56,7 @@ pkg_npm(
"//integration:__pkg__",
],
deps = [
":language-service",
"//packages/language-service/ivy",
":api",
# min bundle is not used at the moment; omit from package to speed up build
"//packages/language-service/bundles:language-service.js",
"//packages/language-service/bundles:ivy.js",
Expand Down
13 changes: 13 additions & 0 deletions packages/language-service/api.ts
Expand Up @@ -14,6 +14,19 @@

import * as ts from 'typescript';

export interface NgLanguageServiceConfig {
/**
* If true, return only Angular results. Otherwise, return Angular + TypeScript
* results.
*/
angularOnly: boolean;
/**
* If true, return factory function for Ivy LS during plugin initialization.
* Otherwise return factory function for View Engine LS.
*/
ivy: boolean;
}

export type GetTcbResponse = {
/**
* The filename of the SourceFile this typecheck block belongs to.
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/bundles/BUILD.bazel
Expand Up @@ -3,7 +3,7 @@ load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", "ng_rollup_b
ng_rollup_bundle(
name = "language-service",
build_optimizer = False,
entry_point = "//packages/language-service:index.ts",
entry_point = "//packages/language-service:src/ts_plugin.ts",
format = "amd",
globals = {
"fs": "fs",
Expand Down
30 changes: 29 additions & 1 deletion packages/language-service/index.ts
Expand Up @@ -6,5 +6,33 @@
* found in the LICENSE file at https://angular.io/license
*/

import * as ts from 'typescript/lib/tsserverlibrary';
import {NgLanguageService, NgLanguageServiceConfig} from './api';

export * from './api';
export {create, getExternalFiles} from './src/ts_plugin';

interface PluginModule extends ts.server.PluginModule {
create(createInfo: ts.server.PluginCreateInfo): NgLanguageService;
onConfigurationChanged?(config: NgLanguageServiceConfig): void;
}

const factory: ts.server.PluginModuleFactory = (tsModule): PluginModule => {
let plugin: PluginModule;

return {
create(info: ts.server.PluginCreateInfo): NgLanguageService {
const config: NgLanguageServiceConfig = info.config;
const bundleName = config.ivy ? 'ivy.js' : 'language-service.js';
plugin = require(`./bundles/${bundleName}`)(tsModule);
return plugin.create(info);
},
getExternalFiles(project: ts.server.Project): string[] {
return plugin?.getExternalFiles?.(project) ?? [];
},
onConfigurationChanged(config: NgLanguageServiceConfig): void {
plugin?.onConfigurationChanged?.(config);
},
};
};

module.exports = factory;
2 changes: 1 addition & 1 deletion packages/language-service/package.json
Expand Up @@ -2,7 +2,7 @@
"name": "@angular/language-service",
"version": "0.0.0-PLACEHOLDER",
"description": "Angular - language services",
"main": "./bundles/language-service.js",
"main": "./index.js",
"typings": "./index.d.ts",
"author": "angular",
"license": "MIT",
Expand Down

0 comments on commit e986a97

Please sign in to comment.