diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index d4cccf374ec7d..952266d5ebcce 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -890,6 +890,10 @@ namespace ts.FindAllReferences { node.kind === SyntaxKind.ReadonlyKeyword ? isReadonlyTypeOperator : undefined); } + if (isImportMeta(node.parent) && node.parent.name === node) { + return getAllReferencesForImportMeta(sourceFiles, cancellationToken); + } + if (isStaticModifier(node) && isClassStaticBlockDeclaration(node.parent)) { return [{ definition: { type: DefinitionKind.Keyword, node }, references: [nodeEntry(node)] }]; } @@ -1435,6 +1439,19 @@ namespace ts.FindAllReferences { } } + function getAllReferencesForImportMeta(sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken): SymbolAndEntries[] | undefined { + const references = flatMap(sourceFiles, sourceFile => { + cancellationToken.throwIfCancellationRequested(); + return mapDefined(getPossibleSymbolReferenceNodes(sourceFile, "meta", sourceFile), node => { + const parent = node.parent; + if (isImportMeta(parent)) { + return nodeEntry(parent); + } + }); + }); + return references.length ? [{ definition: { type: DefinitionKind.Keyword, node: references[0].node }, references }] : undefined; + } + function getAllReferencesForKeyword(sourceFiles: readonly SourceFile[], keywordKind: SyntaxKind, cancellationToken: CancellationToken, filter?: (node: Node) => boolean): SymbolAndEntries[] | undefined { const references = flatMap(sourceFiles, sourceFile => { cancellationToken.throwIfCancellationRequested(); diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 2464b6d967232..3b49ec4b64610 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -198,6 +198,10 @@ namespace ts.GoToDefinition { return undefined; } + if (isImportMeta(node.parent) && node.parent.name === node) { + return definitionFromType(typeChecker.getTypeAtLocation(node.parent), typeChecker, node.parent); + } + const symbol = getSymbol(node, typeChecker); if (!symbol) return undefined; diff --git a/src/services/services.ts b/src/services/services.ts index fa56017da5e16..6d144565dcc57 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1724,6 +1724,9 @@ namespace ts { if (isNamedTupleMember(node.parent) && node.pos === node.parent.pos) { return node.parent; } + if (isImportMeta(node.parent) && node.parent.name === node) { + return node.parent; + } return node; } @@ -1740,6 +1743,8 @@ namespace ts { case SyntaxKind.SuperKeyword: case SyntaxKind.NamedTupleMember: return true; + case SyntaxKind.MetaProperty: + return isImportMeta(node); default: return false; } diff --git a/tests/baselines/reference/findAllRefsImportMeta.baseline.jsonc b/tests/baselines/reference/findAllRefsImportMeta.baseline.jsonc new file mode 100644 index 0000000000000..3d52240d3a867 --- /dev/null +++ b/tests/baselines/reference/findAllRefsImportMeta.baseline.jsonc @@ -0,0 +1,61 @@ +// === /tests/cases/fourslash/foo.ts === +// /// +// /// +// import./*FIND ALL REFS*/[|meta|]; +// import.[|meta|]; + +// === /tests/cases/fourslash/baz.ts === +// /// +// /// +// let x = import +// . // hai :) +// [|meta|]; + +[ + { + "definition": { + "containerKind": "", + "containerName": "", + "fileName": "/tests/cases/fourslash/foo.ts", + "kind": "keyword", + "textSpan": { + "start": 82, + "length": 4 + }, + "displayParts": [ + { + "kind": "keyword" + } + ] + }, + "references": [ + { + "textSpan": { + "start": 82, + "length": 4 + }, + "fileName": "/tests/cases/fourslash/foo.ts", + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 95, + "length": 4 + }, + "fileName": "/tests/cases/fourslash/foo.ts", + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 109, + "length": 4 + }, + "fileName": "/tests/cases/fourslash/baz.ts", + "isWriteAccess": false, + "isDefinition": false + } + ] + } +] \ No newline at end of file diff --git a/tests/baselines/reference/quickInfoImportMeta.baseline b/tests/baselines/reference/quickInfoImportMeta.baseline new file mode 100644 index 0000000000000..e576927262fb2 --- /dev/null +++ b/tests/baselines/reference/quickInfoImportMeta.baseline @@ -0,0 +1,41 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/foo.ts", + "position": 77, + "name": "1" + }, + "quickInfo": { + "kind": "", + "kindModifiers": "", + "textSpan": { + "start": 75, + "length": 6 + }, + "displayParts": [], + "documentation": [] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/foo.ts", + "position": 84, + "name": "2" + }, + "quickInfo": { + "kind": "interface", + "kindModifiers": "declare", + "textSpan": { + "start": 75, + "length": 11 + }, + "displayParts": [], + "documentation": [ + { + "text": "The type of `import.meta`.\n\nIf you need to declare that a given property exists on `import.meta`,\nthis type may be augmented via interface merging.", + "kind": "text" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/findAllRefsImportMeta.ts b/tests/cases/fourslash/findAllRefsImportMeta.ts new file mode 100644 index 0000000000000..1040f9542e4a9 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsImportMeta.ts @@ -0,0 +1,26 @@ +// @noLib: true + +/// + +// @module: esnext +// @Filename: foo.ts +/////// +/////// +////import./**/meta; +////import.[|meta|]; + +//@Filename: bar.d.ts +////interface ImportMeta { +////} + +// @Filename: baz.ts +/////// +/////// +////let x = import +//// . // hai :) +//// meta; + +verify.baselineFindAllReferences(""); + +goTo.rangeStart(test.ranges()[0]); +verify.renameInfoFailed(); diff --git a/tests/cases/fourslash/goToDefinitionImportMeta.ts b/tests/cases/fourslash/goToDefinitionImportMeta.ts new file mode 100644 index 0000000000000..4594fef1b6766 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionImportMeta.ts @@ -0,0 +1,13 @@ +/// + +// @module: esnext +// @Filename: foo.ts +/////// +/////// +////import.me/*reference*/ta; + +//@Filename: bar.d.ts +////interface ImportMeta { +////} + +verify.goToDefinition("reference", []); diff --git a/tests/cases/fourslash/goToTypeDefinitionImportMeta.ts b/tests/cases/fourslash/goToTypeDefinitionImportMeta.ts new file mode 100644 index 0000000000000..a7d1d72495e34 --- /dev/null +++ b/tests/cases/fourslash/goToTypeDefinitionImportMeta.ts @@ -0,0 +1,13 @@ +/// + +// @module: esnext +// @Filename: foo.ts +/////// +/////// +////import.me/*reference*/ta; + +//@Filename: bar.d.ts +////interface /*definition*/ImportMeta { +////} + +verify.goToType("reference", "definition"); diff --git a/tests/cases/fourslash/quickInfoImportMeta.ts b/tests/cases/fourslash/quickInfoImportMeta.ts new file mode 100644 index 0000000000000..928266b860f34 --- /dev/null +++ b/tests/cases/fourslash/quickInfoImportMeta.ts @@ -0,0 +1,19 @@ +/// + +// @module: esnext +// @Filename: foo.ts +/////// +/////// +////im/*1*/port.me/*2*/ta; + +//@Filename: bar.d.ts +/////** +//// * The type of `import.meta`. +//// * +//// * If you need to declare that a given property exists on `import.meta`, +//// * this type may be augmented via interface merging. +//// */ +//// interface ImportMeta { +////} + +verify.baselineQuickInfo()