UNPKG

11.6 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 return new (P || (P = Promise))(function (resolve, reject) {
4 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7 step((generator = generator.apply(thisArg, _arguments || [])).next());
8 });
9};
10Object.defineProperty(exports, "__esModule", { value: true });
11const LinkedList_1 = require("./LinkedList");
12const VueProgram_1 = require("./VueProgram");
13class CompilerHost {
14 constructor(typescript, programConfigFile, compilerOptions, checkSyntacticErrors, userResolveModuleName, userResolveTypeReferenceDirective) {
15 this.typescript = typescript;
16 // intercept all watch events and collect them until we get notification to start compilation
17 this.directoryWatchers = new LinkedList_1.LinkedList();
18 this.fileWatchers = new LinkedList_1.LinkedList();
19 this.knownFiles = new Set();
20 this.gatheredDiagnostic = [];
21 this.afterCompile = () => {
22 /* do nothing */
23 };
24 this.compilationStarted = false;
25 this.createProgram = this.typescript
26 .createEmitAndSemanticDiagnosticsBuilderProgram;
27 this.tsHost = typescript.createWatchCompilerHost(programConfigFile, compilerOptions, typescript.sys, typescript.createEmitAndSemanticDiagnosticsBuilderProgram, (diag) => {
28 if (!checkSyntacticErrors &&
29 diag.code >= 1000 &&
30 diag.code < 2000 &&
31 diag.file // if diag.file is undefined, this is not a syntactic error, but a global error that should be emitted
32 ) {
33 return;
34 }
35 this.gatheredDiagnostic.push(diag);
36 }, () => {
37 // do nothing
38 });
39 this.configFileName = this.tsHost.configFileName;
40 this.optionsToExtend = this.tsHost.optionsToExtend || {};
41 if (userResolveModuleName) {
42 this.resolveModuleNames = (moduleNames, containingFile) => {
43 return moduleNames.map(moduleName => {
44 return userResolveModuleName(this.typescript, moduleName, containingFile, this.optionsToExtend, this).resolvedModule;
45 });
46 };
47 }
48 if (userResolveTypeReferenceDirective) {
49 this.resolveTypeReferenceDirectives = (typeDirectiveNames, containingFile) => {
50 return typeDirectiveNames.map(typeDirectiveName => {
51 return userResolveTypeReferenceDirective(this.typescript, typeDirectiveName, containingFile, this.optionsToExtend, this).resolvedTypeReferenceDirective;
52 });
53 };
54 }
55 }
56 getProgram() {
57 return this.program.getProgram().getProgram();
58 }
59 getAllKnownFiles() {
60 return this.knownFiles;
61 }
62 processChanges() {
63 return __awaiter(this, void 0, void 0, function* () {
64 if (!this.lastProcessing) {
65 const initialCompile = new Promise(resolve => {
66 this.afterCompile = () => {
67 resolve(this.gatheredDiagnostic);
68 this.afterCompile = () => {
69 /* do nothing */
70 };
71 this.compilationStarted = false;
72 };
73 });
74 this.lastProcessing = initialCompile;
75 this.program = this.typescript.createWatchProgram(this);
76 const errors = yield initialCompile;
77 return {
78 results: errors,
79 updatedFiles: Array.from(this.knownFiles),
80 removedFiles: []
81 };
82 }
83 // since we do not have a way to pass cancellation token to typescript,
84 // we just wait until previous compilation finishes.
85 yield this.lastProcessing;
86 const previousDiagnostic = this.gatheredDiagnostic;
87 this.gatheredDiagnostic = [];
88 const resultPromise = new Promise(resolve => {
89 this.afterCompile = () => {
90 resolve(this.gatheredDiagnostic);
91 this.afterCompile = () => {
92 /* do nothing */
93 };
94 this.compilationStarted = false;
95 };
96 });
97 this.lastProcessing = resultPromise;
98 const files = [];
99 this.directoryWatchers.forEach(item => {
100 for (const e of item.events) {
101 item.callback(e.fileName);
102 }
103 item.events.length = 0;
104 });
105 const updatedFiles = [];
106 const removedFiles = [];
107 this.fileWatchers.forEach(item => {
108 for (const e of item.events) {
109 item.callback(e.fileName, e.eventKind);
110 files.push(e.fileName);
111 if (e.eventKind === this.typescript.FileWatcherEventKind.Created ||
112 e.eventKind === this.typescript.FileWatcherEventKind.Changed) {
113 updatedFiles.push(e.fileName);
114 }
115 else if (e.eventKind === this.typescript.FileWatcherEventKind.Deleted) {
116 removedFiles.push(e.fileName);
117 }
118 }
119 item.events.length = 0;
120 });
121 // if the files are not relevant to typescript it may choose not to compile
122 // in this case we need to trigger promise resolution from here
123 if (!this.compilationStarted) {
124 // keep diagnostic from previous run
125 this.gatheredDiagnostic = previousDiagnostic;
126 this.afterCompile();
127 return {
128 results: this.gatheredDiagnostic,
129 updatedFiles: [],
130 removedFiles: []
131 };
132 }
133 const results = yield resultPromise;
134 return { results, updatedFiles, removedFiles };
135 });
136 }
137 setTimeout(callback, _ms, ...args) {
138 // There are 2 things we are hacking here:
139 // 1. This method only called from watch program to wait until all files
140 // are written to filesystem (for example, when doing 'save all')
141 // We are intercepting all change notifications, and letting
142 // them through only when webpack starts processing changes.
143 // Therefore, at this point normally files are already all saved,
144 // so we do not need to waste another 250ms (hardcoded in TypeScript).
145 // On the other hand there may be occasional glitch, when our incremental
146 // compiler will receive the notification too late, and process it when
147 // next compilation would start.
148 // 2. It seems to be only reliable way to intercept a moment when TypeScript
149 // actually starts compilation.
150 //
151 // Ideally we probably should not let TypeScript call all those watching
152 // methods by itself, and instead forward changes from webpack.
153 // Unfortunately, at the moment TypeScript incremental API is quite
154 // immature (for example, minor changes in how you use it cause
155 // dramatic compilation time increase), so we have to stick with these
156 // hacks for now.
157 this.compilationStarted = true;
158 return this.typescript.sys.setTimeout(callback, 1, args);
159 }
160 clearTimeout(timeoutId) {
161 this.typescript.sys.clearTimeout(timeoutId);
162 }
163 onWatchStatusChange(_diagnostic, _newLine, _options) {
164 // do nothing
165 }
166 watchDirectory(path, callback, recursive) {
167 const slot = { callback, events: [] };
168 const node = this.directoryWatchers.add(slot);
169 this.tsHost.watchDirectory(path, fileName => {
170 slot.events.push({ fileName });
171 }, recursive);
172 return {
173 close: () => {
174 node.detachSelf();
175 }
176 };
177 }
178 watchFile(path, callback, _pollingInterval) {
179 const slot = { callback, events: [] };
180 const node = this.fileWatchers.add(slot);
181 this.knownFiles.add(path);
182 this.tsHost.watchFile(path, (fileName, eventKind) => {
183 if (eventKind === this.typescript.FileWatcherEventKind.Created) {
184 this.knownFiles.add(fileName);
185 }
186 else if (eventKind === this.typescript.FileWatcherEventKind.Deleted) {
187 this.knownFiles.delete(fileName);
188 }
189 slot.events.push({ fileName, eventKind });
190 }, _pollingInterval);
191 return {
192 close: () => {
193 node.detachSelf();
194 }
195 };
196 }
197 fileExists(path) {
198 return this.tsHost.fileExists(path);
199 }
200 readFile(path, encoding) {
201 const content = this.tsHost.readFile(path, encoding);
202 // get typescript contents from Vue file
203 if (content && VueProgram_1.VueProgram.isVue(path)) {
204 const resolved = VueProgram_1.VueProgram.resolveScriptBlock(this.typescript, content);
205 return resolved.content;
206 }
207 return content;
208 }
209 directoryExists(path) {
210 return ((this.tsHost.directoryExists && this.tsHost.directoryExists(path)) ||
211 false);
212 }
213 getDirectories(path) {
214 return ((this.tsHost.getDirectories && this.tsHost.getDirectories(path)) || []);
215 }
216 readDirectory(path, extensions, exclude, include, depth) {
217 return this.typescript.sys.readDirectory(path, extensions, exclude, include, depth);
218 }
219 getCurrentDirectory() {
220 return this.tsHost.getCurrentDirectory();
221 }
222 getDefaultLibFileName(options) {
223 return this.tsHost.getDefaultLibFileName(options);
224 }
225 getEnvironmentVariable(name) {
226 return (this.tsHost.getEnvironmentVariable &&
227 this.tsHost.getEnvironmentVariable(name));
228 }
229 getNewLine() {
230 return this.tsHost.getNewLine();
231 }
232 realpath(path) {
233 return this.tsHost.realpath(path);
234 }
235 trace(s) {
236 if (this.tsHost.trace) {
237 this.tsHost.trace(s);
238 }
239 }
240 useCaseSensitiveFileNames() {
241 return this.tsHost.useCaseSensitiveFileNames();
242 }
243 onUnRecoverableConfigFileDiagnostic(_diag) {
244 // do nothing
245 }
246 afterProgramCreate(program) {
247 // all actual diagnostics happens here
248 this.tsHost.afterProgramCreate(program);
249 this.afterCompile();
250 }
251 // the functions below are use internally by typescript. we cannot use non-emitting version of incremental watching API
252 // because it is
253 // - much slower for some reason,
254 // - writes files anyway (o_O)
255 // - has different way of providing diagnostics. (with this version we can at least reliably get it from afterProgramCreate)
256 createDirectory(_path) {
257 // pretend everything was ok
258 }
259 writeFile(_path, _data, _writeByteOrderMark) {
260 // pretend everything was ok
261 }
262 onCachedDirectoryStructureHostCreate(_host) {
263 // pretend everything was ok
264 }
265}
266exports.CompilerHost = CompilerHost;
267//# sourceMappingURL=CompilerHost.js.map
\No newline at end of file