Skip to content

Commit d26b6f3

Browse files
Revert "refactor(language-service): [Ivy] remove temporary compiler (angular#38310)"
This reverts commit 42e16b6.
1 parent acf692c commit d26b6f3

File tree

7 files changed

+261
-119
lines changed

7 files changed

+261
-119
lines changed

packages/language-service/ivy/BUILD.bazel

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,7 @@ ts_library(
77
srcs = glob(["*.ts"]),
88
deps = [
99
"//packages/compiler-cli",
10-
"//packages/compiler-cli/src/ngtsc/core",
11-
"//packages/compiler-cli/src/ngtsc/core:api",
12-
"//packages/compiler-cli/src/ngtsc/file_system",
13-
"//packages/compiler-cli/src/ngtsc/incremental",
14-
"//packages/compiler-cli/src/ngtsc/shims",
15-
"//packages/compiler-cli/src/ngtsc/typecheck",
16-
"//packages/compiler-cli/src/ngtsc/typecheck/api",
10+
"//packages/language-service/ivy/compiler",
1711
"@npm//typescript",
1812
],
1913
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
load("//tools:defaults.bzl", "ts_library")
2+
3+
package(default_visibility = ["//packages/language-service/ivy:__pkg__"])
4+
5+
ts_library(
6+
name = "compiler",
7+
srcs = glob(["*.ts"]),
8+
deps = [
9+
"//packages/compiler-cli",
10+
"//packages/compiler-cli/src/ngtsc/core",
11+
"//packages/compiler-cli/src/ngtsc/file_system",
12+
"//packages/compiler-cli/src/ngtsc/incremental",
13+
"//packages/compiler-cli/src/ngtsc/typecheck",
14+
"//packages/compiler-cli/src/ngtsc/typecheck/api",
15+
"@npm//typescript",
16+
],
17+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
All files in this directory are temporary. This is created to simulate the final
2+
form of the Ivy compiler that supports language service.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
2+
/**
3+
* @license
4+
* Copyright Google LLC All Rights Reserved.
5+
*
6+
* Use of this source code is governed by an MIT-style license that can be
7+
* found in the LICENSE file at https://angular.io/license
8+
*/
9+
10+
import {CompilerOptions} from '@angular/compiler-cli';
11+
import {NgCompiler, NgCompilerHost} from '@angular/compiler-cli/src/ngtsc/core';
12+
import {absoluteFromSourceFile, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system';
13+
import {PatchedProgramIncrementalBuildStrategy} from '@angular/compiler-cli/src/ngtsc/incremental';
14+
import {TypeCheckShimGenerator} from '@angular/compiler-cli/src/ngtsc/typecheck';
15+
import {TypeCheckingProgramStrategy, UpdateMode} from '@angular/compiler-cli/src/ngtsc/typecheck/api';
16+
import * as ts from 'typescript/lib/tsserverlibrary';
17+
18+
import {makeCompilerHostFromProject} from './compiler_host';
19+
20+
interface AnalysisResult {
21+
compiler: NgCompiler;
22+
program: ts.Program;
23+
}
24+
25+
export class Compiler {
26+
private tsCompilerHost: ts.CompilerHost;
27+
private lastKnownProgram: ts.Program|null = null;
28+
private readonly strategy: TypeCheckingProgramStrategy;
29+
30+
constructor(private readonly project: ts.server.Project, private options: CompilerOptions) {
31+
this.tsCompilerHost = makeCompilerHostFromProject(project);
32+
this.strategy = createTypeCheckingProgramStrategy(project);
33+
// Do not retrieve the program in constructor because project is still in
34+
// the process of loading, and not all data members have been initialized.
35+
}
36+
37+
setCompilerOptions(options: CompilerOptions) {
38+
this.options = options;
39+
}
40+
41+
analyze(): AnalysisResult|undefined {
42+
const inputFiles = this.project.getRootFiles();
43+
const ngCompilerHost =
44+
NgCompilerHost.wrap(this.tsCompilerHost, inputFiles, this.options, this.lastKnownProgram);
45+
const program = this.strategy.getProgram();
46+
const compiler = new NgCompiler(
47+
ngCompilerHost, this.options, program, this.strategy,
48+
new PatchedProgramIncrementalBuildStrategy(), this.lastKnownProgram);
49+
try {
50+
// This is the only way to force the compiler to update the typecheck file
51+
// in the program. We have to do try-catch because the compiler immediately
52+
// throws if it fails to parse any template in the entire program!
53+
const d = compiler.getDiagnostics();
54+
if (d.length) {
55+
// There could be global compilation errors. It's useful to print them
56+
// out in development.
57+
console.error(d.map(d => ts.flattenDiagnosticMessageText(d.messageText, '\n')));
58+
}
59+
} catch (e) {
60+
console.error('Failed to analyze program', e.message);
61+
return;
62+
}
63+
this.lastKnownProgram = compiler.getNextProgram();
64+
return {
65+
compiler,
66+
program: this.lastKnownProgram,
67+
};
68+
}
69+
}
70+
71+
function createTypeCheckingProgramStrategy(project: ts.server.Project):
72+
TypeCheckingProgramStrategy {
73+
return {
74+
supportsInlineOperations: false,
75+
shimPathForComponent(component: ts.ClassDeclaration): AbsoluteFsPath {
76+
return TypeCheckShimGenerator.shimFor(absoluteFromSourceFile(component.getSourceFile()));
77+
},
78+
getProgram(): ts.Program {
79+
const program = project.getLanguageService().getProgram();
80+
if (!program) {
81+
throw new Error('Language service does not have a program!');
82+
}
83+
return program;
84+
},
85+
updateFiles(contents: Map<AbsoluteFsPath, string>, updateMode: UpdateMode) {
86+
if (updateMode !== UpdateMode.Complete) {
87+
throw new Error(`Incremental update mode is currently not supported`);
88+
}
89+
for (const [fileName, newText] of contents) {
90+
const scriptInfo = getOrCreateTypeCheckScriptInfo(project, fileName);
91+
const snapshot = scriptInfo.getSnapshot();
92+
const length = snapshot.getLength();
93+
scriptInfo.editContent(0, length, newText);
94+
}
95+
},
96+
};
97+
}
98+
99+
function getOrCreateTypeCheckScriptInfo(
100+
project: ts.server.Project, tcf: string): ts.server.ScriptInfo {
101+
// First check if there is already a ScriptInfo for the tcf
102+
const {projectService} = project;
103+
let scriptInfo = projectService.getScriptInfo(tcf);
104+
if (!scriptInfo) {
105+
// ScriptInfo needs to be opened by client to be able to set its user-defined
106+
// content. We must also provide file content, otherwise the service will
107+
// attempt to fetch the content from disk and fail.
108+
scriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(
109+
ts.server.toNormalizedPath(tcf),
110+
true, // openedByClient
111+
'', // fileContent
112+
ts.ScriptKind.TS, // scriptKind
113+
);
114+
if (!scriptInfo) {
115+
throw new Error(`Failed to create script info for ${tcf}`);
116+
}
117+
}
118+
// Add ScriptInfo to project if it's missing. A ScriptInfo needs to be part of
119+
// the project so that it becomes part of the program.
120+
if (!project.containsScriptInfo(scriptInfo)) {
121+
project.addRoot(scriptInfo);
122+
}
123+
return scriptInfo;
124+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC 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 ts from 'typescript/lib/tsserverlibrary';
10+
11+
export function makeCompilerHostFromProject(project: ts.server.Project): ts.CompilerHost {
12+
const compilerHost: ts.CompilerHost = {
13+
fileExists(fileName: string): boolean {
14+
return project.fileExists(fileName);
15+
},
16+
readFile(fileName: string): string |
17+
undefined {
18+
return project.readFile(fileName);
19+
},
20+
directoryExists(directoryName: string): boolean {
21+
return project.directoryExists(directoryName);
22+
},
23+
getCurrentDirectory(): string {
24+
return project.getCurrentDirectory();
25+
},
26+
getDirectories(path: string): string[] {
27+
return project.getDirectories(path);
28+
},
29+
getSourceFile(
30+
fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void,
31+
shouldCreateNewSourceFile?: boolean): ts.SourceFile |
32+
undefined {
33+
const path = project.projectService.toPath(fileName);
34+
return project.getSourceFile(path);
35+
},
36+
getSourceFileByPath(
37+
fileName: string, path: ts.Path, languageVersion: ts.ScriptTarget,
38+
onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): ts.SourceFile |
39+
undefined {
40+
return project.getSourceFile(path);
41+
},
42+
getCancellationToken(): ts.CancellationToken {
43+
return {
44+
isCancellationRequested() {
45+
return project.getCancellationToken().isCancellationRequested();
46+
},
47+
throwIfCancellationRequested() {
48+
if (this.isCancellationRequested()) {
49+
throw new ts.OperationCanceledException();
50+
}
51+
},
52+
};
53+
},
54+
getDefaultLibFileName(options: ts.CompilerOptions): string {
55+
return project.getDefaultLibFileName();
56+
},
57+
writeFile(
58+
fileName: string, data: string, writeByteOrderMark: boolean,
59+
onError?: (message: string) => void, sourceFiles?: readonly ts.SourceFile[]) {
60+
return project.writeFile(fileName, data);
61+
},
62+
getCanonicalFileName(fileName: string): string {
63+
return project.projectService.toCanonicalFileName(fileName);
64+
},
65+
useCaseSensitiveFileNames(): boolean {
66+
return project.useCaseSensitiveFileNames();
67+
},
68+
getNewLine(): string {
69+
return project.getNewLine();
70+
},
71+
readDirectory(
72+
rootDir: string, extensions: readonly string[], excludes: readonly string[]|undefined,
73+
includes: readonly string[], depth?: number): string[] {
74+
return project.readDirectory(rootDir, extensions, excludes, includes, depth);
75+
},
76+
resolveModuleNames(
77+
moduleNames: string[], containingFile: string, reusedNames: string[]|undefined,
78+
redirectedReference: ts.ResolvedProjectReference|undefined, options: ts.CompilerOptions):
79+
(ts.ResolvedModule | undefined)[] {
80+
return project.resolveModuleNames(
81+
moduleNames, containingFile, reusedNames, redirectedReference);
82+
},
83+
resolveTypeReferenceDirectives(
84+
typeReferenceDirectiveNames: string[], containingFile: string,
85+
redirectedReference: ts.ResolvedProjectReference|undefined, options: ts.CompilerOptions):
86+
(ts.ResolvedTypeReferenceDirective | undefined)[] {
87+
return project.resolveTypeReferenceDirectives(
88+
typeReferenceDirectiveNames, containingFile, redirectedReference);
89+
},
90+
};
91+
92+
if (project.trace) {
93+
compilerHost.trace = function trace(s: string) {
94+
project.trace!(s);
95+
};
96+
}
97+
if (project.realpath) {
98+
compilerHost.realpath = function realpath(path: string): string {
99+
return project.realpath!(path);
100+
};
101+
}
102+
return compilerHost;
103+
}

0 commit comments

Comments
 (0)