diff --git a/client/src/client.ts b/client/src/client.ts index f1406de3c3..1673e47d2c 100644 --- a/client/src/client.ts +++ b/client/src/client.ts @@ -395,7 +395,8 @@ function getProbeLocations(bundled: string): string[] { * Construct the arguments that's used to spawn the server process. * @param ctx vscode extension context */ -function constructArgs(ctx: vscode.ExtensionContext, viewEngine: boolean): string[] { +function constructArgs( + ctx: vscode.ExtensionContext, viewEngine: boolean, isTrustedWorkspace: boolean): string[] { const config = vscode.workspace.getConfiguration(); const args: string[] = ['--logToConsole']; @@ -440,6 +441,10 @@ function constructArgs(ctx: vscode.ExtensionContext, viewEngine: boolean): strin args.push('--forceStrictTemplates'); } + if (!isTrustedWorkspace) { + args.push('--untrustedWorkspace'); + } + const tsdk: string|null = config.get('typescript.tsdk', null); const tsProbeLocations = [tsdk, ...getProbeLocations(ctx.extensionPath)]; args.push('--tsProbeLocations', tsProbeLocations.join(',')); @@ -475,7 +480,7 @@ function getServerOptions(ctx: vscode.ExtensionContext, debug: boolean): lsp.Nod } // Node module for the language server - const args = constructArgs(ctx, viewEngine); + const args = constructArgs(ctx, viewEngine, vscode.workspace.isTrusted); const prodBundle = ctx.asAbsolutePath('server'); const devBundle = ctx.asAbsolutePath(path.join('dist', 'server', 'server.js')); // VS Code Insider launches extensions in debug mode by default but users diff --git a/package.json b/package.json index 127a3f6c81..7d2395e7dd 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ }, "capabilities": { "untrustedWorkspaces": { - "supported": false, - "description": "This extension requires workspace trust because it needs to execute ngcc from the node_modules in the workspace." + "supported": "limited", + "description": "This extension requires workspace trust because it needs to execute ngcc from the node_modules in the workspace. Projects do not require ngcc if all library dependencies are published with partial-Ivy or Full-Ivy." } }, "categories": [ @@ -64,6 +64,10 @@ { "command": "angular.goToTemplateForComponent", "when": "editorLangId == typescript" + }, + { + "command": "angular.runNgcc", + "when": "isWorkspaceTrusted" } ], "editor/context": [ diff --git a/server/src/cmdline_utils.ts b/server/src/cmdline_utils.ts index ae09133889..d536339e03 100644 --- a/server/src/cmdline_utils.ts +++ b/server/src/cmdline_utils.ts @@ -44,6 +44,7 @@ interface CommandLineOptions { includeAutomaticOptionalChainCompletions: boolean; includeCompletionsWithSnippetText: boolean; forceStrictTemplates: boolean; + untrustedWorkspace: boolean; } export function parseCommandLine(argv: string[]): CommandLineOptions { @@ -60,6 +61,7 @@ export function parseCommandLine(argv: string[]): CommandLineOptions { hasArgument(argv, '--includeAutomaticOptionalChainCompletions'), includeCompletionsWithSnippetText: hasArgument(argv, '--includeCompletionsWithSnippetText'), forceStrictTemplates: hasArgument(argv, '--forceStrictTemplates'), + untrustedWorkspace: hasArgument(argv, '--untrustedWorkspace'), }; } diff --git a/server/src/server.ts b/server/src/server.ts index e6250c6e8f..4a2227e3d5 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -43,11 +43,12 @@ function main() { ngPlugin: '@angular/language-service', resolvedNgLsPath: ng.resolvedPath, ivy: isG3 ? true : options.ivy, - disableAutomaticNgcc: options.disableAutomaticNgcc, + disableAutomaticNgcc: options.disableAutomaticNgcc || options.untrustedWorkspace, logToConsole: options.logToConsole, includeAutomaticOptionalChainCompletions: options.includeAutomaticOptionalChainCompletions, includeCompletionsWithSnippetText: options.includeCompletionsWithSnippetText, forceStrictTemplates: isG3 || options.forceStrictTemplates, + untrustedWorkspace: options.untrustedWorkspace, }); // Log initialization info diff --git a/server/src/session.ts b/server/src/session.ts index 664a434940..56c37edf60 100644 --- a/server/src/session.ts +++ b/server/src/session.ts @@ -32,6 +32,7 @@ export interface SessionOptions { includeAutomaticOptionalChainCompletions: boolean; includeCompletionsWithSnippetText: boolean; forceStrictTemplates: boolean; + untrustedWorkspace: boolean; } enum LanguageId { @@ -61,6 +62,7 @@ export class Session { private readonly openFiles = new MruTracker(); private readonly includeAutomaticOptionalChainCompletions: boolean; private readonly includeCompletionsWithSnippetText: boolean; + private readonly untrustedWorkspace: boolean; private snippetSupport: boolean|undefined; // Tracks the spawn order and status of the `ngcc` processes. This allows us to ensure we enable // the LS in the same order the projects were created in. @@ -84,6 +86,7 @@ export class Session { this.ivy = options.ivy; this.disableAutomaticNgcc = options.disableAutomaticNgcc; this.logToConsole = options.logToConsole; + this.untrustedWorkspace = options.untrustedWorkspace; // Create a connection for the server. The connection uses Node's IPC as a transport. this.connection = lsp.createConnection({ // cancelUndispatched is a "middleware" to handle all cancellation requests. @@ -1133,6 +1136,10 @@ export class Session { * Disable the language service, run ngcc, then re-enable language service. */ private async runNgcc(project: ts.server.Project): Promise { + if (this.untrustedWorkspace) { + this.error('Cannot run ngcc in an untrusted workspace.'); + return; + } if (!isConfiguredProject(project) || this.projectNgccQueue.some(p => p.project === project)) { return; }