Skip to content

Commit 9273aed

Browse files
committed
feat: add command to run ngcc manually
Adds a command to manually run ngcc for a given file. This commit also refactors the `disableNgcc` option to indicate that it's disabling the _automatic_ `ngcc` run but it can still be triggered manually with the new command. The `disableAutomaticNgcc` option and new command to manually run ngcc are complementary. We have found that the extension can be overzealous in automatically running ngcc, especially with solution-style project configurations (common practice in Nx projects). If a dev server is not active to rerun ngcc when needed, then the new command gives an easy way to do that when the `disableAutomaticNgcc` option is set. Fixes #991
1 parent f4689b7 commit 9273aed

File tree

7 files changed

+59
-15
lines changed

7 files changed

+59
-15
lines changed

client/src/client.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import * as vscode from 'vscode';
1212
import * as lsp from 'vscode-languageclient/node';
1313

1414
import {OpenOutputChannel, ProjectLoadingFinish, ProjectLoadingStart, SuggestStrictMode, SuggestStrictModeParams} from '../common/notifications';
15-
import {GetComponentsWithTemplateFile, GetTcbRequest, GetTemplateLocationForComponent, IsInAngularProject} from '../common/requests';
15+
import {GetComponentsWithTemplateFile, GetTcbRequest, GetTemplateLocationForComponent, IsInAngularProject, RunNgccRequest} from '../common/requests';
1616
import {resolve, Version} from '../common/resolver';
1717

1818
import {isInsideComponentDecorator, isInsideInlineTemplateRegion, isInsideStringLiteral} from './embedded_support';
@@ -255,6 +255,15 @@ export class AngularLanguageClient implements vscode.Disposable {
255255
};
256256
}
257257

258+
runNgcc(textEditor: vscode.TextEditor): void {
259+
if (this.client === null) {
260+
return;
261+
}
262+
this.client.sendRequest(RunNgccRequest, {
263+
textDocument: this.client.code2ProtocolConverter.asTextDocumentIdentifier(textEditor.document),
264+
});
265+
}
266+
258267
get initializeResult(): lsp.InitializeResult|undefined {
259268
return this.client?.initializeResult;
260269
}
@@ -410,9 +419,9 @@ function constructArgs(ctx: vscode.ExtensionContext, viewEngine: boolean): strin
410419
args.push('--includeCompletionsWithSnippetText');
411420
}
412421

413-
const disableNgcc = config.get<boolean>('angular.disableNgcc');
414-
if (disableNgcc) {
415-
args.push('--disableNgcc');
422+
const disableAutomaticNgcc = config.get<boolean>('angular.disableAutomaticNgcc');
423+
if (disableAutomaticNgcc) {
424+
args.push('--disableAutomaticNgcc');
416425
}
417426

418427
const tsdk: string|null = config.get('typescript.tsdk', null);

client/src/commands.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ function getTemplateTcb(
115115
}
116116
};
117117
}
118+
function runNgcc(ngClient: AngularLanguageClient): Command {
119+
return {
120+
id: 'angular.runNgcc',
121+
isTextEditorCommand: true,
122+
async execute(textEditor: vscode.TextEditor) {
123+
ngClient.runNgcc(textEditor);
124+
}
125+
};
126+
}
118127

119128
/**
120129
* Command goToComponentWithTemplateFile finds components which reference an external template in
@@ -180,6 +189,7 @@ export function registerCommands(
180189
restartNgServer(client),
181190
openLogFile(client),
182191
getTemplateTcb(client, context),
192+
runNgcc(client),
183193
goToComponentWithTemplateFile(client),
184194
goToTemplateForComponent(client),
185195
];

common/requests.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,16 @@ export interface GetTcbParams {
3030
position: lsp.Position;
3131
}
3232

33+
export interface RunNgccParams {
34+
textDocument: lsp.TextDocumentIdentifier;
35+
}
36+
3337
export const GetTcbRequest =
3438
new lsp.RequestType<GetTcbParams, GetTcbResponse|null, /* error */ void>('angular/getTcb');
3539

