1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', { value: true });
|
4 |
|
5 | var ts = require('typescript');
|
6 | var fs = require('fs');
|
7 | var crypto = require('crypto');
|
8 |
|
9 | const useCaseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames;
|
10 | const getCanonicalFileName = ts.sys.useCaseSensitiveFileNames
|
11 | ? (fileName) => fileName
|
12 | : (fileName) => fileName.toLowerCase();
|
13 | const defaultLibLocation = ts.getDirectoryPath(toCanonicalPath(ts.sys.getExecutingFilePath()));
|
14 | function normalizePath(path) {
|
15 | if (path.length === 0) {
|
16 | return path;
|
17 | }
|
18 | return trimTrailingSlash(ts.normalizePath(path));
|
19 | }
|
20 | function isWithin(rootPath, path) {
|
21 | return (path.length > rootPath.length &&
|
22 | path.lastIndexOf(rootPath, 0) === 0 &&
|
23 | path.charCodeAt(rootPath.length) === 47 );
|
24 | }
|
25 | function relativePathWithin(root, path) {
|
26 | let relativePath;
|
27 | if (path.length > root.length &&
|
28 | path.lastIndexOf(root, 0) === 0 &&
|
29 | path.charCodeAt(root.length) === 47 ) {
|
30 | relativePath = path.substring(root.length + 1);
|
31 | }
|
32 | else if (path === root) {
|
33 | relativePath = "";
|
34 | }
|
35 | return relativePath;
|
36 | }
|
37 | function toCanonicalPath(fileName, basePath) {
|
38 | const p = ts.toPath(fileName, basePath === undefined ? currentDirectory() : basePath, getCanonicalFileName);
|
39 | return trimTrailingSlash(p);
|
40 | }
|
41 | function toAbsolutePath(fileName, basePath) {
|
42 | const p = ts.toPath(fileName, basePath === undefined ? currentDirectory() : basePath, name => name);
|
43 | return trimTrailingSlash(p);
|
44 | }
|
45 | function trimTrailingSlash(path) {
|
46 | if (path.charCodeAt(path.length - 1) === 47 ) {
|
47 | return path.slice(0, path.length - 1);
|
48 | }
|
49 | return path;
|
50 | }
|
51 | function currentDirectory() {
|
52 | return normalizePath(process.cwd());
|
53 | }
|
54 |
|
55 | function createParseConfigHost(workingPath, input) {
|
56 | function getFileSystemEntries(path) {
|
57 | return input.getFileSystemEntries(path);
|
58 | }
|
59 | function realpath(path) {
|
60 | return input.realpath(path) || path;
|
61 | }
|
62 | function readDirectory(rootDir, extensions, excludes, includes, depth) {
|
63 | return ts.matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames, workingPath, depth, getFileSystemEntries, realpath);
|
64 | }
|
65 | function fileExists(path) {
|
66 | return input.fileExists(path);
|
67 | }
|
68 | function readFile(path) {
|
69 | return input.readFile(path);
|
70 | }
|
71 | return {
|
72 | fileExists,
|
73 | readDirectory,
|
74 | readFile,
|
75 | useCaseSensitiveFileNames,
|
76 | };
|
77 | }
|
78 |
|
79 | class ConfigParser {
|
80 | constructor(projectPath, rawConfig, configFileName, compilerOptions, workingPath, input) {
|
81 | this.projectPath = projectPath;
|
82 | this.rawConfig = rawConfig;
|
83 | this.configFileName = configFileName;
|
84 | this.compilerOptions = compilerOptions;
|
85 | this.host = createParseConfigHost(workingPath, input);
|
86 | }
|
87 | parseConfig() {
|
88 | const configFileName = this.resolveConfigFileName();
|
89 | const basePath = this.getBasePath(configFileName);
|
90 | const existingOptions = this.convertExistingOptions(basePath);
|
91 | const result = this.parseConfigContent(configFileName, basePath, existingOptions.options);
|
92 | if (existingOptions.errors.length > 0) {
|
93 | result.errors = existingOptions.errors.concat(result.errors);
|
94 | }
|
95 | if (result.options.noEmit === true) {
|
96 | result.options.noEmit = false;
|
97 | }
|
98 | return result;
|
99 | }
|
100 | resolveConfigFileName() {
|
101 | if (this.rawConfig !== undefined) {
|
102 | return;
|
103 | }
|
104 | return ts.findConfigFile(this.projectPath, this.host.fileExists, this.configFileName);
|
105 | }
|
106 | getBasePath(configFilePath) {
|
107 | if (configFilePath === undefined) {
|
108 | return this.projectPath;
|
109 | }
|
110 | return ts.getDirectoryPath(configFilePath);
|
111 | }
|
112 | convertExistingOptions(basePath) {
|
113 | const { compilerOptions } = this;
|
114 | if (compilerOptions === undefined) {
|
115 | return {
|
116 | errors: [],
|
117 | options: undefined,
|
118 | };
|
119 | }
|
120 | return ts.convertCompilerOptionsFromJson(this.compilerOptions, basePath);
|
121 | }
|
122 | readConfigSourceFile(configFilePath) {
|
123 | if (configFilePath === undefined) {
|
124 | return;
|
125 | }
|
126 | const configFileText = this.host.readFile(configFilePath);
|
127 | if (configFileText === undefined) {
|
128 | throw new Error(`File '${configFilePath}' not found.`);
|
129 | }
|
130 | return ts.parseJsonText(configFilePath, configFileText);
|
131 | }
|
132 | parseConfigContent(configFileName, basePath, existingOptions) {
|
133 | const configSourceFile = this.readConfigSourceFile(configFileName);
|
134 | if (configSourceFile === undefined) {
|
135 | return ts.parseJsonConfigFileContent(this.rawConfig || {}, this.host, basePath, existingOptions);
|
136 | }
|
137 | return ts.parseJsonSourceFileConfigFileContent(configSourceFile, this.host, basePath, existingOptions, configFileName);
|
138 | }
|
139 | }
|
140 |
|
141 | function createCompilerHost(workingPath, input, sourceCache, compilerOptions) {
|
142 | const newLine = getNewLine(compilerOptions);
|
143 | return {
|
144 | directoryExists: path => input.directoryExists(path),
|
145 | fileExists: path => input.fileExists(path),
|
146 | getCanonicalFileName,
|
147 | getCurrentDirectory: () => workingPath,
|
148 | getDefaultLibFileName: options => toCanonicalPath(ts.getDefaultLibFileName(options), defaultLibLocation),
|
149 | getDefaultLibLocation: () => defaultLibLocation,
|
150 | getDirectories: path => input.getDirectories(path),
|
151 | getNewLine: () => newLine,
|
152 | getSourceFile: fileName => sourceCache.getSourceFile(fileName),
|
153 | getSourceFileByPath: (fileName, path) => sourceCache.getSourceFileByPath(fileName, path),
|
154 | readFile: path => input.readFile(path),
|
155 | realpath: path => input.realpath(path),
|
156 | trace: s => ts.sys.write(s + newLine),
|
157 | useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
158 | writeFile: () => {
|
159 |
|
160 | throw new Error("compiler host does not write output");
|
161 | },
|
162 | };
|
163 | }
|
164 | function getNewLine(options) {
|
165 | let newLine;
|
166 | if (options.newLine === undefined) {
|
167 | newLine = ts.sys.newLine;
|
168 | }
|
169 | else {
|
170 | newLine = options.newLine === ts.NewLineKind.LineFeed ? "\n" : "\r\n";
|
171 | }
|
172 | return newLine;
|
173 | }
|
174 |
|
175 | class Cache {
|
176 | constructor(delegate) {
|
177 | this.delegate = delegate;
|
178 | this.hits = 0;
|
179 | this.misses = 0;
|
180 | this.store = new Map();
|
181 | }
|
182 | get(key) {
|
183 | const cacheKey = this.delegate.cacheKey(key);
|
184 | let value = this.store.get(cacheKey);
|
185 | if (value === undefined) {
|
186 | this.misses++;
|
187 | value = this.delegate.create(key);
|
188 | this.store.set(cacheKey, value);
|
189 | }
|
190 | else {
|
191 | this.hits++;
|
192 | }
|
193 | return value;
|
194 | }
|
195 | clear() {
|
196 | this.store.clear();
|
197 | }
|
198 | }
|
199 |
|
200 | function readFile(path) {
|
201 | const buffer = fs.readFileSync(path);
|
202 | const hash = crypto.createHash("sha1");
|
203 | hash.update(buffer);
|
204 | return { buffer, version: hash.digest("hex") };
|
205 | }
|
206 | function readFileResolution(resolution) {
|
207 | let path;
|
208 | if (resolution.isFile()) {
|
209 | if (resolution.isInput()) {
|
210 | path = resolution.pathInInput;
|
211 | }
|
212 | else {
|
213 | path = resolution.path;
|
214 | }
|
215 | }
|
216 | if (path) {
|
217 | return readFile(path);
|
218 | }
|
219 | }
|
220 | function stat(path) {
|
221 | try {
|
222 | return fs.statSync(path);
|
223 | }
|
224 | catch (e) {
|
225 | if (e.code === "ENOENT" || e.code === "EACCES") {
|
226 | return;
|
227 | }
|
228 | throw e;
|
229 | }
|
230 | }
|
231 | function readdir(path, resolver) {
|
232 | const prefix = path + "/";
|
233 | const files = [];
|
234 | const directories = [];
|
235 | for (const entry of fs.readdirSync(path).sort()) {
|
236 | const resolution = resolver.resolve(prefix + entry);
|
237 | if (resolution.isFile()) {
|
238 | files.push(entry);
|
239 | }
|
240 | else if (resolution.isDirectory()) {
|
241 | directories.push(entry);
|
242 | }
|
243 | }
|
244 | return { files, directories };
|
245 | }
|
246 |
|
247 | class DirEntriesCacheDelegate {
|
248 | constructor(resolver) {
|
249 | this.resolver = resolver;
|
250 | }
|
251 | cacheKey(path) {
|
252 | return path;
|
253 | }
|
254 | create(path) {
|
255 | return readdir(path, this.resolver);
|
256 | }
|
257 | }
|
258 |
|
259 | class DirEntriesCache extends Cache {
|
260 | constructor(resolver) {
|
261 | super(new DirEntriesCacheDelegate(resolver));
|
262 | }
|
263 | }
|
264 |
|
265 | class Input {
|
266 | constructor(resolver) {
|
267 | this.resolver = resolver;
|
268 | this.realpathCache = Object.create(null);
|
269 | this.entriesCache = new DirEntriesCache(resolver);
|
270 | }
|
271 | fileExists(path) {
|
272 | return this.resolve(path).isFile();
|
273 | }
|
274 | directoryExists(path) {
|
275 | return this.resolve(path).isDirectory();
|
276 | }
|
277 | |
278 |
|
279 |
|
280 |
|
281 |
|
282 | getDirectories(path) {
|
283 | const resolution = this.resolve(path);
|
284 | let directories;
|
285 | if (resolution.isDirectory()) {
|
286 | if (resolution.isInput()) {
|
287 | directories = this.readdir(resolution.canonicalPathInInput).directories;
|
288 | if (resolution.isMerged()) {
|
289 | for (const other in this.readdir(resolution.canonicalPath)
|
290 | .directories) {
|
291 | if (directories.indexOf(other) === -1) {
|
292 | directories.push(other);
|
293 | }
|
294 | }
|
295 | }
|
296 | }
|
297 | else {
|
298 | directories = this.readdir(resolution.canonicalPath).directories;
|
299 | }
|
300 | }
|
301 | else {
|
302 | directories = [];
|
303 | }
|
304 | return directories;
|
305 | }
|
306 | |
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 | getFileSystemEntries(path) {
|
314 | const resolution = this.resolve(path);
|
315 | let entries;
|
316 | if (resolution.isDirectory() && resolution.isInput()) {
|
317 | entries = this.readdir(resolution.canonicalPathInInput);
|
318 | }
|
319 | else {
|
320 | entries = { files: [], directories: [] };
|
321 | }
|
322 | return entries;
|
323 | }
|
324 | readFile(path) {
|
325 | const resolution = this.resolve(path);
|
326 | let resolved;
|
327 | if (resolution.isFile()) {
|
328 | if (resolution.isInput()) {
|
329 | resolved = resolution.pathInInput;
|
330 | }
|
331 | else {
|
332 | resolved = resolution.path;
|
333 | }
|
334 | }
|
335 | if (resolved !== undefined) {
|
336 | return ts.sys.readFile(resolved);
|
337 | }
|
338 | }
|
339 | relativePath(path) {
|
340 | return this.resolve(path).relativePath;
|
341 | }
|
342 | realpath(path) {
|
343 | const resolution = this.resolve(path);
|
344 | if (resolution.isInput()) {
|
345 | return resolution.path;
|
346 | }
|
347 | else if (resolution.exists()) {
|
348 | const realpath = fs.realpathSync(resolution.path, this.realpathCache);
|
349 | return this.resolve(realpath).path;
|
350 | }
|
351 | }
|
352 | reset() {
|
353 | this.entriesCache.clear();
|
354 | this.realpathCache = Object.create(null);
|
355 | }
|
356 | resolve(path) {
|
357 | return this.resolver.resolve(path);
|
358 | }
|
359 | readdir(path) {
|
360 | return this.entriesCache.get(path);
|
361 | }
|
362 | }
|
363 |
|
364 | const FSTree = require("fs-tree-diff");
|
365 | const BroccoliPlugin = require("broccoli-plugin");
|
366 | const walkSync = require("walk-sync");
|
367 | const md5Hex = require("md5-hex");
|
368 | const heimdall = require("heimdalljs");
|
369 |
|
370 | class OutputPatcher {
|
371 | constructor(outputPath) {
|
372 | this.outputPath = outputPath;
|
373 | this.entries = [];
|
374 | this.contents = new Map();
|
375 | this.lastTree = undefined;
|
376 | this.isUnchanged = (entryA, entryB) => {
|
377 | if (entryA.isDirectory() && entryB.isDirectory()) {
|
378 | return true;
|
379 | }
|
380 | if (entryA.mode === entryB.mode && entryA.checksum === entryB.checksum) {
|
381 | return true;
|
382 | }
|
383 | return false;
|
384 | };
|
385 | }
|
386 |
|
387 | add(relativePath, content) {
|
388 | this.entries.push(new Entry(this.outputPath, relativePath, md5Hex(content)));
|
389 | this.contents.set(relativePath, content);
|
390 | }
|
391 | patch() {
|
392 | try {
|
393 | this.lastTree = this._patch();
|
394 | }
|
395 | catch (e) {
|
396 |
|
397 | this.lastTree = undefined;
|
398 | throw e;
|
399 | }
|
400 | finally {
|
401 | this.entries = [];
|
402 | this.contents = new Map();
|
403 | }
|
404 | }
|
405 | _patch() {
|
406 | const entries = this.entries;
|
407 | let lastTree = this.lastTree;
|
408 | const isUnchanged = this.isUnchanged;
|
409 | const outputPath = this.outputPath;
|
410 | const contents = this.contents;
|
411 | const nextTree = FSTree.fromEntries(entries, { sortAndExpand: true });
|
412 | if (!lastTree) {
|
413 | lastTree = FSTree.fromEntries(walkSync.entries(outputPath));
|
414 | }
|
415 | const patch = lastTree.calculatePatch(nextTree, isUnchanged);
|
416 | patch.forEach(change => {
|
417 | var _a;
|
418 | const op = change[0];
|
419 | const path = change[1];
|
420 | const entry = change[2];
|
421 | switch (op) {
|
422 | case "mkdir":
|
423 |
|
424 | fs.mkdirSync(outputPath + "/" + path);
|
425 | break;
|
426 | case "rmdir":
|
427 |
|
428 | fs.rmdirSync(outputPath + "/" + path);
|
429 | break;
|
430 | case "unlink":
|
431 | fs.unlinkSync(entry.fullPath);
|
432 | break;
|
433 | case "create":
|
434 | case "change":
|
435 | fs.writeFileSync(entry.fullPath, (_a = contents.get(path)) !== null && _a !== void 0 ? _a : "");
|
436 | break;
|
437 | default:
|
438 | throw new Error(`unrecognized case ${op}`);
|
439 | }
|
440 | });
|
441 | return nextTree;
|
442 | }
|
443 | }
|
444 |
|
445 | class Entry {
|
446 | constructor(basePath, relativePath, checksum) {
|
447 | this.basePath = basePath;
|
448 | this.relativePath = relativePath;
|
449 | this.checksum = checksum;
|
450 | this.mode = 0;
|
451 | this.size = 0;
|
452 | this.mtime = new Date();
|
453 | this.fullPath = basePath + "/" + relativePath;
|
454 | this.checksum = checksum;
|
455 | }
|
456 | isDirectory() {
|
457 | return false;
|
458 | }
|
459 | }
|
460 |
|
461 | function parsePath(rootPath, inputPath, rawPath) {
|
462 | let path = toAbsolutePath(rawPath, rootPath);
|
463 | let pathInInput;
|
464 | let relativePath = relativePathWithin(rootPath, path);
|
465 | if (relativePath === undefined) {
|
466 | relativePath = relativePathWithin(inputPath, path);
|
467 | if (relativePath !== undefined) {
|
468 | pathInInput = path;
|
469 | path = toAbsolutePath(relativePath, rootPath);
|
470 | }
|
471 | }
|
472 | else {
|
473 | pathInInput = toAbsolutePath(relativePath, inputPath);
|
474 | }
|
475 | const canonicalPath = toCanonicalPath(path);
|
476 | const canonicalPathInInput = pathInInput && toCanonicalPath(pathInInput);
|
477 | return {
|
478 | canonicalPath,
|
479 | canonicalPathInInput,
|
480 | path,
|
481 | pathInInput,
|
482 | relativePath,
|
483 | };
|
484 | }
|
485 |
|
486 | class PathInfoCacheDelegate {
|
487 | constructor(rootPath, inputPath) {
|
488 | this.rootPath = rootPath;
|
489 | this.inputPath = inputPath;
|
490 | }
|
491 | cacheKey(key) {
|
492 | return toCanonicalPath(key, this.rootPath);
|
493 | }
|
494 | create(key) {
|
495 | return parsePath(this.rootPath, this.inputPath, key);
|
496 | }
|
497 | }
|
498 |
|
499 | class PathInfoCache extends Cache {
|
500 | constructor(rootPath, inputPath) {
|
501 | super(new PathInfoCacheDelegate(rootPath, inputPath));
|
502 | }
|
503 | }
|
504 |
|
505 | function resolve(pathInfo) {
|
506 | let flags = 0 ;
|
507 | let stats;
|
508 | let otherStats;
|
509 | if (pathInfo.pathInInput) {
|
510 | stats = stat(pathInfo.pathInInput);
|
511 | if (stats !== undefined) {
|
512 | flags |= 4 ;
|
513 | }
|
514 | }
|
515 | if (stats === undefined) {
|
516 | stats = stat(pathInfo.path);
|
517 | }
|
518 | if (stats !== undefined) {
|
519 | flags |= stats.isDirectory() ? 2 : 1 ;
|
520 | }
|
521 | if ((flags & 6 ) === 6 ) {
|
522 | otherStats = stat(pathInfo.path);
|
523 | if (otherStats !== undefined && otherStats.isDirectory()) {
|
524 | flags |= 8 ;
|
525 | }
|
526 | }
|
527 | return new ResolutionImpl(pathInfo, stats, otherStats, flags);
|
528 | }
|
529 | class ResolutionImpl {
|
530 | constructor(pathInfo, stats, otherStats, flags) {
|
531 | this.stats = stats;
|
532 | this.otherStats = otherStats;
|
533 | this.flags = flags;
|
534 | this.canonicalPath = pathInfo.canonicalPath;
|
535 | this.canonicalPathInInput = pathInfo.canonicalPathInInput;
|
536 | this.path = pathInfo.path;
|
537 | this.pathInInput = pathInfo.pathInInput;
|
538 | this.relativePath = pathInfo.relativePath;
|
539 | }
|
540 | isInput() {
|
541 | return this.hasFlag(4 );
|
542 | }
|
543 | isFile() {
|
544 | return this.hasFlag(1 );
|
545 | }
|
546 | isDirectory() {
|
547 | return this.hasFlag(2 );
|
548 | }
|
549 | isMerged() {
|
550 | return this.hasFlag(1 );
|
551 | }
|
552 | exists() {
|
553 | return this.stats !== undefined;
|
554 | }
|
555 | hasFlag(flag) {
|
556 | return (this.flags & flag) === flag;
|
557 | }
|
558 | }
|
559 |
|
560 | class ResolutionCacheDelegate {
|
561 | cacheKey(pathInfo) {
|
562 | return pathInfo.canonicalPath;
|
563 | }
|
564 | create(pathInfo) {
|
565 | return resolve(pathInfo);
|
566 | }
|
567 | }
|
568 |
|
569 | class ResolutionCache extends Cache {
|
570 | constructor() {
|
571 | super(new ResolutionCacheDelegate());
|
572 | }
|
573 | }
|
574 |
|
575 | class PathResolverImpl {
|
576 | constructor(rootPath, inputPath) {
|
577 | this.resolutionCache = new ResolutionCache();
|
578 | this.pathInfoCache = new PathInfoCache(rootPath, inputPath);
|
579 | }
|
580 | resolve(path) {
|
581 | const pathInfo = this.pathInfoCache.get(path);
|
582 | return this.resolutionCache.get(pathInfo);
|
583 | }
|
584 | reset() {
|
585 |
|
586 |
|
587 | this.resolutionCache.clear();
|
588 | }
|
589 | }
|
590 |
|
591 | const SharedRegistry = ts.createDocumentRegistry();
|
592 | class SourceCache {
|
593 | constructor(resolver, options) {
|
594 | this.resolver = resolver;
|
595 | this.options = options;
|
596 | this.sourceFiles = new Map();
|
597 | this.bucketKey = SharedRegistry.getKeyForCompilationSettings(options);
|
598 | }
|
599 | updateOptions(options) {
|
600 | const bucketKey = SharedRegistry.getKeyForCompilationSettings(options);
|
601 | this.options = options;
|
602 | if (this.bucketKey !== bucketKey) {
|
603 | this.releaseAll();
|
604 | this.bucketKey = bucketKey;
|
605 | }
|
606 | }
|
607 | getSourceFile(fileName) {
|
608 | const resolution = this.resolve(fileName);
|
609 | return this.getSourceFileByPath(fileName, resolution.canonicalPath);
|
610 | }
|
611 | getSourceFileByPath(fileName, path) {
|
612 | const resolution = this.resolve(path);
|
613 | return this.getSourceFileByResolution(resolution, fileName, path);
|
614 | }
|
615 | releaseUnusedSourceFiles(program) {
|
616 | const bucketKey = this.bucketKey;
|
617 | for (const path of this.sourceFiles.keys()) {
|
618 | if (program.getSourceFileByPath(path) === undefined) {
|
619 | SharedRegistry.releaseDocumentWithKey(path, bucketKey);
|
620 | }
|
621 | }
|
622 | }
|
623 | releaseAll() {
|
624 | const { bucketKey } = this;
|
625 | const paths = this.sourceFiles.keys();
|
626 | for (const path of paths) {
|
627 | SharedRegistry.releaseDocumentWithKey(path, bucketKey);
|
628 | }
|
629 | this.sourceFiles.clear();
|
630 | }
|
631 | resolve(fileName) {
|
632 | return this.resolver.resolve(fileName);
|
633 | }
|
634 | getSourceFileByResolution(resolution, fileName, path) {
|
635 | const content = readFileResolution(resolution);
|
636 | if (content) {
|
637 | return this.getOrUpdateSourceFile(fileName, path, content);
|
638 | }
|
639 | }
|
640 | getOrUpdateSourceFile(fileName, path, content) {
|
641 | const existing = this.sourceFiles.get(path);
|
642 | if (existing) {
|
643 | return this.updateSourceFile(existing, fileName, path, content);
|
644 | }
|
645 | else {
|
646 | return this.createSourceFile(fileName, path, content);
|
647 | }
|
648 | }
|
649 | updateSourceFile(existing, fileName, path, content) {
|
650 | const { version } = content;
|
651 | const { options, bucketKey } = this;
|
652 | const sourceFile = SharedRegistry.updateDocumentWithKey(fileName, path, options, bucketKey, snapshot(content.buffer), version);
|
653 | existing.sourceFile = sourceFile;
|
654 | existing.version = version;
|
655 | return sourceFile;
|
656 | }
|
657 | createSourceFile(fileName, path, content) {
|
658 | const { options, bucketKey, sourceFiles } = this;
|
659 | const { buffer, version } = content;
|
660 | const sourceFile = SharedRegistry.acquireDocumentWithKey(fileName, path, options, bucketKey, snapshot(buffer), version);
|
661 | sourceFiles.set(path, { sourceFile, version });
|
662 | return sourceFile;
|
663 | }
|
664 | }
|
665 | function snapshot(buffer) {
|
666 | return ts.ScriptSnapshot.fromString(buffer.toString("utf8"));
|
667 | }
|
668 |
|
669 | class Compiler {
|
670 | constructor(inputPath, outputPath, options, diagnosticsHandler) {
|
671 | this.inputPath = inputPath;
|
672 | this.outputPath = outputPath;
|
673 | this.options = options;
|
674 | this.diagnosticsHandler = diagnosticsHandler;
|
675 | const workingPath = (this.workingPath = options.workingPath);
|
676 | const rootPath = (this.rootPath = options.rootPath);
|
677 | this.buildPath = options.buildPath;
|
678 | const resolver = (this.resolver = new PathResolverImpl(rootPath, inputPath));
|
679 | const input = (this.input = new Input(resolver));
|
680 | this.configParser = new ConfigParser(options.projectPath, options.rawConfig, options.configFileName, options.compilerOptions, workingPath, input);
|
681 | this.output = new OutputPatcher(outputPath);
|
682 | }
|
683 | compile() {
|
684 | const config = this.parseConfig();
|
685 | const sourceCache = this.getSourceCache(config.options);
|
686 | const program = this.createProgram(config, sourceCache);
|
687 | this.emitDiagnostics(program);
|
688 | sourceCache.releaseUnusedSourceFiles(program);
|
689 | this.emitProgram(program, this.resolveBuildPath(config.options));
|
690 | this.patchOutput();
|
691 | this.resetCaches();
|
692 | }
|
693 | parseConfig() {
|
694 | const token = heimdall.start("TypeScript:parseConfig");
|
695 | const config = this.configParser.parseConfig();
|
696 | heimdall.stop(token);
|
697 | return config;
|
698 | }
|
699 | getSourceCache(options) {
|
700 | let sourceCache = this.sourceCache;
|
701 | if (sourceCache === undefined) {
|
702 | sourceCache = this.sourceCache = new SourceCache(this.resolver, options);
|
703 | }
|
704 | else {
|
705 | sourceCache.updateOptions(options);
|
706 | }
|
707 | return sourceCache;
|
708 | }
|
709 | createProgram(config, sourceCache) {
|
710 | const token = heimdall.start("TypeScript:createProgram");
|
711 | const host = createCompilerHost(this.workingPath, this.input, sourceCache, config.options);
|
712 | const oldProgram = this.program;
|
713 | const program = ts.createProgram(config.fileNames, config.options, host, oldProgram);
|
714 | this.program = program;
|
715 | heimdall.stop(token);
|
716 | return program;
|
717 | }
|
718 | emitDiagnostics(program) {
|
719 |
|
720 | const token = heimdall.start("TypeScript:emitDiagnostics");
|
721 | const diagnostics = ts.getPreEmitDiagnostics(program);
|
722 | heimdall.stop(token);
|
723 | this.diagnosticsHandler.check(diagnostics);
|
724 | }
|
725 | resolveBuildPath(options) {
|
726 | if (this.buildPath !== undefined) {
|
727 | return this.buildPath;
|
728 | }
|
729 | if (options.outDir !== undefined) {
|
730 | return normalizePath(options.outDir);
|
731 | }
|
732 | return this.rootPath;
|
733 | }
|
734 | emitProgram(program, buildPath) {
|
735 | const token = heimdall.start("TypeScript:emitProgram");
|
736 | const { output } = this;
|
737 | const emitResult = program.emit(undefined, (fileName, data) => {
|
738 |
|
739 |
|
740 | const relativePath = relativePathWithin(buildPath, toAbsolutePath(fileName, this.workingPath));
|
741 | if (relativePath) {
|
742 | output.add(relativePath, data);
|
743 | }
|
744 | });
|
745 | heimdall.stop(token);
|
746 | this.diagnosticsHandler.check(emitResult.diagnostics);
|
747 | }
|
748 | patchOutput() {
|
749 | const token = heimdall.start("TypeScript:patchOutput");
|
750 | this.output.patch();
|
751 | heimdall.stop(token);
|
752 | }
|
753 | resetCaches() {
|
754 | this.resolver.reset();
|
755 | this.input.reset();
|
756 | }
|
757 | }
|
758 |
|
759 | class DiagnosticsHandlerImpl {
|
760 | constructor(options) {
|
761 | this.write = ts.sys.write;
|
762 | this.throwOnError = options.throwOnError;
|
763 | this.host = createFormatDiagnosticsHost(options.workingPath);
|
764 | }
|
765 | setWrite(write) {
|
766 | this.write = write;
|
767 | }
|
768 | check(diagnostics, throwOnError) {
|
769 | const normalized = normalize(diagnostics);
|
770 | if (normalized === undefined) {
|
771 | return false;
|
772 | }
|
773 | const message = this.format(normalized);
|
774 | if (this.throwOnError || throwOnError === true) {
|
775 | throw new Error(message);
|
776 | }
|
777 | this.write(message);
|
778 | return true;
|
779 | }
|
780 | format(diagnostics) {
|
781 | return ts.formatDiagnostics(diagnostics, this.host);
|
782 | }
|
783 | }
|
784 | function normalize(diagnostics) {
|
785 | if (diagnostics === undefined) {
|
786 | return undefined;
|
787 | }
|
788 | if (Array.isArray(diagnostics)) {
|
789 | return diagnostics.length === 0 ? undefined : diagnostics;
|
790 | }
|
791 | return [diagnostics];
|
792 | }
|
793 | function createFormatDiagnosticsHost(rootPath) {
|
794 | const newLine = ts.sys.newLine;
|
795 | return {
|
796 | getCanonicalFileName,
|
797 | getCurrentDirectory: () => rootPath,
|
798 | getNewLine: () => newLine,
|
799 | };
|
800 | }
|
801 |
|
802 | function normalizeOptions(options) {
|
803 | const workingPath = toAbsolutePath(options.workingPath === undefined ? process.cwd() : options.workingPath);
|
804 | const rootPath = options.rootPath === undefined
|
805 | ? workingPath
|
806 | : toAbsolutePath(options.rootPath, workingPath);
|
807 | const projectPath = options.projectPath === undefined
|
808 | ? rootPath
|
809 | : toAbsolutePath(options.projectPath, workingPath);
|
810 | const buildPath = options.buildPath === undefined
|
811 | ? undefined
|
812 | : toAbsolutePath(options.buildPath, workingPath);
|
813 | const tsconfig = options.tsconfig;
|
814 | if (buildPath !== undefined &&
|
815 | !(rootPath === buildPath || isWithin(rootPath, buildPath))) {
|
816 | throw new Error(`buildPath "${buildPath}" must be at or within rootPath "${rootPath}"`);
|
817 | }
|
818 | let configFileName;
|
819 | let rawConfig;
|
820 | if (typeof tsconfig === "object") {
|
821 | configFileName = undefined;
|
822 | rawConfig = tsconfig;
|
823 | }
|
824 | else if (tsconfig) {
|
825 | configFileName = normalizePath(tsconfig);
|
826 | rawConfig = undefined;
|
827 | }
|
828 | let throwOnError = options.throwOnError;
|
829 | if (throwOnError === undefined) {
|
830 | throwOnError = process.env.NODE_ENV === "production";
|
831 | }
|
832 | return {
|
833 | buildPath,
|
834 | compilerOptions: options.compilerOptions,
|
835 | configFileName,
|
836 | projectPath,
|
837 | rawConfig,
|
838 | rootPath,
|
839 | throwOnError,
|
840 | workingPath,
|
841 | };
|
842 | }
|
843 |
|
844 |
|
845 |
|
846 |
|
847 |
|
848 |
|
849 |
|
850 |
|
851 |
|
852 |
|
853 |
|
854 |
|
855 |
|
856 |
|
857 |
|
858 |
|
859 |
|
860 |
|
861 | function typescript(inputNode, options) {
|
862 | return new TypescriptCompiler(inputNode, options);
|
863 | }
|
864 |
|
865 |
|
866 |
|
867 | class TypescriptCompiler extends BroccoliPlugin {
|
868 | constructor(inputNode, options) {
|
869 | super([inputNode], {
|
870 | annotation: options && options.annotation,
|
871 | name: "broccoli-typescript-compiler",
|
872 | persistentOutput: true,
|
873 | });
|
874 | const normalizedOptions = normalizeOptions(options || {});
|
875 | this.options = normalizedOptions;
|
876 | this.diagnosticHandler = new DiagnosticsHandlerImpl(normalizedOptions);
|
877 | }
|
878 | build() {
|
879 | const token = heimdall.start("TypeScript:compile");
|
880 | let compiler = this.compiler;
|
881 | if (!compiler) {
|
882 | compiler = this.compiler = new Compiler(toAbsolutePath(this.inputPaths[0]), toAbsolutePath(this.outputPath), this.options, this.diagnosticHandler);
|
883 | }
|
884 | compiler.compile();
|
885 | heimdall.stop(token);
|
886 | }
|
887 | setDiagnosticWriter(write) {
|
888 | this.diagnosticHandler.setWrite(write);
|
889 | }
|
890 | }
|
891 |
|
892 | const Funnel = require("broccoli-funnel");
|
893 | const MergeTrees = require("broccoli-merge-trees");
|
894 |
|
895 |
|
896 |
|
897 |
|
898 |
|
899 |
|
900 | function filterLike(inputNode, options) {
|
901 | const passthrough = new Funnel(inputNode, {
|
902 | annotation: "TypeScript passthrough",
|
903 | exclude: ["**/*.ts"],
|
904 | });
|
905 | const filter = new Funnel(inputNode, {
|
906 | annotation: "TypeScript input",
|
907 | include: ["**/*.ts"],
|
908 | });
|
909 | return new MergeTrees([passthrough, new TypescriptCompiler(filter, options)], {
|
910 | annotation: "TypeScript passthrough + output",
|
911 | overwrite: true,
|
912 | });
|
913 | }
|
914 |
|
915 | exports.ConfigParser = ConfigParser;
|
916 | exports.InputIO = Input;
|
917 | exports.PathResolver = PathResolverImpl;
|
918 | exports.TypescriptCompiler = TypescriptCompiler;
|
919 | exports.default = typescript;
|
920 | exports.filterTypescript = filterLike;
|
921 | exports.normalizePath = normalizePath;
|
922 | exports.relativePathWithin = relativePathWithin;
|
923 | exports.toAbsolutePath = toAbsolutePath;
|
924 | exports.toCanonicalPath = toCanonicalPath;
|
925 |
|