Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance for after-compile plugin. #187

Merged
Merged
Prev Previous commit
Next Next commit
Implemented reverseDependencyGraph to get list of files, which are po…
…ssible affected by changes
  • Loading branch information
Strate committed May 21, 2016
commit 31df6b96a000eee038d89e2da292781bacd23e77
62 changes: 56 additions & 6 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ interface TSInstance {
languageService?: typescript.LanguageService;
version?: number;
dependencyGraph: any;
reverseDependencyGraph: any;
modifiedFiles?: TSFiles;
filesWithErrors?: TSFiles;
}

interface TSInstances {
Expand Down Expand Up @@ -197,6 +199,7 @@ function ensureTypeScriptInstance(loaderOptions: LoaderOptions, loader: any): {
languageService: null,
version: 0,
dependencyGraph: {},
reverseDependencyGraph: {},
modifiedFiles: null
};

Expand Down Expand Up @@ -297,7 +300,7 @@ function ensureTypeScriptInstance(loaderOptions: LoaderOptions, loader: any): {
loader._module.errors,
formatErrors(diagnostics, instance, {file: configFilePath || 'tsconfig.json'}));

return { instance: instances[loaderOptions.instance] = { compiler, compilerOptions, loaderOptions, files, dependencyGraph: {} }};
return { instance: instances[loaderOptions.instance] = { compiler, compilerOptions, loaderOptions, files, dependencyGraph: {}, reverseDependencyGraph: {} }};
}

// Load initial files (core lib files, any files specified in tsconfig.json)
Expand Down Expand Up @@ -400,7 +403,15 @@ function ensureTypeScriptInstance(loaderOptions: LoaderOptions, loader: any): {
resolvedModules.push(resolutionResult);
}

instance.dependencyGraph[containingFile] = resolvedModules.filter(m => m != null).map(m => m.resolvedFileName);
let importedFiles = resolvedModules.filter(m => m != null).map(m => m.resolvedFileName);
instance.dependencyGraph[containingFile] = importedFiles;
importedFiles.forEach(importedFileName => {
if (!instance.reverseDependencyGraph[importedFileName]) {
instance.reverseDependencyGraph[importedFileName] = {}
}
instance.reverseDependencyGraph[importedFileName][containingFile] = true
})


return resolvedModules;
}
Expand Down Expand Up @@ -433,6 +444,23 @@ function ensureTypeScriptInstance(loaderOptions: LoaderOptions, loader: any): {
}
}

/**
* Recursive collect all possible dependats of passed file
*/
function collectAllDependants(fileName: string, collected: any = {}): string[] {
let result = {}
result[fileName] = true
collected[fileName] = true
if (instance.reverseDependencyGraph[fileName]) {
Object.keys(instance.reverseDependencyGraph[fileName]).forEach(dependantFileName => {
if (!collected[dependantFileName]) {
collectAllDependants(dependantFileName, collected).forEach(fName => result[fName] = true)
}
})
}
return Object.keys(result)
}

removeTSLoaderErrors(compilation.errors);

// handle compiler option errors after the first compile
Expand Down Expand Up @@ -463,8 +491,29 @@ function ensureTypeScriptInstance(loaderOptions: LoaderOptions, loader: any): {
})

// gather all errors from TypeScript and output them to webpack
let filesWithErrors: TSFiles = null
Object.keys(instance.modifiedFiles || instance.files)
let filesWithErrors: TSFiles = {}
// calculate array of files to check
let filesToCheckForErrors: TSFiles = null
if (!instance.modifiedFiles) {
// check all files on initial run
filesToCheckForErrors = instance.files
} else {
filesToCheckForErrors = {}
// check all modified files, and all dependants
Object.keys(instance.modifiedFiles).forEach(modifiedFileName => {
collectAllDependants(modifiedFileName).forEach(fName => {
filesToCheckForErrors[fName] = instance.files[fName]
})
})
}
// re-check files with errors from previous build
if (instance.filesWithErrors) {
Object.keys(instance.filesWithErrors).forEach(fileWithErrorName =>
filesToCheckForErrors[fileWithErrorName] = instance.filesWithErrors[fileWithErrorName]
)
}

Object.keys(filesToCheckForErrors)
.filter(filePath => !!filePath.match(/(\.d)?\.ts(x?)$/))
.forEach(filePath => {
let errors = languageService.getSyntacticDiagnostics(filePath).concat(languageService.getSemanticDiagnostics(filePath));
Expand Down Expand Up @@ -497,7 +546,7 @@ function ensureTypeScriptInstance(loaderOptions: LoaderOptions, loader: any): {


// gather all declaration files from TypeScript and output them to webpack
Object.keys(instance.modifiedFiles || instance.files)
Object.keys(filesToCheckForErrors)
.filter(filePath => !!filePath.match(/\.ts(x?)$/))
.forEach(filePath => {
let output = languageService.getEmitOutput(filePath);
Expand All @@ -510,7 +559,8 @@ function ensureTypeScriptInstance(loaderOptions: LoaderOptions, loader: any): {
}
});

instance.modifiedFiles = filesWithErrors;
instance.filesWithErrors = filesWithErrors;
instance.modifiedFiles = null;
callback();
});

Expand Down