40+
export const RunNgccRequest =
41+
new lsp.RequestType<RunNgccParams, void, /* error */ void>('angular/runNgcc');
42+
3643
export interface GetTcbResponse {
3744
uri: lsp.DocumentUri;
3845
content: string;

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
"command": "angular.goToTemplateForComponent",
4949
"title": "Go to template",
5050
"category": "Angular"
51+
},
52+
{
53+
"command": "angular.runNgcc",
54+
"title": "Run ngcc",
55+
"category": "Angular"
5156
}
5257
],
5358
"menus": {
@@ -124,10 +129,10 @@
124129
"default": true,
125130
"markdownDescription": "Enable/disable snippet completions from Angular language server. Requires using TypeScript 4.3+ in the workspace and the `legacy View Engine` option to be disabled."
126131
},
127-
"angular.disableNgcc": {
132+
"angular.disableAutomaticNgcc": {
128133
"type": "boolean",
129134
"default": false,
130-
"markdownDescription": "Manually disable the step to run ngcc. [ngcc](https://github.com/angular/angular/blob/master/packages/compiler/design/architecture.md#high-level-proposal) is required to run and gather metadata from libraries not published with Ivy instructions. This can be run outside of VSCode instead (for example, as part of the build/rebuild in the CLI). Note that ngcc needs to run not only at startup, but also whenever the dependencies change. Failing to run ngcc when required can result in incomplete information and spurious errors reported by the language service."
135+
"markdownDescription": "Disable the step to automatically run ngcc. [ngcc](https://github.com/angular/angular/blob/master/packages/compiler/design/architecture.md#high-level-proposal) is required to run and gather metadata from libraries not published with Ivy instructions. This can be run outside of VSCode instead (for example, as part of the build/rebuild in the CLI). Note that ngcc needs to run not only at startup, but also whenever the dependencies change. Failing to run ngcc when required can result in incomplete information and spurious errors reported by the language service."
131136
}
132137
}
133138
},

