mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(migrations): support running existing migrations with plain TS programs (#58541)
Previously we always ran Tsurge migrations with an Angular program, even if it's a plain `ts_library` target. This has changed now, so we also need to properly handle the case where a `ts_library` is analyzed, but no Angular program is available. PR Close #58541
This commit is contained in:
parent
1a0cee543e
commit
55fde8dbac
14 changed files with 103 additions and 80 deletions
|
|
@ -100,7 +100,7 @@ export enum ImportFlags {
|
|||
*/
|
||||
export type ImportedFile = ts.SourceFile | 'unknown' | null;
|
||||
|
||||
export const enum ReferenceEmitKind {
|
||||
export enum ReferenceEmitKind {
|
||||
Success,
|
||||
Failed,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,11 +101,14 @@ export class OutputMigration extends TsurgeFunnelMigration<
|
|||
const reflector = new TypeScriptReflectionHost(checker);
|
||||
const dtsReader = new DtsMetadataReader(checker, reflector);
|
||||
const evaluator = new PartialEvaluator(reflector, checker, null);
|
||||
const ngCompiler = info.ngCompiler;
|
||||
assert(ngCompiler !== null, 'Requires ngCompiler to run the migration');
|
||||
const resourceLoader = ngCompiler['resourceManager'];
|
||||
// Pre-Analyze the program and get access to the template type checker.
|
||||
const {templateTypeChecker} = ngCompiler['ensureAnalyzed']();
|
||||
const resourceLoader = info.ngCompiler?.['resourceManager'] ?? null;
|
||||
|
||||
// Pre-analyze the program and get access to the template type checker.
|
||||
// If we are processing a non-Angular target, there is no template info.
|
||||
const {templateTypeChecker} = info.ngCompiler?.['ensureAnalyzed']() ?? {
|
||||
templateTypeChecker: null,
|
||||
};
|
||||
|
||||
const knownFields: KnownFields<ClassFieldDescriptor> = {
|
||||
// Note: We don't support cross-target migration of `Partial<T>` usages.
|
||||
// This is an acceptable limitation for performance reasons.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import {ResourceLoader} from '@angular/compiler-cli/src/ngtsc/annotations';
|
|||
import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core';
|
||||
import {ReferenceEmitter} from '@angular/compiler-cli/src/ngtsc/imports';
|
||||
import {TemplateTypeChecker} from '@angular/compiler-cli/src/ngtsc/typecheck/api';
|
||||
import assert from 'assert';
|
||||
import {ProgramInfo} from '../../../utils/tsurge/program_info';
|
||||
|
||||
/**
|
||||
|
|
@ -26,12 +25,13 @@ import {ProgramInfo} from '../../../utils/tsurge/program_info';
|
|||
export interface AnalysisProgramInfo extends ProgramInfo {
|
||||
reflector: TypeScriptReflectionHost;
|
||||
typeChecker: ts.TypeChecker;
|
||||
templateTypeChecker: TemplateTypeChecker;
|
||||
metaRegistry: MetadataReader;
|
||||
dtsMetadataReader: DtsMetadataReader;
|
||||
evaluator: PartialEvaluator;
|
||||
refEmitter: ReferenceEmitter;
|
||||
resourceLoader: ResourceLoader;
|
||||
|
||||
templateTypeChecker: TemplateTypeChecker | null;
|
||||
metaRegistry: MetadataReader | null;
|
||||
refEmitter: ReferenceEmitter | null;
|
||||
resourceLoader: ResourceLoader | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -42,37 +42,37 @@ export interface AnalysisProgramInfo extends ProgramInfo {
|
|||
*/
|
||||
export function prepareAnalysisInfo(
|
||||
userProgram: ts.Program,
|
||||
compiler: NgCompiler,
|
||||
compiler: NgCompiler | null,
|
||||
programAbsoluteRootPaths?: string[],
|
||||
) {
|
||||
// Analyze sync and retrieve necessary dependencies.
|
||||
// Note: `getTemplateTypeChecker` requires the `enableTemplateTypeChecker` flag, but
|
||||
// this has negative effects as it causes optional TCB operations to execute, which may
|
||||
// error with unsuccessful reference emits that previously were ignored outside of the migration.
|
||||
// The migration is resilient to TCB information missing, so this is fine, and all the information
|
||||
// we need is part of required TCB operations anyway.
|
||||
const {refEmitter, metaReader, templateTypeChecker} = compiler['ensureAnalyzed']();
|
||||
let refEmitter: ReferenceEmitter | null = null;
|
||||
let metaReader: MetadataReader | null = null;
|
||||
let templateTypeChecker: TemplateTypeChecker | null = null;
|
||||
let resourceLoader: ResourceLoader | null = null;
|
||||
|
||||
// Generate all type check blocks.
|
||||
templateTypeChecker.generateAllTypeCheckBlocks();
|
||||
if (compiler !== null) {
|
||||
// Analyze sync and retrieve necessary dependencies.
|
||||
// Note: `getTemplateTypeChecker` requires the `enableTemplateTypeChecker` flag, but
|
||||
// this has negative effects as it causes optional TCB operations to execute, which may
|
||||
// error with unsuccessful reference emits that previously were ignored outside of the migration.
|
||||
// The migration is resilient to TCB information missing, so this is fine, and all the information
|
||||
// we need is part of required TCB operations anyway.
|
||||
const state = compiler['ensureAnalyzed']();
|
||||
|
||||
resourceLoader = compiler['resourceManager'];
|
||||
refEmitter = state.refEmitter;
|
||||
metaReader = state.metaReader;
|
||||
templateTypeChecker = state.templateTypeChecker;
|
||||
|
||||
// Generate all type check blocks.
|
||||
state.templateTypeChecker.generateAllTypeCheckBlocks();
|
||||
}
|
||||
|
||||
const typeChecker = userProgram.getTypeChecker();
|
||||
|
||||
const reflector = new TypeScriptReflectionHost(typeChecker);
|
||||
const evaluator = new PartialEvaluator(reflector, typeChecker, null);
|
||||
const dtsMetadataReader = new DtsMetadataReader(typeChecker, reflector);
|
||||
const resourceLoader = compiler['resourceManager'];
|
||||
|
||||
// Optional filter for testing. Allows for simulation of parallel execution
|
||||
// even if some tsconfig's have overlap due to sharing of TS sources.
|
||||
// (this is commonly not the case in g3 where deps are `.d.ts` files).
|
||||
const limitToRootNamesOnly = process.env['LIMIT_TO_ROOT_NAMES_ONLY'] === '1';
|
||||
if (limitToRootNamesOnly) {
|
||||
assert(
|
||||
programAbsoluteRootPaths !== undefined,
|
||||
'Expected absolute root paths when limiting to root names.',
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
metaRegistry: metaReader,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ import ts from 'typescript';
|
|||
import {getAngularDecorators} from '@angular/compiler-cli/src/ngtsc/annotations';
|
||||
import {parseDecoratorInputTransformFunction} from '@angular/compiler-cli/src/ngtsc/annotations/directive';
|
||||
import {FatalDiagnosticError} from '@angular/compiler-cli/src/ngtsc/diagnostics';
|
||||
import {Reference, ReferenceEmitter} from '@angular/compiler-cli/src/ngtsc/imports';
|
||||
import {
|
||||
Reference,
|
||||
ReferenceEmitKind,
|
||||
ReferenceEmitter,
|
||||
} from '@angular/compiler-cli/src/ngtsc/imports';
|
||||
import {
|
||||
DecoratorInputTransform,
|
||||
DirectiveMeta,
|
||||
|
|
@ -31,6 +35,7 @@ import {
|
|||
import {CompilationMode} from '@angular/compiler-cli/src/ngtsc/transform';
|
||||
import {MigrationHost} from '../migration_host';
|
||||
import {InputNode, isInputContainerNode} from '../input_detection/input_node';
|
||||
import {NULL_EXPR} from '../../../../../../compiler/src/output/output_ast';
|
||||
|
||||
/** Metadata extracted of an input declaration (in `.ts` or `.d.ts` files). */
|
||||
export interface ExtractedInput extends InputMapping {
|
||||
|
|
@ -46,10 +51,9 @@ export function extractDecoratorInput(
|
|||
reflector: ReflectionHost,
|
||||
metadataReader: DtsMetadataReader,
|
||||
evaluator: PartialEvaluator,
|
||||
refEmitter: ReferenceEmitter,
|
||||
): ExtractedInput | null {
|
||||
return (
|
||||
extractSourceCodeInput(node, host, reflector, evaluator, refEmitter) ??
|
||||
extractSourceCodeInput(node, host, reflector, evaluator) ??
|
||||
extractDtsInput(node, metadataReader)
|
||||
);
|
||||
}
|
||||
|
|
@ -116,7 +120,6 @@ function extractSourceCodeInput(
|
|||
host: MigrationHost,
|
||||
reflector: ReflectionHost,
|
||||
evaluator: PartialEvaluator,
|
||||
refEmitter: ReferenceEmitter,
|
||||
): ExtractedInput | null {
|
||||
if (
|
||||
!isInputContainerNode(node) ||
|
||||
|
|
@ -155,7 +158,7 @@ function extractSourceCodeInput(
|
|||
isRequired = !!evaluatedInputOpts.get('required');
|
||||
}
|
||||
if (evaluatedInputOpts.has('transform') && evaluatedInputOpts.get('transform') != null) {
|
||||
transformResult = parseTransformOfInput(evaluatedInputOpts, node, reflector, refEmitter);
|
||||
transformResult = parseTransformOfInput(evaluatedInputOpts, node, reflector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -180,20 +183,32 @@ function parseTransformOfInput(
|
|||
evaluatedInputOpts: ResolvedValueMap,
|
||||
node: InputNode,
|
||||
reflector: ReflectionHost,
|
||||
refEmitter: ReferenceEmitter,
|
||||
): DecoratorInputTransform | null {
|
||||
const transformValue = evaluatedInputOpts.get('transform');
|
||||
if (!(transformValue instanceof DynamicValue) && !(transformValue instanceof Reference)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// For parsing the transform, we don't need a real reference emitter, as
|
||||
// the emitter is only used for verifying that the transform type could be
|
||||
// copied into e.g. an `ngInputAccept` class member.
|
||||
const noopRefEmitter = new ReferenceEmitter([
|
||||
{
|
||||
emit: () => ({
|
||||
kind: ReferenceEmitKind.Success as const,
|
||||
expression: NULL_EXPR,
|
||||
importedFile: null,
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
try {
|
||||
return parseDecoratorInputTransformFunction(
|
||||
node.parent as ClassDeclaration,
|
||||
node.name.text,
|
||||
transformValue,
|
||||
reflector,
|
||||
refEmitter,
|
||||
noopRefEmitter,
|
||||
CompilationMode.FULL,
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import {
|
|||
} from './passes/problematic_patterns/incompatibility';
|
||||
import {MigrationConfig} from './migration_config';
|
||||
import {ClassFieldUniqueKey} from './passes/reference_resolution/known_fields';
|
||||
import {createNgtscProgram} from '../../../utils/tsurge/helpers/ngtsc_program';
|
||||
import {createBaseProgramInfo} from '../../../utils/tsurge/helpers/create_program';
|
||||
|
||||
/**
|
||||
* Tsurge migration for migrating Angular `@Input()` declarations to
|
||||
|
|
@ -50,9 +50,9 @@ export class SignalInputMigration extends TsurgeComplexMigration<
|
|||
super();
|
||||
}
|
||||
|
||||
// Override the default ngtsc program creation, to add extra flags.
|
||||
// Override the default program creation, to add extra flags.
|
||||
override createProgram(tsconfigAbsPath: string, fs?: FileSystem): BaseProgramInfo {
|
||||
return createNgtscProgram(tsconfigAbsPath, fs, {
|
||||
return createBaseProgramInfo(tsconfigAbsPath, fs, {
|
||||
_compilePoisonedComponents: true,
|
||||
// We want to migrate non-exported classes too.
|
||||
compileNonExportedClasses: true,
|
||||
|
|
@ -85,7 +85,6 @@ export class SignalInputMigration extends TsurgeComplexMigration<
|
|||
|
||||
// Extend the program info with the analysis information we need in every phase.
|
||||
prepareAnalysisDeps(info: ProgramInfo): AnalysisProgramInfo {
|
||||
assert(info.ngCompiler !== null, 'Expected `NgCompiler` to be configured.');
|
||||
const analysisInfo = {
|
||||
...info,
|
||||
...prepareAnalysisInfo(info.program, info.ngCompiler, info.programAbsoluteRootFileNames),
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ export function pass1__IdentifySourceFileAndDeclarationInputs(
|
|||
reflector: TypeScriptReflectionHost,
|
||||
dtsMetadataReader: DtsMetadataReader,
|
||||
evaluator: PartialEvaluator,
|
||||
refEmitter: ReferenceEmitter,
|
||||
knownDecoratorInputs: KnownInputs,
|
||||
result: MigrationResult,
|
||||
) {
|
||||
|
|
@ -43,7 +42,6 @@ export function pass1__IdentifySourceFileAndDeclarationInputs(
|
|||
reflector,
|
||||
dtsMetadataReader,
|
||||
evaluator,
|
||||
refEmitter,
|
||||
);
|
||||
if (decoratorInput !== null) {
|
||||
assert(isInputContainerNode(node), 'Expected input to be declared on accessor or property.');
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ export function pass2_IdentifySourceFileReferences(
|
|||
programInfo: ProgramInfo,
|
||||
checker: ts.TypeChecker,
|
||||
reflector: ReflectionHost,
|
||||
resourceLoader: ResourceLoader,
|
||||
resourceLoader: ResourceLoader | null,
|
||||
evaluator: PartialEvaluator,
|
||||
templateTypeChecker: TemplateTypeChecker,
|
||||
templateTypeChecker: TemplateTypeChecker | null,
|
||||
groupedTsAstVisitor: GroupedTsAstVisitor,
|
||||
knownInputs: KnownInputs,
|
||||
result: MigrationResult,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import {checkInheritanceOfKnownFields} from './problematic_patterns/check_inheri
|
|||
*/
|
||||
export function pass4__checkInheritanceOfInputs(
|
||||
inheritanceGraph: InheritanceGraph,
|
||||
metaRegistry: MetadataReader,
|
||||
metaRegistry: MetadataReader | null,
|
||||
knownInputs: KnownInputs,
|
||||
) {
|
||||
checkInheritanceOfKnownFields(inheritanceGraph, metaRegistry, knownInputs, {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export interface InheritanceTracker<D extends ClassFieldDescriptor> {
|
|||
*/
|
||||
export function checkInheritanceOfKnownFields<D extends ClassFieldDescriptor>(
|
||||
inheritanceGraph: InheritanceGraph,
|
||||
metaRegistry: MetadataReader,
|
||||
metaRegistry: MetadataReader | null,
|
||||
fields: KnownFields<D> & InheritanceTracker<D>,
|
||||
opts: {
|
||||
getFieldsForClass: (node: ts.ClassDeclaration) => D[];
|
||||
|
|
@ -62,21 +62,23 @@ export function checkInheritanceOfKnownFields<D extends ClassFieldDescriptor>(
|
|||
|
||||
assert(ts.isClassDeclaration(inputClass), 'Expected input graph node to be always a class.');
|
||||
const classFields = opts.getFieldsForClass(inputClass);
|
||||
const inputFieldNamesFromMetadataArray = new Set<string>();
|
||||
|
||||
// Iterate through derived class chains and determine all inputs that are overridden
|
||||
// via class metadata fields. e.g `@Component#inputs`. This is later used to mark a
|
||||
// potential similar class input as incompatible— because those cannot be migrated.
|
||||
const inputFieldNamesFromMetadataArray = new Set<string>();
|
||||
for (const derivedClasses of inheritanceGraph.traceDerivedClasses(inputClass)) {
|
||||
const derivedMeta =
|
||||
ts.isClassDeclaration(derivedClasses) && derivedClasses.name !== undefined
|
||||
? metaRegistry.getDirectiveMetadata(new Reference(derivedClasses as ClassDeclaration))
|
||||
: null;
|
||||
if (metaRegistry !== null) {
|
||||
for (const derivedClasses of inheritanceGraph.traceDerivedClasses(inputClass)) {
|
||||
const derivedMeta =
|
||||
ts.isClassDeclaration(derivedClasses) && derivedClasses.name !== undefined
|
||||
? metaRegistry.getDirectiveMetadata(new Reference(derivedClasses as ClassDeclaration))
|
||||
: null;
|
||||
|
||||
if (derivedMeta !== null && derivedMeta.inputFieldNamesFromMetadataArray !== null) {
|
||||
derivedMeta.inputFieldNamesFromMetadataArray.forEach((b) =>
|
||||
inputFieldNamesFromMetadataArray.add(b),
|
||||
);
|
||||
if (derivedMeta !== null && derivedMeta.inputFieldNamesFromMetadataArray !== null) {
|
||||
derivedMeta.inputFieldNamesFromMetadataArray.forEach((b) =>
|
||||
inputFieldNamesFromMetadataArray.add(b),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ export function createFindAllSourceFileReferencesVisitor<D extends ClassFieldDes
|
|||
programInfo: ProgramInfo,
|
||||
checker: ts.TypeChecker,
|
||||
reflector: ReflectionHost,
|
||||
resourceLoader: ResourceLoader,
|
||||
resourceLoader: ResourceLoader | null,
|
||||
evaluator: PartialEvaluator,
|
||||
templateTypeChecker: TemplateTypeChecker,
|
||||
templateTypeChecker: TemplateTypeChecker | null,
|
||||
knownFields: KnownFields<D>,
|
||||
fieldNamesToConsiderForReferenceLookup: Set<string> | null,
|
||||
result: ReferenceResult<D>,
|
||||
|
|
@ -67,7 +67,9 @@ export function createFindAllSourceFileReferencesVisitor<D extends ClassFieldDes
|
|||
const visitor = (node: ts.Node) => {
|
||||
let lastTime = currentTimeInMs();
|
||||
|
||||
if (ts.isClassDeclaration(node)) {
|
||||
// Note: If there is no template type checker and resource loader, we aren't processing
|
||||
// an Angular program, and can skip template detection.
|
||||
if (ts.isClassDeclaration(node) && templateTypeChecker !== null && resourceLoader !== null) {
|
||||
identifyTemplateReferences(
|
||||
programInfo,
|
||||
node,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ export function executeAnalysisPhase(
|
|||
templateTypeChecker,
|
||||
resourceLoader,
|
||||
evaluator,
|
||||
refEmitter,
|
||||
}: AnalysisProgramInfo,
|
||||
) {
|
||||
// Pass 1
|
||||
|
|
@ -61,7 +60,6 @@ export function executeAnalysisPhase(
|
|||
reflector,
|
||||
dtsMetadataReader,
|
||||
evaluator,
|
||||
refEmitter,
|
||||
knownInputs,
|
||||
result,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -100,12 +100,16 @@ export class SignalQueriesMigration extends TsurgeComplexMigration<
|
|||
}
|
||||
|
||||
override async analyze(info: ProgramInfo): Promise<Serializable<CompilationUnitData>> {
|
||||
assert(info.ngCompiler !== null, 'Expected queries migration to have an Angular program.');
|
||||
|
||||
// Pre-Analyze the program and get access to the template type checker.
|
||||
const {templateTypeChecker} = info.ngCompiler['ensureAnalyzed']();
|
||||
// Generate all type check blocks.
|
||||
templateTypeChecker.generateAllTypeCheckBlocks();
|
||||
const {templateTypeChecker} = info.ngCompiler?.['ensureAnalyzed']() ?? {
|
||||
templateTypeChecker: null,
|
||||
};
|
||||
const resourceLoader = info.ngCompiler?.['resourceManager'] ?? null;
|
||||
|
||||
// Generate all type check blocks, if we have Angular template information.
|
||||
if (templateTypeChecker !== null) {
|
||||
templateTypeChecker.generateAllTypeCheckBlocks();
|
||||
}
|
||||
|
||||
const {sourceFiles, program} = info;
|
||||
const checker = program.getTypeChecker();
|
||||
|
|
@ -226,7 +230,7 @@ export class SignalQueriesMigration extends TsurgeComplexMigration<
|
|||
info,
|
||||
checker,
|
||||
reflector,
|
||||
info.ngCompiler['resourceManager'],
|
||||
resourceLoader,
|
||||
evaluator,
|
||||
templateTypeChecker,
|
||||
allFieldsOrKnownQueries,
|
||||
|
|
@ -391,10 +395,13 @@ export class SignalQueriesMigration extends TsurgeComplexMigration<
|
|||
}
|
||||
|
||||
override async migrate(globalMetadata: GlobalUnitData, info: ProgramInfo) {
|
||||
assert(info.ngCompiler !== null, 'Expected queries migration to have an Angular program.');
|
||||
|
||||
// Pre-Analyze the program and get access to the template type checker.
|
||||
const {templateTypeChecker, metaReader} = info.ngCompiler['ensureAnalyzed']();
|
||||
const {templateTypeChecker, metaReader} = info.ngCompiler?.['ensureAnalyzed']() ?? {
|
||||
templateTypeChecker: null,
|
||||
metaReader: null,
|
||||
};
|
||||
const resourceLoader = info.ngCompiler?.['resourceManager'] ?? null;
|
||||
|
||||
const {program, sourceFiles} = info;
|
||||
const checker = program.getTypeChecker();
|
||||
const reflector = new TypeScriptReflectionHost(checker);
|
||||
|
|
@ -472,7 +479,7 @@ export class SignalQueriesMigration extends TsurgeComplexMigration<
|
|||
info,
|
||||
checker,
|
||||
reflector,
|
||||
info.ngCompiler['resourceManager'],
|
||||
resourceLoader,
|
||||
evaluator,
|
||||
templateTypeChecker,
|
||||
knownQueries,
|
||||
|
|
|
|||
|
|
@ -8,11 +8,10 @@
|
|||
|
||||
import {absoluteFrom, FileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
|
||||
import {isShim} from '@angular/compiler-cli/src/ngtsc/shims';
|
||||
import {createNgtscProgram} from './helpers/ngtsc_program';
|
||||
import {BaseProgramInfo, ProgramInfo} from './program_info';
|
||||
import {getRootDirs} from '@angular/compiler-cli/src/ngtsc/util/src/typescript';
|
||||
import {Serializable} from './helpers/serializable';
|
||||
import {createProgramInfo} from './helpers/create_program_info';
|
||||
import {createBaseProgramInfo} from './helpers/create_program';
|
||||
|
||||
/**
|
||||
* Type describing statistics that could be tracked
|
||||
|
|
@ -42,7 +41,7 @@ export abstract class TsurgeBaseMigration<UnitAnalysisMetadata, CombinedGlobalMe
|
|||
* - In 1P: Ngtsc or TS programs are created based on the Blaze target.
|
||||
*/
|
||||
createProgram(tsconfigAbsPath: string, fs?: FileSystem): BaseProgramInfo {
|
||||
return createProgramInfo(tsconfigAbsPath, fs);
|
||||
return createBaseProgramInfo(tsconfigAbsPath, fs);
|
||||
}
|
||||
|
||||
// Optional function to prepare the base `ProgramInfo` even further,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import {createNgtscProgram} from './ngtsc_program';
|
|||
import {parseTsconfigOrDie} from './ts_parse_config';
|
||||
import {createPlainTsProgram} from './ts_program';
|
||||
|
||||
/** Creates the program info for the given tsconfig path. */
|
||||
export function createProgramInfo(
|
||||
/** Creates the base program info for the given tsconfig path. */
|
||||
export function createBaseProgramInfo(
|
||||
absoluteTsconfigPath: string,
|
||||
fs?: FileSystem,
|
||||
optionOverrides: NgCompilerOptions = {},
|
||||
Loading…
Reference in a new issue