blob: 4e5b9f6483e59cbdf732ab2e1f0c6b504a7cebbf [file] [log] [blame]
/**
* @fileoverview utilities to construct a static graph representation of the
* import graph discovered in typescript inputs.
*/
import * as tsickle from 'tsickle';
/**
* Recursively walk the import graph provided by tsickle, populating entries
* in the result map such that if foo imports bar, foo will appear before bar
* in the map.
*/
function topologicalSort(
result: tsickle.FileMap<boolean>, current: string,
modulesManifest: tsickle.ModulesManifest,
visiting: tsickle.FileMap<boolean>) {
const referencedModules = modulesManifest.getReferencedModules(current);
if (!referencedModules) return; // not in the local set of sources.
for (const referencedModule of referencedModules) {
const referencedFileName =
modulesManifest.getFileNameFromModule(referencedModule);
if (!referencedFileName) continue; // Ambient modules.
if (!result[referencedFileName]) {
if (visiting[referencedFileName]) {
const path = current + ' -> ' + Object.keys(visiting).join(' -> ');
throw new Error('Cyclical dependency between files:\n' + path);
}
visiting[referencedFileName] = true;
topologicalSort(result, referencedFileName, modulesManifest, visiting);
delete visiting[referencedFileName];
}
}
result[current] = true;
}
/**
* Create the contents of the .es5.MF file which propagates partial ordering of
* the import graph to later actions.
* Each line in the resulting text corresponds with a workspace-relative file
* path, and the lines are ordered to match the expected load order in a
* browser.
*/
export function constructManifest(
modulesManifest: tsickle.ModulesManifest,
host: {relativeOutputPath: (f: string) => string}): string {
const result: tsickle.FileMap<boolean> = {};
for (const file of modulesManifest.fileNames) {
topologicalSort(result, file, modulesManifest, {});
}
// NB: The object literal maintains insertion order.
return Object.keys(result).map(fn => host.relativeOutputPath(fn)).join('\n') +
'\n';
}