mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Find Source Definition (#48264)
* Prototype resolving to JS when go-to-def aliases all resolve to ambient declarations * Add test infrastructure * Start fleshing out test coverage * Fix some go-to-def stuff * Finish lodash test case * Make go-to-implementation never return ambient results * Build new functionality into go-to-implementation * Update baselines * Two more test cases * Refine definition searches for unresolved imports * Revert "Build new functionality into go-to-implementation" This reverts commit381799d0f1. * Fix tests * Revert go-to-implementation changes * Wow a bunch of code was unnecessary * Update baselines and go-to-def test * Fix navigation on symbols that are not aliases but resolve through aliases in chain * Temporarily replace go-to-def with new command implementation * Revert "Temporarily replace go-to-def with new command implementation" This reverts commit34c6cfdebb. * Revert "Wow a bunch of code was unnecessary" This reverts commit1cb2ba646c. * Bring back some deleted code needed for a new test case * Clean up a little * Rename more stuff * Update test * Update API baseline * Temporarily replace go-to-def with new command implementation * PR review fixes * Fix getTopMostDeclarationNamesInFile * Rename local * Use hash set * Remove option from commandLineParser * Keep noDtsResolution project around * Handle AuxiliaryProject kind in ScriptInfo getDefaultProject etc. * Do not run updateGraph in the background for AuxiliaryProject * Don’t create auxiliary project outside of semantic mode * No-op on scheduled invalidation * Add comments to unit test * Sync compiler options to auxiliary project * Fix case sensitivity * Update extensionIsOk with new file extensions * PR feedback * Update API baseline * Mark scheduleInvalidateResolutionsOfFailedLookupLocations internal * Use same heuristics on property accesses of loosely-resolvable aliases as unresolvable named imports * Rename command, and no need to return the bound span * Update API baseline
This commit is contained in:
@@ -9206,7 +9206,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
const sourceTypes = some(constructorTypes, t => !!(t.flags & ~TypeFlags.Nullable)) ? constructorTypes : types; // TODO: GH#18217
|
||||
type = getUnionType(sourceTypes!, UnionReduction.Subtype);
|
||||
type = getUnionType(sourceTypes!);
|
||||
}
|
||||
}
|
||||
const widened = getWidenedType(addOptionality(type, /*isProperty*/ false, definedInMethod && !definedInConstructor));
|
||||
|
||||
@@ -69,7 +69,8 @@ namespace ts {
|
||||
JavaScript, /** '.js' or '.jsx' */
|
||||
Json, /** '.json' */
|
||||
TSConfig, /** '.json' with `tsconfig` used instead of `index` */
|
||||
DtsOnly /** Only '.d.ts' */
|
||||
DtsOnly, /** Only '.d.ts' */
|
||||
TsOnly, /** '.[cm]tsx?' but not .d.ts variants */
|
||||
}
|
||||
|
||||
interface PathAndPackageId {
|
||||
@@ -1290,7 +1291,19 @@ namespace ts {
|
||||
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations;
|
||||
/* @internal */ export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, lookupConfig?: boolean): ResolvedModuleWithFailedLookupLocations; // eslint-disable-line @typescript-eslint/unified-signatures
|
||||
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, lookupConfig?: boolean): ResolvedModuleWithFailedLookupLocations {
|
||||
return nodeModuleNameResolverWorker(NodeResolutionFeatures.None, moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, lookupConfig ? tsconfigExtensions : (compilerOptions.resolveJsonModule ? tsPlusJsonExtensions : tsExtensions), redirectedReference);
|
||||
let extensions;
|
||||
if (lookupConfig) {
|
||||
extensions = tsconfigExtensions;
|
||||
}
|
||||
else if (compilerOptions.noDtsResolution) {
|
||||
extensions = [Extensions.TsOnly];
|
||||
if (compilerOptions.allowJs) extensions.push(Extensions.JavaScript);
|
||||
if (compilerOptions.resolveJsonModule) extensions.push(Extensions.Json);
|
||||
}
|
||||
else {
|
||||
extensions = compilerOptions.resolveJsonModule ? tsPlusJsonExtensions : tsExtensions;
|
||||
}
|
||||
return nodeModuleNameResolverWorker(NodeResolutionFeatures.None, moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, extensions, redirectedReference);
|
||||
}
|
||||
|
||||
function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, extensions: Extensions[], redirectedReference: ResolvedProjectReference | undefined): ResolvedModuleWithFailedLookupLocations {
|
||||
@@ -1299,6 +1312,11 @@ namespace ts {
|
||||
const failedLookupLocations: string[] = [];
|
||||
// conditions are only used by the node12/nodenext resolver - there's no priority order in the list,
|
||||
//it's essentially a set (priority is determined by object insertion order in the object we look at).
|
||||
const conditions = features & NodeResolutionFeatures.EsmMode ? ["node", "import", "types"] : ["node", "require", "types"];
|
||||
if (compilerOptions.noDtsResolution) {
|
||||
conditions.pop();
|
||||
}
|
||||
|
||||
const state: ModuleResolutionState = {
|
||||
compilerOptions,
|
||||
host,
|
||||
@@ -1306,7 +1324,7 @@ namespace ts {
|
||||
failedLookupLocations,
|
||||
packageJsonInfoCache: cache,
|
||||
features,
|
||||
conditions: features & NodeResolutionFeatures.EsmMode ? ["node", "import", "types"] : ["node", "require", "types"]
|
||||
conditions,
|
||||
};
|
||||
|
||||
const result = forEach(extensions, ext => tryResolve(ext));
|
||||
@@ -1533,20 +1551,22 @@ namespace ts {
|
||||
default: return tryExtension(Extension.Dts);
|
||||
}
|
||||
case Extensions.TypeScript:
|
||||
case Extensions.TsOnly:
|
||||
const useDts = extensions === Extensions.TypeScript;
|
||||
switch (originalExtension) {
|
||||
case Extension.Mjs:
|
||||
case Extension.Mts:
|
||||
case Extension.Dmts:
|
||||
return tryExtension(Extension.Mts) || tryExtension(Extension.Dmts);
|
||||
return tryExtension(Extension.Mts) || (useDts ? tryExtension(Extension.Dmts) : undefined);
|
||||
case Extension.Cjs:
|
||||
case Extension.Cts:
|
||||
case Extension.Dcts:
|
||||
return tryExtension(Extension.Cts) || tryExtension(Extension.Dcts);
|
||||
return tryExtension(Extension.Cts) || (useDts ? tryExtension(Extension.Dcts) : undefined);
|
||||
case Extension.Json:
|
||||
candidate += Extension.Json;
|
||||
return tryExtension(Extension.Dts);
|
||||
return useDts ? tryExtension(Extension.Dts) : undefined;
|
||||
default:
|
||||
return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts);
|
||||
return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || (useDts ? tryExtension(Extension.Dts) : undefined);
|
||||
}
|
||||
case Extensions.JavaScript:
|
||||
switch (originalExtension) {
|
||||
@@ -1813,6 +1833,7 @@ namespace ts {
|
||||
switch (extensions) {
|
||||
case Extensions.JavaScript:
|
||||
case Extensions.Json:
|
||||
case Extensions.TsOnly:
|
||||
packageFile = readPackageJsonMainField(jsonContent, candidate, state);
|
||||
break;
|
||||
case Extensions.TypeScript:
|
||||
@@ -1893,14 +1914,16 @@ namespace ts {
|
||||
function extensionIsOk(extensions: Extensions, extension: Extension): boolean {
|
||||
switch (extensions) {
|
||||
case Extensions.JavaScript:
|
||||
return extension === Extension.Js || extension === Extension.Jsx;
|
||||
return extension === Extension.Js || extension === Extension.Jsx || extension === Extension.Mjs || extension === Extension.Cjs;
|
||||
case Extensions.TSConfig:
|
||||
case Extensions.Json:
|
||||
return extension === Extension.Json;
|
||||
case Extensions.TypeScript:
|
||||
return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Dts;
|
||||
return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Mts || extension === Extension.Cts || extension === Extension.Dts || extension === Extension.Dmts || extension === Extension.Dcts;
|
||||
case Extensions.TsOnly:
|
||||
return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Mts || extension === Extension.Cts;
|
||||
case Extensions.DtsOnly:
|
||||
return extension === Extension.Dts;
|
||||
return extension === Extension.Dts || extension === Extension.Dmts || extension === Extension.Dcts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4668,8 +4668,10 @@ namespace ts {
|
||||
export type AnyImportOrRequire = AnyImportSyntax | VariableDeclarationInitializedTo<RequireOrImportCall>;
|
||||
|
||||
/* @internal */
|
||||
export type AnyImportOrRequireStatement = AnyImportSyntax | RequireVariableStatement;
|
||||
export type AnyImportOrBareOrAccessedRequire = AnyImportSyntax | VariableDeclarationInitializedTo<RequireOrImportCall | AccessExpression>;
|
||||
|
||||
/* @internal */
|
||||
export type AnyImportOrRequireStatement = AnyImportSyntax | RequireVariableStatement;
|
||||
|
||||
/* @internal */
|
||||
export type AnyImportOrReExport = AnyImportSyntax | ExportDeclaration;
|
||||
@@ -6178,6 +6180,8 @@ namespace ts {
|
||||
assumeChangesOnlyAffectDirectDependencies?: boolean;
|
||||
noLib?: boolean;
|
||||
noResolve?: boolean;
|
||||
/*@internal*/
|
||||
noDtsResolution?: boolean;
|
||||
noUncheckedIndexedAccess?: boolean;
|
||||
out?: string;
|
||||
outDir?: string;
|
||||
|
||||
+70
-11
@@ -933,6 +933,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function isAnyImportOrBareOrAccessedRequire(node: Node): node is AnyImportOrBareOrAccessedRequire {
|
||||
return isAnyImportSyntax(node) || isVariableDeclarationInitializedToBareOrAccessedRequire(node);
|
||||
}
|
||||
|
||||
export function isLateVisibilityPaintedStatement(node: Node): node is LateVisibilityPaintedStatement {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
@@ -2558,14 +2562,14 @@ namespace ts {
|
||||
return decl.kind === SyntaxKind.FunctionDeclaration || isVariableDeclaration(decl) && decl.initializer && isFunctionLike(decl.initializer);
|
||||
}
|
||||
|
||||
export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrRequire): string | undefined {
|
||||
export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAccessedRequire): StringLiteralLike | undefined {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return node.initializer.arguments[0].text;
|
||||
return findAncestor(node.initializer, (node): node is RequireOrImportCall => isRequireCall(node, /*requireStringLiteralLikeArgument*/ true))?.arguments[0];
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
return tryCast(node.moduleSpecifier, isStringLiteralLike)?.text;
|
||||
return tryCast(node.moduleSpecifier, isStringLiteralLike);
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return tryCast(tryCast(node.moduleReference, isExternalModuleReference)?.expression, isStringLiteralLike)?.text;
|
||||
return tryCast(tryCast(node.moduleReference, isExternalModuleReference)?.expression, isStringLiteralLike);
|
||||
default:
|
||||
Debug.assertNever(node);
|
||||
}
|
||||
@@ -3131,21 +3135,31 @@ namespace ts {
|
||||
// export = <EntityNameExpression>
|
||||
// export default <EntityNameExpression>
|
||||
// module.exports = <EntityNameExpression>
|
||||
// {<Identifier>}
|
||||
// {name: <EntityNameExpression>}
|
||||
// module.exports.x = <EntityNameExpression>
|
||||
// const x = require("...")
|
||||
// const { x } = require("...")
|
||||
// const x = require("...").y
|
||||
// const { x } = require("...").y
|
||||
export function isAliasSymbolDeclaration(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
|
||||
if (node.kind === SyntaxKind.ImportEqualsDeclaration ||
|
||||
node.kind === SyntaxKind.NamespaceExportDeclaration ||
|
||||
node.kind === SyntaxKind.ImportClause && !!(node as ImportClause).name ||
|
||||
node.kind === SyntaxKind.NamespaceImport ||
|
||||
node.kind === SyntaxKind.NamespaceExport ||
|
||||
node.kind === SyntaxKind.ImportSpecifier ||
|
||||
node.kind === SyntaxKind.ExportSpecifier ||
|
||||
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node as ExportAssignment) ||
|
||||
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node as ExportAssignment)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isInJSFile(node) && (
|
||||
isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node) ||
|
||||
isPropertyAccessExpression(node) && isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.EqualsToken && isAliasableExpression(node.parent.right) ||
|
||||
node.kind === SyntaxKind.ShorthandPropertyAssignment ||
|
||||
node.kind === SyntaxKind.PropertyAssignment && isAliasableExpression((node as PropertyAssignment).initializer);
|
||||
isPropertyAccessExpression(node)
|
||||
&& isBinaryExpression(node.parent)
|
||||
&& node.parent.left === node
|
||||
&& node.parent.operatorToken.kind === SyntaxKind.EqualsToken
|
||||
&& isAliasableExpression(node.parent.right));
|
||||
}
|
||||
|
||||
export function getAliasDeclarationFromName(node: EntityName): Declaration | undefined {
|
||||
@@ -3156,6 +3170,7 @@ namespace ts {
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
case SyntaxKind.ExportAssignment:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.NamespaceExport:
|
||||
return node.parent as Declaration;
|
||||
case SyntaxKind.QualifiedName:
|
||||
do {
|
||||
@@ -5142,6 +5157,11 @@ namespace ts {
|
||||
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent as PropertyAccessExpression).name === node);
|
||||
}
|
||||
|
||||
export function isRightSideOfAccessExpression(node: Node) {
|
||||
return isPropertyAccessExpression(node.parent) && node.parent.name === node
|
||||
|| isElementAccessExpression(node.parent) && node.parent.argumentExpression === node;
|
||||
}
|
||||
|
||||
export function isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName(node: Node) {
|
||||
return isQualifiedName(node.parent) && node.parent.right === node
|
||||
|| isPropertyAccessExpression(node.parent) && node.parent.name === node
|
||||
@@ -5829,6 +5849,45 @@ namespace ts {
|
||||
return expr;
|
||||
}
|
||||
|
||||
export function forEachNameInAccessChainWalkingLeft<T>(name: MemberName | StringLiteralLike, action: (name: MemberName | StringLiteralLike) => T | undefined): T | undefined {
|
||||
if (isAccessExpression(name.parent) && isRightSideOfAccessExpression(name)) {
|
||||
return walkAccessExpression(name.parent);
|
||||
}
|
||||
|
||||
function walkAccessExpression(access: AccessExpression): T | undefined {
|
||||
if (access.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
const res = action(access.name);
|
||||
if (res !== undefined) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else if (access.kind === SyntaxKind.ElementAccessExpression) {
|
||||
if (isIdentifier(access.argumentExpression) || isStringLiteralLike(access.argumentExpression)) {
|
||||
const res = action(access.argumentExpression);
|
||||
if (res !== undefined) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Chain interrupted by non-static-name access 'x[expr()].y.z'
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (isAccessExpression(access.expression)) {
|
||||
return walkAccessExpression(access.expression);
|
||||
}
|
||||
if (isIdentifier(access.expression)) {
|
||||
// End of chain at Identifier 'x.y.z'
|
||||
return action(access.expression);
|
||||
}
|
||||
// End of chain at non-Identifier 'x().y.z'
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function getLeftmostExpression(node: Expression, stopAtCallExpressions: boolean) {
|
||||
while (true) {
|
||||
switch (node.kind) {
|
||||
|
||||
+18
-1
@@ -298,7 +298,7 @@ namespace ts.server {
|
||||
getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan {
|
||||
const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
const request = this.processRequest<protocol.DefinitionRequest>(CommandNames.DefinitionAndBoundSpan, args);
|
||||
const request = this.processRequest<protocol.DefinitionAndBoundSpanRequest>(CommandNames.DefinitionAndBoundSpan, args);
|
||||
const response = this.processResponse<protocol.DefinitionInfoAndBoundSpanResponse>(request);
|
||||
const body = Debug.checkDefined(response.body); // TODO: GH#18217
|
||||
|
||||
@@ -332,6 +332,23 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
getSourceDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfo[] {
|
||||
const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position);
|
||||
const request = this.processRequest<protocol.FindSourceDefinitionRequest>(CommandNames.FindSourceDefinition, args);
|
||||
const response = this.processResponse<protocol.DefinitionResponse>(request);
|
||||
const body = Debug.checkDefined(response.body); // TODO: GH#18217
|
||||
|
||||
return body.map(entry => ({
|
||||
containerKind: ScriptElementKind.unknown,
|
||||
containerName: "",
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
kind: ScriptElementKind.unknown,
|
||||
name: "",
|
||||
unverified: entry.unverified,
|
||||
}));
|
||||
}
|
||||
|
||||
getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
|
||||
const args = this.createFileLocationRequestArgs(fileName, position);
|
||||
|
||||
|
||||
@@ -697,6 +697,13 @@ namespace FourSlash {
|
||||
this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinitionAndBoundSpan());
|
||||
}
|
||||
|
||||
public verifyGoToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, end?: ArrayOrSingle<string | { marker: string, unverified?: boolean }> | { file: string, unverified?: boolean }) {
|
||||
if (this.testType !== FourSlashTestType.Server) {
|
||||
this.raiseError("goToSourceDefinition may only be used in fourslash/server tests.");
|
||||
}
|
||||
this.verifyGoToX(startMarkerNames, end, () => (this.languageService as ts.server.SessionClient).getSourceDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition)!);
|
||||
}
|
||||
|
||||
private getGoToDefinition(): readonly ts.DefinitionInfo[] {
|
||||
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;
|
||||
}
|
||||
@@ -764,7 +771,9 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
if (endMarkers.length !== definitions.length) {
|
||||
this.raiseError(`${testName} failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}`);
|
||||
const markers = definitions.map(d => ({ text: "HERE", fileName: d.fileName, position: d.textSpan.start }));
|
||||
const actual = this.renderMarkers(markers);
|
||||
this.raiseError(`${testName} failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}\n\n${actual}`);
|
||||
}
|
||||
|
||||
ts.zipWith(endMarkers, definitions, (endMarkerOrFileResult, definition, i) => {
|
||||
@@ -774,17 +783,8 @@ namespace FourSlash {
|
||||
ts.Debug.assert(typeof expectedFileName === "string");
|
||||
const expectedPosition = marker?.position || 0;
|
||||
if (ts.comparePaths(expectedFileName, definition.fileName, /*ignoreCase*/ true) !== ts.Comparison.EqualTo || expectedPosition !== definition.textSpan.start) {
|
||||
const filesToDisplay = ts.deduplicate([expectedFileName, definition.fileName], ts.equateValues);
|
||||
const markers = [{ text: "EXPECTED", fileName: expectedFileName, position: expectedPosition }, { text: "ACTUAL", fileName: definition.fileName, position: definition.textSpan.start }];
|
||||
const text = filesToDisplay.map(fileName => {
|
||||
const markersToRender = markers.filter(m => m.fileName === fileName).sort((a, b) => b.position - a.position);
|
||||
let fileContent = this.tryGetFileContent(fileName) || "";
|
||||
for (const marker of markersToRender) {
|
||||
fileContent = fileContent.slice(0, marker.position) + `\x1b[1;4m/*${marker.text}*/\x1b[0;31m` + fileContent.slice(marker.position);
|
||||
}
|
||||
return `// @Filename: ${fileName}\n${fileContent}`;
|
||||
}).join("\n\n");
|
||||
|
||||
const text = this.renderMarkers(markers);
|
||||
this.raiseError(`${testName} failed for definition ${markerName || expectedFileName} (${i}): expected ${expectedFileName} at ${expectedPosition}, got ${definition.fileName} at ${definition.textSpan.start}\n\n${text}\n`);
|
||||
}
|
||||
if (definition.unverified && (typeof endMarkerOrFileResult === "string" || !endMarkerOrFileResult.unverified)) {
|
||||
@@ -798,6 +798,18 @@ namespace FourSlash {
|
||||
});
|
||||
}
|
||||
|
||||
private renderMarkers(markers: { text: string, fileName: string, position: number }[]) {
|
||||
const filesToDisplay = ts.deduplicate(markers.map(m => m.fileName), ts.equateValues);
|
||||
return filesToDisplay.map(fileName => {
|
||||
const markersToRender = markers.filter(m => m.fileName === fileName).sort((a, b) => b.position - a.position);
|
||||
let fileContent = this.tryGetFileContent(fileName) || "";
|
||||
for (const marker of markersToRender) {
|
||||
fileContent = fileContent.slice(0, marker.position) + `\x1b[1;4m/*${marker.text}*/\x1b[0;31m` + fileContent.slice(marker.position);
|
||||
}
|
||||
return `// @Filename: ${fileName}\n${fileContent}`;
|
||||
}).join("\n\n");
|
||||
}
|
||||
|
||||
private verifyDefinitionTextSpan(defs: ts.DefinitionInfoAndBoundSpan, startMarkerName: string) {
|
||||
const range = this.testData.ranges.find(range => this.markerName(range.marker!) === startMarkerName);
|
||||
|
||||
|
||||
@@ -324,6 +324,10 @@ namespace FourSlashInterface {
|
||||
this.state.verifyGoToType(arg0, endMarkerName);
|
||||
}
|
||||
|
||||
public goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, end: { file: string } | ArrayOrSingle<string>) {
|
||||
this.state.verifyGoToSourceDefinition(startMarkerNames, end);
|
||||
}
|
||||
|
||||
public goToDefinitionForMarkers(...markerNames: string[]) {
|
||||
this.state.verifyGoToDefinitionForMarkers(markerNames);
|
||||
}
|
||||
|
||||
@@ -715,6 +715,8 @@ namespace ts.server {
|
||||
readonly newInferredProjectName = createProjectNameFactoryWithCounter(makeInferredProjectName);
|
||||
/*@internal*/
|
||||
readonly newAutoImportProviderProjectName = createProjectNameFactoryWithCounter(makeAutoImportProviderProjectName);
|
||||
/*@internal*/
|
||||
readonly newAuxiliaryProjectName = createProjectNameFactoryWithCounter(makeAuxiliaryProjectName);
|
||||
/**
|
||||
* Open files: with value being project root path, and key being Path of the file that is open
|
||||
*/
|
||||
@@ -989,13 +991,15 @@ namespace ts.server {
|
||||
|
||||
private delayUpdateProjectGraph(project: Project) {
|
||||
project.markAsDirty();
|
||||
const projectName = project.getProjectName();
|
||||
this.pendingProjectUpdates.set(projectName, project);
|
||||
this.throttledOperations.schedule(projectName, /*delay*/ 250, () => {
|
||||
if (this.pendingProjectUpdates.delete(projectName)) {
|
||||
updateProjectIfDirty(project);
|
||||
}
|
||||
});
|
||||
if (project.projectKind !== ProjectKind.AutoImportProvider && project.projectKind !== ProjectKind.Auxiliary) {
|
||||
const projectName = project.getProjectName();
|
||||
this.pendingProjectUpdates.set(projectName, project);
|
||||
this.throttledOperations.schedule(projectName, /*delay*/ 250, () => {
|
||||
if (this.pendingProjectUpdates.delete(projectName)) {
|
||||
updateProjectIfDirty(project);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
@@ -2297,7 +2301,7 @@ namespace ts.server {
|
||||
configFileExistenceInfo.config.watchedDirectoriesStale = undefined;
|
||||
}
|
||||
|
||||
private updateNonInferredProjectFiles<T>(project: ExternalProject | ConfiguredProject | AutoImportProviderProject, files: T[], propertyReader: FilePropertyReader<T>) {
|
||||
private updateNonInferredProjectFiles<T>(project: Project, files: T[], propertyReader: FilePropertyReader<T>) {
|
||||
const projectRootFilesMap = project.getRootFilesMap();
|
||||
const newRootScriptInfoMap = new Map<string, true>();
|
||||
|
||||
@@ -3589,7 +3593,7 @@ namespace ts.server {
|
||||
const toRemoveScriptInfos = new Map(this.filenameToScriptInfo);
|
||||
this.filenameToScriptInfo.forEach(info => {
|
||||
// If script info is open or orphan, retain it and its dependencies
|
||||
if (!info.isScriptOpen() && info.isOrphan() && !info.isContainedByAutoImportProvider()) {
|
||||
if (!info.isScriptOpen() && info.isOrphan() && !info.isContainedByBackgroundProject()) {
|
||||
// Otherwise if there is any source info that is alive, this alive too
|
||||
if (!info.sourceMapFilePath) return;
|
||||
let sourceInfos: Set<Path> | undefined;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace ts.server {
|
||||
Configured,
|
||||
External,
|
||||
AutoImportProvider,
|
||||
Auxiliary,
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -209,6 +210,9 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
private packageJsonsForAutoImport: Set<string> | undefined;
|
||||
|
||||
/*@internal*/
|
||||
private noDtsResolutionProject?: AuxiliaryProject | undefined;
|
||||
|
||||
/*@internal*/
|
||||
getResolvedProjectReferenceToRedirect(_fileName: string): ResolvedProjectReference | undefined {
|
||||
return undefined;
|
||||
@@ -811,6 +815,10 @@ namespace ts.server {
|
||||
this.autoImportProviderHost.close();
|
||||
}
|
||||
this.autoImportProviderHost = undefined;
|
||||
if (this.noDtsResolutionProject) {
|
||||
this.noDtsResolutionProject.close();
|
||||
}
|
||||
this.noDtsResolutionProject = undefined;
|
||||
|
||||
// signal language service to release source files acquired from document registry
|
||||
this.languageService.dispose();
|
||||
@@ -1406,6 +1414,7 @@ namespace ts.server {
|
||||
const oldOptions = this.compilerOptions;
|
||||
this.compilerOptions = compilerOptions;
|
||||
this.setInternalCompilerOptionsForEmittingJsFiles();
|
||||
this.noDtsResolutionProject?.setCompilerOptions(this.getCompilerOptionsForNoDtsResolutionProject());
|
||||
if (changesAffectModuleResolution(oldOptions, compilerOptions)) {
|
||||
// reset cached unresolved imports if changes in compiler options affected module resolution
|
||||
this.cachedUnresolvedImportsPerFile.clear();
|
||||
@@ -1780,6 +1789,54 @@ namespace ts.server {
|
||||
getIncompleteCompletionsCache() {
|
||||
return this.projectService.getIncompleteCompletionsCache();
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getNoDtsResolutionProject(rootFileNames: readonly string[]): Project {
|
||||
Debug.assert(this.projectService.serverMode === LanguageServiceMode.Semantic);
|
||||
if (!this.noDtsResolutionProject) {
|
||||
this.noDtsResolutionProject = new AuxiliaryProject(this.projectService, this.documentRegistry, this.getCompilerOptionsForNoDtsResolutionProject());
|
||||
}
|
||||
|
||||
enumerateInsertsAndDeletes<NormalizedPath, NormalizedPath>(
|
||||
rootFileNames.map(toNormalizedPath),
|
||||
this.noDtsResolutionProject.getRootFiles(),
|
||||
getStringComparer(!this.useCaseSensitiveFileNames()),
|
||||
pathToAdd => {
|
||||
const info = this.projectService.getOrCreateScriptInfoNotOpenedByClient(
|
||||
pathToAdd,
|
||||
this.currentDirectory,
|
||||
this.noDtsResolutionProject!.directoryStructureHost);
|
||||
if (info) {
|
||||
this.noDtsResolutionProject!.addRoot(info, pathToAdd);
|
||||
}
|
||||
},
|
||||
pathToRemove => {
|
||||
// It may be preferable to remove roots only once project grows to a certain size?
|
||||
const info = this.noDtsResolutionProject!.getScriptInfo(pathToRemove);
|
||||
if (info) {
|
||||
this.noDtsResolutionProject!.removeRoot(info);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return this.noDtsResolutionProject;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
private getCompilerOptionsForNoDtsResolutionProject() {
|
||||
return {
|
||||
...this.getCompilerOptions(),
|
||||
noDtsResolution: true,
|
||||
allowJs: true,
|
||||
maxNodeModuleJsDepth: 3,
|
||||
diagnostics: false,
|
||||
skipLibCheck: true,
|
||||
sourceMap: false,
|
||||
types: ts.emptyArray,
|
||||
lib: ts.emptyArray,
|
||||
noLib: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getUnresolvedImports(program: Program, cachedUnresolvedImportsPerFile: ESMap<Path, readonly string[]>): SortedReadonlyArray<string> {
|
||||
@@ -1920,6 +1977,32 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
class AuxiliaryProject extends Project {
|
||||
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) {
|
||||
super(projectService.newAuxiliaryProjectName(),
|
||||
ProjectKind.Auxiliary,
|
||||
projectService,
|
||||
documentRegistry,
|
||||
/*hasExplicitListOfFiles*/ false,
|
||||
/*lastFileExceededProgramSize*/ undefined,
|
||||
compilerOptions,
|
||||
/*compileOnSaveEnabled*/ false,
|
||||
/*watchOptions*/ undefined,
|
||||
projectService.host,
|
||||
/*currentDirectory*/ undefined);
|
||||
}
|
||||
|
||||
isOrphan(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
scheduleInvalidateResolutionsOfFailedLookupLocations(): void {
|
||||
// Invalidation will happen on-demand as part of updateGraph
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export class AutoImportProviderProject extends Project {
|
||||
/*@internal*/
|
||||
private static readonly maxDependencies = 10;
|
||||
@@ -2129,6 +2212,12 @@ namespace ts.server {
|
||||
return hasSameSetOfFiles;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
scheduleInvalidateResolutionsOfFailedLookupLocations(): void {
|
||||
// Invalidation will happen on-demand as part of updateGraph
|
||||
return;
|
||||
}
|
||||
|
||||
hasRoots() {
|
||||
return !!this.rootFileNames?.length;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ namespace ts.server.protocol {
|
||||
SignatureHelp = "signatureHelp",
|
||||
/* @internal */
|
||||
SignatureHelpFull = "signatureHelp-full",
|
||||
FindSourceDefinition = "findSourceDefinition",
|
||||
Status = "status",
|
||||
TypeDefinition = "typeDefinition",
|
||||
ProjectInfo = "projectInfo",
|
||||
@@ -904,6 +905,10 @@ namespace ts.server.protocol {
|
||||
readonly command: CommandTypes.DefinitionAndBoundSpan;
|
||||
}
|
||||
|
||||
export interface FindSourceDefinitionRequest extends FileLocationRequest {
|
||||
readonly command: CommandTypes.FindSourceDefinition;
|
||||
}
|
||||
|
||||
export interface DefinitionAndBoundSpanResponse extends Response {
|
||||
readonly body: DefinitionInfoAndBoundSpan;
|
||||
}
|
||||
|
||||
@@ -501,7 +501,7 @@ namespace ts.server {
|
||||
case 0:
|
||||
return Errors.ThrowNoProject();
|
||||
case 1:
|
||||
return ensureNotAutoImportProvider(this.containingProjects[0]);
|
||||
return ensurePrimaryProjectKind(this.containingProjects[0]);
|
||||
default:
|
||||
// If this file belongs to multiple projects, below is the order in which default project is used
|
||||
// - for open script info, its default configured project during opening is default if info is part of it
|
||||
@@ -536,7 +536,7 @@ namespace ts.server {
|
||||
firstInferredProject = project;
|
||||
}
|
||||
}
|
||||
return ensureNotAutoImportProvider(defaultConfiguredProject ||
|
||||
return ensurePrimaryProjectKind(defaultConfiguredProject ||
|
||||
firstNonSourceOfProjectReferenceRedirect ||
|
||||
firstConfiguredProject ||
|
||||
firstExternalProject ||
|
||||
@@ -622,8 +622,10 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
isContainedByAutoImportProvider() {
|
||||
return some(this.containingProjects, p => p.projectKind === ProjectKind.AutoImportProvider);
|
||||
isContainedByBackgroundProject() {
|
||||
return some(
|
||||
this.containingProjects,
|
||||
p => p.projectKind === ProjectKind.AutoImportProvider || p.projectKind === ProjectKind.Auxiliary);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -669,8 +671,13 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureNotAutoImportProvider(project: Project | undefined) {
|
||||
if (!project || project.projectKind === ProjectKind.AutoImportProvider) {
|
||||
/**
|
||||
* Throws an error if `project` is an AutoImportProvider or AuxiliaryProject,
|
||||
* which are used in the background by other Projects and should never be
|
||||
* reported as the default project for a ScriptInfo.
|
||||
*/
|
||||
function ensurePrimaryProjectKind(project: Project | undefined) {
|
||||
if (!project || project.projectKind === ProjectKind.AutoImportProvider || project.projectKind === ProjectKind.Auxiliary) {
|
||||
return Errors.ThrowNoProject();
|
||||
}
|
||||
return project;
|
||||
|
||||
+159
-2
@@ -1193,6 +1193,7 @@ namespace ts.server {
|
||||
containerName: info.containerName,
|
||||
kind: info.kind,
|
||||
name: info.name,
|
||||
failedAliasResolution: info.failedAliasResolution,
|
||||
...info.unverified && { unverified: info.unverified },
|
||||
};
|
||||
});
|
||||
@@ -1228,6 +1229,159 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
private findSourceDefinition(args: protocol.FileLocationRequestArgs): readonly protocol.DefinitionInfo[] {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
const position = this.getPositionInFile(args, file);
|
||||
const unmappedDefinitions = project.getLanguageService().getDefinitionAtPosition(file, position);
|
||||
let definitions: readonly DefinitionInfo[] = this.mapDefinitionInfoLocations(unmappedDefinitions || emptyArray, project).slice();
|
||||
const needsJsResolution = this.projectService.serverMode === LanguageServiceMode.Semantic && (
|
||||
!some(definitions, d => toNormalizedPath(d.fileName) !== file && !d.isAmbient) ||
|
||||
some(definitions, d => !!d.failedAliasResolution));
|
||||
|
||||
if (needsJsResolution) {
|
||||
const definitionSet = createSet<DefinitionInfo>(d => d.textSpan.start, documentSpansEqual);
|
||||
definitions?.forEach(d => definitionSet.add(d));
|
||||
const noDtsProject = project.getNoDtsResolutionProject([file]);
|
||||
const ls = noDtsProject.getLanguageService();
|
||||
const jsDefinitions = ls.getDefinitionAtPosition(file, position, /*searchOtherFilesOnly*/ true)
|
||||
?.filter(d => toNormalizedPath(d.fileName) !== file);
|
||||
if (some(jsDefinitions)) {
|
||||
for (const jsDefinition of jsDefinitions) {
|
||||
if (jsDefinition.unverified) {
|
||||
const refined = tryRefineDefinition(jsDefinition, project.getLanguageService().getProgram()!, ls.getProgram()!);
|
||||
if (some(refined)) {
|
||||
for (const def of refined) {
|
||||
definitionSet.add(def);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
definitionSet.add(jsDefinition);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const ambientCandidates = definitions.filter(d => toNormalizedPath(d.fileName) !== file && d.isAmbient);
|
||||
for (const candidate of some(ambientCandidates) ? ambientCandidates : getAmbientCandidatesByClimbingAccessChain()) {
|
||||
const fileNameToSearch = findImplementationFileFromDtsFileName(candidate.fileName, file, noDtsProject);
|
||||
if (!fileNameToSearch || !ensureRoot(noDtsProject, fileNameToSearch)) {
|
||||
continue;
|
||||
}
|
||||
const noDtsProgram = ls.getProgram()!;
|
||||
const fileToSearch = Debug.checkDefined(noDtsProgram.getSourceFile(fileNameToSearch));
|
||||
for (const match of searchForDeclaration(candidate.name, fileToSearch, noDtsProgram)) {
|
||||
definitionSet.add(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
definitions = arrayFrom(definitionSet.values());
|
||||
}
|
||||
|
||||
definitions = definitions.filter(d => !d.isAmbient && !d.failedAliasResolution);
|
||||
return this.mapDefinitionInfo(definitions, project);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// In 'foo.bar./**/baz', if we got not results on 'baz', see if we can get an ambient definition
|
||||
// for 'bar' or 'foo' (in that order) so we can search for declarations of 'baz' later.
|
||||
function getAmbientCandidatesByClimbingAccessChain(): readonly { name: string, fileName: string }[] {
|
||||
const ls = project.getLanguageService();
|
||||
const program = ls.getProgram()!;
|
||||
const initialNode = getTouchingPropertyName(program.getSourceFile(file)!, position);
|
||||
if ((isStringLiteralLike(initialNode) || isIdentifier(initialNode)) && isAccessExpression(initialNode.parent)) {
|
||||
return forEachNameInAccessChainWalkingLeft(initialNode, nameInChain => {
|
||||
if (nameInChain === initialNode) return undefined;
|
||||
const candidates = ls.getDefinitionAtPosition(file, nameInChain.getStart(), /*searchOtherFilesOnly*/ true)
|
||||
?.filter(d => toNormalizedPath(d.fileName) !== file && d.isAmbient)
|
||||
.map(d => ({
|
||||
fileName: d.fileName,
|
||||
name: getTextOfIdentifierOrLiteral(initialNode)
|
||||
}));
|
||||
if (some(candidates)) {
|
||||
return candidates;
|
||||
}
|
||||
}) || emptyArray;
|
||||
}
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
function tryRefineDefinition(definition: DefinitionInfo, program: Program, noDtsProgram: Program) {
|
||||
const fileToSearch = noDtsProgram.getSourceFile(definition.fileName);
|
||||
if (!fileToSearch) {
|
||||
return undefined;
|
||||
}
|
||||
const initialNode = getTouchingPropertyName(program.getSourceFile(file)!, position);
|
||||
const symbol = program.getTypeChecker().getSymbolAtLocation(initialNode);
|
||||
const importSpecifier = symbol && getDeclarationOfKind<ImportSpecifier>(symbol, SyntaxKind.ImportSpecifier);
|
||||
if (!importSpecifier) return undefined;
|
||||
|
||||
const nameToSearch = importSpecifier.propertyName?.text || importSpecifier.name.text;
|
||||
return searchForDeclaration(nameToSearch, fileToSearch, noDtsProgram);
|
||||
}
|
||||
|
||||
function searchForDeclaration(declarationName: string, fileToSearch: SourceFile, noDtsProgram: Program) {
|
||||
const matches = FindAllReferences.Core.getTopMostDeclarationNamesInFile(declarationName, fileToSearch);
|
||||
return mapDefined(matches, match => {
|
||||
const symbol = noDtsProgram.getTypeChecker().getSymbolAtLocation(match);
|
||||
const decl = getDeclarationFromName(match);
|
||||
if (symbol && decl) {
|
||||
// I think the last argument to this is supposed to be the start node, but it doesn't seem important.
|
||||
// Callers internal to GoToDefinition already get confused about this.
|
||||
return GoToDefinition.createDefinitionInfo(decl, noDtsProgram.getTypeChecker(), symbol, decl, /*unverified*/ true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function ensureRoot(project: Project, fileName: string) {
|
||||
const info = project.getScriptInfo(fileName);
|
||||
if (!info) return false;
|
||||
if (!project.containsScriptInfo(info)) {
|
||||
project.addRoot(info);
|
||||
project.updateGraph();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private getEmitOutput(args: protocol.EmitOutputRequestArgs): EmitOutput | protocol.EmitOutput {
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
if (!project.shouldEmitFile(project.getScriptInfo(file))) {
|
||||
@@ -2751,12 +2905,15 @@ namespace ts.server {
|
||||
[CommandNames.DefinitionFull]: (request: protocol.DefinitionRequest) => {
|
||||
return this.requiredResponse(this.getDefinition(request.arguments, /*simplifiedResult*/ false));
|
||||
},
|
||||
[CommandNames.DefinitionAndBoundSpan]: (request: protocol.DefinitionRequest) => {
|
||||
[CommandNames.DefinitionAndBoundSpan]: (request: protocol.DefinitionAndBoundSpanRequest) => {
|
||||
return this.requiredResponse(this.getDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ true));
|
||||
},
|
||||
[CommandNames.DefinitionAndBoundSpanFull]: (request: protocol.DefinitionRequest) => {
|
||||
[CommandNames.DefinitionAndBoundSpanFull]: (request: protocol.DefinitionAndBoundSpanRequest) => {
|
||||
return this.requiredResponse(this.getDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ false));
|
||||
},
|
||||
[CommandNames.FindSourceDefinition]: (request: protocol.FindSourceDefinitionRequest) => {
|
||||
return this.requiredResponse(this.findSourceDefinition(request.arguments));
|
||||
},
|
||||
[CommandNames.EmitOutput]: (request: protocol.EmitOutputRequest) => {
|
||||
return this.requiredResponse(this.getEmitOutput(request.arguments));
|
||||
},
|
||||
|
||||
@@ -122,6 +122,11 @@ namespace ts.server {
|
||||
return `/dev/null/autoImportProviderProject${counter}*`;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function makeAuxiliaryProjectName(counter: number): string {
|
||||
return `/dev/null/auxiliaryProject${counter}*`;
|
||||
}
|
||||
|
||||
export function createSortedArray<T>(): SortedArray<T> {
|
||||
return [] as any as SortedArray<T>; // TODO: GH#19873
|
||||
}
|
||||
|
||||
@@ -453,7 +453,7 @@ namespace ts.codefix {
|
||||
// and it is up to the user to decide which one fits best.
|
||||
return firstDefined(existingImports, ({ declaration }): FixUseNamespaceImport | undefined => {
|
||||
const namespacePrefix = getNamespaceLikeImportText(declaration);
|
||||
const moduleSpecifier = tryGetModuleSpecifierFromDeclaration(declaration);
|
||||
const moduleSpecifier = tryGetModuleSpecifierFromDeclaration(declaration)?.text;
|
||||
if (namespacePrefix && moduleSpecifier) {
|
||||
const moduleSymbol = getTargetModuleFromNamespaceLikeImport(declaration, checker);
|
||||
if (moduleSymbol && moduleSymbol.exports!.has(escapeLeadingUnderscores(symbolName))) {
|
||||
@@ -670,7 +670,7 @@ namespace ts.codefix {
|
||||
checker: TypeChecker,
|
||||
compilerOptions: CompilerOptions
|
||||
): FixAddNewImport | undefined {
|
||||
const moduleSpecifier = tryGetModuleSpecifierFromDeclaration(declaration);
|
||||
const moduleSpecifier = tryGetModuleSpecifierFromDeclaration(declaration)?.text;
|
||||
if (moduleSpecifier) {
|
||||
const addAsTypeOnly = useRequire
|
||||
? AddAsTypeOnly.NotAllowed
|
||||
|
||||
@@ -1365,6 +1365,31 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
export function getTopMostDeclarationNamesInFile(declarationName: string, sourceFile: SourceFile): readonly Node[] {
|
||||
const candidates = filter(getPossibleSymbolReferenceNodes(sourceFile, declarationName), name => !!getDeclarationFromName(name));
|
||||
return candidates.reduce((topMost, decl) => {
|
||||
const depth = getDepth(decl);
|
||||
if (!some(topMost.declarationNames) || depth === topMost.depth) {
|
||||
topMost.declarationNames.push(decl);
|
||||
topMost.depth = depth;
|
||||
}
|
||||
else if (depth < topMost.depth) {
|
||||
topMost.declarationNames = [decl];
|
||||
topMost.depth = depth;
|
||||
}
|
||||
return topMost;
|
||||
}, { depth: Infinity, declarationNames: [] as Node[] }).declarationNames;
|
||||
|
||||
function getDepth(declaration: Node | undefined) {
|
||||
let depth = 0;
|
||||
while (declaration) {
|
||||
declaration = getContainerNode(declaration);
|
||||
depth++;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
export function someSignatureUsage(
|
||||
signature: SignatureDeclaration,
|
||||
sourceFiles: readonly SourceFile[],
|
||||
|
||||
+100
-44
@@ -1,6 +1,6 @@
|
||||
/* @internal */
|
||||
namespace ts.GoToDefinition {
|
||||
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): readonly DefinitionInfo[] | undefined {
|
||||
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number, searchOtherFilesOnly?: boolean): readonly DefinitionInfo[] | undefined {
|
||||
const resolvedRef = getReferenceAtPosition(sourceFile, position, program);
|
||||
const fileReferenceDefinition = resolvedRef && [getDefinitionInfoForFileReference(resolvedRef.reference.fileName, resolvedRef.fileName, resolvedRef.unverified)] || emptyArray;
|
||||
if (resolvedRef?.file) {
|
||||
@@ -28,18 +28,49 @@ namespace ts.GoToDefinition {
|
||||
|
||||
if (isStaticModifier(node) && isClassStaticBlockDeclaration(node.parent)) {
|
||||
const classDecl = node.parent.parent;
|
||||
const symbol = getSymbol(classDecl, typeChecker);
|
||||
const { symbol, failedAliasResolution } = getSymbol(classDecl, typeChecker);
|
||||
|
||||
const staticBlocks = filter(classDecl.members, isClassStaticBlockDeclaration);
|
||||
const containerName = symbol ? typeChecker.symbolToString(symbol, classDecl) : "";
|
||||
const sourceFile = node.getSourceFile();
|
||||
return map(staticBlocks, staticBlock => {
|
||||
let { pos } = moveRangePastModifiers(staticBlock);
|
||||
pos = skipTrivia(sourceFile.text, pos);
|
||||
return createDefinitionInfoFromName(typeChecker, staticBlock, ScriptElementKind.constructorImplementationElement, "static {}", containerName, { start: pos, length: "static".length });
|
||||
return createDefinitionInfoFromName(typeChecker, staticBlock, ScriptElementKind.constructorImplementationElement, "static {}", containerName, /*unverified*/ false, failedAliasResolution, { start: pos, length: "static".length });
|
||||
});
|
||||
}
|
||||
|
||||
const symbol = getSymbol(node, typeChecker);
|
||||
let { symbol, failedAliasResolution } = getSymbol(node, typeChecker);
|
||||
let fallbackNode = node;
|
||||
|
||||
if (searchOtherFilesOnly && failedAliasResolution) {
|
||||
// We couldn't resolve the specific import, try on the module specifier.
|
||||
const importDeclaration = forEach([node, ...symbol?.declarations || emptyArray], n => findAncestor(n, isAnyImportOrBareOrAccessedRequire));
|
||||
const moduleSpecifier = importDeclaration && tryGetModuleSpecifierFromDeclaration(importDeclaration);
|
||||
if (moduleSpecifier) {
|
||||
({ symbol, failedAliasResolution } = getSymbol(moduleSpecifier, typeChecker));
|
||||
fallbackNode = moduleSpecifier;
|
||||
}
|
||||
}
|
||||
|
||||
if (!symbol && isModuleSpecifierLike(fallbackNode)) {
|
||||
// We couldn't resolve the module specifier as an external module, but it could
|
||||
// be that module resolution succeeded but the target was not a module.
|
||||
const ref = sourceFile.resolvedModules?.get(fallbackNode.text, getModeForUsageLocation(sourceFile, fallbackNode));
|
||||
if (ref) {
|
||||
return [{
|
||||
name: fallbackNode.text,
|
||||
fileName: ref.resolvedFileName,
|
||||
containerName: undefined!,
|
||||
containerKind: undefined!,
|
||||
kind: ScriptElementKind.scriptElement,
|
||||
textSpan: createTextSpan(0, 0),
|
||||
failedAliasResolution,
|
||||
isAmbient: isDeclarationFileName(ref.resolvedFileName),
|
||||
unverified: fallbackNode !== node,
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find a symbol e.g. node is string or number keyword,
|
||||
// or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
|
||||
@@ -47,17 +78,19 @@ namespace ts.GoToDefinition {
|
||||
return concatenate(fileReferenceDefinition, getDefinitionInfoForIndexSignatures(node, typeChecker));
|
||||
}
|
||||
|
||||
if (searchOtherFilesOnly && every(symbol.declarations, d => d.getSourceFile().fileName === sourceFile.fileName)) return undefined;
|
||||
|
||||
const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node);
|
||||
// Don't go to the component constructor definition for a JSX element, just go to the component definition.
|
||||
if (calledDeclaration && !(isJsxOpeningLikeElement(node.parent) && isConstructorLike(calledDeclaration))) {
|
||||
const sigInfo = createDefinitionFromSignatureDeclaration(typeChecker, calledDeclaration);
|
||||
const sigInfo = createDefinitionFromSignatureDeclaration(typeChecker, calledDeclaration, failedAliasResolution);
|
||||
// For a function, if this is the original function definition, return just sigInfo.
|
||||
// If this is the original constructor definition, parent is the class.
|
||||
if (typeChecker.getRootSymbols(symbol).some(s => symbolMatchesSignature(s, calledDeclaration))) {
|
||||
return [sigInfo];
|
||||
}
|
||||
else {
|
||||
const defs = getDefinitionFromSymbol(typeChecker, symbol, node, calledDeclaration) || emptyArray;
|
||||
const defs = getDefinitionFromSymbol(typeChecker, symbol, node, failedAliasResolution, calledDeclaration) || emptyArray;
|
||||
// For a 'super()' call, put the signature first, else put the variable first.
|
||||
return node.kind === SyntaxKind.SuperKeyword ? [sigInfo, ...defs] : [...defs, sigInfo];
|
||||
}
|
||||
@@ -70,7 +103,7 @@ namespace ts.GoToDefinition {
|
||||
// assignment. This case and others are handled by the following code.
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration);
|
||||
const definitions = shorthandSymbol?.declarations ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node)) : emptyArray;
|
||||
const definitions = shorthandSymbol?.declarations ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node, /*unverified*/ false, failedAliasResolution)) : emptyArray;
|
||||
return concatenate(definitions, getDefinitionFromObjectLiteralElement(typeChecker, node) || emptyArray);
|
||||
}
|
||||
|
||||
@@ -95,7 +128,7 @@ namespace ts.GoToDefinition {
|
||||
});
|
||||
}
|
||||
|
||||
return concatenate(fileReferenceDefinition, getDefinitionFromObjectLiteralElement(typeChecker, node) || getDefinitionFromSymbol(typeChecker, symbol, node));
|
||||
return concatenate(fileReferenceDefinition, getDefinitionFromObjectLiteralElement(typeChecker, node) || getDefinitionFromSymbol(typeChecker, symbol, node, failedAliasResolution));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,25 +232,25 @@ namespace ts.GoToDefinition {
|
||||
}
|
||||
|
||||
if (isImportMeta(node.parent) && node.parent.name === node) {
|
||||
return definitionFromType(typeChecker.getTypeAtLocation(node.parent), typeChecker, node.parent);
|
||||
return definitionFromType(typeChecker.getTypeAtLocation(node.parent), typeChecker, node.parent, /*failedAliasResolution*/ false);
|
||||
}
|
||||
|
||||
const symbol = getSymbol(node, typeChecker);
|
||||
const { symbol, failedAliasResolution } = getSymbol(node, typeChecker);
|
||||
if (!symbol) return undefined;
|
||||
|
||||
const typeAtLocation = typeChecker.getTypeOfSymbolAtLocation(symbol, node);
|
||||
const returnType = tryGetReturnTypeOfFunction(symbol, typeAtLocation, typeChecker);
|
||||
const fromReturnType = returnType && definitionFromType(returnType, typeChecker, node);
|
||||
const fromReturnType = returnType && definitionFromType(returnType, typeChecker, node, failedAliasResolution);
|
||||
// If a function returns 'void' or some other type with no definition, just return the function definition.
|
||||
const typeDefinitions = fromReturnType && fromReturnType.length !== 0 ? fromReturnType : definitionFromType(typeAtLocation, typeChecker, node);
|
||||
const typeDefinitions = fromReturnType && fromReturnType.length !== 0 ? fromReturnType : definitionFromType(typeAtLocation, typeChecker, node, failedAliasResolution);
|
||||
return typeDefinitions.length ? typeDefinitions
|
||||
: !(symbol.flags & SymbolFlags.Value) && symbol.flags & SymbolFlags.Type ? getDefinitionFromSymbol(typeChecker, skipAlias(symbol, typeChecker), node)
|
||||
: !(symbol.flags & SymbolFlags.Value) && symbol.flags & SymbolFlags.Type ? getDefinitionFromSymbol(typeChecker, skipAlias(symbol, typeChecker), node, failedAliasResolution)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function definitionFromType(type: Type, checker: TypeChecker, node: Node): readonly DefinitionInfo[] {
|
||||
function definitionFromType(type: Type, checker: TypeChecker, node: Node, failedAliasResolution: boolean | undefined): readonly DefinitionInfo[] {
|
||||
return flatMap(type.isUnion() && !(type.flags & TypeFlags.Enum) ? type.types : [type], t =>
|
||||
t.symbol && getDefinitionFromSymbol(checker, t.symbol, node));
|
||||
t.symbol && getDefinitionFromSymbol(checker, t.symbol, node, failedAliasResolution));
|
||||
}
|
||||
|
||||
function tryGetReturnTypeOfFunction(symbol: Symbol, type: Type, checker: TypeChecker): Type | undefined {
|
||||
@@ -259,19 +292,23 @@ namespace ts.GoToDefinition {
|
||||
return mapDefined(checker.getIndexInfosAtLocation(node), info => info.declaration && createDefinitionFromSignatureDeclaration(checker, info.declaration));
|
||||
}
|
||||
|
||||
function getSymbol(node: Node, checker: TypeChecker): Symbol | undefined {
|
||||
function getSymbol(node: Node, checker: TypeChecker) {
|
||||
const symbol = checker.getSymbolAtLocation(node);
|
||||
// If this is an alias, and the request came at the declaration location
|
||||
// get the aliased symbol instead. This allows for goto def on an import e.g.
|
||||
// import {A, B} from "mod";
|
||||
// to jump to the implementation directly.
|
||||
let failedAliasResolution = false;
|
||||
if (symbol?.declarations && symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) {
|
||||
const aliased = checker.getAliasedSymbol(symbol);
|
||||
if (aliased.declarations) {
|
||||
return aliased;
|
||||
return { symbol: aliased };
|
||||
}
|
||||
else {
|
||||
failedAliasResolution = true;
|
||||
}
|
||||
}
|
||||
return symbol;
|
||||
return { symbol, failedAliasResolution };
|
||||
}
|
||||
|
||||
// Go to the original declaration for cases:
|
||||
@@ -286,28 +323,44 @@ namespace ts.GoToDefinition {
|
||||
if (node.parent === declaration) {
|
||||
return true;
|
||||
}
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.ImportClause:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return true;
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
return declaration.parent.kind === SyntaxKind.NamedImports;
|
||||
case SyntaxKind.BindingElement:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return isInJSFile(declaration) && isVariableDeclarationInitializedToBareOrAccessedRequire(declaration);
|
||||
default:
|
||||
return false;
|
||||
if (declaration.kind === SyntaxKind.NamespaceImport) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node, declarationNode?: Node): DefinitionInfo[] | undefined {
|
||||
// There are cases when you extend a function by adding properties to it afterwards,
|
||||
// we want to strip those extra properties.
|
||||
// For deduping purposes, we also want to exclude any declarationNodes if provided.
|
||||
const filteredDeclarations =
|
||||
filter(symbol.declarations, d => d !== declarationNode && (!isAssignmentDeclaration(d) || d === symbol.valueDeclaration))
|
||||
|| undefined;
|
||||
return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(filteredDeclarations, declaration => createDefinitionInfo(declaration, typeChecker, symbol, node));
|
||||
/**
|
||||
* ```ts
|
||||
* function f() {}
|
||||
* f.foo = 0;
|
||||
* ```
|
||||
*
|
||||
* Here, `f` has two declarations: the function declaration, and the identifier in the next line.
|
||||
* The latter is a declaration for `f` because it gives `f` the `SymbolFlags.Namespace` meaning so
|
||||
* it can contain `foo`. However, that declaration is pretty uninteresting and not intuitively a
|
||||
* "definition" for `f`. Ideally, the question we'd like to answer is "what SymbolFlags does this
|
||||
* declaration contribute to the symbol for `f`?" If the answer is just `Namespace` and the
|
||||
* declaration looks like an assignment, that declaration is in no sense a definition for `f`.
|
||||
* But that information is totally lost during binding and/or symbol merging, so we need to do
|
||||
* our best to reconstruct it or use other heuristics. This function (and the logic around its
|
||||
* calling) covers our tests but feels like a hack, and it would be great if someone could come
|
||||
* up with a more precise definition of what counts as a definition.
|
||||
*/
|
||||
function isExpandoDeclaration(node: Declaration): boolean {
|
||||
if (!isAssignmentDeclaration(node)) return false;
|
||||
const containingAssignment = findAncestor(node, p => {
|
||||
if (isAssignmentExpression(p)) return true;
|
||||
if (!isAssignmentDeclaration(p as Declaration)) return "quit";
|
||||
return false;
|
||||
}) as AssignmentExpression<AssignmentOperatorToken> | undefined;
|
||||
return !!containingAssignment && getAssignmentDeclarationKind(containingAssignment) === AssignmentDeclarationKind.Property;
|
||||
}
|
||||
|
||||
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node, failedAliasResolution?: boolean, excludeDeclaration?: Node): DefinitionInfo[] | undefined {
|
||||
const filteredDeclarations = filter(symbol.declarations, d => d !== excludeDeclaration);
|
||||
const withoutExpandos = filter(filteredDeclarations, d => !isExpandoDeclaration(d));
|
||||
const results = some(withoutExpandos) ? withoutExpandos : filteredDeclarations;
|
||||
return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(results, declaration => createDefinitionInfo(declaration, typeChecker, symbol, node, /*unverified*/ false, failedAliasResolution));
|
||||
|
||||
function getConstructSignatureDefinition(): DefinitionInfo[] | undefined {
|
||||
// Applicable only if we are in a new expression, or we are on a constructor declaration
|
||||
@@ -335,21 +388,21 @@ namespace ts.GoToDefinition {
|
||||
return declarations.length
|
||||
? declarationsWithBody.length !== 0
|
||||
? declarationsWithBody.map(x => createDefinitionInfo(x, typeChecker, symbol, node))
|
||||
: [createDefinitionInfo(last(declarations), typeChecker, symbol, node)]
|
||||
: [createDefinitionInfo(last(declarations), typeChecker, symbol, node, /*unverified*/ false, failedAliasResolution)]
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a DefinitionInfo from a Declaration, using the declaration's name if possible. */
|
||||
function createDefinitionInfo(declaration: Declaration, checker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo {
|
||||
export function createDefinitionInfo(declaration: Declaration, checker: TypeChecker, symbol: Symbol, node: Node, unverified?: 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) : "";
|
||||
return createDefinitionInfoFromName(checker, declaration, symbolKind, symbolName, containerName);
|
||||
return createDefinitionInfoFromName(checker, declaration, symbolKind, symbolName, containerName, unverified, failedAliasResolution);
|
||||
}
|
||||
|
||||
/** Creates a DefinitionInfo directly from the name of a declaration. */
|
||||
function createDefinitionInfoFromName(checker: TypeChecker, declaration: Declaration, symbolKind: ScriptElementKind, symbolName: string, containerName: string, textSpan?: TextSpan): DefinitionInfo {
|
||||
function createDefinitionInfoFromName(checker: TypeChecker, declaration: Declaration, symbolKind: ScriptElementKind, symbolName: string, containerName: string, unverified?: boolean, failedAliasResolution?: boolean, textSpan?: TextSpan): DefinitionInfo {
|
||||
const sourceFile = declaration.getSourceFile();
|
||||
if (!textSpan) {
|
||||
const name = getNameOfDeclaration(declaration) || declaration;
|
||||
@@ -367,7 +420,10 @@ namespace ts.GoToDefinition {
|
||||
sourceFile,
|
||||
FindAllReferences.getContextNode(declaration)
|
||||
),
|
||||
isLocal: !isDefinitionVisible(checker, declaration)
|
||||
isLocal: !isDefinitionVisible(checker, declaration),
|
||||
isAmbient: !!(declaration.flags & NodeFlags.Ambient),
|
||||
unverified,
|
||||
failedAliasResolution,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -402,8 +458,8 @@ namespace ts.GoToDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
function createDefinitionFromSignatureDeclaration(typeChecker: TypeChecker, decl: SignatureDeclaration): DefinitionInfo {
|
||||
return createDefinitionInfo(decl, typeChecker, decl.symbol, decl);
|
||||
function createDefinitionFromSignatureDeclaration(typeChecker: TypeChecker, decl: SignatureDeclaration, failedAliasResolution?: boolean): DefinitionInfo {
|
||||
return createDefinitionInfo(decl, typeChecker, decl.symbol, decl, /*unverified*/ false, failedAliasResolution);
|
||||
}
|
||||
|
||||
export function findReferenceInPosition(refs: readonly FileReference[], pos: number): FileReference | undefined {
|
||||
|
||||
@@ -1768,9 +1768,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
/// Goto definition
|
||||
function getDefinitionAtPosition(fileName: string, position: number): readonly DefinitionInfo[] | undefined {
|
||||
function getDefinitionAtPosition(fileName: string, position: number, searchOtherFilesOnly?: boolean): readonly DefinitionInfo[] | undefined {
|
||||
synchronizeHostData();
|
||||
return GoToDefinition.getDefinitionAtPosition(program, getValidSourceFile(fileName), position);
|
||||
return GoToDefinition.getDefinitionAtPosition(program, getValidSourceFile(fileName), position, searchOtherFilesOnly);
|
||||
}
|
||||
|
||||
function getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined {
|
||||
|
||||
@@ -472,6 +472,9 @@ namespace ts {
|
||||
|
||||
getSmartSelectionRange(fileName: string, position: number): SelectionRange;
|
||||
|
||||
/*@internal*/
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
getDefinitionAtPosition(fileName: string, position: number, searchOtherFilesOnly: boolean): readonly DefinitionInfo[] | undefined;
|
||||
getDefinitionAtPosition(fileName: string, position: number): readonly DefinitionInfo[] | undefined;
|
||||
getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined;
|
||||
getTypeDefinitionAtPosition(fileName: string, position: number): readonly DefinitionInfo[] | undefined;
|
||||
@@ -1032,6 +1035,8 @@ namespace ts {
|
||||
containerName: string;
|
||||
unverified?: boolean;
|
||||
/* @internal */ isLocal?: boolean;
|
||||
/* @internal */ isAmbient?: boolean;
|
||||
/* @internal */ failedAliasResolution?: boolean;
|
||||
}
|
||||
|
||||
export interface DefinitionInfoAndBoundSpan {
|
||||
|
||||
@@ -169,6 +169,7 @@
|
||||
"unittests/tscWatch/watchEnvironment.ts",
|
||||
"unittests/tsserver/applyChangesToOpenFiles.ts",
|
||||
"unittests/tsserver/autoImportProvider.ts",
|
||||
"unittests/tsserver/auxiliaryProject.ts",
|
||||
"unittests/tsserver/cachingFileSystemInformation.ts",
|
||||
"unittests/tsserver/cancellationToken.ts",
|
||||
"unittests/tsserver/compileOnSave.ts",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace ts.projectSystem {
|
||||
const aTs: File = {
|
||||
path: "/a.ts",
|
||||
content: `import { B } from "./b";`
|
||||
};
|
||||
const bDts: File = {
|
||||
path: "/b.d.ts",
|
||||
content: `export declare class B {}`
|
||||
};
|
||||
const bJs: File = {
|
||||
path: "/b.js",
|
||||
content: `export class B {}`
|
||||
};
|
||||
describe("unittests:: tsserver:: auxiliaryProject", () => {
|
||||
it("AuxiliaryProject does not remove scrips from InferredProject", () => {
|
||||
const host = createServerHost([aTs, bDts, bJs]);
|
||||
const session = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
openFilesForSession([aTs], session);
|
||||
|
||||
// Open file is in inferred project
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
const inferredProject = projectService.inferredProjects[0];
|
||||
|
||||
// getNoDtsResolutionProject will create an AuxiliaryProject with a.ts and b.js
|
||||
const auxProject = inferredProject.getNoDtsResolutionProject([aTs.path]);
|
||||
auxProject.updateGraph();
|
||||
|
||||
// b.js ScriptInfo is now available because it's contained by the AuxiliaryProject.
|
||||
// The AuxiliaryProject should never be the default project for anything, so
|
||||
// the ScriptInfo should still report being an orphan, and getting its default
|
||||
// project should throw.
|
||||
const bJsScriptInfo = Debug.checkDefined(projectService.getScriptInfo(bJs.path));
|
||||
assert(bJsScriptInfo.isOrphan());
|
||||
assert(bJsScriptInfo.isContainedByBackgroundProject());
|
||||
assert.deepEqual(bJsScriptInfo.containingProjects, [auxProject]);
|
||||
assert.throws(() => bJsScriptInfo.getDefaultProject());
|
||||
|
||||
// When b.js is opened in the editor, it should be put into an InferredProject
|
||||
// even though it's still contained by the AuxiliaryProject.
|
||||
openFilesForSession([bJs], session);
|
||||
assert(!bJsScriptInfo.isOrphan());
|
||||
assert(bJsScriptInfo.isContainedByBackgroundProject());
|
||||
assert.equal(bJsScriptInfo.getDefaultProject().projectKind, server.ProjectKind.Inferred);
|
||||
});
|
||||
});
|
||||
}
|
||||
+7
-1
@@ -7038,6 +7038,7 @@ declare namespace ts.server.protocol {
|
||||
Rename = "rename",
|
||||
Saveto = "saveto",
|
||||
SignatureHelp = "signatureHelp",
|
||||
FindSourceDefinition = "findSourceDefinition",
|
||||
Status = "status",
|
||||
TypeDefinition = "typeDefinition",
|
||||
ProjectInfo = "projectInfo",
|
||||
@@ -7657,6 +7658,9 @@ declare namespace ts.server.protocol {
|
||||
interface DefinitionAndBoundSpanRequest extends FileLocationRequest {
|
||||
readonly command: CommandTypes.DefinitionAndBoundSpan;
|
||||
}
|
||||
interface FindSourceDefinitionRequest extends FileLocationRequest {
|
||||
readonly command: CommandTypes.FindSourceDefinition;
|
||||
}
|
||||
interface DefinitionAndBoundSpanResponse extends Response {
|
||||
readonly body: DefinitionInfoAndBoundSpan;
|
||||
}
|
||||
@@ -9905,7 +9909,8 @@ declare namespace ts.server {
|
||||
Inferred = 0,
|
||||
Configured = 1,
|
||||
External = 2,
|
||||
AutoImportProvider = 3
|
||||
AutoImportProvider = 3,
|
||||
Auxiliary = 4
|
||||
}
|
||||
function allRootFilesAreJsOrDts(project: Project): boolean;
|
||||
function allFilesAreJsOrDts(project: Project): boolean;
|
||||
@@ -10640,6 +10645,7 @@ declare namespace ts.server {
|
||||
private getDefinition;
|
||||
private mapDefinitionInfoLocations;
|
||||
private getDefinitionAndBoundSpan;
|
||||
private findSourceDefinition;
|
||||
private getEmitOutput;
|
||||
private mapJSDocTagInfo;
|
||||
private mapDisplayParts;
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noDtsResolution": true
|
||||
}
|
||||
}
|
||||
@@ -317,6 +317,9 @@ declare namespace FourSlashInterface {
|
||||
goToDefinition(startsAndEnds: { [startMarkerName: string]: ArrayOrSingle<string> }): void;
|
||||
/** Verifies goToDefinition for each `${markerName}Reference` -> `${markerName}Definition` */
|
||||
goToDefinitionForMarkers(...markerNames: string[]): void;
|
||||
goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, fileResult: { file: string, unverified?: boolean }): void;
|
||||
goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string | { marker: string, unverified?: boolean }>): void;
|
||||
goToSourceDefinition(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string>): void;
|
||||
goToType(startsAndEnds: { [startMarkerName: string]: ArrayOrSingle<string> }): void;
|
||||
goToType(startMarkerNames: ArrayOrSingle<string>, endMarkerNames: ArrayOrSingle<string>): void;
|
||||
verifyGetEmitOutputForCurrentFile(expected: string): void;
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
////f[/*0*/"x"] = 0;
|
||||
////f[[|/*1*/"x"|]] = 1;
|
||||
|
||||
verify.goToDefinition("1", "0");
|
||||
verify.goToDefinition("1", ["0", "1"]);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: node_modules/foo/index.js
|
||||
////{}
|
||||
//// /*index*/{}
|
||||
|
||||
// @Filename: a.ts
|
||||
////import /*foo*/foo from /*fooModule*/"foo";
|
||||
@@ -11,7 +11,7 @@ goTo.file("a.ts");
|
||||
verify.numberOfErrorsInCurrentFile(0);
|
||||
|
||||
goTo.marker("fooModule");
|
||||
verify.goToDefinitionIs([]);
|
||||
verify.goToDefinitionIs(["index"]);
|
||||
verify.quickInfoIs("");
|
||||
|
||||
goTo.marker("foo");
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/// <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
|
||||
//// export = _;
|
||||
//// export as namespace _;
|
||||
//// declare const _: _.LoDashStatic;
|
||||
//// declare namespace _ {
|
||||
//// interface LoDashStatic {}
|
||||
//// }
|
||||
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { [|/*start*/add|] } from 'lodash';
|
||||
|
||||
verify.goToSourceDefinition("start", [
|
||||
{ marker: "variable", unverified: true },
|
||||
{ marker: "property", unverified: true },
|
||||
]);
|
||||
@@ -0,0 +1,15 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @moduleResolution: node
|
||||
|
||||
// @Filename: /a.js
|
||||
//// export const a = { /*end*/a: 'a' };
|
||||
|
||||
// @Filename: /a.d.ts
|
||||
//// export declare const a: { a: string };
|
||||
|
||||
// @Filename: /b.ts
|
||||
//// import { a } from './a';
|
||||
//// a.[|a/*start*/|]
|
||||
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
@@ -0,0 +1,43 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @moduleResolution: node
|
||||
|
||||
// Actual yargs doesn't work like this because the implementation package's
|
||||
// main entry exports a small function wrapper function whose return value
|
||||
// is derived from something imported from another file where all the
|
||||
// important code lives, and it's not quite clear on what grounds we can
|
||||
// make the jump from the main entry to that other file. But this test
|
||||
// demonstrates the need to do some filename-based jumping: regular go-to-def
|
||||
// on `yargs` goes to the type definition `Yargs`, but a JS-only go-to-def
|
||||
// simply stops on the callback parameter. So we have to start at the type
|
||||
// definition and say "well, maybe I can find a JS counterpart to this .d.ts
|
||||
// and just look for declarations called 'positional' in there."
|
||||
|
||||
// @Filename: /node_modules/@types/yargs/package.json
|
||||
//// {
|
||||
//// "name": "@types/yargs",
|
||||
//// "version": "1.0.0",
|
||||
//// "types": "./index.d.ts"
|
||||
//// }
|
||||
|
||||
// @Filename: /node_modules/@types/yargs/index.d.ts
|
||||
//// export interface Yargs { positional(): Yargs; }
|
||||
//// export declare function command(command: string, cb: (yargs: Yargs) => void): void;
|
||||
|
||||
// @Filename: /node_modules/yargs/package.json
|
||||
//// {
|
||||
//// "name": "yargs",
|
||||
//// "version": "1.0.0",
|
||||
//// "main": "index.js"
|
||||
//// }
|
||||
|
||||
// @Filename: /node_modules/yargs/index.js
|
||||
//// export function command(cmd, cb) { cb({ /*end*/positional: "This is obviously not even close to realistic" }); }
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { command } from "yargs";
|
||||
//// command("foo", yargs => {
|
||||
//// yargs.[|/*start*/positional|]();
|
||||
//// });
|
||||
|
||||
verify.goToSourceDefinition("start", { marker: "end", unverified: true });
|
||||
@@ -0,0 +1,14 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @Filename: /a.js
|
||||
//// export const /*end*/a = "a";
|
||||
|
||||
// @Filename: /a.d.ts
|
||||
//// export declare const a: string;
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { a } from [|"./a"/*moduleSpecifier*/|];
|
||||
//// [|a/*identifier*/|]
|
||||
|
||||
verify.goToSourceDefinition("identifier", "end");
|
||||
verify.goToSourceDefinition("moduleSpecifier", { file: "/a.js" })
|
||||
@@ -0,0 +1,18 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @moduleResolution: node
|
||||
|
||||
// @Filename: /node_modules/foo/package.json
|
||||
//// { "name": "foo", "version": "1.0.0", "main": "./lib/main.js", "types": "./types/main.d.ts" }
|
||||
|
||||
// @Filename: /node_modules/foo/lib/main.js
|
||||
//// export const /*end*/a = "a";
|
||||
|
||||
// @Filename: /node_modules/foo/types/main.d.ts
|
||||
//// export declare const a: string;
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { a } from "foo";
|
||||
//// [|a/*start*/|]
|
||||
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
@@ -0,0 +1,21 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @moduleResolution: node
|
||||
|
||||
// @Filename: /node_modules/foo/package.json
|
||||
//// { "name": "foo", "version": "1.0.0", "main": "./lib/main.js" }
|
||||
|
||||
// @Filename: /node_modules/foo/lib/main.js
|
||||
//// export const /*end*/a = "a";
|
||||
|
||||
// @Filename: /node_modules/@types/foo/package.json
|
||||
//// { "name": "@types/foo", "version": "1.0.0", "types": "./index.d.ts" }
|
||||
|
||||
// @Filename: /node_modules/@types/foo/index.d.ts
|
||||
//// export declare const a: string;
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { a } from "foo";
|
||||
//// [|a/*start*/|]
|
||||
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
@@ -0,0 +1,17 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @Filename: /a.ts
|
||||
//// export const /*end*/a = 'a';
|
||||
|
||||
// @Filename: /a.d.ts
|
||||
//// export declare const a: string;
|
||||
|
||||
// @Filename: /a.js
|
||||
//// export const a = 'a';
|
||||
|
||||
// @Filename: /b.ts
|
||||
//// import { a } from './a';
|
||||
//// [|a/*start*/|]
|
||||
|
||||
verify.goToDefinition("start", "end");
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
@@ -0,0 +1,24 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @Filename: /node_modules/foo/package.json
|
||||
//// { "name": "foo", "version": "1.2.3", "typesVersions": { "*": { "*": ["./types/*"] } } }
|
||||
|
||||
// @Filename: /node_modules/foo/src/a.ts
|
||||
//// export const /*end*/a = 'a';
|
||||
|
||||
// @Filename: /node_modules/foo/types/a.d.ts
|
||||
//// export declare const a: string;
|
||||
//// //# sourceMappingURL=a.d.ts.map
|
||||
|
||||
// @Filename: /node_modules/foo/types/a.d.ts.map
|
||||
//// {"version":3,"file":"a.d.ts","sourceRoot":"","sources":["../src/a.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,OAAO,CAAC;;AACvB,wBAAsB"}
|
||||
|
||||
// @Filename: /node_modules/foo/dist/a.js
|
||||
//// export const a = 'a';
|
||||
|
||||
// @Filename: /b.ts
|
||||
//// import { a } from 'foo/a';
|
||||
//// [|a/*start*/|]
|
||||
|
||||
verify.goToDefinition("start", "end");
|
||||
verify.goToSourceDefinition("start", "end");
|
||||
@@ -0,0 +1,33 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @moduleResolution: node
|
||||
|
||||
// @Filename: /node_modules/react/package.json
|
||||
//// { "name": "react", "version": "16.8.6", "main": "index.js" }
|
||||
|
||||
// @Filename: /node_modules/react/index.js
|
||||
//// 'use strict';
|
||||
////
|
||||
//// if (process.env.NODE_ENV === 'production') {
|
||||
//// module.exports = require('./cjs/react.production.min.js');
|
||||
//// } else {
|
||||
//// module.exports = require('./cjs/react.development.js');
|
||||
//// }
|
||||
|
||||
// @Filename: /node_modules/react/cjs/react.production.min.js
|
||||
//// 'use strict';exports./*production*/useState=function(a){};exports.version='16.8.6';
|
||||
|
||||
// @Filename: /node_modules/react/cjs/react.development.js
|
||||
//// 'use strict';
|
||||
//// if (process.env.NODE_ENV !== 'production') {
|
||||
//// (function() {
|
||||
//// function useState(initialState) {}
|
||||
//// exports./*development*/useState = useState;
|
||||
//// exports.version = '16.8.6';
|
||||
//// }());
|
||||
//// }
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// import { [|/*start*/useState|] } from 'react';
|
||||
|
||||
verify.goToSourceDefinition("start", ["production", "development"]);
|
||||
@@ -0,0 +1,80 @@
|
||||
/// <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", [
|
||||
{ marker: "variable", unverified: true },
|
||||
{ marker: "property", unverified: true },
|
||||
]);
|
||||
@@ -0,0 +1,81 @@
|
||||
/// <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 [|/*defaultImport*/_|], { [|/*unresolvableNamedImport*/foo|] } from [|/*moduleSpecifier*/'lodash'|];
|
||||
//// _.[|/*propertyAccess*/add|]
|
||||
|
||||
verify.goToSourceDefinition("defaultImport", { file: "/node_modules/lodash/lodash.js", unverified: true });
|
||||
verify.goToSourceDefinition("unresolvableNamedImport", { file: "/node_modules/lodash/lodash.js", unverified: true });
|
||||
verify.goToSourceDefinition("moduleSpecifier", { file: "/node_modules/lodash/lodash.js", unverified: true });
|
||||
verify.goToSourceDefinition("propertyAccess", [{ marker: "variable", unverified: true }, { marker: "property", unverified: true }])
|
||||
Reference in New Issue
Block a user