diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index 12a1436ec63..f995b8d1b4d 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -11,10 +11,6 @@ namespace ts { message(diag: DiagnosticMessage, ...args: string[]): void; } - interface DependencyGraph { - buildQueue: ResolvedConfigFileName[]; - } - export interface BuildOptions extends OptionsBase { dry?: boolean; force?: boolean; @@ -313,7 +309,7 @@ namespace ts { // TODO:: All the below ones should technically only be in watch mode. but thats for later time /*@internal*/ resolveProjectName(name: string): ResolvedConfigFileName; /*@internal*/ getUpToDateStatusOfFile(configFileName: ResolvedConfigFileName): UpToDateStatus; - /*@internal*/ getBuildGraph(configFileNames: ReadonlyArray): DependencyGraph; + /*@internal*/ getBuildOrder(): ReadonlyArray; /*@internal*/ invalidateProject(configFileName: string, reloadLevel?: ConfigFileProgramReloadLevel): void; /*@internal*/ buildInvalidatedProject(): void; @@ -390,7 +386,7 @@ namespace ts { const unchangedOutputs = createFileMap(toPath as ToPath); /** Map from config file name to up-to-date status */ const projectStatus = createFileMap(toPath); - let globalDependencyGraph: DependencyGraph | undefined; + let buildOrder: readonly ResolvedConfigFileName[] | undefined; const writeFileName = host.trace ? (s: string) => host.trace!(s) : undefined; let readFileWithCache = (f: string) => host.readFile(f); let projectCompilerOptions = baseCompilerOptions; @@ -422,7 +418,7 @@ namespace ts { getUpToDateStatusOfFile, cleanAllProjects, resetBuildContext, - getBuildGraph, + getBuildOrder, invalidateProject, buildInvalidatedProject, @@ -444,7 +440,7 @@ namespace ts { configFileCache.clear(); unchangedOutputs.clear(); projectStatus.clear(); - globalDependencyGraph = undefined; + buildOrder = undefined; buildInfoChecked.clear(); diagnostics.clear(); @@ -490,8 +486,7 @@ namespace ts { } function startWatching() { - const { buildQueue } = getGlobalDependencyGraph(); - for (const resolved of buildQueue) { + for (const resolved of getBuildOrder()) { // Watch this file watchConfigFile(resolved); @@ -615,12 +610,8 @@ namespace ts { return getUpToDateStatus(parseConfigFile(configFileName)); } - function getBuildGraph(configFileNames: ReadonlyArray) { - return createDependencyGraph(resolveProjectNames(configFileNames)); - } - - function getGlobalDependencyGraph() { - return globalDependencyGraph || (globalDependencyGraph = getBuildGraph(rootNames)); + function getBuildOrder() { + return buildOrder || (buildOrder = createBuildOrder(resolveProjectNames(rootNames))); } function getUpToDateStatus(project: ParsedCommandLine | undefined): UpToDateStatus { @@ -853,7 +844,7 @@ namespace ts { function invalidateResolvedProject(resolved: ResolvedConfigFileName, reloadLevel: ConfigFileProgramReloadLevel) { if (reloadLevel === ConfigFileProgramReloadLevel.Full) { configFileCache.removeKey(resolved); - globalDependencyGraph = undefined; + buildOrder = undefined; } projectStatus.removeKey(resolved); diagnostics.removeKey(resolved); @@ -875,8 +866,7 @@ namespace ts { } function getNextInvalidatedProject() { - const { buildQueue } = getGlobalDependencyGraph(); - for (const project of buildQueue) { + for (const project of getBuildOrder()) { const reloadLevel = projectPendingBuild.getValue(project); if (reloadLevel !== undefined) { projectPendingBuild.removeKey(project); @@ -923,7 +913,7 @@ namespace ts { function reportErrorSummary() { if (options.watch || (host as SolutionBuilderHost).reportErrorSummary) { // Report errors from the other projects - getGlobalDependencyGraph().buildQueue.forEach(project => { + getBuildOrder().forEach(project => { if (!projectErrorsReported.hasKey(project)) { reportErrors(diagnostics.getValue(project) || emptyArray); } @@ -980,11 +970,11 @@ namespace ts { // Only composite projects can be referenced by other projects if (!proj.options.composite) return; - const { buildQueue } = getGlobalDependencyGraph(); + const buildOrder = getBuildOrder(); // Always use build order to queue projects - for (let index = buildQueue.indexOf(resolved) + 1; index < buildQueue.length; index++) { - const project = buildQueue[index]; + for (let index = buildOrder.indexOf(resolved) + 1; index < buildOrder.length; index++) { + const project = buildOrder[index]; if (projectPendingBuild.hasKey(project)) continue; const config = parseConfigFile(project); @@ -1023,18 +1013,16 @@ namespace ts { } } - function createDependencyGraph(roots: ResolvedConfigFileName[]): DependencyGraph { + function createBuildOrder(roots: ResolvedConfigFileName[]): readonly ResolvedConfigFileName[] { const temporaryMarks = createFileMap(toPath); const permanentMarks = createFileMap(toPath); const circularityReportStack: string[] = []; - const buildOrder: ResolvedConfigFileName[] = []; + let buildOrder: ResolvedConfigFileName[] | undefined; for (const root of roots) { visit(root); } - return { - buildQueue: buildOrder, - }; + return buildOrder || emptyArray; function visit(projPath: ResolvedConfigFileName, inCircularContext?: boolean) { // Already visited @@ -1060,7 +1048,7 @@ namespace ts { circularityReportStack.pop(); permanentMarks.setValue(projPath, true); - buildOrder.push(projPath); + (buildOrder || (buildOrder = [])).push(projPath); } } @@ -1330,9 +1318,8 @@ namespace ts { function getFilesToClean(): string[] { // Get the same graph for cleaning we'd use for building - const { buildQueue } = getGlobalDependencyGraph(); const filesToDelete: string[] = []; - for (const proj of buildQueue) { + for (const proj of getBuildOrder()) { const parsed = parseConfigFile(proj); if (parsed === undefined) { // File has gone missing; fine to ignore here @@ -1392,10 +1379,10 @@ namespace ts { loadWithLocalCache(Debug.assertEachDefined(moduleNames), containingFile, redirectedReference, loader); } - const { buildQueue } = getGlobalDependencyGraph(); - reportBuildQueue(buildQueue); + const buildOrder = getBuildOrder(); + reportBuildQueue(buildOrder); let anyFailed = false; - for (const next of buildQueue) { + for (const next of buildOrder) { const proj = parseConfigFile(next); if (proj === undefined) { reportParseConfigFileDiagnostic(next); diff --git a/src/testRunner/unittests/tsbuild/graphOrdering.ts b/src/testRunner/unittests/tsbuild/graphOrdering.ts index bb29892bc61..a8f4c3a57a9 100644 --- a/src/testRunner/unittests/tsbuild/graphOrdering.ts +++ b/src/testRunner/unittests/tsbuild/graphOrdering.ts @@ -38,18 +38,16 @@ namespace ts { }); function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[]) { - const builder = createSolutionBuilder(host!, rootNames, { dry: true, force: false, verbose: false }); + const builder = createSolutionBuilder(host!, rootNames.map(getProjectFileName), { dry: true, force: false, verbose: false }); + const buildQueue = builder.getBuildOrder(); - const projFileNames = rootNames.map(getProjectFileName); - const graph = builder.getBuildGraph(projFileNames); - - assert.deepEqual(graph.buildQueue, expectedBuildSet.map(getProjectFileName)); + assert.deepEqual(buildQueue, expectedBuildSet.map(getProjectFileName)); for (const dep of deps) { const child = getProjectFileName(dep[0]); - if (graph.buildQueue.indexOf(child) < 0) continue; + if (buildQueue.indexOf(child) < 0) continue; const parent = getProjectFileName(dep[1]); - assert.isAbove(graph.buildQueue.indexOf(child), graph.buildQueue.indexOf(parent), `Expecting child ${child} to be built after parent ${parent}`); + assert.isAbove(buildQueue.indexOf(child), buildQueue.indexOf(parent), `Expecting child ${child} to be built after parent ${parent}`); } }