Pete Bacon Darwin 322951af49 refactor(compiler-cli): error on cyclic imports in partial compilation (#40782)
Our approach for handling cyclic imports results in code that is
not easy to tree-shake, so it is not suitable for publishing in a
library.

When compiling in partial compilation mode, we are targeting
such library publication, so we now create a fatal diagnostic
error instead of trying to handle the cyclic import situation.

Closes #40678

PR Close #40782
2021-02-17 06:53:38 -08:00

75 lines
2.1 KiB
TypeScript

/**
* @license
* Copyright Google LLC 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
*/
import * as ts from 'typescript';
import {ImportGraph} from './imports';
/**
* Analyzes a `ts.Program` for cycles.
*/
export class CycleAnalyzer {
constructor(private importGraph: ImportGraph) {}
/**
* Check for a cycle to be created in the `ts.Program` by adding an import between `from` and
* `to`.
*
* @returns a `Cycle` object if an import between `from` and `to` would create a cycle; `null`
* otherwise.
*/
wouldCreateCycle(from: ts.SourceFile, to: ts.SourceFile): Cycle|null {
// Import of 'from' -> 'to' is illegal if an edge 'to' -> 'from' already exists.
return this.importGraph.transitiveImportsOf(to).has(from) ?
new Cycle(this.importGraph, from, to) :
null;
}
/**
* Record a synthetic import from `from` to `to`.
*
* This is an import that doesn't exist in the `ts.Program` but will be considered as part of the
* import graph for cycle creation.
*/
recordSyntheticImport(from: ts.SourceFile, to: ts.SourceFile): void {
this.importGraph.addSyntheticImport(from, to);
}
}
/**
* Represents an import cycle between `from` and `to` in the program.
*
* This class allows us to do the work to compute the cyclic path between `from` and `to` only if
* needed.
*/
export class Cycle {
constructor(
private importGraph: ImportGraph, readonly from: ts.SourceFile, readonly to: ts.SourceFile) {}
/**
* Compute an array of source-files that illustrates the cyclic path between `from` and `to`.
*
* Note that a `Cycle` will not be created unless a path is available between `to` and `from`,
* so `findPath()` will never return `null`.
*/
getPath(): ts.SourceFile[] {
return [this.from, ...this.importGraph.findPath(this.to, this.from)!];
}
}
/**
* What to do if a cycle is detected.
*/
export const enum CycleHandlingStrategy {
/** Add "remote scoping" code to avoid creating a cycle. */
UseRemoteScoping,
/** Fail the compilation with an error. */
Error,
}