-
Notifications
You must be signed in to change notification settings - Fork 1
/
server.ts
123 lines (107 loc) · 4.09 KB
/
server.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import * as ts from "typescript";
import { log, LoggerFunction, setCustomLogger } from "./logger";
import { InMemoryServiceHost } from "./service-host";
import { CompileResult, Diagnostic, enumLibFiles, repeatString, resolveLib, resolveTypings } from "./util";
import { VirtualFileSystem } from "./virtual-fs";
export class Server {
private service: ts.LanguageService;
private fs: VirtualFileSystem;
private host: InMemoryServiceHost;
constructor(
private options?: ts.CompilerOptions,
customLogger?: LoggerFunction,
) {
if (customLogger != null) setCustomLogger(customLogger);
// set default compiler options
this.options = this.options || {};
if (this.options.noEmitOnError == null) this.options.noEmitOnError = true;
// emit declarations if possible
if (this.options.declaration == null) this.options.declaration = true;
this.options.moduleResolution = ts.ModuleResolutionKind.NodeJs;
// set up the build pipeline
this.fs = new VirtualFileSystem();
this.host = new InMemoryServiceHost(this.fs, this.options);
this.service = ts.createLanguageService(this.host, ts.createDocumentRegistry());
// provide the requested lib files
if (!options.noLib) {
// const libFiles = options.lib || [this.host.getDefaultLibFileName(options)];
const libFiles = enumLibFiles();
for (const file of libFiles) {
const path = resolveLib(file);
if (path == null) continue;
const fileContent = ts.sys.readFile(path);
if (fileContent != null) this.fs.writeFile(file, fileContent, true);
}
}
// provide the most basic typings
const basicTypings = [
"@types/node/index.d.ts",
"@types/node/inspector.d.ts",
];
for (const typings of basicTypings) {
// resolving a specific node module
const path = resolveTypings(typings);
const fileContent = ts.sys.readFile(path);
if (fileContent != null) this.fs.writeFile(typings, fileContent, true);
}
}
public provideAmbientDeclarations(declarations: { [filename: string]: string } = {}) {
// provide all ambient declaration files
for (const ambientFile of Object.keys(declarations)) {
if (!/\.d\.ts$/.test(ambientFile)) throw new Error("Declarations must be .d.ts-files");
this.fs.writeFile(ambientFile, declarations[ambientFile], true);
}
}
public compile(filename: string, scriptContent: string): CompileResult {
const sourceLines = scriptContent.split("\n");
this.fs.writeFile(filename, scriptContent, true);
const rawDiagnostics: ts.Diagnostic[] = [];
rawDiagnostics.push(...this.service.getSyntacticDiagnostics(filename));
rawDiagnostics.push(...this.service.getSemanticDiagnostics(filename));
const emitResult = this.service.getEmitOutput(filename);
rawDiagnostics.push(...this.service.getCompilerOptionsDiagnostics());
const diagnostics = rawDiagnostics.map(diagnostic => {
let lineNr = 0;
let charNr = 0;
if (diagnostic.file != null) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
[lineNr, charNr] = [line, character];
}
const description = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
const type = ts.DiagnosticCategory[diagnostic.category].toLowerCase() as "error" | "warning" | "message";
const sourceLine = sourceLines[lineNr];
const annotatedSource = `${sourceLine}
${repeatString(" ", charNr)}^
${type.toUpperCase()}: ${description}`;
return {
type,
lineNr: lineNr + 1,
charNr: charNr + 1,
sourceLine,
description,
annotatedSource,
} as Diagnostic;
});
const hasError = (
(
!diagnostics.every(d => d.type !== "error") ||
(emitResult.emitSkipped && !this.options.emitDeclarationOnly)
)
&& this.options.noEmitOnError
);
let result: string;
let declarations: string;
if (!hasError) {
const resultFile = emitResult.outputFiles.find(f => f.name.endsWith(".js"));
if (resultFile != null) result = resultFile.text;
const declarationFile = emitResult.outputFiles.find(f => f.name.endsWith(".d.ts"));
if (declarationFile != null) declarations = declarationFile.text;
}
return {
success: !hasError,
diagnostics,
result,
declarations,
};
}
}