server/src/cmdline_utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ interface CommandLineOptions {
3535
/**
3636
* If true, skips the running ngcc when using Ivy LS.
3737
*/
38-
disableNgcc: boolean;
38+
disableAutomaticNgcc: boolean;
3939
logFile?: string;
4040
logVerbosity?: string;
4141
logToConsole: boolean;
@@ -49,7 +49,7 @@ export function parseCommandLine(argv: string[]): CommandLineOptions {
4949
return {
5050
help: hasArgument(argv, '--help'),
5151
ivy: !hasArgument(argv, '--viewEngine'),
52-
disableNgcc: hasArgument(argv, '--disableNgcc'),
52+
disableAutomaticNgcc: hasArgument(argv, '--disableAutomaticNgcc'),
5353
logFile: findArgument(argv, '--logFile'),
5454
logVerbosity: findArgument(argv, '--logVerbosity'),
5555
logToConsole: hasArgument(argv, '--logToConsole'),

server/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function main() {
4343
ngPlugin: '@angular/language-service',
4444
resolvedNgLsPath: ng.resolvedPath,
4545
ivy: isG3 ? true : options.ivy,
46-
disableNgcc: options.disableNgcc,
46+
disableAutomaticNgcc: options.disableAutomaticNgcc,
4747
logToConsole: options.logToConsole,
4848
includeAutomaticOptionalChainCompletions: options.includeAutomaticOptionalChainCompletions,
4949
includeCompletionsWithSnippetText: options.includeCompletionsWithSnippetText,

server/src/session.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as lsp from 'vscode-languageserver/node';
1313

1414
import {ServerOptions} from '../common/initialize';
1515
import {NgccProgressEnd, OpenOutputChannel, ProjectLanguageService, ProjectLoadingFinish, ProjectLoadingStart, SuggestStrictMode} from '../common/notifications';
16-
import {GetComponentsWithTemplateFile, GetTcbParams, GetTcbRequest, GetTcbResponse, GetTemplateLocationForComponent, GetTemplateLocationForComponentParams, IsInAngularProject, IsInAngularProjectParams} from '../common/requests';
16+
import {GetComponentsWithTemplateFile, GetTcbParams, GetTcbRequest, GetTcbResponse, GetTemplateLocationForComponent, GetTemplateLocationForComponentParams, IsInAngularProject, IsInAngularProjectParams, RunNgccParams, RunNgccRequest} from '../common/requests';
1717

1818
import {readNgCompletionData, tsCompletionEntryToLspCompletionItem} from './completion';
1919
import {tsDiagnosticToLspDiagnostic} from './diagnostic';
@@ -27,7 +27,7 @@ export interface SessionOptions {
2727
ngPlugin: string;
2828
resolvedNgLsPath: string;
2929
ivy: boolean;
30-
disableNgcc: boolean;
30+
disableAutomaticNgcc: boolean;
3131
logToConsole: boolean;
3232
includeAutomaticOptionalChainCompletions: boolean;
3333
includeCompletionsWithSnippetText: boolean;
@@ -55,7 +55,7 @@ export class Session {
5555
private readonly projectService: ts.server.ProjectService;
5656
private readonly logger: ts.server.Logger;
5757
private readonly ivy: boolean;
58-
private readonly disableNgcc: boolean;
58+
private readonly disableAutomaticNgcc: boolean;
5959
private readonly configuredProjToExternalProj = new Map<string, string>();
6060
private readonly logToConsole: boolean;
6161
private readonly openFiles = new MruTracker();
@@ -82,7 +82,7 @@ export class Session {
8282
this.includeCompletionsWithSnippetText = options.includeCompletionsWithSnippetText;
8383
this.logger = options.logger;
8484
this.ivy = options.ivy;
85-
this.disableNgcc = options.disableNgcc;
85+
this.disableAutomaticNgcc = options.disableAutomaticNgcc;
8686
this.logToConsole = options.logToConsole;
8787
// Create a connection for the server. The connection uses Node's IPC as a transport.
8888
this.connection = lsp.createConnection({
@@ -187,6 +187,7 @@ export class Session {
187187
conn.onRequest(GetComponentsWithTemplateFile, p => this.onGetComponentsWithTemplateFile(p));
188188
conn.onRequest(GetTemplateLocationForComponent, p => this.onGetTemplateLocationForComponent(p));
189189
conn.onRequest(GetTcbRequest, p => this.onGetTcb(p));
190+
conn.onRequest(RunNgccRequest, p => this.onRunNgcc(p));
190191
conn.onRequest(IsInAngularProject, p => this.isInAngularProject(p));
191192
conn.onCodeLens(p => this.onCodeLens(p));
192193
conn.onCodeLensResolve(p => this.onCodeLensResolve(p));
@@ -237,6 +238,18 @@ export class Session {
237238
};
238239
}
239240

241+
private onRunNgcc(params: RunNgccParams): void {
242+
const lsInfo = this.getLSAndScriptInfo(params.textDocument);
243+
if (lsInfo === null) {
244+
return;
245+
}
246+
const project = this.getDefaultProjectForScriptInfo(lsInfo.scriptInfo);
247+
if (!project) {
248+
return;
249+
}
250+
this.runNgcc(project);
251+
}
252+
240253
private onGetTemplateLocationForComponent(params: GetTemplateLocationForComponentParams):
241254
lsp.Location|null {
242255
const lsInfo = this.getLSAndScriptInfo(params.textDocument);
@@ -460,7 +473,7 @@ export class Session {
460473
const {project} = event.data;
461474
const angularCore = this.findAngularCore(project);
462475
if (angularCore) {
463-
if (this.ivy && isExternalAngularCore(angularCore) && !this.disableNgcc) {
476+
if (this.ivy && isExternalAngularCore(angularCore) && !this.disableAutomaticNgcc) {
464477
// Do not wait on this promise otherwise we'll be blocking other requests
465478
this.runNgcc(project);
466479
} else {
@@ -1163,7 +1176,7 @@ export class Session {
11631176
* Disable the language service, run ngcc, then re-enable language service.
11641177
*/
11651178
private async runNgcc(project: ts.server.Project): Promise<void> {
1166-
if (!isConfiguredProject(project)) {
1179+
if (!isConfiguredProject(project) || this.projectNgccQueue.some(p => p.project === project)) {
11671180
return;
11681181
}
11691182
// Disable language service until ngcc is completed.

0 commit comments

Comments
 (0)