forked from angular/angular
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "refactor(language-service): [Ivy] remove temporary compiler (a…
…ngular#38310)" This reverts commit 42e16b6.
- Loading branch information
1 parent
acf692c
commit d26b6f3
Showing
7 changed files
with
261 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
load("//tools:defaults.bzl", "ts_library") | ||
|
||
package(default_visibility = ["//packages/language-service/ivy:__pkg__"]) | ||
|
||
ts_library( | ||
name = "compiler", | ||
srcs = glob(["*.ts"]), | ||
deps = [ | ||
"//packages/compiler-cli", | ||
"//packages/compiler-cli/src/ngtsc/core", | ||
"//packages/compiler-cli/src/ngtsc/file_system", | ||
"//packages/compiler-cli/src/ngtsc/incremental", | ||
"//packages/compiler-cli/src/ngtsc/typecheck", | ||
"//packages/compiler-cli/src/ngtsc/typecheck/api", | ||
"@npm//typescript", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
All files in this directory are temporary. This is created to simulate the final | ||
form of the Ivy compiler that supports language service. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
|
||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {CompilerOptions} from '@angular/compiler-cli'; | ||
import {NgCompiler, NgCompilerHost} from '@angular/compiler-cli/src/ngtsc/core'; | ||
import {absoluteFromSourceFile, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; | ||
import {PatchedProgramIncrementalBuildStrategy} from '@angular/compiler-cli/src/ngtsc/incremental'; | ||
import {TypeCheckShimGenerator} from '@angular/compiler-cli/src/ngtsc/typecheck'; | ||
import {TypeCheckingProgramStrategy, UpdateMode} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; | ||
import * as ts from 'typescript/lib/tsserverlibrary'; | ||
|
||
import {makeCompilerHostFromProject} from './compiler_host'; | ||
|
||
interface AnalysisResult { | ||
compiler: NgCompiler; | ||
program: ts.Program; | ||
} | ||
|
||
export class Compiler { | ||
private tsCompilerHost: ts.CompilerHost; | ||
private lastKnownProgram: ts.Program|null = null; | ||
private readonly strategy: TypeCheckingProgramStrategy; | ||
|
||
constructor(private readonly project: ts.server.Project, private options: CompilerOptions) { | ||
this.tsCompilerHost = makeCompilerHostFromProject(project); | ||
this.strategy = createTypeCheckingProgramStrategy(project); | ||
// Do not retrieve the program in constructor because project is still in | ||
// the process of loading, and not all data members have been initialized. | ||
} | ||
|
||
setCompilerOptions(options: CompilerOptions) { | ||
this.options = options; | ||
} | ||
|
||
analyze(): AnalysisResult|undefined { | ||
const inputFiles = this.project.getRootFiles(); | ||
const ngCompilerHost = | ||
NgCompilerHost.wrap(this.tsCompilerHost, inputFiles, this.options, this.lastKnownProgram); | ||
const program = this.strategy.getProgram(); | ||
const compiler = new NgCompiler( | ||
ngCompilerHost, this.options, program, this.strategy, | ||
new PatchedProgramIncrementalBuildStrategy(), this.lastKnownProgram); | ||
try { | ||
// This is the only way to force the compiler to update the typecheck file | ||
// in the program. We have to do try-catch because the compiler immediately | ||
// throws if it fails to parse any template in the entire program! | ||
const d = compiler.getDiagnostics(); | ||
if (d.length) { | ||
// There could be global compilation errors. It's useful to print them | ||
// out in development. | ||
console.error(d.map(d => ts.flattenDiagnosticMessageText(d.messageText, '\n'))); | ||
} | ||
} catch (e) { | ||
console.error('Failed to analyze program', e.message); | ||
return; | ||
} | ||
this.lastKnownProgram = compiler.getNextProgram(); | ||
return { | ||
compiler, | ||
program: this.lastKnownProgram, | ||
}; | ||
} | ||
} | ||
|
||
function createTypeCheckingProgramStrategy(project: ts.server.Project): | ||
TypeCheckingProgramStrategy { | ||
return { | ||
supportsInlineOperations: false, | ||
shimPathForComponent(component: ts.ClassDeclaration): AbsoluteFsPath { | ||
return TypeCheckShimGenerator.shimFor(absoluteFromSourceFile(component.getSourceFile())); | ||
}, | ||
getProgram(): ts.Program { | ||
const program = project.getLanguageService().getProgram(); | ||
if (!program) { | ||
throw new Error('Language service does not have a program!'); | ||
} | ||
return program; | ||
}, | ||
updateFiles(contents: Map<AbsoluteFsPath, string>, updateMode: UpdateMode) { | ||
if (updateMode !== UpdateMode.Complete) { | ||
throw new Error(`Incremental update mode is currently not supported`); | ||
} | ||
for (const [fileName, newText] of contents) { | ||
const scriptInfo = getOrCreateTypeCheckScriptInfo(project, fileName); | ||
const snapshot = scriptInfo.getSnapshot(); | ||
const length = snapshot.getLength(); | ||
scriptInfo.editContent(0, length, newText); | ||
} | ||
}, | ||
}; | ||
} | ||
|
||
function getOrCreateTypeCheckScriptInfo( | ||
project: ts.server.Project, tcf: string): ts.server.ScriptInfo { | ||
// First check if there is already a ScriptInfo for the tcf | ||
const {projectService} = project; | ||
let scriptInfo = projectService.getScriptInfo(tcf); | ||
if (!scriptInfo) { | ||
// ScriptInfo needs to be opened by client to be able to set its user-defined | ||
// content. We must also provide file content, otherwise the service will | ||
// attempt to fetch the content from disk and fail. | ||
scriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath( | ||
ts.server.toNormalizedPath(tcf), | ||
true, // openedByClient | ||
'', // fileContent | ||
ts.ScriptKind.TS, // scriptKind | ||
); | ||
if (!scriptInfo) { | ||
throw new Error(`Failed to create script info for ${tcf}`); | ||
} | ||
} | ||
// Add ScriptInfo to project if it's missing. A ScriptInfo needs to be part of | ||
// the project so that it becomes part of the program. | ||
if (!project.containsScriptInfo(scriptInfo)) { | ||
project.addRoot(scriptInfo); | ||
} | ||
return scriptInfo; | ||
} |
103 changes: 103 additions & 0 deletions
103
packages/language-service/ivy/compiler/compiler_host.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import * as ts from 'typescript/lib/tsserverlibrary'; | ||
|
||
export function makeCompilerHostFromProject(project: ts.server.Project): ts.CompilerHost { | ||
const compilerHost: ts.CompilerHost = { | ||
fileExists(fileName: string): boolean { | ||
return project.fileExists(fileName); | ||
}, | ||
readFile(fileName: string): string | | ||
undefined { | ||
return project.readFile(fileName); | ||
}, | ||
directoryExists(directoryName: string): boolean { | ||
return project.directoryExists(directoryName); | ||
}, | ||
getCurrentDirectory(): string { | ||
return project.getCurrentDirectory(); | ||
}, | ||
getDirectories(path: string): string[] { | ||
return project.getDirectories(path); | ||
}, | ||
getSourceFile( | ||
fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void, | ||
shouldCreateNewSourceFile?: boolean): ts.SourceFile | | ||
undefined { | ||
const path = project.projectService.toPath(fileName); | ||
return project.getSourceFile(path); | ||
}, | ||
getSourceFileByPath( | ||
fileName: string, path: ts.Path, languageVersion: ts.ScriptTarget, | ||
onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): ts.SourceFile | | ||
undefined { | ||
return project.getSourceFile(path); | ||
}, | ||
getCancellationToken(): ts.CancellationToken { | ||
return { | ||
isCancellationRequested() { | ||
return project.getCancellationToken().isCancellationRequested(); | ||
}, | ||
throwIfCancellationRequested() { | ||
if (this.isCancellationRequested()) { | ||
throw new ts.OperationCanceledException(); | ||
} | ||
}, | ||
}; | ||
}, | ||
getDefaultLibFileName(options: ts.CompilerOptions): string { | ||
return project.getDefaultLibFileName(); | ||
}, | ||
writeFile( | ||
fileName: string, data: string, writeByteOrderMark: boolean, | ||
onError?: (message: string) => void, sourceFiles?: readonly ts.SourceFile[]) { | ||
return project.writeFile(fileName, data); | ||
}, | ||
getCanonicalFileName(fileName: string): string { | ||
return project.projectService.toCanonicalFileName(fileName); | ||
}, | ||
useCaseSensitiveFileNames(): boolean { | ||
return project.useCaseSensitiveFileNames(); | ||
}, | ||
getNewLine(): string { | ||
return project.getNewLine(); | ||
}, | ||
readDirectory( | ||
rootDir: string, extensions: readonly string[], excludes: readonly string[]|undefined, | ||
includes: readonly string[], depth?: number): string[] { | ||
return project.readDirectory(rootDir, extensions, excludes, includes, depth); | ||
}, | ||
resolveModuleNames( | ||
moduleNames: string[], containingFile: string, reusedNames: string[]|undefined, | ||
redirectedReference: ts.ResolvedProjectReference|undefined, options: ts.CompilerOptions): | ||
(ts.ResolvedModule | undefined)[] { | ||
return project.resolveModuleNames( | ||
moduleNames, containingFile, reusedNames, redirectedReference); | ||
}, | ||
resolveTypeReferenceDirectives( | ||
typeReferenceDirectiveNames: string[], containingFile: string, | ||
redirectedReference: ts.ResolvedProjectReference|undefined, options: ts.CompilerOptions): | ||
(ts.ResolvedTypeReferenceDirective | undefined)[] { | ||
return project.resolveTypeReferenceDirectives( | ||
typeReferenceDirectiveNames, containingFile, redirectedReference); | ||
}, | ||
}; | ||
|
||
if (project.trace) { | ||
compilerHost.trace = function trace(s: string) { | ||
project.trace!(s); | ||
}; | ||
} | ||
if (project.realpath) { | ||
compilerHost.realpath = function realpath(path: string): string { | ||
return project.realpath!(path); | ||
}; | ||
} | ||
return compilerHost; | ||
} |
Oops, something went wrong.