Skip to content

Commit

Permalink
Revert "refactor(language-service): [Ivy] remove temporary compiler (a…
Browse files Browse the repository at this point in the history
…ngular#38310)"

This reverts commit 42e16b6.
  • Loading branch information
subratpalhar92 authored Aug 31, 2020
1 parent acf692c commit d26b6f3
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 119 deletions.
8 changes: 1 addition & 7 deletions packages/language-service/ivy/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ ts_library(
srcs = glob(["*.ts"]),
deps = [
"//packages/compiler-cli",
"//packages/compiler-cli/src/ngtsc/core",
"//packages/compiler-cli/src/ngtsc/core:api",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/incremental",
"//packages/compiler-cli/src/ngtsc/shims",
"//packages/compiler-cli/src/ngtsc/typecheck",
"//packages/compiler-cli/src/ngtsc/typecheck/api",
"//packages/language-service/ivy/compiler",
"@npm//typescript",
],
)
17 changes: 17 additions & 0 deletions packages/language-service/ivy/compiler/BUILD.bazel
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",
],
)
2 changes: 2 additions & 0 deletions packages/language-service/ivy/compiler/README.md
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.
124 changes: 124 additions & 0 deletions packages/language-service/ivy/compiler/compiler.ts
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 packages/language-service/ivy/compiler/compiler_host.ts
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;
}
Loading

0 comments on commit d26b6f3

Please sign in to comment.