1 | import * as ts from "typescript";
|
2 | import ConfigParser from "./compiler/config-parser";
|
3 | import createCompilerHost from "./compiler/create-compiler-host";
|
4 | import Input from "./compiler/input-io";
|
5 | import OutputPatcher from "./compiler/output-patcher";
|
6 | import PathResolver from "./compiler/path-resolver";
|
7 | import SourceCache from "./compiler/source-cache";
|
8 | import {
|
9 | normalizePath,
|
10 | relativePathWithin,
|
11 | toAbsolutePath,
|
12 | } from "./fs/path-utils";
|
13 | import { heimdall } from "./helpers";
|
14 | import {
|
15 | AbsolutePath,
|
16 | DiagnosticsHandler,
|
17 | NormalizedOptions,
|
18 | } from "./interfaces";
|
19 |
|
20 | export 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 |
|
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 |
|
140 |
|
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 | }
|