UNPKG

4.64 kBPlain TextView Raw
1import * as ts from "typescript";
2import ConfigParser from "./compiler/config-parser";
3import createCompilerHost from "./compiler/create-compiler-host";
4import Input from "./compiler/input-io";
5import OutputPatcher from "./compiler/output-patcher";
6import PathResolver from "./compiler/path-resolver";
7import SourceCache from "./compiler/source-cache";
8import {
9 normalizePath,
10 relativePathWithin,
11 toAbsolutePath,
12} from "./fs/path-utils";
13import { heimdall } from "./helpers";
14import {
15 AbsolutePath,
16 DiagnosticsHandler,
17 NormalizedOptions,
18} from "./interfaces";
19
20export default class Compiler {
21 private resolver: PathResolver;
22 private workingPath: AbsolutePath;
23 private rootPath: AbsolutePath;
24 private buildPath: AbsolutePath | undefined;
25 private input: Input;
26 private configParser: ConfigParser;
27 private sourceCache: SourceCache | undefined;
28 private output: OutputPatcher;
29 private program: ts.Program | undefined;
30
31 constructor(
32 public inputPath: AbsolutePath,
33 public outputPath: AbsolutePath,
34 public options: NormalizedOptions,
35 private diagnosticsHandler: DiagnosticsHandler
36 ) {
37 const workingPath = (this.workingPath = options.workingPath);
38 const rootPath = (this.rootPath = options.rootPath);
39 this.buildPath = options.buildPath;
40 const resolver = (this.resolver = new PathResolver(rootPath, inputPath));
41 const input = (this.input = new Input(resolver));
42 this.configParser = new ConfigParser(
43 options.projectPath,
44 options.rawConfig,
45 options.configFileName,
46 options.compilerOptions,
47 workingPath,
48 input
49 );
50 this.output = new OutputPatcher(outputPath);
51 }
52
53 public compile() {
54 const config = this.parseConfig();
55
56 const sourceCache = this.getSourceCache(config.options);
57
58 const program = this.createProgram(config, sourceCache);
59
60 this.emitDiagnostics(program);
61
62 sourceCache.releaseUnusedSourceFiles(program);
63
64 this.emitProgram(program, this.resolveBuildPath(config.options));
65
66 this.patchOutput();
67
68 this.resetCaches();
69 }
70
71 protected parseConfig() {
72 const token = heimdall.start("TypeScript:parseConfig");
73 const config = this.configParser.parseConfig();
74 heimdall.stop(token);
75 return config;
76 }
77
78 protected getSourceCache(options: ts.CompilerOptions) {
79 let sourceCache = this.sourceCache;
80 if (sourceCache === undefined) {
81 sourceCache = this.sourceCache = new SourceCache(this.resolver, options);
82 } else {
83 sourceCache.updateOptions(options);
84 }
85 return sourceCache;
86 }
87
88 protected createProgram(
89 config: ts.ParsedCommandLine,
90 sourceCache: SourceCache
91 ): ts.Program {
92 const token = heimdall.start("TypeScript:createProgram");
93
94 const host = createCompilerHost(
95 this.workingPath,
96 this.input,
97 sourceCache,
98 config.options
99 );
100
101 const oldProgram = this.program;
102 const program = ts.createProgram(
103 config.fileNames,
104 config.options,
105 host,
106 oldProgram
107 );
108 this.program = program;
109
110 heimdall.stop(token);
111 return program;
112 }
113
114 protected emitDiagnostics(program: ts.Program) {
115 // this is where bindings are resolved and typechecking is done
116 const token = heimdall.start("TypeScript:emitDiagnostics");
117 const diagnostics = ts.getPreEmitDiagnostics(program);
118 heimdall.stop(token);
119 this.diagnosticsHandler.check(diagnostics);
120 }
121
122 protected resolveBuildPath(options: ts.CompilerOptions): AbsolutePath {
123 if (this.buildPath !== undefined) {
124 return this.buildPath;
125 }
126 if (options.outDir !== undefined) {
127 return normalizePath(options.outDir) as AbsolutePath;
128 }
129 return this.rootPath;
130 }
131
132 protected emitProgram(program: ts.Program, buildPath: AbsolutePath) {
133 const token = heimdall.start("TypeScript:emitProgram");
134 const { output } = this;
135
136 const emitResult = program.emit(
137 undefined,
138 (fileName: string, data: string) => {
139 /* tslint:disable:no-console */
140 // the fileName is absolute but not normalized if outDir is not normalized
141 const relativePath = relativePathWithin(
142 buildPath,
143 toAbsolutePath(fileName, this.workingPath)
144 );
145 if (relativePath) {
146 output.add(relativePath, data);
147 }
148 }
149 );
150 heimdall.stop(token);
151 this.diagnosticsHandler.check(emitResult.diagnostics);
152 }
153
154 protected patchOutput() {
155 const token = heimdall.start("TypeScript:patchOutput");
156 this.output.patch();
157 heimdall.stop(token);
158 }
159
160 protected resetCaches() {
161 this.resolver.reset();
162 this.input.reset();
163 }
164}