зеркало из https://github.com/github/codeql.git
TS: Emit module bindings for all files in node_modules
This commit is contained in:
Родитель
efa7e1112b
Коммит
f4144831ab
|
@ -271,6 +271,15 @@ function handleOpenProjectCommand(command: OpenProjectCommand) {
|
||||||
getCurrentDirectory: () => basePath,
|
getCurrentDirectory: () => basePath,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (let typeRoot of typeRoots || []) {
|
||||||
|
traverseTypeRoot(typeRoot, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let sourceFile of program.getSourceFiles()) {
|
||||||
|
addModuleBindingsFromModuleDeclarations(sourceFile);
|
||||||
|
addModuleBindingsFromFilePath(sourceFile);
|
||||||
|
}
|
||||||
|
|
||||||
/** Concatenates two imports paths. These always use `/` as path separator. */
|
/** Concatenates two imports paths. These always use `/` as path separator. */
|
||||||
function joinModulePath(prefix: string, suffix: string) {
|
function joinModulePath(prefix: string, suffix: string) {
|
||||||
if (prefix.length === 0) return suffix;
|
if (prefix.length === 0) return suffix;
|
||||||
|
@ -300,36 +309,74 @@ function handleOpenProjectCommand(command: OpenProjectCommand) {
|
||||||
if (sourceFile == null) {
|
if (sourceFile == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let symbol = typeChecker.getSymbolAtLocation(sourceFile);
|
addModuleBindingFromRelativePath(sourceFile, importPrefix, child);
|
||||||
if (symbol == null) continue; // Happens if the source file is not a module.
|
|
||||||
|
|
||||||
let canonicalSymbol = getEffectiveExportTarget(symbol); // Follow `export = X` declarations.
|
|
||||||
let symbolId = state.typeTable.getSymbolId(canonicalSymbol);
|
|
||||||
|
|
||||||
let importPath = (child === "index.d.ts")
|
|
||||||
? importPrefix
|
|
||||||
: joinModulePath(importPrefix, pathlib.basename(child, ".d.ts"));
|
|
||||||
|
|
||||||
// Associate the module name with this symbol.
|
|
||||||
state.typeTable.addModuleMapping(symbolId, importPath);
|
|
||||||
|
|
||||||
// Associate global variable names with this module.
|
|
||||||
// For each `export as X` declaration, the global X refers to this module.
|
|
||||||
// Note: the `globalExports` map is stored on the original symbol, not the target of `export=`.
|
|
||||||
if (symbol.globalExports != null) {
|
|
||||||
symbol.globalExports.forEach((global: ts.Symbol) => {
|
|
||||||
state.typeTable.addGlobalMapping(symbolId, global.name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let typeRoot of typeRoots || []) {
|
|
||||||
traverseTypeRoot(typeRoot, "");
|
/**
|
||||||
|
* Emits module bindings for a module with relative path `folder/baseName`.
|
||||||
|
*/
|
||||||
|
function addModuleBindingFromRelativePath(sourceFile: ts.SourceFile, folder: string, baseName: string) {
|
||||||
|
let symbol = typeChecker.getSymbolAtLocation(sourceFile);
|
||||||
|
if (symbol == null) return; // Happens if the source file is not a module.
|
||||||
|
|
||||||
|
let stem = getStem(baseName);
|
||||||
|
let importPath = (stem === "index")
|
||||||
|
? folder
|
||||||
|
: joinModulePath(folder, stem);
|
||||||
|
|
||||||
|
let canonicalSymbol = getEffectiveExportTarget(symbol); // Follow `export = X` declarations.
|
||||||
|
let symbolId = state.typeTable.getSymbolId(canonicalSymbol);
|
||||||
|
|
||||||
|
// Associate the module name with this symbol.
|
||||||
|
state.typeTable.addModuleMapping(symbolId, importPath);
|
||||||
|
|
||||||
|
// Associate global variable names with this module.
|
||||||
|
// For each `export as X` declaration, the global X refers to this module.
|
||||||
|
// Note: the `globalExports` map is stored on the original symbol, not the target of `export=`.
|
||||||
|
if (symbol.globalExports != null) {
|
||||||
|
symbol.globalExports.forEach((global: ts.Symbol) => {
|
||||||
|
state.typeTable.addGlobalMapping(symbolId, global.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit module name bindings for external module declarations, i.e: `declare module 'X' {..}`
|
/**
|
||||||
// These can generally occur anywhere; they may or may not be on the type root path.
|
* Returns the basename of `file` without its extension, while treating `.d.ts` as a
|
||||||
for (let sourceFile of program.getSourceFiles()) {
|
* single extension.
|
||||||
|
*/
|
||||||
|
function getStem(file: string) {
|
||||||
|
if (file.endsWith(".d.ts")) {
|
||||||
|
return pathlib.basename(file, ".d.ts");
|
||||||
|
}
|
||||||
|
let base = pathlib.basename(file);
|
||||||
|
let dot = base.lastIndexOf('.');
|
||||||
|
return dot === -1 || dot === 0 ? base : base.substring(0, dot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits module bindings for a module based on its file path.
|
||||||
|
*
|
||||||
|
* This looks for enclosing `node_modules` folders to determine the module name.
|
||||||
|
* This is needed for modules that ship their own type definitions as opposed to having
|
||||||
|
* type definitions loaded from a type root (conventionally named `@types/xxx`).
|
||||||
|
*/
|
||||||
|
function addModuleBindingsFromFilePath(sourceFile: ts.SourceFile) {
|
||||||
|
let fullPath = sourceFile.fileName;
|
||||||
|
let index = fullPath.lastIndexOf('/node_modules/');
|
||||||
|
if (index === -1) return;
|
||||||
|
let relativePath = fullPath.substring(index + '/node_modules/'.length);
|
||||||
|
// Ignore node_modules/@types folders here as they are typically handled as type roots.
|
||||||
|
if (relativePath.startsWith("@types/")) return;
|
||||||
|
let { dir, base } = pathlib.parse(relativePath);
|
||||||
|
addModuleBindingFromRelativePath(sourceFile, dir, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit module name bindings for external module declarations, i.e: `declare module 'X' {..}`
|
||||||
|
* These can generally occur anywhere; they may or may not be on the type root path.
|
||||||
|
*/
|
||||||
|
function addModuleBindingsFromModuleDeclarations(sourceFile: ts.SourceFile) {
|
||||||
for (let stmt of sourceFile.statements) {
|
for (let stmt of sourceFile.statements) {
|
||||||
if (ts.isModuleDeclaration(stmt) && ts.isStringLiteral(stmt.name)) {
|
if (ts.isModuleDeclaration(stmt) && ts.isStringLiteral(stmt.name)) {
|
||||||
let symbol = (stmt as any).symbol;
|
let symbol = (stmt as any).symbol;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче