2018-04-06 09:53:10 -07:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
import {GeneratedFile} from '@angular/compiler';
|
2018-04-06 09:53:10 -07:00
|
|
|
import * as ts from 'typescript';
|
|
|
|
|
|
|
|
import * as api from '../transformers/api';
|
2018-08-28 14:13:22 -07:00
|
|
|
import {nocollapseHack} from '../transformers/nocollapse_hack';
|
2019-10-24 20:24:39 +02:00
|
|
|
import {verifySupportedTypeScriptVersion} from '../typescript_support';
|
2018-04-06 09:53:10 -07:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
import {NgCompilerHost} from './core';
|
|
|
|
import {NgCompilerOptions} from './core/api';
|
|
|
|
import {NgCompiler} from './core/src/compiler';
|
|
|
|
import {IndexedComponent} from './indexer';
|
2019-03-18 11:16:38 -07:00
|
|
|
import {NOOP_PERF_RECORDER, PerfRecorder, PerfTracker} from './perf';
|
2018-04-06 09:53:10 -07:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Entrypoint to the Angular Compiler (Ivy+) which sits behind the `api.Program` interface, allowing
|
|
|
|
* it to be a drop-in replacement for the legacy View Engine compiler to tooling such as the
|
|
|
|
* command-line main() function or the Angular CLI.
|
|
|
|
*/
|
2018-04-06 09:53:10 -07:00
|
|
|
export class NgtscProgram implements api.Program {
|
2020-01-17 16:00:07 -08:00
|
|
|
private compiler: NgCompiler;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The primary TypeScript program, which is used for analysis and emit.
|
|
|
|
*/
|
2018-04-06 09:53:10 -07:00
|
|
|
private tsProgram: ts.Program;
|
2020-01-17 16:00:07 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The TypeScript program to use for the next incremental compilation.
|
|
|
|
*
|
|
|
|
* Once a TS program is used to create another (an incremental compilation operation), it can no
|
|
|
|
* longer be used to do so again.
|
|
|
|
*
|
|
|
|
* Since template type-checking uses the primary program to create a type-checking program, after
|
|
|
|
* this happens the primary program is no longer suitable for starting a subsequent compilation,
|
|
|
|
* and the template type-checking program should be used instead.
|
|
|
|
*
|
|
|
|
* Thus, the program which should be used for the next incremental compilation is tracked in
|
|
|
|
* `reuseTsProgram`, separately from the "primary" program which is always used for emit.
|
|
|
|
*/
|
2019-04-24 10:26:47 -07:00
|
|
|
private reuseTsProgram: ts.Program;
|
2018-08-28 14:13:22 -07:00
|
|
|
private closureCompilerEnabled: boolean;
|
2020-01-17 16:00:07 -08:00
|
|
|
private host: NgCompilerHost;
|
2019-03-18 11:16:38 -07:00
|
|
|
private perfRecorder: PerfRecorder = NOOP_PERF_RECORDER;
|
|
|
|
private perfTracker: PerfTracker|null = null;
|
2019-06-10 16:22:56 +01:00
|
|
|
|
2018-04-06 09:53:10 -07:00
|
|
|
constructor(
|
2020-01-17 16:00:07 -08:00
|
|
|
rootNames: ReadonlyArray<string>, private options: NgCompilerOptions,
|
|
|
|
delegateHost: api.CompilerHost, oldProgram?: NgtscProgram) {
|
|
|
|
// First, check whether the current TS version is supported.
|
2019-10-24 20:24:39 +02:00
|
|
|
if (!options.disableTypeScriptVersionCheck) {
|
|
|
|
verifySupportedTypeScriptVersion();
|
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
if (options.tracePerformance !== undefined) {
|
2019-03-18 11:16:38 -07:00
|
|
|
this.perfTracker = PerfTracker.zeroedToNow();
|
|
|
|
this.perfRecorder = this.perfTracker;
|
|
|
|
}
|
2018-08-28 14:13:22 -07:00
|
|
|
this.closureCompilerEnabled = !!options.annotateForClosureCompiler;
|
2018-10-16 14:47:08 -07:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
this.host = NgCompilerHost.wrap(delegateHost, rootNames, options);
|
2018-12-05 16:05:29 -08:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
const reuseProgram = oldProgram && oldProgram.reuseTsProgram;
|
|
|
|
this.tsProgram = ts.createProgram(this.host.inputFiles, options, this.host, reuseProgram);
|
2019-04-24 10:26:47 -07:00
|
|
|
this.reuseTsProgram = this.tsProgram;
|
2018-12-13 11:52:20 -08:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
// Create the NgCompiler which will drive the rest of the compilation.
|
|
|
|
this.compiler =
|
|
|
|
new NgCompiler(this.host, options, this.tsProgram, reuseProgram, this.perfRecorder);
|
2018-04-06 09:53:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
getTsProgram(): ts.Program { return this.tsProgram; }
|
|
|
|
|
|
|
|
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken|
|
2020-01-17 16:00:07 -08:00
|
|
|
undefined): readonly ts.Diagnostic[] {
|
2018-04-06 09:53:10 -07:00
|
|
|
return this.tsProgram.getOptionsDiagnostics(cancellationToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
getTsSyntacticDiagnostics(
|
|
|
|
sourceFile?: ts.SourceFile|undefined,
|
2020-01-17 16:00:07 -08:00
|
|
|
cancellationToken?: ts.CancellationToken|undefined): readonly ts.Diagnostic[] {
|
2018-04-06 09:53:10 -07:00
|
|
|
return this.tsProgram.getSyntacticDiagnostics(sourceFile, cancellationToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
getTsSemanticDiagnostics(
|
|
|
|
sourceFile?: ts.SourceFile|undefined,
|
2020-01-17 16:00:07 -08:00
|
|
|
cancellationToken?: ts.CancellationToken|undefined): readonly ts.Diagnostic[] {
|
2018-04-06 09:53:10 -07:00
|
|
|
return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken);
|
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
getNgOptionDiagnostics(cancellationToken?: ts.CancellationToken|
|
|
|
|
undefined): readonly(ts.Diagnostic|api.Diagnostic)[] {
|
|
|
|
return this.compiler.getOptionDiagnostics();
|
2018-04-06 09:53:10 -07:00
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
getNgStructuralDiagnostics(cancellationToken?: ts.CancellationToken|
|
|
|
|
undefined): readonly api.Diagnostic[] {
|
|
|
|
return [];
|
2018-06-26 15:01:09 -07:00
|
|
|
}
|
2018-04-06 09:53:10 -07:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
getNgSemanticDiagnostics(
|
|
|
|
fileName?: string|undefined, cancellationToken?: ts.CancellationToken|
|
|
|
|
undefined): readonly(ts.Diagnostic|api.Diagnostic)[] {
|
|
|
|
let sf: ts.SourceFile|undefined = undefined;
|
|
|
|
if (fileName !== undefined) {
|
|
|
|
sf = this.tsProgram.getSourceFile(fileName);
|
|
|
|
if (sf === undefined) {
|
|
|
|
// There are no diagnostics for files which don't exist in the program - maybe the caller
|
|
|
|
// has stale data?
|
|
|
|
return [];
|
2019-02-07 19:03:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
const diagnostics = this.compiler.getDiagnostics(sf);
|
|
|
|
this.reuseTsProgram = this.compiler.getNextProgram();
|
|
|
|
return diagnostics;
|
2018-08-23 14:34:55 -07:00
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
/**
|
|
|
|
* Ensure that the `NgCompiler` has properly analyzed the program, and allow for the asynchronous
|
|
|
|
* loading of any resources during the process.
|
|
|
|
*
|
|
|
|
* This is used by the Angular CLI to allow for spawning (async) child compilations for things
|
|
|
|
* like SASS files used in `styleUrls`.
|
|
|
|
*/
|
|
|
|
loadNgStructureAsync(): Promise<void> { return this.compiler.analyzeAsync(); }
|
perf(ivy): reuse prior analysis work during incremental builds (#34288)
Previously, the compiler performed an incremental build by analyzing and
resolving all classes in the program (even unchanged ones) and then using
the dependency graph information to determine which .js files were stale and
needed to be re-emitted. This algorithm produced "correct" rebuilds, but the
cost of re-analyzing the entire program turned out to be higher than
anticipated, especially for component-heavy compilations.
To achieve performant rebuilds, it is necessary to reuse previous analysis
results if possible. Doing this safely requires knowing when prior work is
viable and when it is stale and needs to be re-done.
The new algorithm implemented by this commit is such:
1) Each incremental build starts with knowledge of the last known good
dependency graph and analysis results from the last successful build,
plus of course information about the set of files changed.
2) The previous dependency graph's information is used to determine the
set of source files which have "logically" changed. A source file is
considered logically changed if it or any of its dependencies have
physically changed (on disk) since the last successful compilation. Any
logically unchanged dependencies have their dependency information copied
over to the new dependency graph.
3) During the `TraitCompiler`'s loop to consider all source files in the
program, if a source file is logically unchanged then its previous
analyses are "adopted" (and their 'register' steps are run). If the file
is logically changed, then it is re-analyzed as usual.
4) Then, incremental build proceeds as before, with the new dependency graph
being used to determine the set of files which require re-emitting.
This analysis reuse avoids template parsing operations in many circumstances
and significantly reduces the time it takes ngtsc to rebuild a large
application.
Future work will increase performance even more, by tackling a variety of
other opportunities to reuse or avoid work.
PR Close #34288
2019-12-05 16:03:17 -08:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] {
|
|
|
|
return this.compiler.listLazyRoutes(entryRoute);
|
perf(ivy): reuse prior analysis work during incremental builds (#34288)
Previously, the compiler performed an incremental build by analyzing and
resolving all classes in the program (even unchanged ones) and then using
the dependency graph information to determine which .js files were stale and
needed to be re-emitted. This algorithm produced "correct" rebuilds, but the
cost of re-analyzing the entire program turned out to be higher than
anticipated, especially for component-heavy compilations.
To achieve performant rebuilds, it is necessary to reuse previous analysis
results if possible. Doing this safely requires knowing when prior work is
viable and when it is stale and needs to be re-done.
The new algorithm implemented by this commit is such:
1) Each incremental build starts with knowledge of the last known good
dependency graph and analysis results from the last successful build,
plus of course information about the set of files changed.
2) The previous dependency graph's information is used to determine the
set of source files which have "logically" changed. A source file is
considered logically changed if it or any of its dependencies have
physically changed (on disk) since the last successful compilation. Any
logically unchanged dependencies have their dependency information copied
over to the new dependency graph.
3) During the `TraitCompiler`'s loop to consider all source files in the
program, if a source file is logically unchanged then its previous
analyses are "adopted" (and their 'register' steps are run). If the file
is logically changed, then it is re-analyzed as usual.
4) Then, incremental build proceeds as before, with the new dependency graph
being used to determine the set of files which require re-emitting.
This analysis reuse avoids template parsing operations in many circumstances
and significantly reduces the time it takes ngtsc to rebuild a large
application.
Future work will increase performance even more, by tackling a variety of
other opportunities to reuse or avoid work.
PR Close #34288
2019-12-05 16:03:17 -08:00
|
|
|
}
|
|
|
|
|
2018-04-06 09:53:10 -07:00
|
|
|
emit(opts?: {
|
2020-01-17 16:00:07 -08:00
|
|
|
emitFlags?: api.EmitFlags | undefined; cancellationToken?: ts.CancellationToken | undefined;
|
|
|
|
customTransformers?: api.CustomTransformers | undefined;
|
|
|
|
emitCallback?: api.TsEmitCallback | undefined;
|
|
|
|
mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback | undefined;
|
|
|
|
}|undefined): ts.EmitResult {
|
|
|
|
const {transformers, ignoreFiles} = this.compiler.prepareEmit();
|
2018-04-06 09:53:10 -07:00
|
|
|
const emitCallback = opts && opts.emitCallback || defaultEmitCallback;
|
|
|
|
|
|
|
|
const writeFile: ts.WriteFileCallback =
|
|
|
|
(fileName: string, data: string, writeByteOrderMark: boolean,
|
|
|
|
onError: ((message: string) => void) | undefined,
|
2019-03-20 17:47:55 -07:00
|
|
|
sourceFiles: ReadonlyArray<ts.SourceFile>| undefined) => {
|
2019-11-21 14:37:53 -08:00
|
|
|
if (sourceFiles !== undefined) {
|
|
|
|
// Record successful writes for any `ts.SourceFile` (that's not a declaration file)
|
|
|
|
// that's an input to this write.
|
|
|
|
for (const writtenSf of sourceFiles) {
|
|
|
|
if (writtenSf.isDeclarationFile) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
this.compiler.incrementalDriver.recordSuccessfulEmit(writtenSf);
|
2019-11-21 14:37:53 -08:00
|
|
|
}
|
|
|
|
}
|
2020-01-17 16:00:07 -08:00
|
|
|
|
|
|
|
// If Closure annotations are being produced, tsickle should be adding `@nocollapse` to
|
|
|
|
// any static fields present. However, tsickle doesn't yet handle synthetic fields added
|
|
|
|
// during other transformations, so this hack is in place to ensure Ivy definitions get
|
|
|
|
// properly annotated, pending an upstream fix in tsickle.
|
|
|
|
//
|
|
|
|
// TODO(alxhub): remove when tsickle properly annotates synthetic fields.
|
2019-01-24 10:38:58 +00:00
|
|
|
if (this.closureCompilerEnabled && fileName.endsWith('.js')) {
|
2018-08-28 14:13:22 -07:00
|
|
|
data = nocollapseHack(data);
|
2018-04-06 09:53:10 -07:00
|
|
|
}
|
|
|
|
this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
|
|
|
};
|
|
|
|
|
2019-01-03 12:23:00 +02:00
|
|
|
const customTransforms = opts && opts.customTransformers;
|
2020-01-17 16:00:07 -08:00
|
|
|
const beforeTransforms = transformers.before || [];
|
|
|
|
const afterDeclarationsTransforms = transformers.afterDeclarations;
|
fix(ivy): reuse default imports in type-to-value references (#29266)
This fixes an issue with commit b6f6b117. In this commit, default imports
processed in a type-to-value conversion were recorded as non-local imports
with a '*' name, and the ImportManager generated a new default import for
them. When transpiled to ES2015 modules, this resulted in the following
correct code:
import i3 from './module';
// somewhere in the file, a value reference of i3:
{type: i3}
However, when the AST with this synthetic import and reference was
transpiled to non-ES2015 modules (for example, to commonjs) an issue
appeared:
var module_1 = require('./module');
{type: i3}
TypeScript renames the imported identifier from i3 to module_1, but doesn't
substitute later references to i3. This is because the import and reference
are both synthetic, and never went through the TypeScript AST step of
"binding" which associates the reference to its import. This association is
important during emit when the identifiers might change.
Synthetic (transformer-added) imports will never be bound properly. The only
possible solution is to reuse the user's original import and the identifier
from it, which will be properly downleveled. The issue with this approach
(which prompted the fix in b6f6b117) is that if the import is only used in a
type position, TypeScript will mark it for deletion in the generated JS,
even though additional non-type usages are added in the transformer. This
again would leave a dangling import.
To work around this, it's necessary for the compiler to keep track of
identifiers that it emits which came from default imports, and tell TS not
to remove those imports during transpilation. A `DefaultImportTracker` class
is implemented to perform this tracking. It implements a
`DefaultImportRecorder` interface, which is used to record two significant
pieces of information:
* when a WrappedNodeExpr is generated which refers to a default imported
value, the ts.Identifier is associated to the ts.ImportDeclaration via
the recorder.
* when that WrappedNodeExpr is later emitted as part of the statement /
expression translators, the fact that the ts.Identifier was used is
also recorded.
Combined, this tracking gives the `DefaultImportTracker` enough information
to implement another TS transformer, which can recognize default imports
which were used in the output of the Ivy transform and can prevent them
from being elided. This is done by creating a new ts.ImportDeclaration for
the imports with the same ts.ImportClause. A test verifies that this works.
PR Close #29266
2019-03-11 16:54:07 -07:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
if (customTransforms !== undefined && customTransforms.beforeTs !== undefined) {
|
2019-01-03 12:23:00 +02:00
|
|
|
beforeTransforms.push(...customTransforms.beforeTs);
|
refactor(ivy): obviate the Bazel component of the ivy_switch (#26550)
Originally, the ivy_switch mechanism used Bazel genrules to conditionally
compile one TS file or another depending on whether ngc or ngtsc was the
selected compiler. This was done because we wanted to avoid importing
certain modules (and thus pulling them into the build) if Ivy was on or
off. This mechanism had a major drawback: ivy_switch became a bottleneck
in the import graph, as it both imports from many places in the codebase
and is imported by many modules in the codebase. This frequently resulted
in cyclic imports which caused issues both with TS and Closure compilation.
It turns out ngcc needs both code paths in the bundle to perform the switch
during its operation anyway, so import switching was later abandoned. This
means that there's no real reason why the ivy_switch mechanism needed to
operate at the Bazel level, and for the ivy_switch file to be a bottleneck.
This commit removes the Bazel-level ivy_switch mechanism, and introduces
an additional TypeScript transform in ngtsc (and the pass-through tsc
compiler used for testing JIT) to perform the same operation that ngcc
does, and flip the switch during ngtsc compilation. This allows the
ivy_switch file to be removed, and the individual switches to be located
directly next to their consumers in the codebase, greatly mitigating the
circular import issues and making the mechanism much easier to use.
As part of this commit, the tag for marking switched variables was changed
from __PRE_NGCC__ to __PRE_R3__, since it's no longer just ngcc which
flips these tags. Most variables were renamed from R3_* to SWITCH_* as well,
since they're referenced mostly in render2 code.
Test strategy: existing test coverage is more than sufficient - if this
didn't work correctly it would break the hello world and todo apps.
PR Close #26550
2018-10-17 15:44:44 -07:00
|
|
|
}
|
2019-01-03 12:23:00 +02:00
|
|
|
|
2019-03-18 11:16:38 -07:00
|
|
|
const emitSpan = this.perfRecorder.start('emit');
|
2019-03-06 14:26:56 -08:00
|
|
|
const emitResults: ts.EmitResult[] = [];
|
perf(ivy): template type-check the entire program in 1 file if possible (#29698)
The template type-checking engine previously would assemble a type-checking
program by inserting Type Check Blocks (TCBs) into existing user files. This
approach proved expensive, as TypeScript has to re-parse and re-type-check
those files when processing the type-checking program.
Instead, a far more performant approach is to augment the program with a
single type-checking file, into which all TCBs are generated. Additionally,
type constructors are also inlined into this file.
This is not always possible - both TCBs and type constructors can sometimes
require inlining into user code, particularly if bound generic type
parameters are present, so the approach taken is actually a hybrid. These
operations are inlined if necessary, but are otherwise generated in a single
file.
It is critically important that the original program also include an empty
version of the type-checking file, otherwise the shape of the two programs
will be different and TypeScript will throw away all the old program
information. This leads to a painfully slow type checking pass, on the same
order as the original program creation. A shim to generate this file in the
original program is therefore added.
Testing strategy: this commit is largely a refactor with no externally
observable behavioral differences, and thus no tests are needed.
PR Close #29698
2019-04-02 11:25:33 -07:00
|
|
|
|
2019-03-06 14:26:56 -08:00
|
|
|
for (const targetSourceFile of this.tsProgram.getSourceFiles()) {
|
2020-01-17 16:00:07 -08:00
|
|
|
if (targetSourceFile.isDeclarationFile || ignoreFiles.has(targetSourceFile)) {
|
2019-03-06 14:26:56 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
if (this.compiler.incrementalDriver.safeToSkipEmit(targetSourceFile)) {
|
2019-03-18 12:25:26 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-03-18 11:16:38 -07:00
|
|
|
const fileEmitSpan = this.perfRecorder.start('emitFile', targetSourceFile);
|
2019-03-06 14:26:56 -08:00
|
|
|
emitResults.push(emitCallback({
|
|
|
|
targetSourceFile,
|
|
|
|
program: this.tsProgram,
|
|
|
|
host: this.host,
|
|
|
|
options: this.options,
|
|
|
|
emitOnlyDtsFiles: false, writeFile,
|
|
|
|
customTransformers: {
|
|
|
|
before: beforeTransforms,
|
|
|
|
after: customTransforms && customTransforms.afterTs,
|
|
|
|
afterDeclarations: afterDeclarationsTransforms,
|
2020-01-17 16:00:07 -08:00
|
|
|
} as any,
|
2019-03-06 14:26:56 -08:00
|
|
|
}));
|
2019-03-18 11:16:38 -07:00
|
|
|
this.perfRecorder.stop(fileEmitSpan);
|
|
|
|
}
|
|
|
|
this.perfRecorder.stop(emitSpan);
|
|
|
|
|
|
|
|
if (this.perfTracker !== null && this.options.tracePerformance !== undefined) {
|
|
|
|
this.perfTracker.serializeToFile(this.options.tracePerformance, this.host);
|
2019-03-06 14:26:56 -08:00
|
|
|
}
|
|
|
|
|
2018-04-06 09:53:10 -07:00
|
|
|
// Run the emit, including a custom transformer that will downlevel the Ivy decorators in code.
|
2019-03-06 14:26:56 -08:00
|
|
|
return ((opts && opts.mergeEmitResultsCallback) || mergeEmitResults)(emitResults);
|
2018-04-06 09:53:10 -07:00
|
|
|
}
|
2018-06-26 15:01:09 -07:00
|
|
|
|
2019-06-19 17:23:59 -07:00
|
|
|
getIndexedComponents(): Map<ts.Declaration, IndexedComponent> {
|
2020-01-17 16:00:07 -08:00
|
|
|
return this.compiler.getIndexedComponents();
|
2018-06-26 15:01:09 -07:00
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
getLibrarySummaries(): Map<string, api.LibrarySummary> {
|
|
|
|
throw new Error('Method not implemented.');
|
2018-06-26 15:01:09 -07:00
|
|
|
}
|
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
getEmittedGeneratedFiles(): Map<string, GeneratedFile> {
|
|
|
|
throw new Error('Method not implemented.');
|
2018-06-26 15:01:09 -07:00
|
|
|
}
|
2019-01-08 11:49:58 -08:00
|
|
|
|
2020-01-17 16:00:07 -08:00
|
|
|
getEmittedSourceFiles(): Map<string, ts.SourceFile> {
|
|
|
|
throw new Error('Method not implemented.');
|
2019-01-08 11:49:58 -08:00
|
|
|
}
|
2018-04-06 09:53:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const defaultEmitCallback: api.TsEmitCallback =
|
|
|
|
({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles,
|
|
|
|
customTransformers}) =>
|
|
|
|
program.emit(
|
|
|
|
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
|
|
|
|
|
|
|
function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult {
|
|
|
|
const diagnostics: ts.Diagnostic[] = [];
|
|
|
|
let emitSkipped = false;
|
|
|
|
const emittedFiles: string[] = [];
|
|
|
|
for (const er of emitResults) {
|
|
|
|
diagnostics.push(...er.diagnostics);
|
|
|
|
emitSkipped = emitSkipped || er.emitSkipped;
|
|
|
|
emittedFiles.push(...(er.emittedFiles || []));
|
|
|
|
}
|
2019-03-18 11:16:38 -07:00
|
|
|
|
2018-04-06 09:53:10 -07:00
|
|
|
return {diagnostics, emitSkipped, emittedFiles};
|
|
|
|
}
|