-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Add language service API to get the compile and runtime dependencies of a source file #3687
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1027,9 +1027,11 @@ namespace ts { | |
getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[]; | ||
getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[]; | ||
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[]; | ||
|
||
getDependencies(fileName: string): DependencyInfo; | ||
|
||
getEmitOutput(fileName: string): EmitOutput; | ||
|
||
getProgram(): Program; | ||
|
||
getSourceFile(fileName: string): SourceFile; | ||
|
@@ -1267,11 +1269,20 @@ namespace ts { | |
autoCollapse: boolean; | ||
} | ||
|
||
export interface DependencyInfo { | ||
/** The file name of the dependency information */ | ||
fileName: string; | ||
/** The compile time dependencies of a file */ | ||
compileTime: string[]; | ||
/** The runtime dependencies of a file */ | ||
runtime: string[]; | ||
} | ||
|
||
export interface EmitOutput { | ||
outputFiles: OutputFile[]; | ||
emitSkipped: boolean; | ||
} | ||
|
||
export const enum OutputFileType { | ||
JavaScript, | ||
SourceMap, | ||
|
@@ -5762,6 +5773,64 @@ namespace ts { | |
return forEach(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error); | ||
} | ||
|
||
function getDependencies(fileName: string): DependencyInfo { | ||
synchronizeHostData(); | ||
|
||
let sourceFile = getValidSourceFile(fileName); | ||
if (!sourceFile) { | ||
return undefined; | ||
} | ||
|
||
let resolver = program.getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile); | ||
|
||
let runtime: string[] = []; | ||
let compileTime: string[] = []; | ||
for (let node of sourceFile.amdDependencies) { | ||
runtime.push(node.path); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. honoring these is dependent on the module target, if it is AMD or UMD, we use them, otherwise they are ignored i.e. in SystemJS and CommonJS. it might be useful to split them in their own list, instead of in "runtime". this way you can have: "imports", "amdDependencies", and "typeOnlyImports"/"designTimeImports" |
||
} | ||
|
||
function getModuleNameText(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): string { | ||
let moduleName = getExternalModuleName(node); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name is not sufficient. We are making some changes to the resolution logic to honor node resolution process, e.g. looking under node_moduels, and looking in package.json. so you want to leverage that here, so this should be a tuple of name (possibly for error reporting) and resolved name. @vladima has a change for this in #3616. |
||
return (<LiteralExpression>moduleName).text; | ||
} | ||
|
||
for (let node of sourceFile.statements) { | ||
switch (node.kind) { | ||
case SyntaxKind.ImportDeclaration: | ||
let importDeclaration = <ImportDeclaration>node; | ||
if (!importDeclaration.importClause || resolver.isReferencedAliasDeclaration(importDeclaration.importClause, /*checkChildren*/ true)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you need to force a typecheck on the file to make sure this is set correctly. so you need to query for all semantic diagnostics. |
||
runtime.push(getModuleNameText(importDeclaration)); | ||
} else { | ||
compileTime.push(getModuleNameText(importDeclaration)); | ||
} | ||
break; | ||
case SyntaxKind.ImportEqualsDeclaration: | ||
let importEqualsDeclaration = <ImportEqualsDeclaration>node; | ||
if (importEqualsDeclaration.moduleReference.kind === SyntaxKind.ExternalModuleReference) { | ||
if (resolver.isReferencedAliasDeclaration(importEqualsDeclaration)) { | ||
runtime.push(getModuleNameText(importEqualsDeclaration)); | ||
} else { | ||
compileTime.push(getModuleNameText(importEqualsDeclaration)); | ||
} | ||
} | ||
break; | ||
case SyntaxKind.ExportDeclaration: | ||
let exportDeclaration = <ExportDeclaration>node; | ||
if (exportDeclaration.moduleSpecifier && (!exportDeclaration.exportClause || resolver.isValueAliasDeclaration(exportDeclaration))) { | ||
// export * as mod from 'mod' && export {x , y } from 'mod' | ||
runtime.push(getModuleNameText(exportDeclaration)); | ||
} | ||
// export { x, y } does neither generate a runtime nor a compile time dependency | ||
break; | ||
} | ||
} | ||
return { | ||
fileName: sourceFile.fileName, | ||
compileTime: compileTime, | ||
runtime: runtime | ||
} | ||
} | ||
|
||
function getEmitOutput(fileName: string): EmitOutput { | ||
synchronizeHostData(); | ||
|
||
|
@@ -5783,7 +5852,7 @@ namespace ts { | |
emitSkipped: emitOutput.emitSkipped | ||
}; | ||
} | ||
|
||
function getMeaningFromDeclaration(node: Node): SemanticMeaning { | ||
switch (node.kind) { | ||
case SyntaxKind.Parameter: | ||
|
@@ -6810,6 +6879,7 @@ namespace ts { | |
getFormattingEditsForRange, | ||
getFormattingEditsForDocument, | ||
getFormattingEditsAfterKeystroke, | ||
getDependencies, | ||
getEmitOutput, | ||
getSourceFile, | ||
getProgram | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
initially i thought that compileTime is a subset of runtime, but then looking at the code, that did not turn out to be the case. i think we need better names here. maybe "compileTimeOnly", or "non-js dependencies.."