UNPKG

30.1 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var ts = require('typescript');
6var fs = require('fs');
7var crypto = require('crypto');
8
9const useCaseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames;
10const getCanonicalFileName = ts.sys.useCaseSensitiveFileNames
11 ? (fileName) => fileName
12 : (fileName) => fileName.toLowerCase();
13const defaultLibLocation = ts.getDirectoryPath(toCanonicalPath(ts.sys.getExecutingFilePath()));
14function normalizePath(path) {
15 if (path.length === 0) {
16 return path;
17 }
18 return trimTrailingSlash(ts.normalizePath(path));
19}
20function isWithin(rootPath, path) {
21 return (path.length > rootPath.length &&
22 path.lastIndexOf(rootPath, 0) === 0 &&
23 path.charCodeAt(rootPath.length) === 47 /* Slash */);
24}
25function relativePathWithin(root, path) {
26 let relativePath;
27 if (path.length > root.length &&
28 path.lastIndexOf(root, 0) === 0 &&
29 path.charCodeAt(root.length) === 47 /* Slash */) {
30 relativePath = path.substring(root.length + 1);
31 }
32 else if (path === root) {
33 relativePath = "";
34 }
35 return relativePath;
36}
37function toCanonicalPath(fileName, basePath) {
38 const p = ts.toPath(fileName, basePath === undefined ? currentDirectory() : basePath, getCanonicalFileName);
39 return trimTrailingSlash(p);
40}
41function toAbsolutePath(fileName, basePath) {
42 const p = ts.toPath(fileName, basePath === undefined ? currentDirectory() : basePath, name => name);
43 return trimTrailingSlash(p);
44}
45function trimTrailingSlash(path) {
46 if (path.charCodeAt(path.length - 1) === 47 /* Slash */) {
47 return path.slice(0, path.length - 1);
48 }
49 return path;
50}
51function currentDirectory() {
52 return normalizePath(process.cwd());
53}
54
55function 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
79class 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
141function 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 // we provide a write file on emit.
160 throw new Error("compiler host does not write output");
161 },
162 };
163}
164function 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
175class 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
200function 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}
206function 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}
220function 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}
231function 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
247class 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
259class DirEntriesCache extends Cache {
260 constructor(resolver) {
261 super(new DirEntriesCacheDelegate(resolver));
262 }
263}
264
265class 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 * Used for type resolution.
279 *
280 * Will merge the view of input path and root path.
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 * Used by config parser for matching input.
308 *
309 * Unlike getDirectories which merges the view of input node and root.
310 * We only allow this to return entries for things within the
311 * broccoli input node.
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
364const FSTree = require("fs-tree-diff");
365const BroccoliPlugin = require("broccoli-plugin");
366const walkSync = require("walk-sync");
367const md5Hex = require("md5-hex");
368const heimdall = require("heimdalljs");
369
370class 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 // relativePath should be without leading '/' and use forward slashes
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 // walkSync(output);
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 // the expanded dirs don't have a base
424 fs.mkdirSync(outputPath + "/" + path);
425 break;
426 case "rmdir":
427 // the expanded dirs don't have a base
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/* tslint:disable:max-classes-per-file */
445class 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
461function 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
486class 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
499class PathInfoCache extends Cache {
500 constructor(rootPath, inputPath) {
501 super(new PathInfoCacheDelegate(rootPath, inputPath));
502 }
503}
504
505function resolve(pathInfo) {
506 let flags = 0 /* None */;
507 let stats;
508 let otherStats;
509 if (pathInfo.pathInInput) {
510 stats = stat(pathInfo.pathInInput);
511 if (stats !== undefined) {
512 flags |= 4 /* Input */;
513 }
514 }
515 if (stats === undefined) {
516 stats = stat(pathInfo.path);
517 }
518 if (stats !== undefined) {
519 flags |= stats.isDirectory() ? 2 /* Dir */ : 1 /* File */;
520 }
521 if ((flags & 6 /* InputDir */) === 6 /* InputDir */) {
522 otherStats = stat(pathInfo.path);
523 if (otherStats !== undefined && otherStats.isDirectory()) {
524 flags |= 8 /* Merge */;
525 }
526 }
527 return new ResolutionImpl(pathInfo, stats, otherStats, flags);
528}
529class 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 /* Input */);
542 }
543 isFile() {
544 return this.hasFlag(1 /* File */);
545 }
546 isDirectory() {
547 return this.hasFlag(2 /* Dir */);
548 }
549 isMerged() {
550 return this.hasFlag(1 /* File */);
551 }
552 exists() {
553 return this.stats !== undefined;
554 }
555 hasFlag(flag) {
556 return (this.flags & flag) === flag;
557 }
558}
559
560class ResolutionCacheDelegate {
561 cacheKey(pathInfo) {
562 return pathInfo.canonicalPath;
563 }
564 create(pathInfo) {
565 return resolve(pathInfo);
566 }
567}
568
569class ResolutionCache extends Cache {
570 constructor() {
571 super(new ResolutionCacheDelegate());
572 }
573}
574
575class 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 // PathInfo cache is not build specific
586 // resolutions are
587 this.resolutionCache.clear();
588 }
589}
590
591const SharedRegistry = ts.createDocumentRegistry();
592class 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}
665function snapshot(buffer) {
666 return ts.ScriptSnapshot.fromString(buffer.toString("utf8"));
667}
668
669class 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 // this is where bindings are resolved and typechecking is done
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 /* tslint:disable:no-console */
739 // the fileName is absolute but not normalized if outDir is not normalized
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
759class 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}
784function 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}
793function createFormatDiagnosticsHost(rootPath) {
794 const newLine = ts.sys.newLine;
795 return {
796 getCanonicalFileName,
797 getCurrentDirectory: () => rootPath,
798 getNewLine: () => newLine,
799 };
800}
801
802function 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 * Returns a Broccoli plugin instance that compiles
846 * the files in the tsconfig.
847 *
848 * It is rooted to the inputNode's outputPath, all
849 * files it imports must be resolvable from its input
850 * except for the default library file.
851 *
852 * Errors are logged and it will try to emit whatever
853 * it could successfully compile.
854 *
855 * It will only emit based on the root source files
856 * you give it, by default it will look for all .ts
857 * files, but if you specify a files or filesGlob
858 * it will use these as entry points and only compile
859 * the files and files they reference from the input.
860 */
861function typescript(inputNode, options) {
862 return new TypescriptCompiler(inputNode, options);
863}
864/**
865 * TypeScript Broccoli plugin class.
866 */
867class 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
892const Funnel = require("broccoli-funnel");
893const MergeTrees = require("broccoli-merge-trees");
894/**
895 * Backwards compat filter behavior.
896 *
897 * Preserves the filter aspect of compiling only .ts
898 * and passing through all other files.
899 */
900function 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
915exports.ConfigParser = ConfigParser;
916exports.InputIO = Input;
917exports.PathResolver = PathResolverImpl;
918exports.TypescriptCompiler = TypescriptCompiler;
919exports.default = typescript;
920exports.filterTypescript = filterLike;
921exports.normalizePath = normalizePath;
922exports.relativePathWithin = relativePathWithin;
923exports.toAbsolutePath = toAbsolutePath;
924exports.toCanonicalPath = toCanonicalPath;
925//# sourceMappingURL=index.cjs.js.map