mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 11:24:29 -05:00
Finish lodash test case
This commit is contained in:
@@ -41488,8 +41488,14 @@ namespace ts {
|
||||
return isMetaProperty(node.parent) ? checkMetaPropertyKeyword(node.parent).symbol : undefined;
|
||||
case SyntaxKind.MetaProperty:
|
||||
return checkExpression(node as Expression).symbol;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
// See binary expression handling in `getDeclarationFromName`
|
||||
return getSymbolOfNode(node as BinaryExpression) || getSymbolOfNode((node as BinaryExpression).left);
|
||||
|
||||
default:
|
||||
if (isDeclaration(node)) {
|
||||
return getSymbolOfNode(node);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1285,12 +1285,40 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
let definitions = this.mapDefinitionInfoLocations(unmappedDefinitionAndBoundSpan.definitions, project).slice();
|
||||
const needsJsResolution = every(definitions.filter(d => d.isAliasTarget), d => !!d.isAmbient) || some(definitions, d => !!d.failedAliasResolution);
|
||||
const needsJsResolution = !some(definitions, d => !!d.isAliasTarget && !d.isAmbient) || some(definitions, d => !!d.failedAliasResolution);
|
||||
if (needsJsResolution) {
|
||||
project.withAuxiliaryProjectForFiles([file], auxiliaryProject => {
|
||||
const jsDefinitions = auxiliaryProject.getLanguageService().getDefinitionAndBoundSpan(file, position);
|
||||
for (const jsDefinition of jsDefinitions?.definitions || emptyArray) {
|
||||
pushIfUnique(definitions, jsDefinition, (a, b) => a.fileName === b.fileName && a.textSpan.start === b.textSpan.start);
|
||||
const ls = auxiliaryProject.getLanguageService();
|
||||
const jsDefinitions = ls.getDefinitionAndBoundSpan(file, position, /*aliasesOnly*/ true);
|
||||
if (some(jsDefinitions?.definitions)) {
|
||||
for (const jsDefinition of jsDefinitions!.definitions) {
|
||||
pushIfUnique(definitions, jsDefinition, (a, b) => a.fileName === b.fileName && a.textSpan.start === b.textSpan.start);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const ambientCandidates = definitions.filter(d => d.isAliasTarget && d.isAmbient);
|
||||
for (const candidate of ambientCandidates) {
|
||||
const candidateFileName = getEffectiveFileNameOfDefinition(candidate, project.getLanguageService().getProgram()!);
|
||||
if (candidateFileName) {
|
||||
const fileNameToSearch = findImplementationFileFromDtsFileName(candidateFileName, file, auxiliaryProject);
|
||||
const scriptInfo = fileNameToSearch ? auxiliaryProject.getScriptInfo(fileNameToSearch) : undefined;
|
||||
if (!scriptInfo) {
|
||||
continue;
|
||||
}
|
||||
if (!auxiliaryProject.containsScriptInfo(scriptInfo)) {
|
||||
auxiliaryProject.addRoot(scriptInfo);
|
||||
}
|
||||
const auxiliaryProgram = auxiliaryProject.getLanguageService().getProgram()!;
|
||||
const fileToSearch = Debug.checkDefined(auxiliaryProgram.getSourceFile(fileNameToSearch!));
|
||||
const matches = FindAllReferences.Core.getTopMostDeclarationsInFile(candidate.name, fileToSearch);
|
||||
for (const match of matches) {
|
||||
const symbol = auxiliaryProgram.getTypeChecker().getSymbolAtLocation(match);
|
||||
if (symbol) {
|
||||
pushIfUnique(definitions, GoToDefinition.createDefinitionInfo(match, auxiliaryProgram.getTypeChecker(), symbol, match));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1309,6 +1337,72 @@ namespace ts.server {
|
||||
definitions: definitions.map(Session.mapToOriginalLocation),
|
||||
textSpan,
|
||||
};
|
||||
|
||||
function getEffectiveFileNameOfDefinition(definition: DefinitionInfo, program: Program) {
|
||||
const sourceFile = program.getSourceFile(definition.fileName)!;
|
||||
const checker = program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(getTouchingPropertyName(sourceFile, definition.textSpan.start));
|
||||
if (symbol) {
|
||||
let parent = symbol.parent;
|
||||
while (parent && !isExternalModuleSymbol(parent)) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
if (parent?.declarations && some(parent.declarations, isExternalModuleAugmentation)) {
|
||||
// Always CommonJS right now, but who knows in the future
|
||||
const mode = getModeForUsageLocation(sourceFile, find(parent.declarations, isExternalModuleAugmentation)!.name as StringLiteral);
|
||||
const fileName = sourceFile.resolvedModules?.get(stripQuotes(parent.name), mode)?.resolvedFileName;
|
||||
if (fileName) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
const fileName = tryCast(parent?.valueDeclaration, isSourceFile)?.fileName;
|
||||
if (fileName) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findImplementationFileFromDtsFileName(fileName: string, resolveFromFile: string, auxiliaryProject: Project) {
|
||||
const nodeModulesPathParts = getNodeModulePathParts(fileName);
|
||||
if (nodeModulesPathParts && fileName.lastIndexOf(nodeModulesPathPart) === nodeModulesPathParts.topLevelNodeModulesIndex) {
|
||||
// Second check ensures the fileName only contains one `/node_modules/`. If there's more than one I give up.
|
||||
const packageDirectory = fileName.substring(0, nodeModulesPathParts.packageRootIndex);
|
||||
const packageJsonCache = project.getModuleResolutionCache()?.getPackageJsonInfoCache();
|
||||
const compilerOptions = project.getCompilationSettings();
|
||||
const packageJson = getPackageScopeForPath(project.toPath(packageDirectory + "/package.json"), packageJsonCache, project, compilerOptions);
|
||||
if (!packageJson) return undefined;
|
||||
// Use fake options instead of actual compiler options to avoid following export map if the project uses node12 or nodenext -
|
||||
// Mapping from an export map entry across packages is out of scope for now. Returned entrypoints will only be what can be
|
||||
// resolved from the package root under --moduleResolution node
|
||||
const entrypoints = getEntrypointsFromPackageJsonInfo(
|
||||
packageJson,
|
||||
{ moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
project,
|
||||
project.getModuleResolutionCache());
|
||||
// This substring is correct only because we checked for a single `/node_modules/` at the top.
|
||||
const packageNamePathPart = fileName.substring(
|
||||
nodeModulesPathParts.topLevelPackageNameIndex + 1,
|
||||
nodeModulesPathParts.packageRootIndex);
|
||||
const packageName = getPackageNameFromTypesPackageName(unmangleScopedPackageName(packageNamePathPart));
|
||||
const path = project.toPath(fileName);
|
||||
if (entrypoints && some(entrypoints, e => project.toPath(e) === path)) {
|
||||
// This file was the main entrypoint of a package. Try to resolve that same package name with
|
||||
// the auxiliary project that only resolves to implementation files.
|
||||
const [implementationResolution] = auxiliaryProject.resolveModuleNames([packageName], resolveFromFile);
|
||||
return implementationResolution?.resolvedFileName;
|
||||
}
|
||||
else {
|
||||
// It wasn't the main entrypoint but we are in node_modules. Try a subpath into the package.
|
||||
const pathToFileInPackage = fileName.substring(nodeModulesPathParts.packageRootIndex + 1);
|
||||
const specifier = `${packageName}/${removeFileExtension(pathToFileInPackage)}`;
|
||||
const [implementationResolution] = auxiliaryProject.resolveModuleNames([specifier], resolveFromFile);
|
||||
return implementationResolution?.resolvedFileName;
|
||||
}
|
||||
}
|
||||
// We're not in node_modules, and we only get to this function if non-dts module resolution failed.
|
||||
// I'm not sure what else I can do here that isn't already covered by that module resolution.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private getEmitOutput(args: protocol.EmitOutputRequestArgs): EmitOutput | protocol.EmitOutput {
|
||||
|
||||
@@ -1333,6 +1333,30 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
export function getTopMostDeclarationsInFile(declarationName: string, sourceFile: SourceFile): readonly Declaration[] {
|
||||
const candidates = mapDefined(getPossibleSymbolReferenceNodes(sourceFile, declarationName), getDeclarationFromName);
|
||||
return candidates.reduce((topMost, decl) => {
|
||||
const depth = getDepth(decl);
|
||||
if (!some(topMost.declarations) || depth === topMost.depth) {
|
||||
topMost.declarations.push(decl);
|
||||
}
|
||||
else if (depth < topMost.depth) {
|
||||
topMost.declarations = [decl];
|
||||
}
|
||||
topMost.depth = depth;
|
||||
return topMost;
|
||||
}, { depth: Infinity, declarations: [] as Declaration[] }).declarations;
|
||||
|
||||
function getDepth(declaration: Declaration | undefined) {
|
||||
let depth = 0;
|
||||
while (declaration) {
|
||||
declaration = getContainerNode(declaration);
|
||||
depth++;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
export function someSignatureUsage(
|
||||
signature: SignatureDeclaration,
|
||||
sourceFiles: readonly SourceFile[],
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace ts.GoToDefinition {
|
||||
const { symbol, isAliasTarget, failedAliasResolution } = getSymbol(node, typeChecker);
|
||||
if (!symbol && isModuleSpecifierLike(node)) {
|
||||
// We couldn't resolve the symbol as an external module, but it could
|
||||
// that module resolution succeeded but the target was not a module.
|
||||
// be that module resolution succeeded but the target was not a module.
|
||||
const ref = sourceFile.resolvedModules?.get(node.text, getModeForUsageLocation(sourceFile, node));
|
||||
if (ref) {
|
||||
return [{
|
||||
@@ -379,7 +379,7 @@ namespace ts.GoToDefinition {
|
||||
}
|
||||
|
||||
/** Creates a DefinitionInfo from a Declaration, using the declaration's name if possible. */
|
||||
function createDefinitionInfo(declaration: Declaration, checker: TypeChecker, symbol: Symbol, node: Node, isAliasTarget?: boolean, failedAliasResolution?: boolean): DefinitionInfo {
|
||||
export function createDefinitionInfo(declaration: Declaration, checker: TypeChecker, symbol: Symbol, node: Node, isAliasTarget?: boolean, failedAliasResolution?: boolean): DefinitionInfo {
|
||||
const symbolName = checker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol
|
||||
const symbolKind = SymbolDisplay.getSymbolKind(checker, symbol, node);
|
||||
const containerName = symbol.parent ? checker.symbolToString(symbol.parent, node) : "";
|
||||
|
||||
77
tests/cases/fourslash/server/goToSource8_mapFromAtTypes.ts
Normal file
77
tests/cases/fourslash/server/goToSource8_mapFromAtTypes.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @moduleResolution: node
|
||||
|
||||
// @Filename: /node_modules/lodash/package.json
|
||||
//// { "name": "lodash", "version": "4.17.15", "main": "./lodash.js" }
|
||||
|
||||
// @Filename: /node_modules/lodash/lodash.js
|
||||
//// ;(function() {
|
||||
//// /**
|
||||
//// * Adds two numbers.
|
||||
//// *
|
||||
//// * @static
|
||||
//// * @memberOf _
|
||||
//// * @since 3.4.0
|
||||
//// * @category Math
|
||||
//// * @param {number} augend The first number in an addition.
|
||||
//// * @param {number} addend The second number in an addition.
|
||||
//// * @returns {number} Returns the total.
|
||||
//// * @example
|
||||
//// *
|
||||
//// * _.add(6, 4);
|
||||
//// * // => 10
|
||||
//// */
|
||||
//// var [|/*variable*/add|] = createMathOperation(function(augend, addend) {
|
||||
//// return augend + addend;
|
||||
//// }, 0);
|
||||
////
|
||||
//// function lodash(value) {}
|
||||
//// lodash.[|/*property*/add|] = add;
|
||||
////
|
||||
//// /** Detect free variable `global` from Node.js. */
|
||||
//// var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
|
||||
//// /** Detect free variable `self`. */
|
||||
//// var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
|
||||
//// /** Used as a reference to the global object. */
|
||||
//// var root = freeGlobal || freeSelf || Function('return this')();
|
||||
//// /** Detect free variable `exports`. */
|
||||
//// var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;////
|
||||
//// /** Detect free variable `module`. */
|
||||
//// var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
|
||||
//// if (freeModule) {
|
||||
//// // Export for Node.js.
|
||||
//// (freeModule.exports = _)._ = _;
|
||||
//// // Export for CommonJS support.
|
||||
//// freeExports._ = _;
|
||||
//// }
|
||||
//// else {
|
||||
//// // Export to the global object.
|
||||
//// root._ = _;
|
||||
//// }
|
||||
//// }.call(this));
|
||||
|
||||
// @Filename: /node_modules/@types/lodash/package.json
|
||||
//// { "name": "@types/lodash", "version": "4.14.97", "types": "index.d.ts" }
|
||||
|
||||
// @Filename: /node_modules/@types/lodash/index.d.ts
|
||||
//// /// <reference path="./common/math.d.ts" />
|
||||
//// export = _;
|
||||
//// export as namespace _;
|
||||
//// declare const _: _.LoDashStatic;
|
||||
//// declare namespace _ {
|
||||
//// interface LoDashStatic {}
|
||||
//// }
|
||||
|
||||
// @Filename: /node_modules/@types/lodash/common/math.d.ts
|
||||
//// import _ = require("../index");
|
||||
//// declare module "../index" {
|
||||
//// interface LoDashStatic {
|
||||
//// add(augend: number, addend: number): number;
|
||||
//// }
|
||||
//// }
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { [|/*start*/add|] } from 'lodash';
|
||||
|
||||
verify.goToSourceDefinition("start", ["variable", "property"]);
|
||||
Reference in New Issue
Block a user