Skip to content

Commit 2585e03

Browse files
kyliauKeen Yee Liau
authored andcommitted
fix: Use View Engine LS for projects < v9
`ngcc` does not work correctly for Angular projects < version 9, so automatically switch to View Engine LS when such projects are detected in the workspace.
1 parent c153670 commit 2585e03

File tree

3 files changed

+118
-93
lines changed

3 files changed

+118
-93
lines changed

client/src/client.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as lsp from 'vscode-languageclient/node';
1414
import {ProjectLoadingFinish, ProjectLoadingStart, SuggestStrictMode, SuggestStrictModeParams} from '../common/notifications';
1515
import {NgccProgress, NgccProgressToken, NgccProgressType} from '../common/progress';
1616
import {GetComponentsWithTemplateFile, GetTcbRequest, IsInAngularProject} from '../common/requests';
17+
import {resolve, Version} from '../common/resolver';
1718

1819
import {isInsideComponentDecorator, isInsideInlineTemplateRegion} from './embedded_support';
1920
import {ProgressReporter} from './progress-reporter';
@@ -314,7 +315,8 @@ function registerProgressHandlers(client: lsp.LanguageClient) {
314315
client.onProgress(NgccProgressType, NgccProgressToken, async (params: NgccProgress) => {
315316
const {configFilePath} = params;
316317
if (!progressReporters.has(configFilePath)) {
317-
progressReporters.set(configFilePath, new ProgressReporter());
318+
const reporter = new ProgressReporter();
319+
progressReporters.set(configFilePath, reporter);
318320
}
319321
const reporter = progressReporters.get(configFilePath)!;
320322
if (params.done) {
@@ -388,7 +390,8 @@ function constructArgs(ctx: vscode.ExtensionContext): string[] {
388390
const ngProbeLocations = getProbeLocations(ngdk, ctx.extensionPath);
389391
args.push('--ngProbeLocations', ngProbeLocations.join(','));
390392

391-
const viewEngine: boolean = config.get('angular.view-engine', false);
393+
console.error(vscode.workspace.workspaceFolders);
394+
const viewEngine: boolean = config.get('angular.view-engine', !allProjectsSupportIvy());
392395
if (viewEngine) {
393396
args.push('--viewEngine');
394397
}
@@ -432,4 +435,15 @@ function getServerOptions(ctx: vscode.ExtensionContext, debug: boolean): lsp.Nod
432435
execArgv: debug ? devExecArgv : prodExecArgv,
433436
},
434437
};
438+
}
439+
440+
function allProjectsSupportIvy() {
441+
const workspaceFolders = vscode.workspace.workspaceFolders || [];
442+
for (const workspaceFolder of workspaceFolders) {
443+
const angularCore = resolve('@angular/core', workspaceFolder.uri.fsPath);
444+
if (angularCore?.version.greaterThanOrEqual(new Version('9')) === false) {
445+
return false;
446+
}
447+
}
448+
return true;
435449
}

common/resolver.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import * as fs from 'fs';
10+
11+
/**
12+
* Represents a valid node module that has been successfully resolved.
13+
*/
14+
export interface NodeModule {
15+
name: string;
16+
resolvedPath: string;
17+
version: Version;
18+
}
19+
20+
export function resolve(packageName: string, location: string, rootPackage?: string): NodeModule|
21+
undefined {
22+
rootPackage = rootPackage || packageName;
23+
try {
24+
const packageJsonPath = require.resolve(`${rootPackage}/package.json`, {
25+
paths: [location],
26+
});
27+
// Do not use require() to read JSON files since it's a potential security
28+
// vulnerability.
29+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
30+
const resolvedPath = require.resolve(packageName, {
31+
paths: [location],
32+
});
33+
return {
34+
name: packageName,
35+
resolvedPath,
36+
version: new Version(packageJson.version),
37+
};
38+
} catch {
39+
}
40+
}
41+
42+
export class Version {
43+
readonly major: number;
44+
readonly minor: number;
45+
readonly patch: number;
46+
47+
constructor(private readonly versionStr: string) {
48+
const [major, minor, patch] = Version.parseVersionStr(versionStr);
49+
this.major = major;
50+
this.minor = minor;
51+
this.patch = patch;
52+
}
53+
54+
greaterThanOrEqual(other: Version): boolean {
55+
if (this.major < other.major) {
56+
return false;
57+
}
58+
if (this.major > other.major) {
59+
return true;
60+
}
61+
if (this.minor < other.minor) {
62+
return false;
63+
}
64+
if (this.minor > other.minor) {
65+
return true;
66+
}
67+
return this.patch >= other.patch;
68+
}
69+
70+
toString(): string {
71+
return this.versionStr;
72+
}
73+
74+
/**
75+
* Converts the specified `versionStr` to its number constituents. Invalid
76+
* number value is represented as negative number.
77+
* @param versionStr
78+
*/
79+
static parseVersionStr(versionStr: string): [number, number, number] {
80+
const [major, minor, patch] = versionStr.split('.').map(parseNonNegativeInt);
81+
return [
82+
major === undefined ? 0 : major,
83+
minor === undefined ? 0 : minor,
84+
patch === undefined ? 0 : patch,
85+
];
86+
}
87+
}
88+
89+
/**
90+
* Converts the specified string `a` to non-negative integer.
91+
* Returns -1 if the result is NaN.
92+
* @param a
93+
*/
94+
function parseNonNegativeInt(a: string): number {
95+
// parseInt() will try to convert as many as possible leading characters that
96+
// are digits. This means a string like "123abc" will be converted to 123.
97+
// For our use case, this is sufficient.
98+
const i = parseInt(a, 10 /* radix */);
99+
return isNaN(i) ? -1 : i;
100+
}

server/src/version_provider.ts

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,12 @@
99
import * as fs from 'fs';
1010
import * as path from 'path';
1111

12+
import {NodeModule, resolve, Version} from '../common/resolver';
13+
1214
const MIN_TS_VERSION = '4.2';
1315
const MIN_NG_VERSION = '12.0';
1416
const TSSERVERLIB = 'typescript/lib/tsserverlibrary';
1517

16-
/**
17-
* Represents a valid node module that has been successfully resolved.
18-
*/
19-
interface NodeModule {
20-
name: string;
21-
resolvedPath: string;
22-
version: Version;
23-
}
24-
25-
export function resolve(packageName: string, location: string, rootPackage?: string): NodeModule|
26-
undefined {
27-
rootPackage = rootPackage || packageName;
28-
try {
29-
const packageJsonPath = require.resolve(`${rootPackage}/package.json`, {
30-
paths: [location],
31-
});
32-
// Do not use require() to read JSON files since it's a potential security
33-
// vulnerability.
34-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
35-
const resolvedPath = require.resolve(packageName, {
36-
paths: [location],
37-
});
38-
return {
39-
name: packageName,
40-
resolvedPath,
41-
version: new Version(packageJson.version),
42-
};
43-
} catch {
44-
}
45-
}
46-
4718
/**
4819
* Resolve the node module with the specified `packageName` that satisfies
4920
* the specified minimum version.
@@ -125,63 +96,3 @@ export function resolveNgLangSvc(probeLocations: string[]): NodeModule {
12596
export function resolveNgcc(directory: string): NodeModule|undefined {
12697
return resolve('@angular/compiler-cli/ngcc/main-ngcc.js', directory, '@angular/compiler-cli');
12798
}
128-
129-
/**
130-
* Converts the specified string `a` to non-negative integer.
131-
* Returns -1 if the result is NaN.
132-
* @param a
133-
*/
134-
function parseNonNegativeInt(a: string): number {
135-
// parseInt() will try to convert as many as possible leading characters that
136-
// are digits. This means a string like "123abc" will be converted to 123.
137-
// For our use case, this is sufficient.
138-
const i = parseInt(a, 10 /* radix */);
139-
return isNaN(i) ? -1 : i;
140-
}
141-
142-
export class Version {
143-
readonly major: number;
144-
readonly minor: number;
145-
readonly patch: number;
146-
147-
constructor(private readonly versionStr: string) {
148-
const [major, minor, patch] = Version.parseVersionStr(versionStr);
149-
this.major = major;
150-
this.minor = minor;
151-
this.patch = patch;
152-
}
153-
154-
greaterThanOrEqual(other: Version): boolean {
155-
if (this.major < other.major) {
156-
return false;
157-
}
158-
if (this.major > other.major) {
159-
return true;
160-
}
161-
if (this.minor < other.minor) {
162-
return false;
163-
}
164-
if (this.minor > other.minor) {
165-
return true;
166-
}
167-
return this.patch >= other.patch;
168-
}
169-
170-
toString(): string {
171-
return this.versionStr;
172-
}
173-
174-
/**
175-
* Converts the specified `versionStr` to its number constituents. Invalid
176-
* number value is represented as negative number.
177-
* @param versionStr
178-
*/
179-
static parseVersionStr(versionStr: string): [number, number, number] {
180-
const [major, minor, patch] = versionStr.split('.').map(parseNonNegativeInt);
181-
return [
182-
major === undefined ? 0 : major,
183-
minor === undefined ? 0 : minor,
184-
patch === undefined ? 0 : patch,
185-
];
186-
}
187-
}

0 commit comments

Comments
 (0)