Skip to content

Commit 429013a

Browse files
Keen Yee Liaukyliau
authored andcommitted
fix: detect @angular/core in google3 and don't run ngcc
Refactors the detection logic for Angular project to take into account `@angular/core` in google3. If internal `@angular/core` is detected, do not run ngcc.
1 parent d893724 commit 429013a

File tree

1 file changed

+106
-106
lines changed

1 file changed

+106
-106
lines changed

server/src/session.ts

Lines changed: 106 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -160,63 +160,23 @@ export class Session {
160160
};
161161
}
162162

163-
private async runNgcc(configFilePath: string) {
164-
this.connection.sendProgress(NgccProgressType, NgccProgressToken, {
165-
done: false,
166-
configFilePath,
167-
message: `Running ngcc for project ${configFilePath}`,
168-
});
169-
170-
let success = false;
171-
172-
try {
173-
await resolveAndRunNgcc(configFilePath, {
174-
report: (msg: string) => {
175-
this.connection.sendProgress(NgccProgressType, NgccProgressToken, {
176-
done: false,
177-
configFilePath,
178-
message: msg,
179-
});
180-
},
181-
});
182-
success = true;
183-
} catch (e) {
184-
this.error(
185-
`Failed to run ngcc for ${configFilePath}:\n` +
186-
` ${e.message}\n` +
187-
` Language service will remain disabled.`);
188-
} finally {
189-
this.connection.sendProgress(NgccProgressType, NgccProgressToken, {
190-
done: true,
191-
configFilePath,
192-
success,
193-
});
194-
}
195-
196-
// Re-enable language service even if ngcc fails, because users could fix
197-
// the problem by running ngcc themselves. If we keep language service
198-
// disabled, there's no way users could use the extension even after
199-
// resolving ngcc issues. On the client side, we will warn users about
200-
// potentially degraded experience.
201-
202-
this.reenableLanguageServiceForProject(configFilePath);
203-
}
204-
205-
private reenableLanguageServiceForProject(configFilePath: string) {
206-
const project = this.projectService.findProject(configFilePath);
207-
if (!project) {
208-
this.error(
209-
`Failed to find project for ${configFilePath} returned by ngcc.\n` +
210-
`Language service will remain disabled.`);
163+
private enableLanguageServiceForProject(project: ts.server.Project, angularCore: string) {
164+
const {projectName} = project;
165+
if (!project.languageServiceEnabled) {
166+
project.enableLanguageService();
167+
// When the language service got disabled, the program was discarded via
168+
// languageService.cleanupSemanticCache(). However, the program is not
169+
// recreated when the language service is re-enabled. We manually mark the
170+
// project as dirty to force update the graph.
171+
project.markAsDirty();
172+
}
173+
if (!this.ivy) {
174+
// Immediately enable Legacy / View Engine language service
175+
this.info(`Enabling View Engine language service for ${projectName}.`);
176+
this.promptToEnableIvyIfAvailable(project, angularCore);
211177
return;
212178
}
213-
project.enableLanguageService();
214-
// When the language service got disabled, the program was discarded via
215-
// languageService.cleanupSemanticCache(). However, the program is not
216-
// recreated when the language service is re-enabled. We manually mark the
217-
// project as dirty to force update the graph.
218-
project.markAsDirty();
219-
this.info(`Enabling Ivy language service for ${project.projectName}.`);
179+
this.info(`Enabling Ivy language service for ${projectName}.`);
220180
this.handleCompilerOptionsDiagnostics(project);
221181
// Send diagnostics since we skipped this step when opening the file
222182
// (because language service was disabled while waiting for ngcc).
@@ -276,7 +236,16 @@ export class Session {
276236
this.isProjectLoading = false;
277237
this.connection.sendNotification(ProjectLoadingFinish);
278238
}
279-
this.checkProject(event.data.project);
239+
const {project} = event.data;
240+
const angularCore = this.findAngularCoreOrDisableLanguageService(project);
241+
if (angularCore) {
242+
if (this.ivy && isExternalAngularCore(angularCore)) {
243+
// Do not wait on this promise otherwise we'll be blocking other requests
244+
this.runNgcc(project, angularCore);
245+
} else {
246+
this.enableLanguageServiceForProject(project, angularCore);
247+
}
248+
}
280249
break;
281250
}
282251
case ts.server.ProjectsUpdatedInBackgroundEvent:
@@ -853,42 +822,91 @@ export class Session {
853822
}
854823

855824
/**
856-
* Disable the language service if the specified `project` is not Angular or
857-
* Ivy mode is enabled.
825+
* Find the main declaration file for `@angular/core` in the specified
826+
* `project`. If found, return the declaration file. Otherwise, disable the
827+
* language service and return undefined.
828+
*
829+
* @returns main declaration file in `@angular/core`.
858830
*/
859-
private checkProject(project: ts.server.Project) {
831+
private findAngularCoreOrDisableLanguageService(project: ts.server.Project): string|undefined {
860832
const {projectName} = project;
861833
if (!project.languageServiceEnabled) {
862834
this.info(
863835
`Language service is already disabled for ${projectName}. ` +
864836
`This could be due to non-TS files that exceeded the size limit (${
865837
ts.server.maxProgramSizeForNonTsFiles} bytes).` +
866838
`Please check log file for details.`);
867-
868839
return;
869840
}
841+
if (!project.hasRoots() || project.isNonTsProject()) {
842+
return undefined;
843+
}
844+
const angularCore = project.getFileNames().find(isAngularCore);
845+
if (angularCore === undefined) {
846+
project.disableLanguageService();
847+
this.info(
848+
`Disabling language service for ${projectName} because it is not an Angular project ` +
849+
`('@angular/core' could not be found).`);
850+
if (project.getExcludedFiles().some(isAngularCore)) {
851+
this.info(
852+
`Please check your tsconfig.json to make sure 'node_modules' directory is not excluded.`);
853+
}
854+
}
855+
return angularCore;
856+
}
870857

871-
const coreDts = this.checkIsAngularProject(project);
872-
if (coreDts === undefined) {
858+
/**
859+
* Disable the language service, run ngcc, then re-enable language service.
860+
*/
861+
private async runNgcc(project: ts.server.Project, angularCore: string): Promise<void> {
862+
if (!isConfiguredProject(project)) {
873863
return;
874864
}
865+
// Disable language service until ngcc is completed.
866+
project.disableLanguageService();
867+
const configFilePath = project.getConfigFilePath();
875868

876-
if (this.ivy && isConfiguredProject(project)) {
877-
// Keep language service disabled until ngcc is completed.
878-
project.disableLanguageService();
879-
// Do not wait on this promise otherwise we'll be blocking other requests
880-
this.runNgcc(project.getConfigFilePath()).catch((error: Error) => {
881-
this.error(error.toString());
869+
this.connection.sendProgress(NgccProgressType, NgccProgressToken, {
870+
done: false,
871+
configFilePath,
872+
message: `Running ngcc for ${configFilePath}`,
873+
});
874+
875+
let success = false;
876+
877+
try {
878+
await resolveAndRunNgcc(configFilePath, {
879+
report: (msg: string) => {
880+
this.connection.sendProgress(NgccProgressType, NgccProgressToken, {
881+
done: false,
882+
configFilePath,
883+
message: msg,
884+
});
885+
},
886+
});
887+
success = true;
888+
} catch (e) {
889+
this.error(
890+
`Failed to run ngcc for ${configFilePath}:\n` +
891+
` ${e.message}\n` +
892+
` Language service will remain disabled.`);
893+
} finally {
894+
this.connection.sendProgress(NgccProgressType, NgccProgressToken, {
895+
done: true,
896+
configFilePath,
897+
success,
882898
});
883-
} else {
884-
// Immediately enable Legacy/ViewEngine language service
885-
this.info(`Enabling VE language service for ${projectName}.`);
886-
this.promptToEnableIvyIfAvailable(project, coreDts);
887899
}
900+
901+
// Re-enable language service even if ngcc fails, because users could fix
902+
// the problem by running ngcc themselves. If we keep language service
903+
// disabled, there's no way users could use the extension even after
904+
// resolving ngcc issues. On the client side, we will warn users about
905+
// potentially degraded experience.
906+
this.enableLanguageServiceForProject(project, angularCore);
888907
}
889908

890-
private promptToEnableIvyIfAvailable(
891-
project: ts.server.Project, coreDts: ts.server.NormalizedPath) {
909+
private promptToEnableIvyIfAvailable(project: ts.server.Project, coreDts: string): void {
892910
let angularCoreVersion = this.angularCoreVersionMap.get(project);
893911
if (angularCoreVersion === undefined) {
894912
angularCoreVersion = resolve('@angular/core', coreDts)?.version;
@@ -901,37 +919,6 @@ export class Session {
901919
});
902920
}
903921
}
904-
905-
/**
906-
* Determine if the specified `project` is Angular, and disable the language
907-
* service if not.
908-
*
909-
* @returns The `ts.server.NormalizedPath` to the `@angular/core/core.d.ts` file.
910-
*/
911-
private checkIsAngularProject(project: ts.server.Project): ts.server.NormalizedPath|undefined {
912-
const {projectName} = project;
913-
const NG_CORE = '@angular/core/core.d.ts';
914-
const ngCoreDts = project.getFileNames().find(f => f.endsWith(NG_CORE));
915-
const isAngularProject =
916-
project.hasRoots() && !project.isNonTsProject() && ngCoreDts !== undefined;
917-
918-
if (isAngularProject) {
919-
return ngCoreDts;
920-
}
921-
922-
project.disableLanguageService();
923-
this.info(
924-
`Disabling language service for ${projectName} because it is not an Angular project ` +
925-
`('${NG_CORE}' could not be found). ` +
926-
`If you believe you are seeing this message in error, please reinstall the packages in your package.json.`);
927-
928-
if (project.getExcludedFiles().some(f => f.endsWith(NG_CORE))) {
929-
this.info(
930-
`Please check your tsconfig.json to make sure 'node_modules' directory is not excluded.`);
931-
}
932-
933-
return undefined;
934-
}
935922
}
936923

937924
function toArray<T>(it: ts.Iterator<T>): T[] {
@@ -942,6 +929,19 @@ function toArray<T>(it: ts.Iterator<T>): T[] {
942929
return results;
943930
}
944931

932+
// TODO: Replace with `isNgLanguageService` from `@angular/language-service`.
945933
function isNgLs(ls: ts.LanguageService|NgLanguageService): ls is NgLanguageService {
946934
return 'getTcb' in ls;
947-
}
935+
}
936+
937+
function isAngularCore(path: string): boolean {
938+
return isExternalAngularCore(path) || isInternalAngularCore(path);
939+
}
940+
941+
function isExternalAngularCore(path: string): boolean {
942+
return path.endsWith('@angular/core/core.d.ts');
943+
}
944+
945+
function isInternalAngularCore(path: string): boolean {
946+
return path.endsWith('angular2/rc/packages/core/index.d.ts');
947+
}

0 commit comments

Comments
 (0)